Szybka mapka z miastami

Jak szybko przygotować mapkę z (mniejszymi) miastami Polski? Szybki temat, bo i potrzeba była szybka. Żona zapytała jak ma zrobić mapkę z lokalizacją oddziałów jej firmy w poszczególnych miastach Polski. I nie były to miasta wojewódzkie (to znaczy takie też), ale też powiatowe. Szukanie mapy konturowej naszego kraju, a później szukanie gdzie na owej mapie zaznaczyć jakiś przysłowiowy Pcim Dolny to nie jest zadanie na godzinę dwudziestą trzecią. Zajęłoby (mi) to jakieś dwie albo trzy godziny. Od czego jest R? Rozwiązania na szybko przyszły mi do głowy dwa, oba na podstawie doświadczeń oczywiście. Lubię mapy i dane na mapach, to mam też trochę doświadczenia w tym temacie.

Wersja z Google Maps

Skorzystamy z biblioteki ggmap do znalezienia punktów na mapie, pobrania mapy oraz jej narysowania - ta biblioteka to po prostu interface R do GoogleMaps (ale nie tylko). Dodatkowe elementy narysujemy z pomocą ggplot2.

library(ggplot2)
library(ggmap)

Przygotujmy sobie dane. Wybrałem z Wikipedii trochę (losowo) miast powiatowych. Najpierw wtłoczymy je w ramkę danych i przygotujemy w niej odpowiednie pola na informacje dodatkowe:

miasta_df <- data.frame(nazwa = c("Legnica", "Grudziądz",
                     "Włocławek", "Chełm", "Gorzów Wielkopolski",
                     "Piotrków Trybunalski", "Tarnów", "Siedlce",
                     "Krosno", "Tarnobrzeg", "Łomża", "Słupsk",
                     "Bielsko-Biała", "Piekary Śląskie", "Żory",
                     "Elbląg", "Kalisz", "Leszno", "Świnoujście"),
                     stringsAsFactors = FALSE)

# miejsce na długość i szerokość geograficzną
miasta_df$long <- NA
miasta_df$lat <- NA

Teraz dla każdego z miast pobierzemy współrzędne geograficzne. Zapytamy o nie po prostu Google. Nazwę miasta konwertuję (iconv()) na UTF8 (system na którym używam R to Windows, więc znaki kodowane są w Latin2, z którym internety mają raczej problem) i dodaję “Polska”, coby doprecyzować moje zapytanie. W odpowiedzi dostajemy zmienną ze współrzędnymi - dopisujemy je do stosownego wiersza w całej tabeli.

for(i in 1:nrow(miasta_df)) {
    loc <- geocode(paste0(iconv(miasta_df[i, "nazwa"], to="UTF8"), ", Polska"))
    miasta_df[i, "long"] <- loc$lon
    miasta_df[i, "lat"] <- loc$lat
}

Mamy wszystko co trzeba:

nazwalonglat
Legnica16.1553251.20701
Grudziądz18.7535653.48375
Włocławek19.0677452.64833
Chełm23.4712051.14312
Gorzów Wielkopolski15.2369352.73253
Piotrków Trybunalski19.7030251.40517
Tarnów20.9858450.01210
Siedlce22.2901652.16760
Krosno21.7660549.68248
Tarnobrzeg21.6790750.57291
Łomża22.0590353.17812
Słupsk17.0284854.46415
Bielsko-Biała19.0583849.82238
Piekary Śląskie18.9270550.37891
Żory18.7006450.04472
Elbląg19.4044954.15606
Kalisz18.0853551.76728
Leszno16.5937551.84199
Świnoujście14.2475853.91003

więc pora to narysować. Pobierzmy najpierw tło - google’owę mapę naszego kraju. Wiecie, że środek Polski leży w okolicach Kutna? Dlatego poniżej pierwszym parametrem (środek obszaru) jest właśnie Kutno. Taka wiedza ze szkoły czasem się przydaje ;) Eksperymentalnie dobieramy (metodą prób i błędów) wielkość parametru zoom i w efekcie otrzymujemy coś takiego:

polska <- get_map("Kutno, Polska", zoom=6)
ggmap(polska)

Jest OK. Jak trochę przyciemnimy, dodamy kontrastowe punkty z miastami i ich nazwami będzie ok. Proszę bardzo:

ggmap(polska, darken = 0.6) + 
   geom_point(data=miasta_df,
              aes(long, lat),
              size=3, color="red") +
   geom_text(data=miasta_df,
             aes(long, lat, label=nazwa),
             size=3, color="white", hjust=-0.1) +
   theme_void()

Wersja bez Google Maps

Może jednak z różnych względów nam się to nie podobać (bo na mapie z GMaps są napisy, bo za dużo kolorów, bo cokolwiek). Wykorzystamy więc dane wektorowe (pliki Shapefile, więcej mięsa po angielsku znajdziecie). Najpierw jednak trzeba mieć te pliki. Są ogólnodostępne i za darmo na stronie Centralnego Ośrodka Dokumentacji Geodezyjnej i Kartograficznej i przydadzą się wielokrotnie. Interesuje nas paczka “PRG – jednostki administracyjne”, która zawiera dane o obrębach gmin, powiatów oraz województw. Archiwum trzeba ściągnąć i rozpakować. Najpierw jednak potrzebujemy w samym R biblioteki rgdal ze wszystkimi dodatkami. Zainstalujemy ją z CRANa.

install.packages("rgdal")
library(rgdal)

W kodzie musimy odpowiednio podać ścieżkę do katalogu z plikami dla województw:

wojewodztwa <- readOGR("ŚCIEŻKA_DO_MAP", "wojewodztwa")

wojewodztwa <- spTransform(wojewodztwa, CRS("+init=epsg:4326"))
wojewodztwa <- fortify(wojewodztwa, region = "jpt_kod_je")

W wyniku tego działania dostajemy ramkę (dłuuugą) z punktami wyznaczającymi granice województw. Możemy ją narysować i od razu nałożyć na nią nasze punkty z miastami:

ggplot() +
    geom_polygon(data=wojewodztwa,
                 aes(long, lat, group=group),
                 fill="white", color="gray") +
   geom_point(data=miasta_df,
              aes(long, lat),
              size=3, color="red") +
   geom_text(data=miasta_df,
             aes(long, lat, label=nazwa),
             size=3, color="red", hjust=-0.2) +
   coord_map() +
   theme_void()

Gotowe! Jakieś 15-20 minut roboty, tylko kilkanaście linii kodu. Gotowy obrazek eksportujemy do pliku (można przez ggsave() od razu albo z poziomu RStudio) i przekazujemy żonie do prezentacji. A nie minęła jeszcze północ…