System dedykowany
Bezpieczne Bielsko-Biała
Mapa, która dla wybranego miejsca w Bielsku-Białej pokazuje odległość drogą do jednostek straży pożarnej, schronów i punktów akcji jodowej. Trasy liczą się w przeglądarce na stronie statycznej bez własnego serwera, a wynik zmierzono względem OSRM według jawnego kryterium zgodności.

Mapa "Bezpieczne Bielsko-Biała" odpowiada, jak daleko drogą jest z wybranego miejsca w mieście do jednostek straży pożarnej, schronów i punktów akcji jodowej, i robi to bez własnego serwera. Trasy liczą się w przeglądarce, wynik zmierzono względem OSRM według jawnego kryterium akceptacji, a dane da się odbudować z kopii publicznych źródeł. Po pierwszej wizycie mapa działa także bez internetu. Pierwsza wersja powstała w 2021 roku. W 2026 napisano ją od nowa.
Odległość drogą, nie w linii prostej
Miejsce wskazuje się kliknięciem w mapę, wyszukaniem adresu albo lokalizacją urządzenia. Zakładka Straż pożarna sortuje jednostki JRG, WSP i OSP po odległości samochodem, a znacznik "rejon" pokazuje OSP, w której rejonie leży punkt. Schrony i punkty akcji jodowej liczą się domyślnie pieszo, z czasem marszu, a przełącznik zmienia tryb na samochód.
Bliskie punkty w jednej grupie
Na zakładkach Schrony i Akcja jodowa bliskie znaczniki łączą się w grupę z liczbą miejsc. Jednostek straży mapa nie grupuje. Stuknięcie w grupę przybliża mapę najdalej do zoomu 19 i dopasowuje widok do części mapy wolnej od paneli, a gdy dopasowanie nie przybliżyłoby mapy choć o jeden poziom, przybliża ją o dwa poziomy na środek grupy. Grupa, która trzyma się razem także na zoomie 19, otwiera jedną kartę ze wszystkimi swoimi miejscami. Punkty o współrzędnych równych do szóstego miejsca po przecinku liczą się jako jeden adres, więc grupa złożona tylko z nich od razu otwiera kartę.
Znacznik grupy rośnie z liczbą miejsc tylko do stałej, niewielkiej średnicy, żeby jak najmniej zasłaniał mapę. Zaznaczanie tekstu jest wyłączone w całej aplikacji poza kartą miejsca i polem wyszukiwania, na telefonie i na komputerze.
Routing bez serwera routingu
Mapa jest stroną statyczną, bez serwera i bazy, więc zadziała na dowolnym hostingu plików. Zamiast pytać o trasę, przeglądarka liczy ją sama. Interfejs napisano w TypeScripcie z Vite i OpenLayers.
Graf drogowy generuje osobny skrypt z zapisanego stanu OpenStreetMap, a build aplikacji tylko dołącza go jako zwarty plik binarny. Przeglądarka wczytuje graf dopiero wtedy, gdy potrzebuje go zakładka lub tryb trasy. Jedno przejście odwróconego algorytmu Dijkstry z wybranego punktu daje trasy z każdego miejsca w zbiorze naraz.
Profil samochodowy odtwarza OSRM 5.27.1 nawet tam, gdzie nowsza gałąź robi inaczej: szlabany gate i lift_gate przechodzą bez kary, a gałąź master doliczyłaby 60 s. Profil pieszy przenosi profil foot z tej samej wersji, z prędkością 5 km/h po drogach, chodnikach, ścieżkach i schodach.
Pomiar z jawnym kryterium akceptacji
Kryterium akceptacji ma trzy warunki. Najbliższa jednostka ma się zgadzać z OSRM w co najmniej 99% punktów, p90 różnicy odległości ma wynosić najwyżej 1%, a każda zmiana najbliższej jednostki na grafie aktualnym ma mieć opisaną przyczynę. Pomiar z 2 października 2026 objął 300 punktów, rozłożonych równo po dziewięciu rejonach OSP przy stałym ziarnie losowania. Dla każdego punktu porównano 12 tras z remiz.
Serwer OSRM ma starsze dane OSM niż aktualny graf, dlatego powstały dwa porównania. Pierwsze używa grafu aktualnego, drugie grafu zbudowanego tym samym kodem ze stanu OSM z 1 kwietnia 2021, który mierzy sam router i profil bez wpływu wieku danych. Dwa pierwsze warunki dotyczą tego drugiego. Mediana różnicy odległości wynosi na nim 0,1%, a p90 0,2%, więc ten warunek jest spełniony. Najbliższa jednostka zgadza się jednak w 295 z 300 punktów, czyli w 98,3%. To poniżej 99%. Trzy z pięciu niezgodności to punkty leśne, do których OSRM prowadzi objazdem poza prostokąt otaczający graf. Bez nich zgodność wynosi 99,3%. Zasięg grafu czeka na decyzję, a po niej pomiar trzeba powtórzyć.
Na grafie aktualnym najbliższa jednostka zmienia się w 12 punktach. Dla 10 z nich przyczyna jest ustalona: zmiany w OSM od 2021 roku (m.in. nowe odcinki S1), te same punkty leśne i jeden niemal remis. O dwóch pozostałych wiadomo tylko, że znikają na grafie z 2021 roku, więc trzeci warunek też nie jest jeszcze spełniony.
Dla pieszych żaden publiczny serwer nie uruchamia profilu OSRM 5.27.1, więc referencją jest serwer z własnym profilem (4,5 km/h, preferencja dla chodników). Z 1200 par punkt-schron mediana różnicy odległości wynosi 1,6%, najbliższy schron różni się w 3 z 120 punktów, a 11-procentowa różnica czasu odpowiada stosunkowi prędkości 5 do 4,5 km/h.

