Dane rastrowe obsługiwane są przez bibliotekę GDAL. Biblioteka dostarcza narzędzi do otwarcia, zapisu i manipulacji ponad 100 formatów rastrowych. Lista formatów znajduje się na stronie projektu GDAL: (http://gdal.org/formats_list.html).
Proces zarządzania danymi rozpoczyna się od ustalenia lokalizacji pliku i przygotowania dla niego ścieżki dostępu. Należy pamietać o użyciu ścieżki dostępu właściwej dla systemu operacyjnego oraz uniwersalnej nazwy użytkownika. Skrypty pythona są znacznie częściej używane poza środowiskiem Windows.
Geoprzestrzenny obiekt rastrowy to obraz rozszerzony o parametry geoprzestrzenne pozwalające na lokalizację każdego pixela (komórki rastra) w odniesieniu do przestrzenii geograficznej.
import os #dostosować ścieżkę dostępu
path = os.path.join(os.path.expanduser("~"),"Dropbox","dydaktyka","program_geodata","01_raster")
rastfile = os.path.join(path,"data","dem.tif")
print rastfile
Do zarządzania danymi będziemy potrzebowali biblioteki GDAL, którą trzeba zaimportować (form osgeo). Następnie przy pomocy plecenia open() otwieramy połączenie z plikem. Otwarcie połączenia nie wczytuje danych.
from osgeo import gdal
raster = gdal.Open(rastfile)
Dostępne funkcje można sprawidzć poleceniem dir(raster). Najważniejsze informacje można pozyskać na temat obiketu raster to przy pomocy wybranych funkcji (z () na końcu) lub atrybutów (bez ()):
Pozwala określić jakiego typu jest plik z danymi wejściowymi. Często zależy nam aby zapisać wyniki w tym samym formacie
raster.GetDriver().LongName
Wiele zbiorów geoprzestrzennych posiada więcej niż jedno pasmo. Pliki RGB 3, multispektralne - więcej. Minimalna liczba to 1. Będzie wykorzystywana przy pobieraniu danych.
raster.RasterCount
Określa gdzie zapisany jest plik wejściowy
raster.GetDescription()
Podstawowe informacje o pochodzeniu pliku
raster.GetMetadata()
ilość wierszy (XSize) i kolumn (YSize)
raster.RasterXSize,raster.RasterYSize # rozmiary zwraca tuplet
Elipsoida i układ odniesienia
raster.GetProjection() # projekcja
Parametry pozwalające przeliczyć siatkę rastra (macierz wartości) na punkty w przestrzeni geograficznej:
gt=raster.GetGeoTransform() # geotransformacje
gt
Parametry geo-transformacji pozwalają przeliczyć dowolne współrzędne siatki (wiersz i kolumna) na współrzędne geograifczne, na przykład dla komórki col,row=(7,2)
col,row=(7,2)
x = gt[0] + col * gt[1] + row * gt[2]
y = gt[3] + col * gt[4] + row * gt[5]
x,y
Lub poprzez funkcję ApplyGeoTransform()
x,y=gdal.ApplyGeoTransform(gt,col,row)
x,y
W przeciwnym kierunku:
col = int((x - gt[0]) / gt[1])
row = int((y - gt[3]) / gt[5])
col,row
W celu pobrania danych należy wskazać pasmo, z którego będziemy pobierać dane. Wszystkie pasma posiadają ten sam zakres przestrzenny i rozdzielczość (to cecha rastra) ale mogą posiadać inny typ danych i inną reprezentację wartości pustej (NULL). Wybrane dostępne właściwości to:
band = raster.GetRasterBand(1)
Dostępne typy danych:
GDT_Unknown = 0, GDT_Byte = 1, GDT_UInt16 = 2, GDT_Int16 = 3, GDT_UInt32 = 4, GDT_Int32 = 5, GDT_Float32 = 6, GDT_Float64 = 7, GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11, GDT_TypeCount = 12
band.DataType #zwraca integer,
Dla każdego pasma można określić podstawowe statystyki: Min, Max, Avg, Sd
band.GetStatistics(1,0) # zgodność z orginalną funkcją C, pierwszy parametr: akceptacja wartości przybliżonych, drugi wymuszenie przeliczania
Zwraca wartość symbolizującą wartość pustą
band.GetNoDataValue() #Nieokreślona
Do pełniejszej charakterystyki warstwy możemy użyć histogramu, który jest jedną z najpopularniejszych metod charakteryzowania rozkładu zmiennej. W trakcie obliczania hitogramu można podać zakres wartości i ilość przedziału historamu w wyniku otrzymamy listę liczb całkowitych określających liczbę komórek w każdym przedziale.
import math
minVal,maxVal,meanVal,sdVal=band.GetStatistics(0,1) # (wyjasnic expand tuple)
nbin=100
hist=band.GetHistogram(math.floor(minVal),math.floor(maxVal),nbin) # min, max , planowana liczba
print hist
W następnym kroku przygotujemy pomocnicze elementy do wyświetlenia histogramu. Użyjemy bliblotek numpy i matplotlib do przygotowania wykresu:
import numpy as np
x=np.arange(nbin) # zbudowanie listy przedziałów
xtics=np.linspace(math.floor(minVal),math.floor(maxVal),nbin/10) # zbudowanie listy etykiet (co dziesiąty przedział)
xat=np.linspace(0,nbin,nbin/10) # zbudowanie listy lokalizacji etykiet
import matplotlib.pyplot as plt
plt.bar(x, np.asarray(hist),linewidth=0,width=1,color='#A11207') #dodanie histogramu
plt.xticks(xat, xtics.astype(int)) # dodanie etykiet jako liczb całkowitych, brak cudzysłowia
plt.show()
Dane do daleszego przetwarzania najprościej jest pobrać w postaci dwuwymiarowej tablicy numpy, nunkcją ReadAsArray(). Należy uważać na wielkość pliku wiejściowego.
arr=band.ReadAsArray()
arr
plt.imshow(arr)
plt.show()
Osie zostaną zmodyfikowane do ukladu odniesienia oraz dodany zostanie pasek legendy. Dla kolorów zostanie wybrana inna paleta: (https://matplotlib.org/examples/color/colormaps_reference.html) Do wyznaczenia zasięgów osi, zostaną wykorzystane geotransformacje:
east,south = gdal.ApplyGeoTransform(gt,raster.RasterXSize,raster.RasterYSize) # prawy dolny
west,north = gdal.ApplyGeoTransform(gt,0,0) #lewy górny
plt.imshow(arr,cmap='viridis',extent=[west,east,south,north])
plt.colorbar()
plt.show()
Przydatna opcja jeżeli zależy nam na poznaniu dokładych wartości. Dostosujemy zasięgi poprzez modyfikację geotransformat. Należy wyłączyć domyślną interpolację aby zobaczyć pojedyncze komórki. inne przykłady interpolacji można znaleźć w linku. (https://matplotlib.org/examples/images_contours_and_fields/interpolation_methods.html)
Dodanie wartości (zaokrąglonych) komórek zostanie wykonane przy pomocy funkcji text. W takiej sytuacji nie możemy zastosować współrzędnych geograficznych (przynajmniej w prosty sposób). W celu nadpisania wartości została wykonana tablica wspórzędnych (np.meshgrid), która następnie została połączona we wspólną listę współrzędnych.
col1,row1=(40,10) #lewy górny róg
col2,row2=(50,20) # prawy dolny róg
arrP=arr[col1:col2,row1:row2]
#fig,ax=plt.subplots()
plt.imshow(arrP,cmap='viridis',interpolation='none') #Nie można użyć extend
ind_array = np.arange(0, 10, 1)
x, y = np.meshgrid(ind_array, ind_array)
for x_val, y_val in zip(x.flatten(), y.flatten()):
plt.text(x_val, y_val, '%.0f' % arrP[x_val, y_val], va='center', ha='center')
plt.colorbar()
plt.show()
Aby utworzyć raster (grid) musimy znać docelowy rozmiar, parametry geotransformacji (north,west,resolutution) oraz układ odniesienia. W ramach prezentacji wykonamy tablicę o rozmiarze $100 \times 100$,dla którego następnie zostaną wyliczone pozostałe parametry. Taka tablica nie ma żadnego odniesienia geoprzestrzennego, jest jedynie zbiorem liczb poukładanych w wiersze i kolumny. Istnieje kilka funkcji inicjujących taką macierz, my użyjemy funkcję zeros(), która wypełnia tablicę zerami, jako typ przyjmiemy unit8 czyli Byte. W następnych krokach tablica zostanie wypełniona różnymi wartościami, tworząc mozaikę. Wartości przypisywane są wycinkom tablicy, wybieramym poprzez indeksowanie:
arr=np.zeros((100,100),dtype='uint8') # tuplet jako argument
arr[10:20,15:38]=1
arr[36:78,21:66]=2
arr[81:100,81:100]=3
arr[0:3,0:3]=4
arr[45:72,11:44]=6
plt.imshow(arr)
plt.show()
Zostanie utworzony przy pomocy funkcji ImportFromEPSG(), która buduje odpowiedni układ na podstawie podanego kodu (użyjemy 2180). Więcej o układach odniesienia w osobnej części kursu.
from osgeo import osr
epsg=2180
spatialRef = osr.SpatialReference()
spatialRef.ImportFromEPSG(epsg)
spatialRef.ExportToWkt()
Po wyborze parametrów geotransformacji tworzymy plik rastrowy. W pierwszej kolejności wybieramy format pliku (driver - tu użyjemy najbardziej uniwersalny GeoTIFF), a następnie:
Taki plik nie zawiera danych.
north=450000
west=520000
res=250
drv=gdal.GetDriverByName('GTiff')
target = drv.Create("mozaika.tif", arr.shape[0],arr.shape[1], 1, gdal.GDT_Byte)
target.SetProjection(spatialRef.ExportToWkt())
target.SetGeoTransform((west, res, 0, north, 0, -res))
na tym etapie należy określić jaka wartość będzie reprezentowała wartość pustą. Użyjemy maxymalnej, 255. następnie musimy pobrać pasmo danych (band) i przypisać do niego wartość NODATA, dane WriteArray() oraz wyczyścić chache.
noData=255
tband=target.GetRasterBand(1)
tband.SetNoDataValue(noData)
tband.WriteArray(arr)
tband.FlushCache()
tband.ComputeStatistics(True)
Ostatnim krokiem jest zamknięcie pliku. Realizujemy to poprzez przypisanie obiektowy rastra wartoci pustej (None)
target=None
Aby utworzyć nowy obiekt rastrowy można skorzystać z funkcji CreateCopy z biblioteki GDAL, która pozwala utworzyć kopię otwartego pliku rastrowego, zawierającego wszystkie niezbędne informację, w tym również projekcję, zasięg, rozdzielczość itp. Ponieważ w analizach geoprzestrzennych najczęściej obiekt wynikowy będący efektem przetworzenia danych naczęściej obejmuje ten sam obszar co dane źródłowe jest to szybka forma przygotowania odpowiedniego zbioru źródłowego. Zarówno format źródłowy jak i docelowy są od siebie niezależne, ale nie wszystkie formaty są obsługiwane.
Po utworzeniu kopii rastra można nadpisać dane a następnie przeładować Cache. Ostatnim krokiem jest zamknięcie obu dowiązań do obiektów (rasterCopy = None)
# Orginalne dane
raster = gdal.Open(rastfile)
dane = raster.GetRasterBand(1).ReadAsArray()
# kopia danych
driver = gdal.GetDriverByName("GTiff")
rasterCopy = driver.CreateCopy(os.path.join(path,"demCopy.tif"),raster,strict=0)
# Przetwarzanie danych
dane = dane / 0.2991 # przeliczenie na stopy
# Zapis danych
rasterCopy.GetRasterBand(1).WriteArray(dane)
rasterCopy.GetRasterBand(1).FlushCache()
# Zamknięcie plików
raster = None
rasterCopy = None
del dane