Jak wykorzystać dane o numerycznym modelu terenu (NMT)? Skąd je wziąć i jak przygotować do wygodniej pracy?
Dzisiaj wpis mocno techniczny, bez szczególnych analiz. Gratka dla osób szukających sposobów na rozdzielenie danych punktowych na gminy, powiaty i województwa oraz wskazówki jak pokazywać takie dane na mapie (i agregować je zgodnie z podziałem terytorialnym). Oprzemy się na danych o wysokości terenu, ale to tylko przykład.
Potrzebne nam będą następujące biblioteki:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						library(tidyverse) # do wszystkiego :) library(rvest) # do srappingu strony library(rgdal) # do obsługi plików SHP library(rgeos) library(raster) library(ggmap) # do wczytania mapy z Google Maps library(sf) # do wczytywania plików SHP i łatwego rysowania library(RPostgreSQL) # do zapisu i odczytu danych z bazy, w tym przypadku baza PostgreSQL  | 
					
Kolejne fragmenty kodu to kilka skryptów złączonych w jedno. Stąd mogą trafić się powtórzenia w kodzie albo te same dane pod różnymi nazwami zmiennych. Nie przejmujcie się tym – czytając kod i rozumiejąc go znajdziecie sposób na wybranie tego, co najbardziej Was interesuje.
Zaczniemy od zgromadzenia danych o wysokości terenu. Centralny Ośrodek Dokumentacji Geodezyjnej i Kartograficznej (CODGiK) udostępnia takie dane w postaci plików tekstowych (jeden plik to jedno województwo). Struktura plików jest bardzo prosta – w każdym wierszu mamy informację dla jednego punktu: współrzędne punktu i jego wysokość nad poziomem morza. Pliki na serwerze CODGiK są spakowane – pobierzmy je i rozpakujmy.
Aby nie bawić się w ręczne pobieranie każdego pliku zeskanujmy automatycznie stronę i wyciągnijmy linki do plików. Nie raz już scrappowaliśmy strony, więc pójdzie łatwo:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  | 
						base_url <- "http://www.codgik.gov.pl/index.php/darmowe-dane/nmt-100.html" # wyciągamy linki do plików data_urls <- read_html(base_url) %>% html_node("table") %>% html_nodes("a") %>% html_attr("href") # każdy link: for(i in 1:length(data_urls)) {   file_url <- data_urls[[i]]   # sciagamy plik   download.file(file_url, paste0("dane/", basename(file_url)))   # rozpakowujemy go   unzip(paste0("dane/", basename(file_url)), exdir = "dane/")   # chwilę czekamy   Sys.sleep(1) }  | 
					
Po tej operacji mamy 16 plików tekstowych w folderze dane/. Weźmy na warsztat Małopolskę.
Pierwszym krokiem będzie wczytanie danych i przygotowanie ich w odpowiednim układzie współrzędnych (takim, jaki znamy z geografii). Niestety pliki zapisane są w układzie PUWG 1992, musimy je przemapować. Oczywiście istnieją odpowiednie do tego funkcje, które potrafią to zrobić na obiektach typu Shape:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  | 
						rm(list = ls()) # sprzątamy to co było do tej pory - tutaj zaczyna się oddzielny skrypt # przygotowanie palety paleta_rgb <- c("#41786E", "#5AA03C", "#BED200", "#FFFA78", "#FCDC00",                 "#F5BE00", "#F0A04B", "#E68246", "#E15F32", "#D2412D") # układy współrzędnych # dane są w układzie PUWG 1992 CRS_puwg1992 <- crs("+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +units=m +no_defs ") # układ WGS84 (współrzędne "normalne") CRS_wgs84 <- crs("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs;") # wczytujemy plik dane_plik <- "dane/malopolskie.txt" dane_nmt <- read_delim(dane_plik, " ", col_names = c("x", "y", "z"), col_types = "ddd") # przeliczenie wspołrzędnych coordinates(dane_nmt) <- c("x", "y") projection(dane_nmt) <- CRS_puwg1992 dane_nmt_wgs84 <- spTransform(dane_nmt, CRS_wgs84) dane_nmt <- as.data.frame(dane_nmt_wgs84@coords) %>% mutate(z = dane_nmt_wgs84$z) # zaokrąglenie danych = zmniejszenie dokładności i jednocześnie liczby punktów do narysowania plot_data <- dane_nmt %>%   mutate(x = round(x, 2),          y = round(y, 2)) %>%   group_by(x, y) %>%   summarise(z = mean(z)) %>%   ungroup()  | 
					
Dane zaokrągliliśmy, aby szybciej się rysowały. Oczywiście nie musicie tego robić. Pierwsza nasza mapka to wysokość w wybranym województwie:
| 
					 1 2 3 4 5 6  | 
						# rysujemy naszą pierwszą mapę plot_data %>%   ggplot(aes(x, y, color = z)) +   geom_point() +   coord_map() +   scale_color_gradientn(colors = paleta_rgb)  | 
					

Pięknie to wygląda, prawdziwa mapa hipsometryczna. Możemy zmienić paletę z ciągłej na dyskretną – przyczyna to pokazanie niebieskim kolorem tego co jest poniżej poziomu morza.
| 
					 1 2 3 4 5 6 7 8 9 10  | 
						# dzielimy wysokość na 9 przedziałów, dziesiąty to poniżej 0 przedzialy <- c(-1000, seq(0, round(max(plot_data$z), -2), length.out=9)) # rysujemy mapę z kolorami według podziału ma przedziały plot_data %>%   mutate(z_fct = cut(z, breaks = przedzialy)) %>%   ggplot(aes(x, y, color = z_fct)) +   geom_point() +   coord_map() +   scale_color_manual(values = paleta_rgb)  | 
					

Nałóżmy nasze kolorki na rzeczywistą mapę – pobraną chociażby z Google Maps. Łatwiej będzie rozpoznać poszczególne górki i doliny. Niech będzie Kraków:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						# pobieramy współrzędne Krakowa loc <- geocode("Kraków, Poland") # ograniczamy zakres rysowanej mapy (punktów wysokości) dane_nmt_krakow <- dane_nmt %>%   filter(y > as.numeric(loc[2]) - 0.25 & y < as.numeric(loc[2]) + 0.25) %>%   filter(x > as.numeric(loc[1]) - 0.5 & x < as.numeric(loc[1]) + 0.5) %>%   # dzielimy wysokość na przedziały   mutate(z_fct = cut(z, breaks = przedzialy)) dane_nmt_krakow %>%   # rysujemy mapkę   ggplot(aes(x, y, color = z_fct)) +   geom_point() +   # zaznaczamy współrzędne Krakowa   geom_point(aes(x = as.numeric(loc[1]), y = as.numeric(loc[2])), size = 5, color = "red") +   coord_map() +   scale_color_manual(values = paleta_rgb)  | 
					

Ale to ciągle tylko dane z obszaru Krakowa. Co jest w którym miejscu?
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14  | 
						# pobieramy mapę Krakowa z Google Maps mapa_gg <- get_map(loc, source = "google", maptype = "roadmap", zoom = 13) # bierzemy wycinek odpowiadający mapie z oryginalnych danych dane_krakow <- dane_nmt %>%   filter(x >= attr(mapa_gg, "bb")$ll.lon, x <= attr(mapa_gg, "bb")$ur.lon) %>%   filter(y >= attr(mapa_gg, "bb")$ll.lat, y <= attr(mapa_gg, "bb")$ur.lat) # na tej mapie rysujemy wysokość ggmap(mapa_gg, darken = 0.7) +   geom_point(data = dane_krakow,              aes(x, y, alpha = z, color = z)) +   scale_alpha_continuous(range = c(0.4, 0.7)) +   scale_color_gradientn(colors = paleta_rgb)  | 
					

