This section introduces geospatial dataset conversion with Python. In particular, the goal of this section is to guide to an understanding of conversions from raster to vector data formats and vice versa. For interactive reading and executing code blocks and find geo03-conversion.ipynb, or install Python and JupyterLab locally.
Watch this section as a video
Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.
Import relevante Bibliotheken¶
Stellen Sie sicher, die relevanten Pakete für die Verarbeitung von Rastern, Formdateien und geospatialen Referenzen zu importieren:
from osgeo import gdal
from osgeo import osr
from osgeo import ogrVectoris¶
Raster nach Linie¶
In diesem Abschnitt konvertieren wir den least cost path raster dataset (least cost.tif) in eine (Poly)-Line Shapefile. Dazu schreiben wir zunächst eine Funktion namens offset2coords(), die das Inverse der coords2offset()-Funktion darstellt, und wandelt x/y Offset (in integer Pixelnummern) in Koordinaten der Geotransformation eines Geodatensatzes um:
def offset2coords(geo_transform, offset_x, offset_y):
# get origin and pixel dimensions from geo_transform (osgeo.gdal.Dataset.GetGeoTransform() object)
origin_x = geo_transform[0]
origin_y = geo_transform[3]
pixel_width = geo_transform[1]
pixel_height = geo_transform[5]
# calculate x and y coordinates
coord_x = origin_x + pixel_width * (offset_x + 0.5)
coord_y = origin_y + pixel_height * (offset_y + 0.5)
# return x and y coordinates
return coord_x, coord_yAls nächstes können wir eine Kernfunktion schreiben, um einen Rasterdatensatz in eine Zeilenformdatei zu konvertieren. Wir nennen diese Funktion raster2line() und sie baut auf dem folgenden Workflow:
Öffne eine
raster, ihre Band alsarrayundgeo_transform(geo-transformation) mit demraster_file_nameArgument in der open_raster-Funktion aus dem Raster.Berechnen Sie den maximalen Abstand (
max_distance) zwischen zwei Pixeln, die als connect-able gelten, basierend auf der Hypothese, dass die Pixelhöhe Δy und Breite Δx gleich sind:
Erhalten Sie die
trajectoryvon Pixeln, die einen benutzerdefiniertenpixel_value(z.B.1, um 1-Pixel im binären least cost.tif) zu verfolgen und einen Fehler zu werfen, wenn die Trajektorie leer ist (z.B.np.count_nonzero(trajectory) == 0).Verwenden Sie die oben definierte
offset2coords-Funktion, um Punktkoordinaten an einepoints-Liste anzupassen.Erstellen Sie ein
multi_lineObjekt (Instance vonogr.Geometry(ogr.wkbMultiLineString)), das den (void) letzten Kostenpfad darstellt.Iterate through all possible combinations of points (excluding combinations of points with themselves) with
itertools.combinations(iterable, r=number-of-combinations=2).Punkte werden in der
points-Liste gespeichert.Um die Distanz zwischen Punktenpaaren zu erreichen, sind
point1undpoint2erforderlich.Ist die
distancezwischen den Punkten kleiner alsmax_distance, so erstellt die Funktion ein Zeilenobjekt aus den beiden Punkten und gibt es an dasmulti_lineObjekt an.
Erstellen Sie eine neue Formdatei (Name
out_shp_fn) unter Verwendung der create_shp()funktion (mit integrierter Formdateiname Längenverifikation von flusstoolsgeotools.create_shp()).Fügen Sie das
multi_line-Objekt als neue Funktion in die Shapefile ein (gemäß den Beschreibungen im shapefile section).Erstellen Sie eine
.prj-Projektionsdatei (Recall-Beschreibungen in der shapefile section) unter Verwendung des räumlichen Referenzsystems der Eingaberastermit der get_srs()-Funktion.
The raster2line function is also implemented in the flusstools.geotools.geotools script.
import os
import itertools
import numpy as np
from osgeo import ogr
from flusstools.geotools import (
raster2array, open_raster, create_raster, create_shp, get_srs, make_prj,
)
def raster2line(raster_file_name, out_shp_fn, pixel_value):
"""
Convert a raster to a line shapefile, where pixel_value determines line start and end points
:param raster_file_name: STR of input raster file name, including directory; must end on ".tif"
:param out_shp_fn: STR of target shapefile name, including directory; must end on ".shp"
:param pixel_value: INT/FLOAT of a pixel value
:return: None (writes new shapefile).
"""
# calculate max. distance between points
# ensures correct neighbourhoods for start and end pts of lines
raster, array, geo_transform = raster2array(raster_file_name)
pixel_width = geo_transform[1]
max_distance = np.ceil(np.sqrt(2 * pixel_width**2))
# extract pixels with the user-defined pixel value from the raster array
trajectory = np.where(array == pixel_value)
if np.count_nonzero(trajectory) == 0:
print("ERROR: The defined pixel_value (%s) does not occur in the raster band." % str(pixel_value))
return None
# convert pixel offset to coordinates and append to nested list of points
points = []
count = 0
for offset_y in trajectory[0]:
offset_x = trajectory[1][count]
points.append(offset2coords(geo_transform, offset_x, offset_y))
count += 1
# create multiline (write points dictionary to line geometry (wkbMultiLineString)
multi_line = ogr.Geometry(ogr.wkbMultiLineString)
for i in itertools.combinations(points, 2):
point1 = ogr.Geometry(ogr.wkbPoint)
point1.AddPoint(i[0][0], i[0][1])
point2 = ogr.Geometry(ogr.wkbPoint)
point2.AddPoint(i[1][0], i[1][1])
distance = point1.Distance(point2)
if distance < max_distance:
line = ogr.Geometry(ogr.wkbLineString)
line.AddPoint(i[0][0], i[0][1])
line.AddPoint(i[1][0], i[1][1])
multi_line.AddGeometry(line)
# write multiline (wkbMultiLineString2shp) to shapefile
new_shp = create_shp(out_shp_fn, layer_name="raster_pts", layer_type="line")
lyr = new_shp.GetLayer()
feature_def = lyr.GetLayerDefn()
new_line_feat = ogr.Feature(feature_def)
new_line_feat.SetGeometry(multi_line)
lyr.CreateFeature(new_line_feat)
# create projection file
srs = get_srs(raster)
make_prj(out_shp_fn, int(srs.GetAuthorityCode(None)))
print("Success: Wrote %s" % str(out_shp_fn))Die raster2line()-Funktion kann wie folgt aufgerufen werden, um den kostengünstigsten Pfad vom Pixel (raster) in das Zeilenformat (Vektor) umzuwandeln:
source_raster_fn = r"" + os.path.abspath("") + "/geodata/rasters/least-cost.tif"
target_shp_fn = r"" + os.path.abspath("") + "/geodata/shapefiles/least-cost.shp"
pixel_value = 1
raster2line(source_raster_fn, target_shp_fn, pixel_value)Success: Wrote /home/schwindt/jupyter/geodata/shapefiles/least_cost.shp

