library("terra")
Dane satelitarne odgrywają kluczową rolę w systemach informacji geograficznej i wykorzystywane są w szerokim zakresie do kartowania, analizy pokrycia terenu, monitorowania zmian w środowisku, zarządzania kryzysowego czy rolnictwa precyzyjnego. Źródłem danych mogą być:
W kontekście ogromnych zbiorów danych satelitarnych nie sposób pominąć zasadniczych technologii ułatwiających ich przetwarzanie:
vsicurl
, który zapewnia bezpośredni dostęp
do zdalnych plików przez protokoły HTTP(S) i FTP bez konieczności
pełnego pobierania, tym samym minimalizując transfer danych.Przetwarzanie potokowe w kontekście analizy danych odnosi się organizacji przepływu pracy w sposób liniowy (sekwencyjny). Oznacza to, że dane przepływają przez serię etapów (funkcji), w których wynik jednego etapu służy jako wejście do kolejnego. W praktyce sprawia to, że kod jest bardziej przejrzysty i łatwiejszy do zrozumienia.
W R wbudowanym operatorem przypływu jest
|>
. Do jego zapisu można wykorzystać skrót klawiszowy
CTRL + SHIFT + M
, jednak wymaga to zaznaczenia opcji
Use native pipe operator
(zakłada
Code > Editing
) w RStudio.
Przykładowo, możemy wylosować 10 liczb z rozkładu normalnego,
następnie obliczyć wartość bezwzględną, posortować w kolejności rosnącej
i finalnie nadpisać obiekt dane
.
dane = rnorm(10)
dane |>
abs() |>
sort() -> dane
dane
## [1] 0.1542144 0.1747443 0.1949715 0.2234891 0.2378379 0.7143219 1.0003207
## [8] 1.0516586 1.3784942 2.0626081
Do obsługi katalogów STAC w R służy pakiet rstac, który umożliwia wyszukiwanie i pobieranie danych zgodnie z tym standardem.
library("rstac")
W pierwszym kroku należy zdefiniować źródło danych używając funkcji
stac()
. Wykaz źródeł można znaleźć na STAC Index. W naszym przykładzie
wykorzystamy usługę Earth
Search, która dostarcza m. in. zdjęcia satelitarne z Landsata oraz
Sentinela.
stac_source = stac("https://earth-search.aws.element84.com/v1")
stac_source
## ###rstac_query
## - url: https://earth-search.aws.element84.com/v1/
## - params:
## - field(s): version, base_url, endpoint, params, verb, encode
W wyniku tej operacji otrzymaliśmy obiekt rstac_query
,
który zawiera informacje o zapytaniu HTTP, które zostanie wysłane do
serwera. Możemy wyróżnić dwie metody wysyłania żądań:
get_request()
) – służy głównie do pobierania
danych z serwera. Dane przesyłane są poprzez adres URL jako parametry
zapytania.post_request()
) – służy głównie do przesyłania
danych na serwer. Dane są wysyłane w treści (body) żądania
HTTP.Wybór metody zależy od serwera.
Spróbujmy teraz odpytać serwer jakie zbiory danych znajdują się na nim.
kolekcje <- stac_source |>
collections() |>
get_request()
kolekcje
## ###Collections
## - collections (9 item(s)):
## - sentinel-2-pre-c1-l2a
## - cop-dem-glo-30
## - naip
## - cop-dem-glo-90
## - landsat-c2-l2
## - sentinel-2-l2a
## - sentinel-2-l1c
## - sentinel-2-c1-l2a
## - sentinel-1-grd
## - field(s): collections, links, context
W odpowiedzi otrzymaliśmy obiekt, który jest wielopoziomową listą
list. Można samodzielnie sprawdzić jego strukturę używając funkcji
View()
. Eksploracja takich obiektów jest bardziej
skomplikowana w porównaniu do obiektów jednowymiarowych (np. wektorów).
Przykładowo, jeżeli chcemy sprawdzić pozostałe dostępne zbiory danych,
to należy wykorzystać funkcje lapply()
, która wykonuje
iterację po każdym elemencie danych wejściowych (w tym przypadku listy),
stosując przy tym podaną funkcję. Jako wynika zwracana jest lista, którą
następnie można uprościć do wektora używając funkcji
unlist()
.
Dodatkowo, dla uproszczenia możemy zastosować funkcję lambda (inaczej
funkcję anonimową). Pozwala to zamienić słowo kluczowe
function()
na znak \()
, np. zamiast
lapply(lista, function(x) x^2)
będzie
lapply(lista, \(x) x^2)
.
kolekcje_nazwy = unlist(lapply(kolekcje$collections, \(x) x$id))
kolekcje_nazwy
## [1] "sentinel-2-pre-c1-l2a" "cop-dem-glo-30" "naip"
## [4] "cop-dem-glo-90" "landsat-c2-l2" "sentinel-2-l2a"
## [7] "sentinel-2-l1c" "sentinel-2-c1-l2a" "sentinel-1-grd"
Uwaga! Dane z Sentinela-2 znajdują się w dwóch kolekcjach, tj.
sentinel-2-l2a
(starsza) i sentinel-2-c1-l2a
(nowsza). Aktualnie trwa aktualizacja produktów do nowszej kolekcji
sentinel-2-c1-l2a
i ta powinna być preferowana, natomiast
starsza kolekcja w przyszłości nie będzie obsługiwana.
Teraz wyszukajmy dostępne produkty w ramach kolekcji
sentinel-2-c1-l2a
. Do wyszukiwania danych służy funkcja
stac_search()
i umożliwia ona zdefiniowanie takich
parametrów jak:
collections
)bbox
)datetime
)limit
)W tym przypadku żądanie musimy wysłać za pomocą metody POST.
stac_source |>
stac_search(
collections = "sentinel-2-c1-l2a",
bbox = c(22.5, 51.1, 22.6, 51.2), # xmin, ymin, xmax, ymax (WGS84)
datetime = "2023-01-01T00:00:00Z/2023-12-31T00:00:00Z", # RFC 3339
limit = 5) |>
post_request() -> obrazy
obrazy
## ###Items
## - matched feature(s): 288
## - features (5 item(s) / 283 not fetched):
## - S2A_T34UEB_20231230T094411_L2A
## - S2A_T34UFB_20231230T094411_L2A
## - S2A_T34UEB_20231227T093411_L2A
## - S2A_T34UFB_20231227T093411_L2A
## - S2B_T34UEB_20231225T094326_L2A
## - assets:
## aot, blue, cloud, coastal, granule_metadata, green, nir, nir08, nir09, preview, product_metadata, red, rededge1, rededge2, rededge3, scl, snow, swir16, swir22, thumbnail, tileinfo_metadata, visual, wvp
## - item's fields:
## assets, bbox, collection, geometry, id, links, properties, stac_extensions, stac_version, type
Zostało znalezionych 288 produktów, które spełniają zadane warunki. Możemy zobaczyć jakie warstwy (assets) i atrybuty (fields) są powiązane. Sprawdźmy przykładowe cechy produktu (properties).
# pierwsze 15 cech
names(obrazy$features[[1]]$properties)[1:15]
## [1] "created" "platform" "constellation"
## [4] "instruments" "eo:cloud_cover" "proj:epsg"
## [7] "proj:centroid" "mgrs:utm_zone" "mgrs:latitude_band"
## [10] "mgrs:grid_square" "grid:code" "view:azimuth"
## [13] "view:incidence_angle" "view:sun_azimuth" "view:sun_elevation"
Mamy dostęp do 44 cech, które opisują produkt, m. in. data pozyskania, nazwa platformy satelitarnej, procentowe zachmurzenie sceny, układ współrzędnych, ID sceny, itd.
Sprawdźmy zachmurzenie wyszukanych scen. Określone jest one w
atrybucie eo:cloud_cover
. W tym celu ponownie musimy
wykorzystać funkcję lapply()
.
unlist(lapply(obrazy$features, \(x) x$properties$"eo:cloud_cover"))
## [1] 71.93396 68.93441 83.81743 94.01084 99.99993
Wyszukane sceny charakteryzują się wysokim zachmurzeniem w związku z
czym ich przydatność jest ograniczona. Oczywiście możemy dokonać prostej
filtracji modyfikując nasze poprzednie zapytanie. Do filtrowania
przeznaczona jest funkcja ext_query()
.
stac_source |>
stac_search(
collections = "sentinel-2-c1-l2a",
bbox = c(22.5, 51.1, 22.6, 51.2),
datetime = "2023-01-01T00:00:00Z/2023-12-31T00:00:00Z",
limit = 5) |>
ext_query(`eo:cloud_cover` < 10) |>
post_request() -> obrazy
unlist(lapply(obrazy$features, \(x) x$properties$"eo:cloud_cover"))
## [1] 0.067071 0.043523 1.478151 0.678711 0.000757
Alternatywnie, zamiast używać wielopoziomowych list, możemy dokonać
konwersji obiektu obrazy
do “przestrzennej ramki danych”
(simple features data frame) za pomocą funkcji
items_as_sf()
. Jednakże, na ten moment jest ona
eksperymentalna i mogą pojawić się niespodziewane problemy.
df = items_as_sf(obrazy)
plot(sf::st_geometry(df)[1], main = "Zasięg sceny", axes = TRUE)
Po dokonaniu selekcji scen, możemy przystąpić do ich pobrania. Zasadniczo, można to wykonać na kilka różnych sposobów używając:
assets_download()
z pakietu
rstacdownload.file()
wbudowanej w
R/vsicurl/
dostępnego w pakietach obsługujących
GDAL, np. terraassets_download()
W funkcji assets_download()
możemy zdefiniować, które
warstwy chcemy pobrać (argument asset_names
). Jednak ma ona
pewien mankament związany z dość skomplikowanym zapisem ścieżek do
pobranych plików wynikający ze struktury katalogów STAC.
obrazy |>
items_select(1) |>
assets_download(asset_names = c("blue", "green", "red"))
Sceny satelitarne zazwyczaj zawierają niskorozdzielcze miniaturki w
kompozycji RGB. W przypadku danych z Sentinela-2 zapisane są one jako
thumbnail.jpg
. Jeśli chcemy szybko podejrzeć dane, to
możemy odwołać się do tej warstwy i pozyskać do niej odnośnik przy
pomocy funkcji assets_url()
. Do wczytania pliku .jpg
wymagany jest dodatkowy pakiet, np. imager, który
posiada funkcję load.image()
.
library("imager")
par(mar = c(0, 0, 0, 0)) # usuń marginesy
obrazy |>
items_select(1) |>
assets_select(asset_names = "thumbnail") |>
assets_url() |>
load.image() |>
plot(axes = FALSE)
download.file()
Tak jak w powyższym przypadku, możliwe jest pozyskanie odnośników do
pozostałych warstw i pobranie danych przy użyciu funkcji
download.file()
, wymagającej wskazania odnośników, z
których zostaną pobrane dane oraz ścieżek zapisu.
obrazy |>
items_select(1) |>
assets_select(asset_names = c("blue", "green", "red")) |>
assets_url() -> urls
urls
## [1] "https://e84-earth-search-sentinel-data.s3.us-west-2.amazonaws.com/sentinel-2-c1-l2a/34/U/EB/2023/11/S2A_T34UEB_20231107T093339_L2A/B02.tif"
## [2] "https://e84-earth-search-sentinel-data.s3.us-west-2.amazonaws.com/sentinel-2-c1-l2a/34/U/EB/2023/11/S2A_T34UEB_20231107T093339_L2A/B03.tif"
## [3] "https://e84-earth-search-sentinel-data.s3.us-west-2.amazonaws.com/sentinel-2-c1-l2a/34/U/EB/2023/11/S2A_T34UEB_20231107T093339_L2A/B04.tif"
Utwórzmy nowy folder o nazwie sentinel
używając funkcji
dir.create()
. Do stworzenia ścieżek zapisu należy podać
nazwę pliku wraz z rozszerzeniem. W tym celu można zastosować funkcję
basename()
do wyodrębnienia tych nazw z odnośników.
Następnie należy połączyć ścieżkę do katalogu ze ścieżką do pliku
wykorzystując funkcję file.path()
.
dir.create("sentinel")
rastry = file.path("sentinel", basename(sentinel))
Finalnie, zastosujemy pętlę, która pobierze wszystkie pliki do lokalizacji określonej przez nas.
for (i in seq_along(sentinel)) {
download.file(sentinel[i], rastry[i], mode = "wb")
}
/vsicurl/
Ostatnim sposobem jest wykorzystanie modułu /vsicurl/
pochodzącego z GDAL bez potrzeby pobierania całego zbioru danych.
Działanie jest proste – wystarczy dodać przedrostek
/vsicurl/
do odnośników z rastrami używając funkcji
paste0()
.
urls = paste0("/vsicurl/", urls)
r = rast(urls)
r
## class : SpatRaster
## dimensions : 10980, 10980, 3 (nrow, ncol, nlyr)
## resolution : 10, 10 (x, y)
## extent : 499980, 609780, 5590200, 5700000 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 34N (EPSG:32634)
## sources : B02.tif
## B03.tif
## B04.tif
## names : B02, B03, B04
## min values : 1, 1, 473
## max values : 18640, 17728, 17104
Więcej informacji na temat wyszukiwania i pozyskiwania danych za pomocą standardu STAC znajdziesz w oficjalnym tutorialu Download data from a STAC API using R, rstac, and GDAL napisanym przez Michael Mahoney.
9. Pobierz dowolną scenę satelitarną z Sentinela-2 o niskim zachmurzeniu (maksymalnie 20%) i następnie: