import os
print(os.getcwd())
C:\Users\Krzysztof\Desktop
Algorytmy danych geoprzestrzennych
Wprowadzenie do PyQGIS
Krzysztof Dyba
PyQGIS to biblioteka języka Python udostępniająca interfejs programistyczny do aplikacji QGIS, dzięki czemu możliwa jest interakcja pomiędzy kodem napisanym w Pythonie, a środowiskiem i elementami QGIS (np. projekt, warstwy, funkcje, itd.). Biblioteka odgrywa kluczową rolę w automatyzacji zadań oraz tworzeniu niestandardowych narzędzi i wtyczek do rozszerzenia funkcjonalności QGIS. PyQGIS jest częścią projektu QGIS, który jest darmowy i otwarty.
Przykładowe zastosowania:
numpy
, scikit-learn
).Instalacja jest bardzo prosta – należy pobrać program QGIS z oficjalnej strony https://qgis.org/download/ i zainstalować według wyświetlanych instrukcji. Środowisko Python i podstawowe biblioteki są automatycznie instalowane wraz z QGIS.
Zasadniczo bibliotekę PyQGIS możemy wykorzystać na dwa sposoby:
QGIS ma wbudowaną konsolę Pythona, w której można wykonywać skrypty interaktywnie. Aby ją włączyć kliknij Wtyczki
> Konsola Pythona
w pasku narzędzi. Można również wykorzystać skrót klawiszowy: CTRL
+ ALT
+ P
. Oprócz konsoli, wbudowany jest bardzo prosty edytor kodu.
PyQGIS można również wykorzystać poza aplikacją QGIS w samodzielnych skryptach Pythona.
Sprawdź czy wszystko działa prawidłowo wykonując następujące polecenie:
from qgis.core import *
W folderze dane
znajduje się plik powiaty.gpkg, który zawiera granice powiatów w wielkopolsce z Państwowego Rejestru Granic. Żeby go wczytać należy wykorzystać klasę QgsVectorLayer i zdefiniować trzy argumenty:
Ścieżkę do pliku można zdefiniować na dwa sposoby. Pierwszy to podanie ścieżki bezwzględnej, tj. wskazanie dokładnej lokalizacji, w której znajduje się plik. Na przykład:
sciezka = "C:\Users\Krzysztof\Desktop\dane.csv"
# lub sciezka = "C:\\Users\\Krzysztof\\Desktop\\dane.csv"
Nie jest to jednak rekomendowana metoda, ponieważ zakłada, że struktura katalogów pomiędzy różnymi komputerami i systemami jest identyczna. Drugi sposób polega na podaniu ścieżki względnej. W tym przypadku podajemy lokalizację pliku względem bieżącego katalogu roboczego lub projektu. Aby dowiedzieć się, gdzie znajduje się katalog roboczy, możemy użyć metody getcwd()
z biblioteki os
, a do jego zmiany metody chdir()
. Uwaga: domyślna ścieżka katalogu roboczego w konsoli QGIS, a systemowej są różne!
import os
print(os.getcwd())
C:\Users\Krzysztof\Desktop
Zauważ również, że w zależności od systemu jest wykorzystywany różny separator katalogów w hierarchii. Aby prawidłowo wskazać ścieżkę do pliku należy wykorzystać metodę os.path.join()
.
print(os.path.join("projekt", "dane", "pomiary.csv"))
projekt\dane\pomiary.csv
Spróbujmy teraz wczytać nasz plik.
from qgis.core import QgsVectorLayer
# określenie ścieżki do pliku
= os.path.join("algorytmy-geoprzestrzenne", "dane", "powiaty.gpkg")
sciezka_do_pliku
# wczytanie warstwy wektorowej
= QgsVectorLayer(sciezka_do_pliku, "powiaty", "ogr")
wektor
# sprawdzenie czy warstwa została załadowana prawidłowo
print(wektor.isValid())
True
Warstwa została wczytana prawidłowo. Następnie możemy wyświetlić ją w QGIS (pod warunkiem, że kod wykonujemy w QGIS).
from qgis.core import QgsProject
QgsProject.instance().addMapLayer(wektor)
W kolejnym kroku możemy wyświetlić podstawowe informacje o warstwie, tj.:
# rodzaj geometrii
= wektor.geometryType()
rodzaj_geometrii = rodzaj_geometrii.name # zwraca nazwę
rodzaj_geometrii print("Rodzaj geometrii:", rodzaj_geometrii)
# zakres przestrzenny
= wektor.extent() # obiekt QgsRectangle
zakres = zakres.toString() # konwersja na tekst
zakres print("Zakres przestrzenny:", zakres)
# liczba obiektów
= wektor.featureCount()
liczba_obiekty print("Liczba obiektów:", liczba_obiekty)
# liczba atrybutów
= wektor.fields()
liczba_atrybuty = liczba_atrybuty.count()
liczba_atrybuty print("Liczba atrybutów:", liczba_atrybuty)
# układ współrzędnych
= wektor.crs()
uklad = uklad.authid() # zwraca kod EPSG
uklad print("CRS:", uklad)
Rodzaj geometrii: Polygon
Zakres przestrzenny: 281949.3437500000000000,360241.7500000000000000 : 507167.3125000000000000,645513.6250000000000000
Liczba obiektów: 35
Liczba atrybutów: 6
CRS: EPSG:2180
Do zapisu danych wektorowych można wykorzystać dwie klasy:
Pierwszy sposób używając QgsVectorFileWriter
.
from qgis.core import QgsVectorFileWriter, QgsCoordinateTransformContext
= "test.geojson"
output_path # opcje zapisu
= QgsVectorFileWriter.SaveVectorOptions()
options = "GeoJSON"
options.driverName = "UTF-8"
options.fileEncoding
= QgsVectorFileWriter.writeAsVectorFormatV3(
writer = wektor,
layer = output_path,
fileName = QgsCoordinateTransformContext(),
transformContext = options
options
)
if writer[0] != 0:
print("Błąd zapisu")
else:
print("OK")
Alternatywne podejście z zastosowaniem QgsVectorLayerExporter
.
from qgis.core import QgsVectorLayerExporter
= "test.geojson"
output_path = "GeoJSON"
output_format
QgsVectorLayerExporter.exportLayer(= wektor,
layer = output_path,
uri = "ogr",
providerKey = wektor.crs(),
destCRS = {"driverName": output_format} # jako słownik
options
)
if writer[0] != 0:
print("Błąd zapisu")
else:
print("OK")
Wczytanie danych rastrowych wygląda podobnie jak w przypadku danych wektorowych z tą różnicą, iż służy do tego klasa QgsRasterLayer. Wczytajmy plik DEM.tif
, który znajduje się w katalogu dane
.
from qgis.core import QgsRasterLayer
= os.path.join("algorytmy-geoprzestrzenne", "dane", "DEM.tif")
sciezka_do_pliku = QgsRasterLayer(sciezka_do_pliku, "DEM")
raster print(raster.isValid())
True
Po prawidłowym wczytaniu warstwy, również możemy ją zwizualizować w QGIS.
from qgis.core import QgsProject
QgsProject.instance().addMapLayer(raster)
Używając odpowiednich metod na rastrze możemy uzyskać następujące informacje:
print("Liczba kolumn (Szerokość):", raster.width())
print("Liczba wierszy (Wysokość):", raster.height())
print("Liczba kanałów:", raster.bandCount())
print("Zakres:", raster.extent().toString())
print("CRS:", raster.crs().authid())
print("Rozdzielczość (Rozmiar komórki):", raster.rasterUnitsPerPixelX(),
raster.rasterUnitsPerPixelY())
Liczba kolumn (Szerokość): 533
Liczba wierszy (Wysokość): 608
Liczba kanałów: 1
Zakres: 253698.3311999999859836,353734.3616000000038184 : 520058.2303000000538304,657570.7552000000141561
CRS: EPSG:2180
Rozdzielczość (Rozmiar komórki): 499.73714652908075 499.7309105263158
Co więcej, każdy z kanałów rastra zawiera dodatkowe informacje, takie jak:
# nasz raster składa się tylko z jednego kanału
for band in range(1, raster.bandCount() + 1):
print("Nazwa kanału:", raster.bandName(band))
print("Wartość NoData:", raster.dataProvider().sourceNoDataValue(band))
print("Typ danych:", raster.dataProvider().dataType(band).name)
print("Wartość minimalna:", raster.dataProvider().bandStatistics(band).minimumValue)
print("Wartość maksymalna:", raster.dataProvider().bandStatistics(band).maximumValue)
Nazwa kanału: Band 1
Wartość NoData: 9999.0
Typ danych: Float32
Wartość minimalna: -68.71467590332031
Wartość maksymalna: 459.58660888671875
Do zapisu danych rastrowych służy klasa QgsRasterFileWriter.
from qgis.core import QgsRasterFileWriter, QgsRasterPipe, QgsCoordinateTransformContext
= "test.tif"
output_path
= QgsRasterFileWriter(output_path)
writer "COMPRESS=LZW"]) # opcje jako lista
writer.setCreateOptions([= QgsRasterPipe()
pipe = raster.dataProvider()
provider set(provider.clone())
pipe.
= writer.writeRaster(
status = pipe,
pipe = provider.xSize(),
nCols = provider.ySize(),
nRows = provider.extent(),
outputExtent = provider.crs(),
crs = QgsCoordinateTransformContext()
transformContext
)
# writeRaster zwraca RasterFileWriterResult
if status != 0:
print("Błąd zapisu")
else:
print("OK")
wyswietl_metadane
), która zaprezentuje metadane warstwy wektorowej oraz rastrowej w czytelny sposób. Do sprawdzenia typu obiektu wykorzystaj funkcję isinstance
.