In [1]:
# setup
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import warnings
warnings.filterwarnings('ignore')
In [2]:
import sqlite3
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
os.chdir('/home/jarekj/Dropbox/dydaktyka/programowanie4/powiatData')

Czytanie kolekcji danych rastrowych do pandas dataFrame

Modelowanie predykcyjne stosuje się również, a może przede wszystkim dla danych rastrowych. Wymaga to jednak przetworzenia kolekcji plików rastrowych do postaci tabelarycznej, gdzie każdy wiersz będzie odpowiadał komórce o określonej lokalizacji a każda kolumna określonej warstwie rastrowej. Tak zorganizowane dane noszą nazwę uporządkowanych (ang. tidy) i podobnie jak atrybuty piku wektorowgo odpowiadają wymogą pierwszej postaci normalnej. Oczywiście zakładamy, że dane spełniają wymóg zgodności geoprzestrzennej, który na tym etapie nie jest sprawdzany (zob. poprzednie części kursu na temat sprawdzania zgodności geoprzestrzennej). Poniważ dane rastrowe składają się z bardo dzużej ilości komórek, przed zamianą do postaci dataFrame należy w pierwszej kolejności ocenić wielkości przetwarzanego pliku aby nie zbudować ramki danych która nie będzie mogła być obsłużona w pamięci. W przypadku, gdy dane są zbyt duże należy zastosować inne rozwiązania (kótre zostaną omówione w innej części kursu). Należy pamiętać, że dane rastrowe zbudowane są tylko z danych liczbowych, które mogą przechowywać zarówno wartości (liczby zmiennoprzecinkowe i "duże" typy całkowitoliczbowe) oraz kategorie (typy całkowite bez znaku: unit8 i unit16) przechowujace tylko wartości nieujemne. Oznacza to że w jednej dataFrame możemy mieć kolumny o różnych typach danych.

Kolejność importu kolekcji warst rastrowych jest następująca:

  1. Należy przygotować listę plików które zostaną przetworzone do. Listę można przygotować ręcznie lub poprzez skanowanie katalogu
  2. Pandas wymagają nazw kolumn. W tym celu z nazw plików trzeba usunąć rozszerzenia aby przygotować nazwy kolumn dla dataFrame (nie są dodawane automatycznie)
  3. Należy przygotować pusty słownik: jest to najprostrza metoda budowania dataFrame
  4. Następnie w pętli otwieramy po kolei wszystkie pliki i wczytujemy ich zawartość do kolejnych kluczy słownika (A) w formie spłaszczonej (B), gdzie kluczem jest nazwa kolumny a wartością - odczytany numpy array
  5. Po zakończeniu pętli przekształcamy słownik do dataFrame

Jako przykład zostannie przeanalizowana kolekcja danych zaierająca zarówno dane kategoryzowane jak i numeryczne. Kolekcja zostanie przygotowana przy pomocy pętli skanującej katalog i pobierającej wszystkie pliki z rozszerzeniem *tif.

In [3]:
from osgeo import gdal
os.chdir("/home/jarekj/Dropbox/dydaktyka/programowanie4/landData/")
files=[f for f in os.listdir(".") if f.endswith('.tif')] #1
files.sort()
files
colnames=[os.path.splitext(file)[0] for file in files] #2

dct = {} #3
for file,colname in zip(files, colnames): #4A
    raster = gdal.Open(file)
    dct[colname] = raster.GetRasterBand(1).ReadAsArray().flatten() #4B

df = pd.DataFrame(dct)
df.sample(8)
Out[3]:
['01_roughness.tif',
 '02_green.tif',
 '03_distwaters.tif',
 '04_terange.tif',
 '05_imperm.tif',
 '06_highbuildup.tif',
 '07_buildup.tif',
 '08_road_dens.tif',
 '10_landuse.tif',
 '20_dzielnice.tif']