Teraz ładnie widać na przykład Wzgórze Wawelskie. Oraz przyczynę krakowskiego smogu – położenie miasta w dolinie.
Jak wygląda rozkład wysokości na pokazanym obszarze?
| 
					 1 2 3  | 
						dane_nmt_krakow %>%   ggplot() +   geom_histogram(aes(z), binwidth = 1)  | 
					

Sprawdźmy co się stanie jak podniesie się woda w Krakowie – na przykład niech poziom morza będzie teraz na wysokości 150 metrów:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12  | 
						# 150 w poniższej linii to nowy poziom morza przedzialy_powodz <- c(-1000, seq(150, round(max(dane_krakow$z), -2), length.out=9)) # na nowo przydzielamy punkty do przedziałów dane_krakow <- dane_krakow %>% mutate(z_fct = cut(z, breaks = przedzialy_powodz)) # na mapie miasta rysujemy wysokość: ggmap(mapa_gg, darken = 0.7) +   geom_point(data = dane_krakow,              aes(x, y, alpha = z, color = z_fct)) +   scale_alpha_continuous(range = c(0.4, 0.7)) +   scale_color_manual(values = paleta_rgb)  | 
					

Widzimy, że całe koryto Wisły zostaje zapełnione wodą. Wawel ostaje się na lądzie. Między innymi do tego celu mogą służyć mapy z wysokością terenu.
No dobrze – wybraliśmy Kraków na postawie jakiegoś otoczenia współrzędnych jego środka. Ale na początku napisałem, że dowiemy się jak dane punktowe przypisać do gmin (i co za tym idzie powiatów i województw). Poniższy kod to właśnie robi dla jednego (małopolskiego) województwa.
Algorytm działania jest następujący (i stosunkowo prosty, chociaż obliczenia są długotrwałe):
- przygotuj punkty w takim samym układzie jak dane o granicach gmin
 - dla wszystkich punktów z danymi przygotuj macierz binarną mówiącą o tym czy dany punkt należy do obszarów kolejnych gmin
 - na podstawie tej macierzy przypisz do punktu kod TERYT gminy (i powiatu, i województwa – bo te są fragmentami kodu gminy)
 - aby nie liczyć wszystkiego na raz dla wszystkich danych (potrzeba dużo pamięci) dzielimy punkty z danymi na mniejsze paczki
 
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75  | 
						rm(list = ls()) # sprzątamy to co było do tej pory - tutaj zaczyna się oddzielny skrypt dane_plik <- "dane/malopolskie.txt" woj_TERYT <- "12" # definicja układu współrzednych CRS_puwg1992 <- crs("+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +units=m +no_defs ") CRS_wgs84 <- crs("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs;") # układ WGS84 (współrzędne "normalne") # wczytujemy mapę gmin gminy_shp <- readOGR("../!mapy_shp/gminy.shp", "gminy") gminy_shp <- spTransform(gminy_shp, CRS_wgs84) # wybieramy gminy z odpowiedniego województwa wojewodztwo <- gminy_shp[substr(gminy_shp$jpt_kod_je, 1, 2) == woj_TERYT, ] # kasujemy gminy_shp dla oszczednosci pamieci rm(gminy_shp) # wyczytujemy dane o wysokoąci punków w województwie dane_nmt <- read_delim(dane_plik, " ", col_names = c("x", "y", "z"), col_types = "ddd") # funkcja sprawdza czy punkty z f_dataframe są w ramach f_shape i dodaje im kod TERYT gminy add_TERYT_code <- function(f_dataframe, f_shape) {   # wyimek przerabiamy na SHP   coordinates(f_dataframe) <- c("x", "y")   projection(f_dataframe) <- CRS_puwg1992   f_dataframe <- spTransform(f_dataframe, CRS_wgs84)   # macierz przypisania punktu do powiatu - to trochę trwa   miejscowosci_mat <- gWithin(f_dataframe, f_shape, byid = TRUE)   miejscowosci_mat <- t(miejscowosci_mat)   # teraz numer kolumny to numer gminy, numer wiersza to numer punktu   # bierzemy punkty z powrotem do tabelki   punkty <- as.data.frame(f_dataframe@coords) %>% set_names(c("long", "lat"))   punkty$height <- f_dataframe@data$z   # szukamy kodu TERYT dla kolejnych punktów   punkty$TERYT_gmn <- miejscowosci_mat %>%     apply(1, function(x) min(which(x, arr.ind = TRUE), na.rm=TRUE)) %>%     as.character(f_shape$jpt_kod_je)[.]   # zwracamy paczkę punktów z odpowiednimi przypisaniami TERYT   return(punkty) } # dla całego województwa przygotowujemy dane punkty_all <- tibble() step_i <- 100 # paczki po 2000 punktów na raz wydaja sie byc optymalne n_steps <- ceiling(nrow(dane_nmt)/step_i) # dla każdej paczki punktów: for(i in 1:n_steps) {   # bierzemy jedna paczkę punktów   dane_nmt_small <- dane_nmt[ ((i-1)*step_i+1):(i*step_i), ] %>% na.omit()   # dopisujemy kody TERYT gminy do punktów   punkty <- add_TERYT_code(dane_nmt_small, wojewodztwo)   # z TERYT gminy robimy TERYT województwa (pierwsze 2 znaki) i TERYT powiatu (pierwsze 4 znaki)   punkty <- punkty %>%     mutate(TERYT_woj = substr(TERYT_gmn, 1, 2),            TERYT_pow = substr(TERYT_gmn, 1, 4))   # łaczymy razem z już przerobionymi punktami   punkty_all <- bind_rows(punkty_all, punkty) } # zapisujemy wynikowe dane saveRDS(punkty_all, "dane/malopolskie.rds")  | 
					