Figure 1:Der Raster des am wenigsten kostenpfads, der in eine Linienformdatei umgewandelt wird, zeigt in QGIS.
Raster nach Polygon¶
gdal kommt mit der leistungsstarken Polygonize-Funktion für die einfache Umwandlung eines Rasterdatensatzes in eine Polygonformdatei. gdal.Polygonize ermöglicht es, einfach zu schreiben raster2polygon() Python-Funktion, es hat den Nachteil, dass es nur ganzzahlige Werte verarbeiten kann und es nur zufällig FID (automatisch zugeschriebene Kennungen ohne physikalische Bedeutung) Werte standardmäßig Attribute. Da die FID-Werte nicht aussagekräftig sind, erstellen wir eine float2int()-Funktion, um den ursprünglichen Wertebereich zu erhalten (verwendet die Funktionen raster2array() und create_raster() aus dem Rasterbereich):
def float2int(raster_file_name, band_number=1):
"""
:param raster_file_name: STR of target file name, including directory; must end on ".tif"
:param band_number: INT of the raster band number to open (default: 1)
:output: new_raster_file_name (STR)
"""
# use raster2array function to get raster, np.array and the geo transformation
raster, array, geo_transform = raster2array(raster_file_name, band_number=band_number)
# convert np.array to integers
try:
array = array.astype(int)
except ValueError:
print("ERROR: Invalid raster pixel values.")
return raster_file_name
# get spatial reference system
src_srs = get_srs(raster)
# create integer raster
new_name = raster_file_name.split(".tif")[0] + "_int.tif"
create_raster(new_name, array, epsg=int(src_srs.GetAuthorityCode(None)),
rdtype=gdal.GDT_Int32, geo_info=geo_transform)
# return name of integer raster
return new_nameAls nächstes erstellen wir die raster2polygon()-Funktion, die den folgenden Workflow implementiert:
Verwenden Sie die
float2int()-Funktion, um sicherzustellen, dass alle bereitgestellten rasterfile_namein rein ganze Werte umgewandelt werden können.Erstellen Sie eine neue Formdatei (Name
out_shp_fn) mit der create_shp()-Funktion (auch erhältlich bei flusstools:geotools.create_shp()).Fügen Sie ein neues
ogr.OFTIntegerFeld (Recall how field creation works) in der Formfile-Sektion hinzu, das durch das optionalefield_name-Eingabe-Argument benannt wird.Run
gdal.Polygonizewith:hSrcBand=raster_bandhMaskBand=None(optionale Rasterband zur Definition von Polygonen)hOutLayer=dst_layeriPixValField=0(if no field was added, set to-1in order to create anFIDfield; if more fields were added, set to1,2, ... )papszOptions=[](kein Effekt fürESRI ShapefileTreibertyp)callback=Nonefür die Nichtnutzung des Berichtsalgorithmus (GDALProgressFunc())
Erstellen Sie eine
.prj-Projektionsdatei (Recall-Beschreibungen in der shapefile section) unter Verwendung des räumlichen Referenzsystems der Eingaberastermit der get_srs()-Funktion.
def raster2polygon(file_name, out_shp_fn, band_number=1, field_name="values"):
"""
Convert a raster to polygon
:param file_name: STR of target file name, including directory; must end on ".tif"
:param out_shp_fn: STR of a shapefile name (with directory e.g., "C:/temp/poly.shp")
:param band_number: INT of the raster band number to open (default: 1)
:param field_name: STR of the field where raster pixel values will be stored (default: "values")
:return: None
"""
# ensure that the input raster contains integer values only and open the input raster
file_name = float2int(file_name)
raster, raster_band = open_raster(file_name, band_number=band_number)
# create new shapefile with the create_shp function
new_shp = create_shp(out_shp_fn, layer_name="raster_data", layer_type="polygon")
dst_layer = new_shp.GetLayer()
# create new field to define values
new_field = ogr.FieldDefn(field_name, ogr.OFTInteger)
dst_layer.CreateField(new_field)
# Polygonize(band, hMaskBand[optional]=None, destination lyr, field ID, papszOptions=[], callback=None)
gdal.Polygonize(raster_band, None, dst_layer, 0, [], callback=None)
# create projection file
srs = get_srs(raster)
make_prj(out_shp_fn, int(srs.GetAuthorityCode(None)))
print("Success: Wrote %s" % str(out_shp_fn))Die raster2polygon()-Funktion kann z.B. implementiert werden, um den Wassertiefenraster für 1000 CFS (h001000.tif vom River Architect Sample datasets) in eine Polygon-Formdatei umzuwandeln:
src_raster = r"" + os.path.abspath("") + "/geodata/rasters/h001000.tif"
tar_shp = r"" + os.path.abspath("") + "/geodata/shapefiles/h_poly_cls.shp"
raster2polygon(src_raster, tar_shp)Success: Wrote /home/schwindt/jupyter/geodata/shapefiles/h_poly_cls.shp

Figure 2:Das Raster der Wassertiefen umgewandelt in eine Polygon-Formdatei mit Zonen, in QGIS gezeigt.
Rasterize (Vector Shapefile zu Raster)¶
Similar to gdal.Polygonize, gdal.RasterizeLayer represents a handy option to convert a shapefile into a raster. However, to be precise, a shapefile is not really converted into a raster but burned onto a raster. Thus, values stored in a field of a shapefile feature are used (burned) as pixel values for creating a new raster. Attention is required to ensure that the correct values and data types are used. To this end, the below shown rasterize() function implements the following workflow that avoids potential conversion headaches:
Öffnen Sie den benutzerdefinierten Eingabeformdateinamen und -schicht.
Lesen Sie die räumliche Ausdehnung der Schicht.
Ableiten der x-y-Auflösung in Abhängigkeit von der räumlichen Ausdehnung und einem benutzerdefinierten
pixel_size(optional Keyword Argument mit Standardwert).Erstellen eines neuen GeoTIFF-Rasters mit
benutzerdefinierte
output_raster_file_name,berechnete x- und y-Auflösung und
eType(default istgdal.GDT_Float32- Rufen Sie an alle im raster section aufgeführten Datentyp-Optionen.
Die durch die Quellschichterstreckung definierte Geotransformation und die
pixel_sizeanwenden.Erstellen Sie einen raster
band, füllen Sie diebandmit dem benutzerdefiniertenno_data_value(Standard ist-9999) und setzen Sie dieno_data_value.Setzen Sie das räumliche Referenzsystem des Rasters auf das gleiche wie die Quellformdatei.
Apply
gdal.RasterizeLayerdataset=target_dsbands=[1](list(integer) - erhöhen Sie auf definierte mehr Rasterbänder und vergeben andere Werte, zum Beispiel aus anderen Feldern der Quellformdatei),layer=source_lyr(Layer mit Features zum Verbrennen an den Raster),pfnTransformer=None(weiterlesen in den gdal docs)pTransformArg=None(weiterlesen in den gdal docs)burn_values=[0](ein Standardwert, der an den Raster gebrannt wird),options=["ALL_TOUCHED=TRUE"]definiert, dass alle Pixel, die von einem Polygon berührt werden, den Feldwert des Polygons erhalten - wenn nicht gesetzt: nur Pixel, die vollständig im Polygon sind, einen Wert zugewiesen werden,options=["ATTRIBUTE=" + str(kwargs.get("field_name"))]definiert den Feldnamen mit Werten zu verbrennen.
def rasterize(in_shp_file_name, out_raster_file_name, pixel_size=10, no_data_value=-9999,
rdtype=gdal.GDT_Float32, **kwargs):
"""
Converts any shapefile to a raster
:param in_shp_file_name: STR of a shapefile name (with directory e.g., "C:/temp/poly.shp")
:param out_raster_file_name: STR of target file name, including directory; must end on ".tif"
:param pixel_size: INT of pixel size (default: 10)
:param no_data_value: Numeric (INT/FLOAT) for no-data pixels (default: -9999)
:param rdtype: gdal.GDALDataType raster data type - default=gdal.GDT_Float32 (32 bit floating point)
:kwarg field_name: name of the shapefile's field with values to burn to the raster
:return: produces the shapefile defined with in_shp_file_name
"""
# open data source
try:
source_ds = ogr.Open(in_shp_file_name)
except RuntimeError as e:
print("Error: Could not open %s." % str(in_shp_file_name))
return None
source_lyr = source_ds.GetLayer()
# read extent
x_min, x_max, y_min, y_max = source_lyr.GetExtent()
# get x and y resolution
x_res = int((x_max - x_min) / pixel_size)
y_res = int((y_max - y_min) / pixel_size)
# create destination data source (GeoTIff raster)
target_ds = gdal.GetDriverByName('GTiff').Create(out_raster_file_name, x_res, y_res, 1, eType=rdtype)
target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
band = target_ds.GetRasterBand(1)
band.Fill(no_data_value)
band.SetNoDataValue(no_data_value)
# get spatial reference system and assign to raster
srs = get_srs(source_ds)
try:
srs.ImportFromEPSG(int(srs.GetAuthorityCode(None)))
except RuntimeError as e:
print(e)
return None
target_ds.SetProjection(srs.ExportToWkt())
# RasterizeLayer(Dataset dataset, int bands, Layer layer, pfnTransformer=None, pTransformArg=None,
# int burn_values=0, options=None, GDALProgressFunc callback=0, callback_data=None)
gdal.RasterizeLayer(target_ds, [1], source_lyr, None, None, burn_values=[0],
options=["ALL_TOUCHED=TRUE", "ATTRIBUTE=" + str(kwargs.get("field_name"))])
# release raster band
band.FlushCache()Schließlich kann die rasterize()-Funktion aufgerufen werden, die polygonisierte Wassertiefe Polygon Shapefile /geodata/shapefiles/h poly cls.shp (download es als zip file) zurück zu einem Raster (dies ist praktisch nutzlos, aber eine illustrative Übung). Achten Sie auf den Datentyp, der gdal.GDT_Int32 in Kombination mit dem korrekt definierten field_nameArgument ist.
src_shp = r"" + os.path.abspath("") + "/geodata/shapefiles/h_poly_cls.shp"
tar_ras = r"" + os.path.abspath("") + "/geodata/rasters/h_re_rastered.tif"
rasterize(src_shp, tar_ras, pixel_size=5, rdtype=gdal.GDT_Int32, field_name="values")
Figure 3:Der rekonvertierte Raster von Wassertiefen basierend auf der in QGIS gezeigten Polygon-Formdatei mit Tiefenzonen.