Out[3]:
01_roughness 02_green 03_distwaters 04_terange 05_imperm 06_highbuildup 07_buildup 08_road_dens 10_landuse 20_dzielnice
27603 0.868271 0.352435 2388.484863 1.074829 0.0072 0.0 0.0000 0.000000 5 255
39398 6.003937 0.445743 2022.740112 0.967308 0.2040 0.0 0.0156 38646.696660 5 1
79462 22.534790 0.577222 60.114624 6.535461 0.0000 0.0 0.0000 0.000000 5 255
9677 0.441919 0.109512 1678.234009 1.712570 0.0000 0.0 0.0000 268.642478 5 255
76681 5.512113 0.369144 1913.184082 6.925018 0.0000 0.0 0.0000 0.000000 5 3
58533 0.701714 0.436900 3999.983643 1.865013 0.4132 0.0 0.1000 13175.404085 2 255
19591 9.246553 0.531803 1741.514771 1.542725 0.1340 0.0 0.0000 4155.518098 5 2
67898 8.612882 0.347178 1526.074219 3.858002 0.2384 0.0 0.0800 14332.280673 5 255
In [4]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 81000 entries, 0 to 80999
Data columns (total 10 columns):
01_roughness      79715 non-null float64
02_green          79715 non-null float32
03_distwaters     79715 non-null float32
04_terange        79715 non-null float64
05_imperm         79715 non-null float64
06_highbuildup    79715 non-null float64
07_buildup        79715 non-null float64
08_road_dens      79715 non-null float64
10_landuse        81000 non-null uint8
20_dzielnice      81000 non-null uint8
dtypes: float32(2), float64(6), uint8(2)
memory usage: 4.5 MB

Note: Komórki które nie zawierają wartości

W zbiorach danych liczbowych i kategoryzowanych wartości reprezentowane są przez liczby oznaczające wartości lub kategorie. Często jednak w danym miejscu wartości nie są zdefiniowane. W przypadku danych mamy następujące możliwości:

  • wartość w danym miejscu nie jest znana lub nie należy do zbioru
  • wartości w danym miejscu nie udało się wyliczyć (najczęściej jako efekt niedozwolonej operacji matematycznej: na przykład pierwiastek z liczby ujemnej) lub jest nieskończona - na przykład w wyniku dzielenia przez 0

Wartości nie będące liczbą nie powinny występować i są reprezentowane pzez wartości puste. W danych geoprzestrzennych wartości puste mogą być one reprezentowane jako "nie liczba" (NaN, w praktyce wartość reprezentująca nie-liczbę) lub może to też być dowolna wartość liczbowa która nie występuje w zakresie danych i symbolizuje wartość pustą. Często jest to wartość -9999 w plikach z wartościami numerycznymi. W przypadku danych całkowitych bez znaku (uint8 i uint16), które są stosowane do danych kategoryzowanych są to wartości 0 lub 255.

W języku Python w numpy nie ma możliwości reprezentowania wartości pustej przez nie-liczbę dla typów całkowitych. W zamian stosuje się liczbę całkowitą - najczęściej 255. W każdym przypadku, niezależnie od typu danych gdy wartość pusta jest reprezentowana przez liczbę po wczytaniu danych do dataFrame będzie ona zwykłą wartością. Aby wskazać ją jako wartość pustą, należy ręcznie zamienić wszystkie wystąpienia liczbowe reprezentujące wartość pustą na symbole not-a-number.

Aby wprowadzić do danych wartości puste należy ponownie przetworzyć listę plików i po otwarciu pliku pobrać informację o NodataValue do zmiennej. Następnie należy sprawdzić czy zmienna jest zdefiniowana (nie jest None) i czy jest liczbą (nie jest NaN). Jeżeli nie jest zdefiniowana ma wartość None oznacza to że albo w zbiorze danych nie ma wartości pustych albo plik został źle przygotowany. jeżeli natomiast jest zdefiniowana należy przeprowadzić test czy wartość jest liczbą. Najprościej wykonać go poprzez porównanie zmiennej z... samą sobą. Otóż jeżeli porównamy dwie nie-liczby, w wyniku otrzymamy zawsze wartość False. Zakładając że zmienna ma nazwę nodata test wygląda następująco: if nodata and nodata == nodata: Jeżeli zmienna jest zdefiniowana i jest reprezentowana przez liczbę należy zamienić ją na np.NaN w dataFrame. Problem polega na tym, że zamiana serii liczbowej na np.nan spowoduje kowersję typu danych do float64 (numerycznego) co nie jest porządane. W związku z tym kolumny co do których uważamy że przechowują dane kategoryzowane należy zamienić na typ kategory. Uznajemy że dane kategoryzowane ograniczajś się do typu uint8 (_GDTByte).

W tym celu dla każdej kolumny, sprawdzamy czy nie dane nie są typu Byte i zamieniamy ją na typ kategoryzowany (astype('category')). Następnie, niezależnie od typu danych każda która posiada wartość pustą reprezentowaną przez liczbę należy wszystkie wystąpienia liczby zamienić na np.NaN. Wykorzystujemy do tego metodę df.replace(): df[colname] = df[colname].replace(nodata,np.NaN)

In [5]:
for file,colname in zip(files, colnames):
    raster = gdal.Open(file)
    band = raster.GetRasterBand(1)
    if band.DataType == gdal.GDT_Byte:
        df[colname] = df[colname].astype('category')
    nodata = band.GetNoDataValue()
    if nodata and nodata == nodata: # test czy jest to liczba
        df[colname] = df[colname].replace(nodata,np.NaN)
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 81000 entries, 0 to 80999
Data columns (total 10 columns):
01_roughness      79715 non-null float64
02_green          79715 non-null float32
03_distwaters     79715 non-null float32
04_terange        79715 non-null float64
05_imperm         79715 non-null float64
06_highbuildup    79715 non-null float64
07_buildup        79715 non-null float64
08_road_dens      79715 non-null float64
10_landuse        81000 non-null category
20_dzielnice      26222 non-null category
dtypes: category(2), float32(2), float64(6)
memory usage: 4.5 MB

Oczywiście całą procedurę najlepiej zamknąć w funkcji. Funkcja jako argument przyjmuje listę nazw plików w formie rozwijanej krotki. Jeżeli zamierzamy przekazać gotową listę należy ją poprzedzić operatorem rozwijania krotki: *.

In [6]:
def rasterToDataFrame(*files):
    dct = {}
    colnames=[os.path.splitext(file)[0] for file in files]
    for file,colname in zip(files, colnames):
        raster = gdal.Open(file)
        dct[colname] = raster.GetRasterBand(1).ReadAsArray().flatten()
    
    df = pd.DataFrame(dct)
    
    for file,colname in zip(files, colnames):
        raster = gdal.Open(file)
        band = raster.GetRasterBand(1)
        if band.DataType == gdal.GDT_Byte:
            df[colname] = df[colname].astype('category')
        nodata = band.GetNoDataValue()
        if nodata and nodata == nodata: # test czy jest to liczba
            df[colname] = df[colname].replace(nodata,np.NaN)

    return df

data = rasterToDataFrame(*files)
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 81000 entries, 0 to 80999
Data columns (total 10 columns):
01_roughness      79715 non-null float64
02_green          79715 non-null float32
03_distwaters     79715 non-null float32
04_terange        79715 non-null float64
05_imperm         79715 non-null float64
06_highbuildup    79715 non-null float64
07_buildup        79715 non-null float64
08_road_dens      79715 non-null float64
10_landuse        81000 non-null category
20_dzielnice      26222 non-null category
dtypes: category(2), float32(2), float64(6)
memory usage: 4.5 MB

Zapis kolumny data frame do pliku rastrowego

Aby zapisać wybraną kolumnę do pliku rastrowego należy wykonać czynności w odwrotnej kolejności co do jego czytania. Dodatkowo należy określić wymiary rastra i docelowy typ danych. Najprostszym sposobem pozyskania informacji o rozmiarze pliku jest użycie jednego z plików wejściowych jako źródła rozmiaru, projekcji i geotransformacji (parametry te zostały opisane w innej części kursu).

