6 min read

Gdzie mieszka Smok Wawelski, czyli mapa w R w kilku linijkach kodu

Nieraz tworzę mapy. Może nawet nie “nieraz” ale dość często. Najczęściej są to mapy zawierające punkty, a więc dość proste, ale jeśli doda się do nich jakąś interakcje, to stają się bardzo wartościowe. Używam do tego języka programowania R, bo zrobienie profesjonalnie wyglądającej mapy to kilka linijek kodu. Ponieważ w polskich mediach zarówno papierowych jak i elektronicznych, mapy - jeśli są - to są fatalne, to postanowiłem pokazać, że w większości wypadków, zrobienie mapy to betka. Oczywiście jeśli umie się programować :-)

Często do tworzenia “na szybko” map, używam biblioteki Leaflet w wersji dla R. To bardzo wygodne i dobrze udokumentowany zbiór różnych funkcji, dzięki któremu można zrobić mapy, a następnie wyeksportować je w formie html i osadzić np. przy pomocy htmlowego iframe na stronie.

Zresztą zobaczcie sami. Poniżej trzy linijki kodu wystarczą, by na ekranie pojawiła się mapa.

library(leaflet)

leaflet() %>% 
  addTiles()

Tak naprawdę, to nie trzy, a dwie, bo pierwsza to wczytanie biblioteki Leaflet, co robi się tylko raz. Mapa, którą stworzyłem nie jest specjalnie przydatna. Spróbujmy się nią trochę pobawić. Załózmy, że chcemy przedstawić na mapie krakowski Rynek Główny w sporym przybliżeniu. Wystarczy dosłownie kilka linijek kodu.

  • Pierwsze dwie to współrzędne miejsca, które ma być w centralnym punkcie naszej mapy. W tym przypadku jest to oczywiście środek Sukiennic, bo znajdują się na środku Rynku.

  • Następna linijka - leaflet() to stworzenie obiektu zawierającego mapę. addTiles dodaje tzw. podkłady mapy, czyli jak ona ma wyglądać, jak i jakim kolorem są oznaczone ulice, domy i inne obiekty. Zauwazyliście pewnie, że ta mapa rózni się wyglądaem od znanych pewnie każdemu Map Googla. To właśnie efekt wykorzystania innego, niż ma Google, podkładu mapy. W tym wypadku, ponieważ nie zdefiniowaliśmy inaczej, to Leaflet wykorzystuje OpenStreetMap.

  • No i ostatnie polecenie setView ma za zadanie przedstawić obszar mapy z centralnym punktem o podanych współrzędnych i zdefiniowanym przybliżeniem (zoom).

Prawda, że proste?

# współrzedne krakowskich Sukiennic, czyli centralnego punktu Rynku Głównego
# współrzedne dowolnego punktu na mapie, mozna sobie pobrać z Google Maps

lanKrk <- "19.937474"
latKrK <- "50.061652"

# Narysowanie mapy i ustawienie jej w punkcie o zdefiniowanych współrzędnych
leaflet() %>% 
  addTiles() %>% 
  setView(lng=lanKrk, lat = latKrK, zoom = 17)

Standardowym podkładem map jest wspomniany wcześniej OpenStreetMap. Powód jest oczywisty: jest za darmo i niewiele ustępuje popualrnemu Google Maps. Jednak nieraz możemy potrzebowac innego podkładu. Są ich dziesiątki, chociaż nie wszystkie są za darmo, a niektóre, które są za darmo, wymagają rejestracji. Nie będziemy się tym teraz zajmować, więc pokażę tylko, że podkład mapy, a więc jej wygląd można zmienić, właściwie nie dodając nowej linijki kodu, tylko definiując odpowiedni parametr funkcji addTitle. Ten paraetr to urlTemplate i wskazujemy w nim adres url gdzie znajdują się podkłady map. Zauważmy, że mapa tego samego obszaru, wygląda zupełnie inaczej gdy zastosujemy podkład Stamen Lite

leaflet() %>% 
  addTiles(urlTemplate = "//stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png") %>% 
  setView(lng=lanKrk, lat = latKrK, zoom = 17)

… czy zmieniając jedynie adres do podkładów map, Stamen Toner.

leaflet() %>% 
  addTiles(urlTemplate = "//stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.png") %>% 
  setView(lng=lanKrk, lat = latKrK, zoom = 17)

Powiedzmy sobie szczerze, że taki sposób wyboru podkładu mapy do wygodnych nie należy. Jest on przydatny w niektórych sytuacjach, ale gdy chcemy zastosować jakiś popualrny podkład, to na szczęście jest sposób o wiele prostszy. Leaflet ma zdefiniowane w ramce danych o nazwie providers wszystkie popularne podkłady. Wystarczy więc jak w przykładzie poniżej użyć funkcji addProviderTiles() i jako parametr providers podać nazwę podkładu. Reszta kodu pozostaje bez zmian.

leaflet() %>% 
  addProviderTiles(provider = providers$OpenStreetMap.BlackAndWhite) %>%  
  setView(lng=lanKrk, lat = latKrK, zoom = 17) 

Uważny obserwator zauważył pewnie, że mapka powyżej ma… inne rozmiary. To jeszcze jedna z fajnych możliwości, jakie daje nam zaprogramowanie mapy. Takie coś jest niezwykle przydatne, gdy chcemy zwrócić uwagę czytelnika na wybrany fragment mapy.

Możemy też dokładnie zdefiniować czworobok mapy, podając - jak poniżej - w funkcji fitBoundsTiles() współrzędne lewego, górnego i dolnego prawego rogu.