Dla jednego województwa proces trwał u mnie kilkanaście minut. Ale dzięki temu możemy w łatwy sposób wybierać dane o wysokości dla konkretnej gminy. Zobaczmy gminę w której leżą Rysy. Ale na początek – całe województwo małopolskie, na które nałożymy granice gmin:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14  | 
						rm(list = ls()) # sprzątamy to co było do tej pory - tutaj zaczyna się oddzielny skrypt # wczytujemy zapisane dane punkty_all <- readRDS("dane/malopolskie.rds") # przygotoiwanie palety paleta_rgb <- c("#41786E", "#5AA03C", "#BED200", "#FFFA78", "#FCDC00",                 "#F5BE00", "#F0A04B", "#E68246", "#E15F32", "#D2412D") # definicja układu współrzednych CRS_wgs84 <- crs("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs;") # układ WGS84 (współrzędne "normalne") # wczytujemy mapę gmin gminy_shp <- readOGR("../!mapy_shp/gminy.shp", "gminy")  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  | 
						gminy_shp <- spTransform(gminy_shp, CRS_wgs84) # wybieramy powiaty z odpowiedniego województwa wojewodztwo <- gminy_shp[substr(gminy_shp$jpt_kod_je, 1, 2) == "12", ] # kasujemy gminy_shp dla oszczednosci pamieci rm(gminy_shp) # całe województwo ggplot() +   geom_point(data = punkty_all, aes(long, lat, color = height)) +   # granice gmin   geom_polygon(data = wojewodztwo, aes(long, lat, group=group), color = "gray50", fill = NA) +   scale_color_gradientn(colors = paleta_rgb) +   coord_map()  | 
					

