Subskrybuj newsletter o cyfrowej humanistyce i innowacjach w sektorze kultury

Rekonstruujemy w Leaflet linie kolejowe na gdańskiej Wyspie Spichrzów

Okładka lekcji: kot śpiący na wagonie towarowym stojącym na bocznicy / Źródło: AI

Wprowadzenie

Budowaniem map cyfrowych z wykorzystaniem biblioteki Leaflet poświęciliśmy już kilka lekcji:

  • w pierwszej poznaliśmy podstawy GIS, nauczyliśmy się dodawać mapę do strony internetowej oraz definiować punkty i interakcje z nimi,
  • w drugiej nauczyliśmy się robić to samo za pomocą języka R i dostępnych dla niego bibliotek,
  • w trzeciej kontynuowaliśmy pracę w R z biblioteką Leaflet, korzystaliśmy z narzędzi przekształcających adresy na koordynaty geograficzne i grupowaliśmy punkty w klastry.

W ramach tych lekcji pracowaliśmy z mapą współczesną. W ostatniej lekcji skorzystaliśmy z alternatywnej wobec standardowej warstwy kolorystycznej, ale warstwa geograficzna - układ ulic czy obecność i rozmieszczenie budynków wciąż była aktualna. Tymczasem Leaflet byłby bezużyteczny w wielu zastosowaniach w sektorze kultury czy projektach naukowych i edukacyjnych, gdyby pozwalał na pracę wyłącznie z mapami współczesnymi.

Cele lekcji

Niniejsza lekcja będzie wprowadzeniem do pracy z mapami historycznymi w Leaflet. Skorzystamy z serwisu CodePen, który znamy już z pierwszej lekcji na temat map cyfrowych. Dzięki zasobom witryny Map Warper wyświetlimy na własnej stronie mapę historyczną i dodamy do niej elementy dokumentujące nieistniejące już obiekty - zabytki techniki i przemysłu. Na cyfrowej wersji mapy Gdańska z 1920 roku zrekonstruujemy w Leaflet układ torów kolejowych na Wyspie Spichrzów.

Efekty

Efektem naszej pracy będzie strona internetowa zawierającą mapę historyczną Gdańska z odzwierciedlonym układem torów na Wyspie Spichrzów (Speicherinsel). Nauczymy się przy tym dodawać polilinie oraz programistycznie przełączać warstwy mapy między warstwą współczesną i historyczną, co pozwoli nam zobaczyć historyczny układ linii kolejowej także we współczesnym kontekście.

Wymagania

Do skorzystanie z tej lekcji niezbędne są podstawowe kompetencje w pracy w CodePen oraz w tworzeniu mapy z wykorzystaniem Leaflet - tego wszystkiego nauczyć się można w pierwszej lekcji z cyklu.

Część merytoryczna

Wyspa Spichrzów to dziś popularne, turystyczne miejsce Gdańska, pełne restauracji, hoteli i apartamentów. Nowoczesna zabudowa zasłania jednak bardzo historyczną przestrzeń, w której odkrywać można nie tylko dziedzictwo Gdańska - miasta handlu czy zabytki techniki portowej, ale też historię sceny artystycznej, w której pojawiają się takie nazwiska jak Grzegorz Klaman, Kazimierz Kowalczyk, Marek „Rogulus” Rogulski czy Andrzej Awsiej.

Wyspa Spichrzów na wizerunku z XVII w. / Źródło: Wikimedia Commons

Na terenie Wyspy znajdują się jeszcze pozostałości infrastruktury kolei spichrzowej. Spróbujemy je oznaczyć na cyfrowej reprodukcji planu Gdańska z 1920 roku.

Warstwa mapy ze skanu

OpenStreetMap to usługa typu Tile Map Service. TMS polega na dynamicznym (na żądanie) udostępnianiu warstw graficznych mapy, podzielonych na kafelki (tiles). Żądanie wysyłane jest za pomocą ustandaryzowanego adresu URL w schemacie

http://serwermapy.pl/Z/X/Y.png

gdzie Z oznacza poziom zoomu (skalę), a X i Y definiują wybrany kafelek. Nie jest to oczywiście jedyna możliwa postać udostępniania map cyfrowych - warto poznać także standard Web Map Service (WMS).