Cała procedura zostanie zamknięta w postaci funkcji. Funkcja przyjmuje cztery argumenty: dwa z nich, seria danych oraz raster referencyjny są obowiązkowe, dwa kolejne: nazwa pliku oraz wartość nodata są opcjonalne. W przypadku braku nazwy zostanie ona pobrana z nazwy serii danych (1), natomiast wartość nodata - dla danych zmiennoprzecinkowych przyjmuje wartość -9999. Jako seria danych może zostać przekazana zarówno kolumna dataFrame jak i jednowymiarowa tablica numpy. W pierwszej kolejności funkcja sprawdza czy dane są typu kategoryzowanego i w takiej sytuacji nastąpi zamiana na typ uint8 (2). Następnie funkcja sprawdzi czy dostarczona seria jest typu seria pandas czy numpy array (3), i w przypadku gdy jest to seria danych zostanie zamieniona na numpy array. Następnie jeżeli w danych występują wartości nan (nie liczby) i zamienione są na wartości nodata (4).

Utworzenie pliku geoprzestrznnego wymaga podanie typu danych, który podawany jest w formie stałej GDT_. W tym celu korzystamy z funkcji NumericTypeCodeToGDALTypeCode z modułu gdal_array, która pozwala na określenie typu danych gdal na podstawie typu danych numerycznych tablicy numpy (5). Po określeniu typu danych pozostała część funkcji to standardowa procedura utworzenia nowego pliku danych, gdzie parametry rastra (za wyjątkiem typu danych) tworzona jest na podstawie rastra referencyjnego.

In [7]:
from osgeo import gdal_array

def seriesToRaster(series,raster,newfile=None,nodata=-9999):
    if not newfile:
        newfile = series.name + '.tif' #1
    
    if series.dtype.name== 'object':
        return False
    
    if series.dtype.name == 'category': #2
        nodata=0
        series.replace(np.NaN,nodata,inplace=True)
        series = series.astype('uint8')

    if type(series) == pd.core.series.Series: #3
        series = series.values
    series[np.where(np.isnan(series))]=nodata #4

    gdt_Type = gdal_array.NumericTypeCodeToGDALTypeCode(series.dtype) #5
    
    target = gdal.GetDriverByName('GTiff').Create(newfile, raster.RasterXSize, raster.RasterYSize, 1, gdt_Type) #6
    target.SetProjection(raster.GetProjection())
    target.SetGeoTransform(raster.GetGeoTransform())
    tband=target.GetRasterBand(1)

    series.shape=(raster.RasterYSize, raster.RasterXSize)
    tband.WriteArray(series)
    tband.FlushCache()
    tband.SetNoDataValue(nodata)
    target=None
    return True

Jako przykład dodamy kolumnę zawierającą dane losowe na obszarze zajmowanym przez granice miasta Poznania. Przygotowanie tej kolumny wymaga kilku narzędzi pandas, z którymi zapoznamy się w dalszej części kursu

In [8]:
complete = df['20_dzielnice'].notna()

df['wynik'] = 0
df['wynik'][complete] = np.random.randint(1,10,size=sum(complete))     
df['wynik'] = df['wynik'].astype('category')
df['wynik'].replace(0,np.NaN,inplace=True)

# funkcja wykonująca konwersję
seriesToRaster(df['wynik'],raster,'dzielnice.tif')
Out[8]:
True

Wizualizacja danych rastrowych

Wizualizacja danych geoprzestrzenych w Pythonie obejmuje co najmniej dwa zagadnienia:

  • wizualizację informacji przestrzennej (w postaci map lub serii map)
  • Wizualizację informacji nieprzestrzennej - w postaci diagramów, wykresów itp.