Dane, które da się odtworzyć
Lista jednostek to własne dane projektu, a rejony OSP złożono z obrębów ewidencyjnych. Resztę skrypt odtwarza z publicznych źródeł: rejestru schronów Komendy Głównej PSP z dane.gov.pl (CC BY 4.0), wykazu punktów akcji jodowej z urzędu miasta, punktów adresowych PRG i obrębów z ULDK, obu z GUGiK, oraz OpenStreetMap (ODbL, któremu podlegają też oba grafy i podkład offline). Każdy przebieg zapisuje kopię pliku i wpis w manifeście z adresem, datami, rozmiarem i sumą SHA-256. Gdy źródło zniknie, indeks ulic, obręby i oba grafy odbudują się z kopii bez sieci, a punkty akcji jodowej i nazwy ulic przy schronach potrzebują jeszcze geokodera GUGiK.
Co miesiąc automatyczny przebieg pobiera od nowa schrony, punkty jodowe, punkty adresowe, obręby i oba grafy dróg, a podkład mapy tylko raz na kwartał, bo każda jego wersja to około 18 MB w historii repozytorium i w pamięci telefonu. Zbiór, w którym zmieniły się same daty pobrania, wraca do poprzedniej wersji, a grafy porównuje się z pominięciem daty stanu OSM, więc data danych w aplikacji oznacza ostatnią rzeczywistą zmianę, a nie ostatnie sprawdzenie źródła. Zbiór, który straci więcej niż 20% punktów, ulic albo obrębów, też zostaje w poprzedniej wersji, bo taki spadek częściej oznacza okrojony plik źródła niż prawdziwą zmianę w mieście.
Stan mapy w adresie
Adres niesie stan mapy: zakładkę, punkt, otwartą kartę jednostki albo punktu z bazy, tryb trasy, warstwy podziału (rejony i obręby) oraz środek i zoom. Wartości domyślne zostają poza nim, z wyjątkiem zakładki. Przełączenie zakładki dodaje wpis do historii, każda inna zmiana podmienia bieżący, więc Wstecz wraca do poprzedniej zakładki. Link skopiowany z paska adresu otwiera tę samą zakładkę, to samo miejsce i tę samą kartę z bazy.
Gdy lista jest ułożona od wskazanego miejsca, w jej nagłówku pojawia się przycisk udostępniania. Daje tekst z pięcioma najbliższymi miejscami, odległością i czasem marszu, a pod nimi link do mapy, który w odróżnieniu od adresu z paska niesie tylko zakładkę, punkt i tryb trasy, bez karty, warstw i widoku. Na ekranie dotykowym przycisk otwiera systemowe udostępnianie, na komputerze kopiuje tekst do schowka. Link jest ostatnią linią tekstu, a nie osobnym polem, bo w teście na iPhonie Wiadomości pokazały wtedy samą kartę strony i pominęły listę.

Trasy, wyszukiwanie i dane działają w przeglądarce, bez ciasteczek. Współrzędne wskazanego miejsca trafiają do usług GUGiK, które ustalają adres, a kafelki mapy, gdy jest sieć, pochodzą z serwerów OpenStreetMap. Punkt jest też w adresie strony, więc widzi go odbiorca linku, a sam adres zostaje w historii przeglądarki i w dzienniku serwera, który serwuje mapę. Tekst z przycisku udostępniania zawiera adres albo współrzędne wskazanego miejsca i trafia tylko tam, gdzie użytkownik go wyśle albo wklei.
Mapa bez internetu
Aplikacja instaluje się jak PWA. Przy pierwszej wizycie service worker pobiera w tle wszystkie pliki z builda, razem ze zbiorami danych, oboma grafami dróg i podkładem mapy: około 30 MB, a z kompresją brotli około 20 MB do pobrania. Instalacja udaje się w całości albo wcale, więc przerwane pobieranie zaczyna się od nowa przy następnym otwarciu strony. Potem mapa, wyszukiwarka i trasy działają bez sieci, także pod linkiem z parametrami. Safari na iOS kasuje jednak dane strony nieotwieranej przez 7 dni, chyba że dodano ją do ekranu początkowego.
Z internetem mapa pokazuje kafelki OpenStreetMap. Na podkład z pliku basemap.pmtiles przechodzi, gdy przeglądarka zgłasza brak sieci, gdy kafelek się nie wczyta albo gdy żaden oczekujący kafelek nie odpowie przez 3 sekundy. Plik to wycinek dziennego buildu Protomaps dla obszaru mapy, z którego narzędzie usuwa rodzaje obiektów pomijane przez styl, co zmniejsza go o około jedną czwartą bez zmiany wyglądu mapy. Przeglądarka czyta podkład fragmentami, a bez sieci service worker wycina te fragmenty z kopii zapisanej w przeglądarce. Bez sieci adres, obręb i gminę wskazanego miejsca podają punkty adresowe i obręby zapisane w przeglądarce.
Nowa wersja po wdrożeniu nie przeładowuje otwartej strony. Service worker pobiera ją w tle, a pasek u dołu ekranu proponuje jej wczytanie.
Testy i bramki w CI
Testy jednostkowe na node:test obejmują router, wyszukiwanie, adresy i dane. Testy Playwright biegną w Chromium na osobnym buildzie: kafelki dostają pusty obraz, usługi GUGiK nagrane odpowiedzi, a każde inne żądanie wychodzące kończy test porażką. Osobny test odcina sieć albo serwer kafelków w trakcie pierwszej wizyty i po przeładowaniu, a potem sprawdza podkład, wyszukiwarkę i trasy pieszo. GitHub Actions uruchamia na każdym pull requeście lint, kontrolę typów, testy, build i testy w przeglądarce.
Co z tego wynika
Pytanie, które zwykle wymaga serwera routingu, dostaje odpowiedź z plików, więc hosting jest przenośny, a po buildzie nie ma serwera do utrzymania. Podział, w którym dane przygotowują skrypty, a liczy przeglądarka, projektujemy przy oprogramowaniu na zamówienie.
Zaufanie do wyniku bierze się z pomiaru z jawnym kryterium, opisanego także tam, gdzie wynik go nie spełnia, oraz z danych, które mają źródło, datę i kopię. Od tych pytań zaczynamy prace przy architekturze IT i modernizacji systemu już działającego.
Wykorzystane technologie
- OpenStreetMap
- OpenLayers
- TypeScript
- Vite
- Playwright
- GitHub Actions