Z lekcji poświęconej wizualizacji wydarzeń z Nocy Muzeów wiemy, że w Leaflet zmiana warstwy mapy (map layer) wymaga podania odpowiedniego schematu adresu URL. Standardowy adres wyświetlający kafelki OpenStreetMap to:

https://tile.openstreetmap.org/{z}/{x}/{y}.png

a adres jednej z warstw alternatywnych to:

https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.{ext}

Jeśli chcielibyśmy pokazać pewne informacje na mapie historycznej, potrzebowalibyśmy odpowiedniego adresu (serwera), pozwalającego na generowanie kafelków zawierających tę mapę. Oznacza to, że ktoś musiałby

  • zdigitalizować mapę papierową w odpowiedniej jakości,
  • powiązać skan z siatką geograficzną - układem współrzędnych (wykonać georeferencję),
  • umieścić taką warstwę na serwerze udostępniającym dynamicznie kafelki w odpowiedzi na żądanie wysłane za pomocą ustandaryzowanego adresu URL. Serwer musi być odpowiednio wydajny i obsługiwać wiele symultanicznych żądań - kafelki budujące warstwę mapy muszą być wcześniej wygenerowane i obejmować odpowiednie zakresy skali.

Tego, jak można samodzielnie przygotować i udostępnić taką mapę, dowiemy się w jednej z kolejnych lekcji. Teraz skorzystajmy z gotowych rozwiązań i zasobów, które publikuje serwis Map Warper.

Mapy historyczne, z którymi chcemy pracować, bardzo rzadko obejmują obszar całego globu. Z oczywistych względów są też niedokładne lub nawet błędne. Oznacza to, że taka dynamicznie generowana mapa cyfrowa zawierać będzie wiele pustych miejsc - warstwa graficzna, której źródłem jest skan, będzie ograniczona do wybranego prostokąta lub kwadratu, dokumentującego ograniczony obszar. Oczywiście nie musi to być problemem - nieraz interesuje nas przecież konkretne miejsce, region, miasto i to w takiej przestrzeni, opisanej mapą historyczną, chcemy umieszczać informacje o budynkach, infrastrukturze transportowej, ale też np. liczbie ludności, dominującej strukturze narodowościowej czy religijnej, skali bezrobocia czy… historycznych wartościach czynszu. Znów to przede wszystkim cel wizualizacji i dostępne dane źródłowe określają nam możliwości projektu cyfrowego, a nie narzędzia i metody cyfrowe.

Dobrym przykładem są mapy publikowane w serwisie Istanbul Urban Database. Mapa Stambułu z 1815 roku z oczywistych powodów nie obejmuje całego współczesnego obszaru miasta, jednak w ograniczonych co przestrzeni i skali ramach może być użyteczna.

Mapa cyfrowa z warstwą współczesną i historyczną / Źródło: Istanbul Urban Database

Map Warper

Map Warper to serwis przygotowany i zarządzany przez Tima Watersa. Za jego pomocą publikować, przeglądać i wykorzystywać w projektach cyfrowych mapy historyczne. Korzystanie z serwisu jest darmowe, należy jednak założyć konto i potwierdzić je przy rejestracji (warto sprawdzić SPAM).

Zaletą witryny jest jej dostępność i łatwość pracy z mapami historycznymi. Wykorzystując ją we własnym projekcie warto jednak zwrócić uwagę na ważne ograniczenia i ryzyka. Po pierwsze, Map Warper jest projektem prywatnym, jego stabilność jest niepewna - czy nie zniknie w przypadku awarii lub braku finansowania? Po drugie, mapy dostępne w tym serwisie publikowane są w bardzo różnych jakości, bardzo często bez żadnych metadanych wskazujących źródło skanu. Po trzecie, jakość georeferencji - odwzorowania układu współrzędnych na skanie - może być niska. Pomimo tego Map Warper to świetne narzędzie do samodzielnych ćwiczeń z budowaniem map cyfrowych i do przygotowywania wizualizacji - np. w ramach warsztatów.

Przygotowanie mapy w CodePen

Sposób na przygotowywanie mapy cyfrowej na podstawie własnego skanu poznamy w jednej z kolejnych lekcji. Teraz skoncentrujmy się na użyciu przygotowanej już mapy historycznej w naszym projekcie. W realizacji tego zadania przyda nam się na pewno część lekcji poświęconej podstawom CodePen i Leaflet.