Teraz wybierzmy jedną gminę – po jej kodzie TERYT. Do tego wybierzmy granice powiatu w którym leży gmina. I narysujmy taką mapkę:
| 
					 1 2 3 4 5 6 7 8 9 10 11  | 
						# wybierzmy jedną gminę i pokażmy powiat w którym leży dane_gmina <- punkty_all %>% filter(TERYT_gmn == "1217032") ggplot() +   # punkty w wybranej gminie   geom_point(data = dane_gmina, aes(long, lat, color = height)) +   # granice powiatu   geom_polygon(data = wojewodztwo[substr(wojewodztwo$jpt_kod_je, 1, 4) == "1217", ],                aes(long, lat, group=group), color = "gray50", fill = NA) +   scale_color_gradientn(colors = paleta_rgb) +   coord_map()  | 
					

Teraz podobnie jak wcześniej z Krakowem – dla łatwiejszej orientacji w położeniu naszych kolorowych punktów – nałóżmy je na mapę z Google Maps:
| 
					 1 2 3 4 5 6 7 8  | 
						# pobieramy mapę gminy (precyzyjniej: okolic jej środka) mapa_gg <- get_map(c(mean(dane_gmina$long), mean(dane_gmina$lat)), source = "google", maptype = "roadmap", zoom = 11) # narysyjmy punkty na tej mapie ggmap(mapa_gg) +   # punkty w wybranej gminie   geom_point(data = dane_gmina, aes(long, lat, color = height), alpha = 0.1) +   scale_color_gradientn(colors = paleta_rgb)  | 
					

Fajnie jest mieć dane podzielone po kodach TERYT, prawda? Mamy ogrom danych – zamiast trzymać je w plikach wrzućmy je do bazy. Idea jest podobna jak poprzednio dla województwa, z tą różnicą że tutaj musimy wykonać to samo dla wszystkich 16 plików. I zamiast zapisywać plik lokalnie – dodajemy kolejne wiersze do stosownej tabeli. W dużej części jest to powtórzenie powyższego kodu (ale dla ułatwienia daję całość).
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115  | 
						rm(list = ls()) # sprzątamy to co było do tej pory - tutaj zaczyna się oddzielny skrypt # dostęp do bazy danych dbname = "***" user = "***" password = "***" host = "***" # użyjemy bazy PostgreSQL db_connector <- dbDriver("PostgreSQL") # definicja układu współrzednych CRS_puwg1992 <- crs("+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +units=m +no_defs ") CRS_wgs84 <- crs("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs;") # układ WGS84 (współrzędne "normalne") # wczytujemy mapę gmin gminy_shp <- readOGR("../!mapy_shp/gminy.shp", "gminy") gminy_shp <- spTransform(gminy_shp, CRS_wgs84) # lista plików z danymi o województwach i odpowiadające im kody TERYT województw pliczki <- tribble(~plik, ~teryt,                    "dolnoslaskie.txt", "02",                    "kujawsko_pomorskie.txt", "04",                    "lubelskie.txt", "06",                    "lubuskie.txt", "08",                    "lodzkie.txt", "10",                    "malopolskie.txt", "12",                    "mazowieckie.txt", "14",                    "opolskie.txt", "16",                    "podkarpackie.txt", "18",                    "podlaskie.txt", "20",                    "pomorskie.txt", "22",                    "slaskie.txt", "24",                    "swietokrzyskie.txt", "26",                    "warminsko_mazurskie.txt", "28",                    "wielkopolskie.txt", "30",                    "zachodniopomorskie.txt", "32") # funkcja sprawdza czy punkty z f_dataframe są w ramach f_shape i dodaje im kod TERYT gminy add_TERYT_code <- function(f_dataframe, f_shape) {   # wyimek przerabiamy na SHP   coordinates(f_dataframe) <- c("long", "lat")   projection(f_dataframe) <- CRS_puwg1992   f_dataframe <- spTransform(f_dataframe, CRS_wgs84)   # macierz przypisania punktu do powiatu - to trochę trwa   miejscowosci_mat <- gWithin(f_dataframe, f_shape, byid = TRUE) %>% t()   # teraz numer kolumny to numer powiatu, numer wiersza to numer punktu   # bierzemy punkty z powrotem do tabelki   punkty <- as.data.frame(f_dataframe@coords)   # dodajemy ich wysokość   punkty$height <- f_dataframe@data$height   # na podstawie numeru kolumny z TRUE dla każdego wiersza wypełniamy wartość z kodem TERYT gminy   punkty$TERYT_gmn <- miejscowosci_mat %>%     apply(1, function(x) min(which(x, arr.ind = TRUE), na.rm=TRUE)) %>%     as.character(f_shape$jpt_kod_je)[.]   # zwracamy gotową paczkę danych   return(punkty) } # wielkość paczki step_i <- 100 # podpinamy się do bazy danych db_con <- dbConnect(db_connector,              dbname = dbname,              user = user, password = password,              host = host) # dla kazdego wojewodztwa for(woj_num in 1:nrow(pliczki)) {   # jaki plik i jaki kod TERYT wojweództwa?   dane_plik <- paste0("dane/", as.character(pliczki[woj_num, 1]))   woj_TERYT <- as.character(pliczki[woj_num, 2])   # wybieramy gminy z odpowiedniego województwa - żeby nie sprawdzać dla całego kraju   wojewodztwo <- gminy_shp[str_sub(gminy_shp$jpt_kod_je, 1, 2) == woj_TERYT, ]   # wyczytujemy dane o wysokości punków w województwie   dane_nmt <- read_delim(dane_plik, " ", col_names = c("long", "lat", "height"), col_types = "ddd")   # ile paczek będzie?   n_steps <- ceiling(nrow(dane_nmt)/step_i)   # jedziemy plik z wysokościami, w paczkach   for(i in 1:n_steps) {     # bierzemy jedna paczkę punktów     dane_nmt_small <- dane_nmt[ ((i-1)*step_i+1):(i*step_i), ] %>% na.omit()     # dopisujemy kody TERYT gminy do punktów     punkty <- add_TERYT_code(dane_nmt_small, wojewodztwo)     # z TERYT gminy robimy TERYT województwa (pierwsze 2 znaki) i TERYT powiatu (pierwsze 4 znaki)     punkty <- punkty %>%       mutate(TERYT_woj = str_sub(TERYT_gmn, 1, 2),              TERYT_pow = str_sub(TERYT_gmn, 1, 4))     # dopisujemy wynik do pełnej listy w bazie     dbWriteTable(db_con, "nmt_polska", punkty,                  append = TRUE, row.names = FALSE)   } } # odłączamy się od bazy danych dbDisconnect(db_con)  | 
					