Podstawowy moduł GDAL nie zawiera żadnych narzędzi do wizualizacji danych geoprzestrzennych. Python nie posiada w struktur danych które były by odpowiedzialne bezpośrednio za dane geoprzestrzenne (poza mało przydatnym rasterio). Są one importowane bezpośrednio do formatu numpy array i tym samym cała informacja geoprzestrzenna zostajOznacza to że jeżeli chcemy wizualizować dane kategoryzowane - tabelę kolorów musimy importować osobno do formatu zrozumiałego dla języka Python jest o tyle istotne, że jeżeli chcemy używać tabeli barwnej w prezentacjach nieprzestrzennych musimy korelować kody barwne pomiędzy mapami a wykresami.

W ramach tej części kursu zapoznamy się z następującymi zagadnieniami:

  • jak wydobywać wbudowane tabele barwne z plików geoprzestrzennych jak wizualizować pojedyncze mapy i serie map wraz z ich własnymi paletami kolorów (jeżeli dostępne)
  • jak kolerować tabele barwne dla map i dla wykresów wykonywanych przy pomocy bibliotek seaborn i matplotlib

Import tabeli barwnej

tabela barwna jest własnością warstwy(band) i jest pozyskiwana funkcją band.GetRasterColorTable(). Jeżeli tabela nie jest dostępna - na przkład jest to plik numeryczny funkcja zwraca None. W ten sposób możemy sprawdzić czy tabela barwna istnieje. Tabel jest przechowywana jako krotka trzech lub czterech wartości (RGBA), natomiast dla języka Python lepiej jest przechowywać kod koloru w postaci szesnastkowej #RRGGBBAA. W tym celu można krotkę barwną zmodyfikować poprzez fomratowanie łańcucha tekstowego: "#{0:02x}{1:02x}{2:02x}".format(*ct.GetColorEntry(i), gdzie 02x oznacza prezentowanie wartości jako dwuznakowego kodu szesnastkowego a* przed wywołaniem funkcji to operator rozwinięcia krotki. e zredukowana do postaci macierzy liczb. Pozostałe elementy takie jak zasięg, typ danych, tabela kolorów są importowane do osobnych struktur.

Systemy informacji geograficznej używają kolorów do prezentacji informacji dla człowieka. Sposób obsługi różni się w zależości od typu danych przestrzennych

  • dane kategoryzowane - reprezentowane przez typ unit8 i unit16 (nieujemne), które mogą przechowywać wewnętrznie tabelę kolorów
  • dane numeryczne - reprezentowane przez pozostałe typy danych, które nie posiadają wbudowanej tabeli kolorów i są wizualizowane przy pomocy wskazanej tabeli barw.

Oznacza to że jeżeli chcemy wizualizować dane kategoryzowane - tabelę kolorów musimy importować osobno do formatu zrozumiałego dla języka Python jest o tyle istotne, że jeżeli chcemy używać tabeli barwnej w prezentacjach nieprzestrzennych musimy korelować kody barwne pomiędzy mapami a wykresami.

W ramach tej części kursu zapoznamy się z następującymi zagadnieniami:

  • jak wydobywać wbudowane tabele barwne z plików geoprzestrzennych jak wizualizować pojedyncze mapy i serie map wraz z ich własnymi paletami kolorów (jeżeli dostępne)
  • jak kolerować tabele barwne dla map i dla wykresów wykonywanych przy pomocy bibliotek seaborn i matplotlib

Import tabeli barwnej

tabela barwna jest własnością warstwy(band) i jest pozyskiwana funkcją band.GetRasterColorTable(). Jeżeli tabela nie jest dostępna - na przkład jest to plik numeryczny funkcja zwraca None. W ten sposób możemy sprawdzić czy tabela barwna istnieje. Tabel jest przechowywana jako krotka trzech lub czterech wartości (RGBA), natomiast dla języka Python lepiej jest przechowywać kod koloru w postaci szesnastkowej #RRGGBBAA. W tym celu można krotkę barwną zmodyfikować poprzez fomratowanie łańcucha tekstowego: "#{0:02x}{1:02x}{2:02x}".format(*ct.GetColorEntry(i), gdzie 02x oznacza prezentowanie wartości jako dwuznakowego kodu szesnastkowego a* przed wywołaniem funkcji to operator rozwinięcia krotki.