Nie ma sensu powtarzać całej treści tej lekcji, dlatego w kilku punktach przypomnijmy sobie niezbędne kroki:

  • musimy zaimportować do projektu odpowiednie biblioteki JavaScript i CSS,
  • musimy przygotować kod HTML (strukturę strony) i określić jego podstawowy układ i estetykę (kod CSS),
  • w kodzie JavaScript zdefiniować podstawowy obiekt mapy z wykorzystaniem metod Leaflet,
  • do obiektu mapy dodać odpowiednią warstwę oraz wybrane elementy (punkty - markery, linie itp.), dla których określić możemy metody interakcji (np. wyświetlanie odpowiednio zdefiniowanej informacji po kliknięciu w marker).

W efekcie otrzymamy podstawową wersję strony z mapą Leaflet - można ją zobaczyć w CodePen i wykorzystać do własnej pracy.

Aby to zrobić logujemy się na swoje konto CodePen, wchodzimy na adres codepen.io/humanistyka_dev/pen/ZENaQqX i klikamy przycisk fork na dole strony. Podgląd wszystkich elementów kodu dostępny jest poniżej:

See the Pen leaflet-base by humanistyka_dev (@humanistyka_dev) on CodePen.

Leaflet jako zestaw metod

Teraz pracować będziemy w kodzie JavaScript. Leaflet to biblioteka (framework), a w świecie programowania biblioteka to m.in. zestaw metod i narzędzi. Już do tej pory korzystaliśmy z różnych metod:

  • metoda map, która inicjuje obiekt mapy cyfrowej,
  • metoda setView, która centruje mapę na wybranym punkcie i wywoływana jest na obiekcie mapy,
  • metoda tileLayer, która tworzy warstwę mapy,
  • metoda addTo, która dodaje do obiektu mapy wybrane elementy (warstwy, markery, elementy interfejsu użytkownika),
  • metody marker i bindPopup, które pozwalają definiować obiekt markera oraz dodawać do niego popup wyświetlany po kliknięciu.

Oto cały kod JavaScript naszej podstawowej mapy:

// definiowanie podstawowego obiektu mapy do elementu HTML o id "map-wrapper" 

var map = L.map('map-wrapper');

// ustawienie centrowania mapy na Sopot
// 54.444092, 18.570328

map.setView([54.444092, 18.570328], 13);

// dodanie podstawowej warstwy (mapa współczesna, Open Street Map)

// tworzenie obiektu reprezentującego warstwę

var presentLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
});

// dodawanie warstwy

presentLayer.addTo(map);

// dodanie markera wskazującego na wejście na molo
// 54.44664945601264, 18.57202593998244

var marker1 = L.marker([54.44664945601264, 18.57202593998244]).addTo(map);

// dodanie informacji w popupie

marker1.bindPopup("To jest wejście na sopockie molo");

Dodajemy warstwę historyczną

Dodanie do naszego projektu mapy historycznej z zasobów serwisu Map Warper nie jest trudne. Skorzystamy z planu Gdańska z 1920 roku.

Skan mapy historycznej w infrastrukturze serwisu Map Warper / Źródło: Map Warper

Po wejściu na stronę zobaczymy kilka zakładek. W zakładce Export znajdziemy adres kafelków (tiles), wygenerowanych ze skanu mapy historycznej:

https://mapwarper.net/maps/tile/79046/{z}/{x}/{y}.png 

Pozostaje nam teraz

  • stworzyć warstwę zawierającą plan Gdańska z 1920 roku (użyjemy metody tileLayer).
  • dodać tę warstwę do obiektu naszej mapy (użyjemy metody addTo).

Zadania te zrealizujemy kodem w takiej postaci:

// tworzenie obiektu reprezentującego warstwę
// Warto zwrócić uwagę na zmienioną atrybucję.

var histLayer = L.tileLayer('https://mapwarper.net/maps/tile/79046/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: 'Źródło warstwy historycznej: <a href="https://mapwarper.net/maps/79046#Show_tab">Map Warper</a>'
});

// dodawanie warstwy do obiektu mapy

histLayer.addTo(map);

Nasza mapa zawiera teraz dwie warstwy: współczesną i historyczną. Obie są skalowalne, można je przewijać i można dodawać do nich rozmaite elementy.

