Mapy hipsometryczne
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:
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:
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:
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:
# 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.
# 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:
# 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?
# 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?
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:
# 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
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:
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")
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ę:
# 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:
# 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ść).
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:
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:
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:
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:
# 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:
# 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:
summary(gminy_sf$height)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -0.1786 105.3366 155.2762 185.6863 226.9315 1204.1620
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):
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:
wysokosc_gmina("1465011")

Kraków:
to czerwone po lewej to miejsce gdzie znajduje się klasztor w Tyńcu (Bielańsko-Tyniecki Park Krajobrazowy)
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.
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)
wysokosc_powiat("1801")

okolice Karpacza:
tam gdzie najbardziej czerwono leży Wielki Szyszak
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.