In [9]:
os.chdir('/home/jarekj/Dropbox/dydaktyka/programowanie4/landData')

raster = gdal.Open('10_landuse.tif')
band = raster.GetRasterBand(1)
ct   = band.GetRasterColorTable()

catlist = np.unique(band.ReadAsArray())
cols=[]
for i in catlist.tolist():
    cols.append("#{0:02x}{1:02x}{2:02x}".format(*ct.GetColorEntry(i)))

cols
Out[9]:
['#7f0000',
 '#f17372',
 '#f400cc',
 '#5bff42',
 '#fff8b4',
 '#005112',
 '#d5ff4d',
 '#1b2df2',
 '#69fffe']

Wizualizacja pojedynczej mapy z paletą barwną.

Wizualizację pojedynczej mapy bez palety barwnej - takie jak pobór danych, wyznaczenie zasięgu i wizualizacja macierzy została zaprezentowana w poprzedniej części kursu dlatego w tej części ograniczymy się jedynie do omówienia tych zagadnień, które dotyczą palety barwnej. Całość zostanie zamknięta w postaci uproszczonej funkcji, której efektem jest wyświetlenie mapy.

Po nawiązaniu połączenia z plikiem i pobraniu warstwy (band) pobierana jest tabela. Jeżeli istnieje, z danych pobierane są wartości unikalne lub histogram. Następnie dla każdej wartości unikalnej lub pozycji histogramu, która zawiera elementy budowany jest kod barwny i dodawany do listy kolorów. Jeżeli chcemy tę paletę kolorów użyć w bibliotece matplotlib musimy ją skonwertować do ListedColorMap z modułu colors biblioteki matplotlib. Ostatnim krokiem jest wyświetlenie mapy przy pomocy znanego już polecenia plt.imshow() ze wskazaniem krotki zawierającej zasięg oraz paletę kolorów.

In [10]:
from matplotlib import colors
def plot_raster_by_file(file,ax=None,cmap=None):
    '''
    docstring
    '''
    ax = ax or plt.gca() # pobranie aktywnego axes
    rast = gdal.Open(file)
    if not rast:
        print("data not found or not raster data")
        return None

    gt = rast.GetGeoTransform()

    east,south = gdal.ApplyGeoTransform(gt,rast.RasterXSize,rast.RasterYSize) # prawy dolny
    west,north = gdal.ApplyGeoTransform(gt,0,0) #lewy górny
    extent = [west,east,south,north]

    band = rast.GetRasterBand(1)
    arr = band.ReadAsArray()
    ct = band.GetRasterColorTable()

    if ct and not cmap:
        arr[arr == band.GetNoDataValue()]=0
        hist = band.GetHistogram()
        cols = ['#FFFFFF'] # simulate NA
        for i in range(ct.GetCount()):
            if hist[i]:
              cols.append("#{0:02x}{1:02x}{2:02x}".format(*ct.GetColorEntry(i)))
        cmap = colors.ListedColormap(cols)

    ax.imshow(arr,cmap=cmap,extent = extent)

Wyświetlanie kolekcji map

Aby wyświetlić kolekcję map musimy skorzystać z poprzednio utworzonej funkcji a następnie zastosować narzędzie suplots() z biblioteki matplotlib. Jak to wykonać?

W pierwszym kroku musimy pobrać listę wszystkich plików, które chcemy wizualizować. Gdy lista jest gotowa (może być wykonana ręcznie), możemy wykonać naszą rycinę (fig), którą definiujemy jako serię podfigur (subplots). Każda figura zawiera wykres (axes), która jest dodawana do pierwszej wolnej podfigury. Funkcja subplots zwraca dwa elementy: figurę oraz listę wykresów (axes) do których przy pomocy pętli dodajemy kolejne mapy. W naszym przykładzie zdefiniowaliśmy macierz wykresów o wymiarach 2 wiersze x 5 kolumn oraz wymiary obrazka 15 x 5 cali. Niestety, nie ma jednoznacznego klucza co do doboru wielkości, parametry te należy dobrać ręcznie.