Dodajemy przełącznik między warstwami

Leaflet umożliwia nie tylko pracę z warstwami mapy, ale też budowanie interfejsu użytkownika. Za pomocą metody control.layers dodamy do okna mapy przełącznik między warstwami. Oto jak to zrobimy:

// stworzymy obiekt mapLayers, który zawierał będzie informacje o dostępnych warstwach (współczesnej i historycznej)

var mapLayers = {
"mapa współczesna": presentLayer,
"Gdańsk 1920": histLayer };

// stworzymy obiekt reprezentujący widżet do przełączania warstw

var layerControl = L.control.layers(mapLayers);

// i ostatecznie dodamy go do mapy

layerControl.addTo(map);

Udało się 😎! Widget znajduje się w prawym górnym rogu - lektura dokumentacji Leaflet pozwoliłaby nam umieścić go w dowolnej części okna mapy, a nawet poza nim. Zwróćmy uwagę, że praca z Leaflet to praca z obiektami (obiektem jest mapa, warstwa, widżet przełączania warst i treść tego widżetu - lista dostępnych warstw).

Widget przełączania warstw mapy / Źródło: Leaflet / CodePen

Gromadzimy współrzędne układu torów

Polilinia (poliline) to w geometrii oraz w kontekście map i systemów GIS linia składająca się z serii połączonych odcinków prostych. Każdy odcinek jest definiowany przez parę punktów (współrzędnych), które określają jego początek i koniec. Polilinie są używane do reprezentowania różnych obiektów i cech geograficznych, takich jak drogi, rzeki, granice administracyjne czy szlaki turystyczne. W naszym przypadku będą reprezentować tory i bocznice kolei spichrzowej.

Aby dodać polilinie, potrzebujemy koordynatów. Zróbmy sobie proste narzędzie - po kliknięciu w wybrany punkt mapy wyświetli się popup z informacją o współrzędnych:

// narzędzie do pokazywania współrzędnych po kliknięciu

// Funkcja obsługi zdarzenia kliknięcia
function onMapClick(e) {
  // Pobierz współrzędne miejsca kliknięcia
  var lat = e.latlng.lat;
  var lng = e.latlng.lng;

  // Utwórz treść popupu z współrzędnymi
  var popupContent = "Koordynaty: " + lat.toFixed(5) + ", " + lng.toFixed(5);

  // Utwórz i otwórz popup w miejscu kliknięcia
  L.popup().setLatLng(e.latlng).setContent(popupContent).openOn(map);
}

// Dodaj obsługę zdarzenia kliknięcia na mapie
map.on("click", onMapClick);

W tym kodzie stworzyliśmy funkcję JavaScript. Funkcja to zestaw instrukcji, które wykonują określone zadanie lub obliczają jakąś wartość. Zamiast pisać polecenia jedno po drugim w każdym miejscu, w którym chcemy uzyskać jakiś efekt, możemy zdefiniować funkcję (używając słowa function) i odwoływać się do niej w razie potrzeby w dowolnym miejscu naszego programu.

Nasza funkcja onMapClick buduje widżet przełączania warstw mapy. Przyjmuje jeden parametr (e jak event). Event w JavaScript to zdarzenie, które występuje w odpowiedzi na interakcję użytkownika lub zmianę stanu w aplikacji. Przykłady zdarzeń obejmują kliknięcia myszy, naciśnięcia klawiszy, zmiany wartości pól formularza, załadowanie strony internetowej czy przesuwanie myszy.

Po kliknięciu w dowolne miejsce mapy pozyskujemy wybrane wartości wydarzenia (eventu) - szerokość i długość geograficzną. Wartości te przypisujemy do zmiennych lat i lng i następnie przekazujemy je do popupa. Aby nasza funkcja została wykonana, musimy ją tylko podłączyć do mapy - robimy to w ostatniej linijce kodu.

Rekonstruujemy linię kolejową - dodajemy polilinie

Teraz możemy przełączyć się na warstwę historyczną i kilkając w odpowiednie miejsca linii kolejowej wyznaczymy punkty, które zbudują nam polilinie. Im więcej punktów, tym nasza rekonstrukcja będzie dokładniejsza.

Oto dane do torów biegnących na osi północ-południe (wzdłuż wyspy):