Gotowe! Tylko kilkadziesiąt godzin i sprawa załatwiona. Ale za to jak uprości to dalszą pracę! Zobaczmy sami.
Najpierw parametry dostępu do bazy danych:
| 
					 1 2 3 4 5 6  | 
						rm(list = ls()) # sprzątamy to co było do tej pory - tutaj zaczyna się oddzielny skrypt dbname = "***" user = "***" password = "***" host = "***"  | 
					
A teraz się z nią łączymy i zapytaniami w SQL wyciągamy średnią wysokość na poziomie gminy, powiatu i województwa:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | 
						sterownik <- dbDriver("PostgreSQL") polaczenie <- dbConnect(sterownik, dbname = dbname, user = user, password = password, host = host) # pobieramy zagregowane dane na 3 poziomach # srednia wysokość w wojewodztwie sr_h_woj <- dbGetQuery(polaczenie,'            SELECT "TERYT_woj", AVG(height) AS height FROM nmt_polska GROUP BY "TERYT_woj";') # srednia wysokość w powiecie sr_h_pow <- dbGetQuery(polaczenie,'            SELECT "TERYT_pow", AVG(height) AS height FROM nmt_polska GROUP BY "TERYT_pow";') # srednia wysokość w gminie sr_h_gmn <- dbGetQuery(polaczenie,'            SELECT "TERYT_gmn", AVG(height) AS height FROM nmt_polska GROUP BY "TERYT_gmn";') dbDisconnect(polaczenie)  | 
					
Możemy już zamknąć połączenie z bazą danych. Jeśli spojrzycie w wielkość danych jakie zajmują w pamięci będziecie zachwyceni ;) Całą matematykę (grupowanie i liczenie średnich) wykonał serwer bazodanowy – my mamy gotowy wynik.
Zobaczmy jak wyglądają średnie wysokości dla każdego z województw. Uwaga – poniżej do wczytania mapy (plików shp) i połączenia jej z danymi wykorzystuję pakiet sf – o wiele bardziej przyjemny w użyciu niż cała reszta readOGR() i tak dalej:
| 
					 1 2 3 4 5 6  | 
						wojewodztwa_sf <- read_sf("~/RProjects/!mapy_shp/wojewodztwa.shp") wojewodztwa_sf <- left_join(wojewodztwa_sf, sr_h_woj, by = c("jpt_kod_je" = "TERYT_woj")) ggplot(wojewodztwa_sf) +   geom_sf(aes(fill = height), color = "white", size = 0.1) +   scale_fill_distiller(palette = "RdYlGn")  | 
					

To samo możemy zrobić na poziomie powiatów:
| 
					 1 2 3 4 5 6 7  | 
						# mapa na poziomie powiatów powiaty_sf <- read_sf("~/RProjects/!mapy_shp/powiaty.shp") powiaty_sf <- left_join(powiaty_sf, sr_h_pow, by = c("jpt_kod_je" = "TERYT_pow")) ggplot(powiaty_sf) +   geom_sf(aes(fill = height), color = "white", size = 0.1) +   scale_fill_distiller(palette = "RdYlGn")  | 
					

oraz gmin:
| 
					 1 2 3 4 5 6 7  | 
						# mapa na poziomie gmin gminy_sf <- read_sf("~/RProjects/!mapy_shp/gminy.shp") gminy_sf <- left_join(gminy_sf, sr_h_gmn, by = c("jpt_kod_je" = "TERYT_gmn")) ggplot(gminy_sf) +   geom_sf(aes(fill = height), color = "white", size = 0.1) +   scale_fill_distiller(palette = "RdYlGn")  | 
					

W samych mapach oczywiście nie ma zaskoczenia – na południu mamy góry, a reszta to mniej więcej równina na jakiejś średniej wysokości… Właśnie – jakiej? Skorzystajmy z danych uśrednionych na poziom gmin:
| 
					 1  | 
						summary(gminy_sf$height)  | 
					
| 
					 1 2  | 
						##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.  ##   -0.1786  105.3366  155.2762  185.6863  226.9315 1204.1620  | 
					
| 
					 1 2  | 
						ggplot(gminy_sf) +   geom_histogram(aes(height), bins = 380)  | 
					

Z rozkładu widzimy, że 3/4 kraju jest na wysokości nie większej niż około 225 metrów. Gdyby poziom wód podniósł się o te 225 metrów byłby prawdziwy potop…
Na koniec możemy przygotować dwie funkcje, które na mapie z Google Maps narysują nam wysokość terenu – na podstawie kodu TERYT gminy lub powiatu (korzystając z danych zapisanych w bazie):
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42  | 
						library(tidyverse) library(ggmap) library(RPostgreSQL) dbname = "***" user = "***" password = "***" host = "***" sterownik <- dbDriver("PostgreSQL") wysokosc_gmina <- function(TERYT_gmn) {   polaczenie <- dbConnect(sterownik, dbname = dbname, user = user, password = password, host = host)   dane_gmina <- dbGetQuery(polaczenie, paste0('SELECT long, lat, height FROM nmt_polska WHERE "TERYT_gmn" = \'', TERYT_gmn ,'\';'))   dbDisconnect(polaczenie)   mapa_gg <- get_map(c(mean(dane_gmina$long), mean(dane_gmina$lat)), source = "google", maptype = "roadmap", zoom = 11)   ggmap(mapa_gg) +     geom_point(data = dane_gmina, aes(long, lat, color = height), alpha = 0.15) +     scale_color_distiller(palette = "RdYlGn") +     labs(x = "", y = "", color = "Wysokość n.p.m.") +     theme(legend.position = "bottom") } wysokosc_powiat <- function(TERYT_pow) {   polaczenie <- dbConnect(sterownik, dbname = dbname, user = user, password = password, host = host)   dane_gmina <- dbGetQuery(polaczenie, paste0('SELECT long, lat, height FROM nmt_polska WHERE "TERYT_pow" = \'', TERYT_pow ,'\';'))   dbDisconnect(polaczenie)   mapa_gg <- get_map(c(mean(dane_gmina$long), mean(dane_gmina$lat)), source = "google", maptype = "roadmap", zoom = 10)   ggmap(mapa_gg) +     geom_point(data = dane_gmina, aes(long, lat, color = height), alpha = 0.05) +     scale_color_distiller(palette = "RdYlGn") +     labs(x = "", y = "", color = "Wysokość n.p.m.") +     theme(legend.position = "bottom") }  | 
					