leaflet() %>% 
  addProviderTiles(provider = providers$Esri.WorldImagery) %>%  
  fitBounds(lng1 = 19.932979, lat1 = 50.063454, lng2 = 19.943965, lat2 = 50.061202)

Jeśli bawiliście się trochę mapami, które tu pokazałem, to zapewne zauważyliście, że można dowolnie powiększać czy pomniejszać widoczny obszar mapy. Nie zawsze chcemy czytelnikowi dać takie możliwości, bo na przykład chcemy, by skupił się na tym, co jest na mapie przedstawione. No i masz! Też to przewidziano, czyli można usunąć przyciski służące do powiększenie/pomniejszenia mapy. Służą temu opcje funkcji tworzenia mapy leaflet(), które widać w poniższym kodzie. Standardowo opcja zoomControl ma wartość TRUE. Jednak jeśli damy FALSE, to komputer nie będzie wyświetlał przycisków służących do zoomowania.

leaflet(options = leafletOptions(zoomControl = FALSE)) %>% 
  addProviderTiles(provider = providers$Esri.WorldImagery) %>%  
  fitBounds(lng1 = 19.932979, lat1 = 50.063454, lng2 = 19.943965, lat2 = 50.061202)

Sposób podany wyżej jest skuteczny jeśli mamy do czynienia z użytkownikiem niezbyt dociekliwym :-) Taki od razu zorientuje się, że można powiększyć mapę dwukrotnie klikając w wybrane miejsce na mapie :-). Na to też jest sposób! Wystarczy w opcjach leaflet określić minZoom i maxZoom - dwie wartości do jakich można odpowiednio pomniejszyć/powiększyć mapę - wpisać tą samą wartość zoomu. Przy okazji zauważ, że użyłem kolejnego podkładu, czyli mapy satelitarnej. Nie z Google Maps, tylko z Esri.

leaflet(options = leafletOptions(zoomControl = FALSE,minZoom = 15, maxZoom = 15)) %>% 
  addProviderTiles(provider = providers$Esri.WorldImagery) %>%  
  fitBounds(lng1 = 19.932979, lat1 = 50.063454, lng2 = 19.943965, lat2 = 50.061202)

No dobra, trochę sie pobawiliśmy wyświetlaniem map, ale przecież pokazujemy je czy w gazecie czy na stronie internetowej w jakimś celu. Najczęściej chcemy przy pomocy mapy coś umiejscowić, albo zachęcić czytelnika do eksploracji, zastanowienia się, nad tym co jest na mapie. Czas więc na przedstawienie czegoś praktycznego.

Każde dziecko w Polsce znało, zna i będzie znać legendę o Smoku Wawelskim i dzielnym szewczyku Dratewce. Powiedzmy, że chcemy zmapować, czyli przedstawić na mapie, gdzie mieszkał Smok Wawelski, gdzie była owieczka wypełniona siarką, gdzie stał Dratewka i gdzie smok pił wodę z Wisły i w koncu pękł. Jednym zdaniem, chcemy na mapie zaznaczyć wybrane punkty. Zamiast smoka może to być np. miejsce na ulicy, gdzie doszło do wypadku itp.

Dodajmy więc cztery punkty na mapie. Kod będzie nieco dłuższy, ale równie banalny.

  • Najpierw definiujemy sobie tzw. ramkę danych - czyli mówiąc językiem zwykłych ludzi: tabelę, która ma dwie kolumny: nazwa, która zawiera opis punktu, oraz współrzędne geograficzne punktu, który chcemy zaprezentować na mapie.
  • Następnie wywołujemy funkcję leaflet() z parametrem, którym jest nazwa ramki danych z danymi,
  • definiujemy podkład
  • umieszczamy pinezki funkcją addMarkers.

Nasza nowa funkcja ma w tym wypadku 3 parametry: dwie współrzędne, oraz popup czyli paramter, który mówi co ma zostać wyświetlone gdy internauta kliknie na pinezkę.

Poklikaj sobie na pinezki i zobaczysz co jest w tych miejscach. Dowiesz się m.in. gdzie mieszkał Smok Wawelski

punkty_krk <- tibble(nazwa=c("Tu mieszka Smok",
                             "Baranek",
                             "Szewc Dratewka",
                             "Tu smok pił wodę"),
                     lan = c(19.933570,19.933006, 19.933076,19.933301),
                     lat = c(50.053124,50.053221,50.053104,50.052567))

leaflet(punkty_krk) %>% 
  addProviderTiles(provider = providers$OpenStreetMap) %>%  
  addMarkers(lng = ~lan, lat = ~lat,popup = htmlEscape(~nazwa))

Tak, tak, wiem, że te pinezki są brzydkie jak kupa. Jest na to rada, to znaczy można je usunąć, albo użyć innych ikonek, ale o tym w następnym odcinku.

Zauważ, że właściwie wszędzie gdzie rysowałem te mapy, nie użyłem więcej niż dosłownie kilka linijek kodu. Mało tego, taki kod można zrobić raz, a później nauczyć praktykanta, by tylko zmieniał np. współrzedne :-)

No dobra, żartowałem, ale mam nadzieję, że udało mi się pokazać, że zrobienie - na razie bardzo prostej - mapki, zajmuje naprawdę niewiele czasu.

W następnej części przedstawię jak można zmienić pinezki i co może być wyświetlane po kliknięciu w każdą z nich.

Możecie nie wierzyć, ale znowu dzięki kilku linijkom kodu powstanie… prawie aplikacja.