// tym razem nie pracujemy z obiektami, ale z tablicami
// w js tablicę budują nawiasy kwadratowe []
// mamy tu do czynienia z tablicami w tablicy

// tory na osi północ-południe

var coords1 = [
  [54.34952, 18.65867],
  [54.34758, 18.65732],
  [54.3472, 18.65689],
  [54.34444, 18.6551],
  [54.34417, 18.65488],
  [54.34379, 18.65444],
  [54.34348, 18.6538]
];

var coords2 = [
  [54.34349, 18.65378],
  [54.343, 18.65279]
];

var coords3 = [
  [54.34392, 18.65527],
  [54.34285, 18.65327]
];

var coords4 = [
  [54.34348, 18.65379],
  [54.3428, 18.65305]
];

Skoro mamy koordynaty, możemy dodać polilinie. Aby to zrobić, konieczne jest zastosowanie takiego kodu:

// każda polilinia jest obiektem, tworzonym za pomocą metody polyline.
// metoda ta wymaga podania tablicy koordynatów, dodajemy też ustawienia (kolor linii)
// i następnie dodajemy do mapy za pomocą addTo()

var polyline1 = L.polyline(coords1, { color: "purple" }).addTo(map);
var polyline2 = L.polyline(coords2, { color: "purple" }).addTo(map);
var polyline3 = L.polyline(coords3, { color: "purple" }).addTo(map);
var polyline4 = L.polyline(coords4, { color: "purple" }).addTo(map);

Podobnie przygotować należy koordynaty dla torów na osi wschód-zachód. W efekcie otrzymamy schemat układu torów, który da się podejrzeć zarówno na mapie historycznej jak i na współczesnej (na czym nam najbardziej zależało). Leaflet pozwolił nam nie tylko na umieszczenie na stronie mapy historycznej, ale też wykorzystanie jej do rekonstrukcji nieistniejącego już we współczesnej przestrzeni obiektu (chociaż niewielkie fragmenty torów zachowały się jeszcze na Wyspie Spichrzów).

Gotowy kod dostępny jest pod tym adresem, można go podejrzeć poniżej:

See the Pen leaflet-base by humanistyka_dev (@humanistyka_dev) on CodePen.


Przygotowany kod JavaScript, fundamentalny dla działania naszej mapy, nie jest specjalnie zoptymalizowany. Z pewnością można go skrócić i uniknąć powtarzania całych fragmentów (jak np. przy deklarowaniu polilinii), jednak zachowajmy go w takiej postaci - powinien być czytelny dla wszystkich, którzy dopiero zaczynają pracę z Leaflet.

Podsumowanie

Udało nam się przygotować prostą rekonstrukcję fragmentu historycznej gdańskiej infrastruktury kolejowej. Wykorzystaliśmy plan miasta z 1920 roku i mapę współczesną OpenStreetMap. Leaflet pozwolił nie tylko na umieszczenie tych map na stronie, ale też na zbudowanie narzędzia do pobierania koordynatów z wybranego punktu mapy. Pozwoliło to nam na zdefiniowanie polilinii i zwizualizowanie infrastruktury kolejowej na Wyspie Spichrzów.

Wykorzystanie metod

Leaflet może być wartościową biblioteką nie tylko do publikowania map, ale też budowania narzędzi do pracy z mapami. Projekty tworzone z tą biblioteką mogą być łatwo publikowane online. Ich wadą może być jednak ograniczona dokładność - w naszej pracy przebieg linii kolejowej jest zrekonstruowany za pomocą bardzo ograniczonej liczby punktów. To bardziej wizualizacja niż rekonstrukcja.

Pomysł na warsztat

Nasze zadanie dałoby się także zrealizować bez korzystania z komputera 🤓. Wystarczy wydrukować fragment historycznego planu (obszar Wyspy Spichrzów) oraz ten sam fragment mapy współczesnej. Następnie na półprzezroczystym papierze (można wykorzystać papier śniadaniowy) zaznaczamy kilka referencyjnych punktów oraz przebieg linii kolejowej - warstwą dolną jest oczywiście mapa historyczna. Tak przygotowany materiał nakładamy na wydrukowaną mapę współczesną. Nasza papierowa rekonstrukcja-wizualizacja jest gotowa i możemy interpretować zmiany w przestrzeni Wyspy.