Zobaczmy kilka przykładów:
Warszawa:
| 
					 1  | 
						wysokosc_gmina("1465011")  | 
					

Kraków:
to czerwone po lewej to miejsce gdzie znajduje się klasztor w Tyńcu (Bielańsko-Tyniecki Park Krajobrazowy)
| 
					 1  | 
						wysokosc_gmina("1261011")  | 
					

powiat Nowy Dwór Gdański
czyli Żuławy Wiślane (ich fragment) – zwróćcie uwagę, że to co jest mocno zielone jest poniżej poziomu morza.
| 
					 1  | 
						wysokosc_powiat("2210")   | 
					

Bieszczady:
pięknie widać połoniny (dwa czerwone pasma nad Wetliną) oraz masyw z Tarnicą i Krzemieniem (ta większa czerwona plama)
| 
					 1  | 
						wysokosc_powiat("1801")  | 
					

okolice Karpacza:
tam gdzie najbardziej czerwono leży Wielki Szyszak
| 
					 1  | 
						wysokosc_powiat("0201")  | 
					

Z dzisiejszego postu nauczyliśmy się (mam nadzieję) przede wszystkim:
- 
- jak sprawdzić czy dany punkt leży w określonym obszarze – to ćwiczyliśmy w dopisywaniu kodu TERYT do punktów
 - jak połączyć dane punktowe z mapą
 - że pakiet sf jest bardzo wygodny jeśli chodzi o pliki SHP
 - jak dane zapisać do bazy danych oraz je z niej pobrać
 
 
Jeśli Ci się podobało – udostępniaj, ślij znajomym. I wracaj tutaj w przyszłości.
Super, zawsze wiele się uczę z tych przykładów!
Przy okazji, zainteresowałem się tematem przekształceń, odwzorowania: kod:
CRS_puwg1992 <- crs("+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +units=m +no_defs ")
# układ WGS84 (współrzędne "normalne")
CRS_wgs84 <- crs("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs;")
W pakiecie simple features (SF) można posługiwać się czterocyfrowym kodem (EPSG) odwzorowań już przy ładowaniu lub przy przekształcaniu danych, np:
dane_nmt %
sample_n(size = 1000, replace=F) %>%
st_as_sf( coords = c(„x”, „y”), crs = 2180, agr = „constant”, precision = 0.1) %>%
st_transform(crs = 4326) %>%
as_Spatial(.)
korekta, ucieło pierwszą linię:
dane_nmt =
read_delim(dane_plik, ” „, col_names = c(„x”, „y”, „z”), col_types = „ddd”) %>%
sample_n(size = 1000, replace=F) %>%
st_as_sf( coords = c(„x”, „y”), crs = 2180, agr = „constant”, precision = 0.1) %>%
st_transform(crs = 4326) %>%
as_Spatial(.)