Często niepożądanym efektem takich wykresów jest nakładanie się legend. Niestety, obrócenie etykiet pod pewnym kątem nie jest proste, a właściwie nie jest łatwe do znalezienia dla kolekcji wykresów. Aby to wykonać, należy po wykonaniu figury ale przed jej uruchoniemiem, dla każdego wykresu (axes) zmodyfikować rotację xticks to prządanej wartości (tu 45 stopni). Następnie można wyświetlić figurę.

Note: funkcje: sca i gca

Powyższe funkcje to sktóry od set current axes i get current axes są bardzo pomocne w sytuacji gdy włączymy wykresy na jednej figurze. Funkcje pochodzą z modułu pyplot biblioteki matplotlib. gca() pobiera aktywny wykres (axes) zgodny z argumentami, jeżeli nie może takiego pobrać tworzy nowy. sca() ustawia aktwne axes jako domyślne w danej figurze.

Kolory: Problem wartości pustych.

W plikach geoprzestrzennych wartość pusta jest reprezentowana przez wybraną liczbę, która w sposób jawny jest wskazana we właściwościach pliku geoprzestrzennego. Dla pliku numerycznego może do być liczba symbolizująca NaN (Not a Number) często jest to jednak liczba, której wartość nie występuje w zbiorze danych (często stosowana jest -9999). Dla danych kategoryzowanych może to być wartość 0 (nie ma kategorii zerowej) ale często stosuje się wartość 255. Ponieważ kategorii jest z reguły nie więcej niż kilkanaście w zbiorze danych, wartość 255 rozciąga nam histogram na pełen zakres wartości, co oznacza problemy z dopasowaniem palety kolorów. W takich sytuacjach lepiej jest zastąpić wartości "NULL" w pliku wartością 0 i przypisać dla tej barwy kolor biały. Natępnie dla pozostałych kategorii przypisujemy kolory we właściwej im kolejności.

In [11]:
os.chdir('/home/jarekj/Dropbox/dydaktyka/programowanie4/landData')
files=[f for f in os.listdir(".") if f.endswith('.tif')]
tmpraster = gdal.Open(files[2])
div = 1
xsize,ysize=(int(np.round(tmpraster.RasterXSize/div)),int(np.round(tmpraster.RasterYSize/div)))
In [12]:
files.sort()
files
Out[12]:
['01_roughness.tif',
 '02_green.tif',
 '03_distwaters.tif',
 '04_terange.tif',
 '05_imperm.tif',
 '06_highbuildup.tif',
 '07_buildup.tif',
 '08_road_dens.tif',
 '10_landuse.tif',
 '20_dzielnice.tif',
 'dzielnice.tif']
In [13]:
# Uruchomić całość w jednym kroku
fig, axes = plt.subplots(2, 5, sharex='all', sharey='all',figsize=(15,5))
for ax,file in zip(axes.flat[0:10],files):
  plot_raster_by_file(file,ax)

for ax in fig.axes:
    plt.sca(ax) # atywowanie axes
    plt.xticks(rotation=45)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 0 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 0 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 0 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 0 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 0 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 5 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 5 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 5 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 5 Text xticklabel objects>)
Out[13]:
(array([340000., 350000., 360000., 370000., 380000.]),
 <a list of 5 Text xticklabel objects>)

Aby wyświetlić histogram zgodny z kolorami danych rastrowych mapy należy wykorzystać wcześniej utworzoną paletę kolorów cols, która jest po prostu listą kolorów.

In [14]:
import seaborn as sns
sns.countplot(x="10_landuse",data=df.sample(10000),palette=cols)
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f2da48d8198>