Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Shapefile (Vector) Handling

This section introduces geospatial analysis of shapefiles with gdal, ogr, and osr. For interactive reading and executing code blocks Binder and find geo01-shp.ipynb, or install Python and JupyterLab locally.

Laden Sie eine bestehende Shapefile

OSGeo’s ogr module is an excellent source for handling shapefiles. After importing the libraries, the correct driver for handling shapefiles ("ESRI Shapefile") with Python needs to be called. Next, with the driver object (ogr.GetDriverByName("SHAPEFILE")), we can open (instantiate) a shapefile (object with shp_driver.Open("SHAPEFILE")), which contains layer information. It is precisely the layer information (i.e., references to shapefile attributes) that we want to work with. Therefore we have to instantiate a shapefile layer object with shp_dataset.GetLayer().

from osgeo import ogr
shp_driver = ogr.GetDriverByName("ESRI Shapefile")
shp_dataset = shp_driver.Open("geodata/shapefiles/cities.shp")
shp_layer = shp_dataset.GetLayer()

Erstellen Sie eine neue Formdatei

ogr ermöglicht auch eine neue Punkt-, Zeilen- oder Polygonformdatei zu erstellen. Der folgende Codeblock definiert eine Funktion zur Erstellung einer Formdatei, wobei das optionale Keyword-Argument overwrite verwendet wird, um zu kontrollieren, ob eine bestehende Formdatei mit dem gleichen Namen überschrieben werden soll (Standard: True). Der Befehl shp_driver.CreateDataSource(SHP-FILE-DIR) erstellt eine neue Formdatei und der Rest der Funktion fügt der Formdatei eine Schicht hinzu, wenn die optionalen Keyword-Argumente layer_name und layer_type bereitgestellt werden. Beide optionalen Schlüsselwörter müssen strings sein, wobei layer_name für die neue Schicht jeder Name sein kann. layer_type muss entweder "point", "line" oder "polygon" sein, um einen Punkt, (Poly)line oder Polygon formfile zu erstellen. Die Funktion verwendet den geometry_dict dictionary, um der Schicht den richtigen ogr.SHP-TYPE zuzuordnen. Weitere Optionen zur Erweiterung der create_shp(...)-Funktion finden Sie unter pcjericks’ Github page.

from osgeo import ogr
import os


def create_shp(shp_file_dir, overwrite=True, *args, **kwargs):
    """
    :param shp_file_dir: STR of the (relative shapefile directory (ends on ".shp")
    :param overwrite: [optional] BOOL - if True, existing files are overwritten
    :kwarg layer_name: [optional] STR of the layer_name - if None: no layer will be created (max. 13 chars)
    :kwarg layer_type: [optional] STR ("point, "line", or "polygon") of the layer_name - if None: no layer will be created
    :output: returns an ogr shapefile layer
    """
    shp_driver = ogr.GetDriverByName("ESRI Shapefile")

    # check if output file exists if yes delete it
    if os.path.exists(shp_file_dir) and overwrite:
        shp_driver.DeleteDataSource(shp_file_dir)

    # create and return new shapefile object
    new_shp = shp_driver.CreateDataSource(shp_file_dir)

    # create layer if layer_name and layer_type are provided
    if kwargs.get("layer_name") and kwargs.get("layer_type"):
        # create dictionary of ogr.SHP-TYPES
        geometry_dict = {"point": ogr.wkbPoint,
                         "line": ogr.wkbMultiLineString,
                         "polygon": ogr.wkbMultiPolygon}
        # create layer
        try:
            new_shp.CreateLayer(str(kwargs.get("layer_name")),
                                geom_type=geometry_dict[str(kwargs.get("layer_type").lower())])
        except KeyError:
            print("Error: Invalid layer_type provided (must be 'point', 'line', or 'polygon').")
        except TypeError:
            print("Error: layer_name and layer_type must be string.")
        except AttributeError:
            print("Error: Cannot access layer - opened in other program?")
    return new_shp

The create_shp function is also provided with flusstools (flusstools.geotools.create_shp()) and aids to create a new shapefile (make sure to get the directory right):

a_new_shp_file = create_shp(r"" + os.getcwd() + "/geodata/shapefiles/new_polygons.shp", layer_name="basemap", layer_type="polygon")

# release data source
a_new_shape_file = None

Shapefiles können auch in QGIS erstellt und gezeichnet werden und die folgenden Figuren führen durch die Prozedur der Erstellung einer Polygon-Formdatei. Wir brauchen nicht mehr die resultierende Formdatei in diesem Abschnitt, sondern später, damit sie mit Rasterdatensätzen interagiert.

The first step to making a shapefile with QGIS is obviously to run QGIS and create a new project. The following example uses water depth and flow velocity raster data as background information to delineate the so-called morphological unit of slackwater. Both the water depth and flow velocity rasters are part of the River Architect sample datasets (precisely located at RiverArchitect/SampleData/01_Conditions/2100_sample/). After downloading the sample data, they can be imported in QGIS by dragging the files from the Browser panel into the Layers panel. Then:

qgis create new shapefile

Figure 1:QGIS: Erstellen von Ebenen > Neue Formdateiebene...

qgis define new shapefile

Figure 2:Neue Formdateiebene definieren

qgis shapefile toggle-editing

Figure 3:Formdatei bearbeiten aktivieren

qgis draw polygon shapefile

Figure 4:Zeichne Polygone in einer Formdatei in QGIS.

Holen Sie sich und setzen Sie Shapefile-Projektionen

Die in den .prj-Dateien einer Formdatei verwendete Terminologie entspricht den Definitionen im Abschnitt geospatial data. In Python erhalten Sie Informationen über das Koordinatensystem unter shp_layer.GetSpatialRef() der ogr Bibliothek:

shp_srs = shp_layer.GetSpatialRef()
print(shp_srs)
GEOGCS["WGS 84",
    DATUM["WGS_1984",
        SPHEROID["WGS 84",6378137,298.257223563,
            AUTHORITY["EPSG","7030"]],
        AUTHORITY["EPSG","6326"]],
    PRIMEM["Greenwich",0,
        AUTHORITY["EPSG","8901"]],
    UNIT["degree",0.0174532925199433,
        AUTHORITY["EPSG","9122"]],
    AXIS["Latitude",NORTH],
    AXIS["Longitude",EAST],
    AUTHORITY["EPSG","4326"]]

Diese GEOGCS-Definition der obigen Formdatei entspricht Esri’s well-known Text (WKT). Da das Formdateiformat von Esri entwickelt wurde, muss esris WKT (esriwkt) Format in .prj Dateien verwendet werden. Das Open Geospatial Consortium (OGC) verwendet einen anderen bekannten Text in ihren EPSG:XXXXDefinitionen (z.B. erhältlich unter spatialreference.org).

GEOGCS["WGS 84",
       DATUM["WGS_1984", SPHEROID["WGS84", 6378137, 298.257223563, AUTHORITY["EPSG", "7030"]], AUTHORITY["EPSG","6326"]],
       PRIMEM["Greenwich", 0, AUTHORITY["EPSG", "8901"]],
       UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]

Um das Koordinatensystem einer Formdatei neu definieren oder neu definieren zu können, können wir spatialreference.org durch Pythons Standard urllibbibliothek verwenden.

import urllib

# function to get spatialreferences with epsg code
def get_esriwkt(epsg):    
    # usage get_epsg_code(4326)
    try:
        with urllib.request.urlopen("https://spatialreference.org/ref/epsg/{0}/esriwkt/".format(epsg)) as response:
            return str(response.read()).strip("b").strip("'")
    except Exception:
        pass
    try:
        with urllib.request.urlopen("https://spatialreference.org/ref/sr-org/epsg{0}-wgs84-web-mercator-auxiliary-sphere/esriwkt/".format(epsg)) as response:
            return str(response.read()).strip("b").strip("'")
        # sr-org codes are available at "https://spatialreference.org/ref/sr-org/{0}/esriwkt/".format(epsg)
        # for example EPSG:3857 = SR-ORG:6864 -> https://spatialreference.org/ref/sr-org/6864/esriwkt/ = EPSG:3857
    except Exception:
        print("ERROR: Could not find epsg code on spatialreference.org. Returning default WKT(epsg=4326).")
        return 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295],UNIT["Meter",1]]'

Diese Funktion kann verwendet werden, um eine neue Projektionsdatei zu erstellen:

# open the hypy-area shapefile
shp_file = "hypy-area"

# create new .prj file for the shapefile (.shp and .prj must have the same name)
with open("geodata/shapefiles/{0}.prj".format(shp_file), "w") as prj:
    epsg_code = get_esriwkt(4326)
    prj.write(epsg_code)
    print("Wrote projection file : " + epsg_code)
Wrote projection file : GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]

Eine Offline-Alternative zur Generierung einer .prj-Datei ist die osr-Bibliothek, die zusammen mit gdal:

from osgeo import osr

def get_wkt(epsg, wkt_format="esriwkt"):
    default = 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295],UNIT["Meter",1]]'
    spatial_ref = osr.SpatialReference()
    try:
        spatial_ref.ImportFromEPSG(epsg)
    except TypeError:
        print("ERROR: epsg must be integer. Returning default WKT(epsg=4326).")
        return default
    except Exception:
        print("ERROR: epsg number does not exist. Returning default WKT(epsg=4326).")
        return default
    if wkt_format=="esriwkt":
        spatial_ref.MorphToESRI()
    # return a nicely formatted WKT string (alternatives: ExportToPCI(), ExportToUSGS(), or ExportToXML())
    return spatial_ref.ExportToPrettyWkt()

Transformieren (Re-Projekt) a Shapefile

Eine Re-Projektion kann beispielsweise erforderlich sein, um eine Formdatei in EPSG:4326 (z.B. mit QGIS erstellt) in einer Web-GIS-Anwendung (z.B. offene Straßenkarten) zu verwenden, die typischerweise EPSG:3857 verwendet. Um eine andere Projektion auf geometrische Objekte einer Formdatei anzuwenden, reicht es nicht aus, die .prj-Datei einfach neu zu schreiben. Das folgende Beispiel zeigt daher die Neuprojektion der countries.shp formfile aus der Natural Earth quick start kit. Der folgende Workflow führt die Reprojektion durch:

  • Die zu transformierende Formdatei befindet sich im Unterverzeichnis geodata/shapefiles/countries.shp und öffnet sie wie oben beschrieben im Python-Skript.

  • Lesen und identifizieren Sie das räumliche Referenzsystem in der Eingabeformdatei:

    • Erstellen Sie ein räumliches Referenzobjekt mit in_sr = osr.SpatialReference(str(shapefile.GetSpatialRef())).

    • Erkennen Sie das räumliche Referenzsystem im EPSG-Format mit AutoIdentifyEPSG().

    • Zuordnen des EPSG-formatierten räumlichen Referenzsystems an das räumliche Referenzobjekt der Eingabeformdatei (ImportFromEPSG(int(in_sr.GetAuthorityCode(None)))).

  • Erstellen Sie die Ausgaberaumreferenz mit out_sr = osr.SpatialReference() und wenden Sie den Ziel-EPSG-Code an (out_sr.ImportFromEPSG(3857)).

  • Erstellen Sie ein Koordinatentransformationsobjekt (coord_trans = osr.CoordinateTransformation(in_sr, out_sr)), das später eine Neuprojektierung von Geometrieobjekten ermöglicht.

  • Erstellen Sie die Ausgabeformdatei, die einer Kopie der Eingabeformdatei entspricht (verwenden Sie die oben definierte create_shp Funktion mit layer_name="basemap" und layer_type="line").

  • Kopieren Sie die Feldnamen und Typen der Eingabeformdatei:

    • Lesen Sie die Attributschicht aus den Layer Definitionen der Eingabedatei mit in_lyr_def = in_shp_lyr.GetLayerDefn()

    • Iterate durch die Felddefinitionen und an die Ausgabeformfile-Schicht anhängen (out_shp_lyr)

  • Iterate durch die Geometriemerkmale in der Eingabeformdatei:

    • Verwenden Sie die Layer-Definitionen der neuen (Output-)Formdatei (out_shp_lyr_def = out_shp_lyr.GetLayerDefn()) um transformierte Geometrieobjekte später anzuhängen.

    • Definieren Sie eine Iterationsvariable in_feature als Instanz von in_shp_lyr.GetNextFeature.

    • In einer while-Schleife wandeln Sie jede Geometrie (geometry = in_feature.GetGeometryRef()) in der Eingabeformdatei um, wandeln Sie die geometry (geometry.Transform(coord_trans)) in eine ogr.Feature() mit der SetGeometry(geometry)-Methode, kopieren Sie Felddefinitionen (nästed for-loop) um und fügen Sie die neue Funktion an die Ausgabeformfile-Schicht (out_shp_lyr_def.CreateFeature(out_feature).

    • Am Ende des while-loop suchen Sie das nächste Feature in den Attributen der Eingabeformdatei mit in_feature = in_shp_lyr.GetNextFeature()

  • Entsperren Sie alle Schichten und Formdateien, indem Sie die Objekte mit None überschreiben (Nichts wird in jede Datei geschrieben, solange diese Variablen existieren!).

  • Senden Sie die neue Projektion EPSG:3857 mit der oben definierten get_wkt Funktion.

from osgeo import ogr
from osgeo import osr

shp_driver = ogr.GetDriverByName("ESRI Shapefile")

# open input shapefile and layer
in_shp = shp_driver.Open(r"" + os.path.abspath("") + "/geodata/shapefiles/countries.shp")
in_shp_lyr = in_shp.GetLayer()

# get input SpatialReference
in_sr = osr.SpatialReference(str(in_shp_lyr.GetSpatialRef()))
# auto-detect epsg
in_sr.AutoIdentifyEPSG()
# assign input SpatialReference
in_sr.ImportFromEPSG(int(in_sr.GetAuthorityCode(None)))

# create SpatialReference for new shapefile
out_sr = osr.SpatialReference()
out_sr.ImportFromEPSG(3857)

# create a CoordinateTransformation object
coord_trans = osr.CoordinateTransformation(in_sr, out_sr)

# create output shapefile and get layer
out_shp = create_shp(r"" + os.path.abspath("") + "/geodata/shapefiles/countries-web.shp", layer_name="basemap", layer_type="line")
out_shp_lyr = out_shp.GetLayer()

# look up layer (features) definitions in input shapefile
in_lyr_def = in_shp_lyr.GetLayerDefn()
# copy field names of input layer attribute table to output layer
for i in range(0, in_lyr_def.GetFieldCount()):
    out_shp_lyr.CreateField(in_lyr_def.GetFieldDefn(i))

# instantiate feature definitions object for output layer (currently empty)
out_shp_lyr_def = out_shp_lyr.GetLayerDefn()

# iteratively append all input features in new projection
in_feature = in_shp_lyr.GetNextFeature()
while in_feature:
    # get the input geometry
    geometry = in_feature.GetGeometryRef()
    # re-project (transform) geometry to new system
    geometry.Transform(coord_trans)
    # create new output feature
    out_feature = ogr.Feature(out_shp_lyr_def)
    # assign in-geometry to output feature and copy field values
    out_feature.SetGeometry(geometry)
    for i in range(0, out_shp_lyr_def.GetFieldCount()):
        out_feature.SetField(out_shp_lyr_def.GetFieldDefn(i).GetNameRef(), in_feature.GetField(i))
    # add the feature to the shapefile
    out_shp_lyr.CreateFeature(out_feature)
    # prepare next iteration
    in_feature = in_shp_lyr.GetNextFeature()

# release shapefiles and layers
in_shp = None
in_shp_lyr = None
out_shp = None
out_shp_lyr = None

# create .prj file for  output shapefile for web application references
with open(r"" + os.path.abspath('') + "/geodata/shapefiles/countries-web.prj", "w+") as prj:
    prj.write(get_wkt(3857))

In the case of success, the code sequence in_sr.AutoIdentifyEPSG() returns 0 (i.e., it successfully recognized the EPSG number), but unfortunately, many EPSG numbers are not known to AutoIdentifyEPSG(). In the case that AutoIdentifyEPSG() did not function properly, the method does not return 0, but 7, for example. A workaround for the limited functionality of srs.AutoIdentifyEPSG() is srs.FindMatches. srs.FindMatches returns a matching srs_match from a larger database, which is somewhat nested; for instance, use:

matches = srs.FindMatches()

Dann sieht matches so aus: [(osgeo.osr.SpatialReference, INT)]. So sieht ein kompletter Workaround für srs.AutoIdentifyEPSG() (oder in_sr.AutoIdentifyEPSG() im obigen Codeblock) so aus:

# set epsg and create spatial reference object
epsg = 3857
srs = osr.SpatialReference()
srs.ImportFromEPSG(epsg)

# identify spatial reference
auto_detect = srs.AutoIdentifyEPSG()
if auto_detect != 0:
    srs = srs.FindMatches()[0][0]  # Find matches returns list of tuple of SpatialReferences
    srs.AutoIdentifyEPSG()  # Re-perform auto-identification

Fügen Sie Fields und Point Features zu einer Shapefile hinzu

Ein Shapefile-Feature kann ein Punkt, eine Zeile oder ein Polygon sein, das Feld-Attribute hat (z.B. "id"=1, um zu beschreiben, dass es sich um Polygon Nummer 1 handelt oder einem id Block 1 zugeordnet ist). Feldattribute können mehr als nur ein IDentifier sein und z.B. die Polygon- oder Stadtmarken enthalten, wie im oben dargestellten Beispiel, das cities.shp (shp_driver.Open("geodata/shapefiles/cities.shp")) illustriert.

Um ** einen Punkt formfile** zu erstellen, können wir die oben vorgestellte create_shp-Funktion (oder flusstools.geotools.create_shp(shp_file_dir="")) verwenden und die Projektion mit der get_wkt()-Funktion (auch oben vorgestellt) einstellen. Der folgende Codeblock zeigt die Nutzung beider Funktionen, um einen river zu erstellen. schp Punkt formfile, die drei Punkte an drei Flüssen in Mitteleuropa enthält. Der Codeblock verdeutlicht auch die Erstellung eines Feldes in der Attributtabelle und die Erstellung von drei Punktmerkmalen. Hier ist, wie der Code funktioniert:

  • Die Shapefile befindet sich in der Variable rivers_pts. Beachten Sie, dass die layer_type bereits die Art der Geometrien bestimmt, die in der Formdatei verwendet werden können. So wird z.B. das Hinzufügen einer Zeile oder eines Polygons zu einer ogr.wkbPointSchicht zu einer ERROR 1-Nachricht führen.

  • Die basemap (Schicht) wird der Variablen lyr = river_pts.GetLayer() zugeschrieben.

  • Ein string-Feld wird hinzugefügt und der Attributtabelle angepasst:

    • sofort ein neues Feld mit field_gname = ogr.FieldDefn("FIELD-NAME", ogr.OFTString) (Recall: der Feldname darf nicht mehr als 10 Zeichen haben!)

    • das neue Feld mit lyr.CreateField(field_gname)

    • andere Feldtypen als OFTString können sein:OFTInteger,OFTReal,OFTDate,OFTTime,OFTDateTime, OFTBinary,@@@@@,OFTIntegerList,OFTRealList, oder OFTStringList.

  • Fügen Sie drei Punkte hinzu, die unter pt_names = {RIVER-NAME: (x-coordinate, y-coordinate)} in einer Schleife über die Wörterbuchschlüssel gespeichert sind:

    • für jeden neuen Punkt, erstellen Sie eine Funktion als Kind der Schichtdefinitionen mit feature = ogr.Feature(lyr.GetLayerDefn())

    • den Wert des Feldnamens für jeden Punkt mit feature.SetField(FIELD-NAME, FIELD-VALUE)

    • eine Reihe des neuen Punktes im WKT-Format mit wkt = "POINT(X-COORDINATE Y-COORDINATE)"

    • den WKT-formatierten Punkt in eine Punktgeometrie mit point = ogr.CreateGeometryFromWkt(wkt)

    • den neuen Punkt als die Geometrie der neuen Funktion mit feature.SetGeometry(point)

    • die neue Funktion mit lyr.CreateFeature(feature)

  • Entsperren Sie die Formdatei, indem Sie die Variable lyr und river_pts mit None überschreiben.

shp_dir = r"" + os.path.abspath('') + "/geodata/shapefiles/rivers.shp"
river_pts = create_shp(shp_dir, layer_name="basemap", layer_type="point")

# create .prj file for the shapefile for web application references
with open(shp_dir.split(".shp")[0] + ".prj", "w+") as prj:
    prj.write(get_wkt(3857))

# get basemap layer
lyr = river_pts.GetLayer()

# add string field "rivername"
field_gname = ogr.FieldDefn("rivername", ogr.OFTString)
lyr.CreateField(field_gname)

# names and coordinates of central EU rivers in EPSG:3857 WG84 / Pseudo-Mercator
pt_names = {"Aare": (916136.03, 6038687.72),
            "Ain": (623554.12, 5829154.69),
            "Inn": (1494878.95, 6183793.83)}

# add the three rivers as points to the basemap layer
for n in pt_names.keys():
    # create Feature as child of the layer
    pt_feature = ogr.Feature(lyr.GetLayerDefn())
    # define value n (river) in the rivername field
    pt_feature.SetField("rivername", n)
    # use WKT format to add a point geometry to the Feature
    wkt = "POINT(%f %f)" % (float(pt_names[n][0]), float(pt_names[n][1]))
    point = ogr.CreateGeometryFromWkt(wkt)
    pt_feature.SetGeometry(point)
    # append the new feature to the basemap layer
    lyr.CreateFeature(pt_feature)
    
# release files
lyr = None
river_pts = None

Die daraus resultierende rivers.shp formfile kann in QGIS zusammen mit einer Digitales Oberflächenmodell (DOM) aus dem Natural Earth quick start kit] importiert werden.

river map shapefile

Figure 5:Die Flussnamen, die an Punkte in einer Punktformdatei in QGIS angezeigt.

Multiline (Polyline) Shapefile

Ähnlich wie bei dem Verfahren zum Erstellen und Hinzufügen von Punkten zu einer neuen Punktformdatei kann eine (multi) Linie (oder Polyline) zu einer Shapefile hinzugefügt werden. Die create_shp-Funktion erstellt eine Multi-Line-Formdatei, wenn der Schichttyp als "line" (flusstools.geotools.create_shp(shp_file_dir="", layer_type="line")) definiert ist. Das Koordinatensystem wird mit der oben definierten get_wkt()-Funktion erstellt.

Der folgende Codeblock verwendet die Koordinaten der Städte entlang des Rheins, die in einem Dictionary namens station_names gespeichert sind. Die Stadtnamen werden nicht verwendet und nur die Koordinaten werden mit line.AddPoint(X, Y) angehängt. Wie zuvor wird ein Feld geschaffen, um dem Fluss einen Namen zu geben. Die eigentliche Zeilenfunktion wird wieder als Kind der Schicht mit line_feature = ogr.Feature(lyr.GetLayerDefn()) erstellt. Dieser Codeblock führt eine Linie hervor, die etwa dem Rhein zwischen Frankreich und Deutschland folgt.

shp_dir = r"" + os.path.abspath("") + "/geodata/shapefiles/rhine_proxy.shp"
rhine_line = create_shp(shp_dir, layer_name="basemap", layer_type="line")

# create .prj file for the shapefile for web application references
with open(shp_dir.split(".shp")[0] + ".prj", "w+") as prj:
    prj.write(get_wkt(3857))

# get basemap layer
lyr = rhine_line.GetLayer()

# coordinates for EPSG:3857 WG84 / Pseudo-Mercator
station_names = {"Basel": (844361.68, 6035047.42),
                 "Kembs": (835724.27, 6056449.76),
                 "Breisach": (842565.32, 6111140.43),
                 "Rhinau": (857547.04, 6158569.58),
                 "Strasbourg": (868439.31, 6203189.68)}

# create line object and add points from station names
line = ogr.Geometry(ogr.wkbLineString)
for stn in station_names.values():
    line.AddPoint(stn[0], stn[1])

# create field named "river"
field_name = ogr.FieldDefn("river", ogr.OFTString)
lyr.CreateField(field_name)

# create feature, geometry, and field entry
line_feature = ogr.Feature(lyr.GetLayerDefn())
line_feature.SetGeometry(line)
line_feature.SetField("river", "Rhine")

# add feature to layer
lyr.CreateFeature(line_feature)

lyr = None
rhine_line = None

Die daraus resultierende rhine_proxy.shp formfile kann in QGIS zusammen mit einer Digitales Oberflächenmodell (DOM) importiert werden und die Städte weisen formfile aus der Natural Earth quick start kit.

rhine line shapefile

Figure 6:Die Linie rund um den Rhein in QGIS.

Polygon Shapefile

Polygone sind Oberflächenpatches, die punktweise, linienweise oder aus einer "Multipolygon"WKB-Definition erstellt werden können. Bei der Erstellung von Polygonen aus Punkten oder Linien wollen wir eine Oberfläche schaffen, und deshalb wird der entsprechende Geometrietyp wkbLinearRing zum Aufbau von Polygonen aus beiden Punkten oder Linien (anstatt wkbPoint bzw. wkbLine) genannt. Der folgende Codeblock bietet ein Beispiel für den Aufbau einer Polygon-Formdatei, die das Hydrauliklabor der Universität Stuttgart abgrenzt. Der Unterschied zwischen dem obigen Beispiel zum Erstellen einer Linienformdatei sind:

  • Die Projektion ist EPSG:4326.

  • Die Punktkoordinaten werden innerhalb eines ogr.wkbLinearRing Objekt Schritt für Schritt anstatt in einer Schleife über diktionäre Einträge erzeugt.

  • Datei, Variable und Feldnamen.

shp_dir = r"" + os.path.abspath("") + "/geodata/shapefiles/va4wasserbau.shp"
va_geo = create_shp(shp_dir, layer_name="basemap", layer_type="polygon")

# create .prj file for the shapefile for GIS map applications
with open(shp_dir.split(".shp")[0] + ".prj", "w+") as prj:
    prj.write(get_wkt(4326))

# get basemap layer
lyr = va_geo.GetLayer()

# create polygon points
pts = ogr.Geometry(ogr.wkbLinearRing)
pts.AddPoint(9.103686, 48.744251)
pts.AddPoint(9.104689, 48.744198)
pts.AddPoint(9.104667, 48.743960)
pts.AddPoint(9.103557, 48.744009)

# create polygon geometry
poly = ogr.Geometry(ogr.wkbPolygon)
# build polygon geometry from points
poly.AddGeometry(pts)

# add field to classify building type
field = ogr.FieldDefn("building", ogr.OFTString)
lyr.CreateField(field)

poly_feature_defn = lyr.GetLayerDefn()
poly_feature = ogr.Feature(poly_feature_defn)
poly_feature.SetGeometry(poly)
poly_feature.SetField("building", "Versuchsanstalt")

lyr.CreateFeature(poly_feature)

lyr = None
va_geo = None

Gestaltdatei von JSON erstellen

Die Belastung von Geometriedaten aus in-line definierten Variablen ist in der Praxis umständlich, wo Geospatialdaten häufig auf öffentlichen Plattformen (z.B. Landnutzung oder Abdeckung) zur Verfügung gestellt werden. Das folgende Beispiel verwendet eine JSON-Datei, die mit Kartendienstdaten des Baden-Württemberg State Institute for the Environment, Survey and Nature Conservation (LUBW) erstellt wurde, in der Polygonknoten im WKT-Polygongeometrieformat gespeichert werden ("MultiPolygon (((node1_x node1_y, nodej_x, nodej_y, ... ...)))"):

  • Die JSON-Datei (herunterladen hq100-dreisam.json und speichern Sie sie an ein Unterverzeichnis namens /geodata/json/) wird mit Pandas gelesen und die Formdatei wird wie zuvor mit der create_shp-Funktion erstellt.

  • Die Projektion ist EPSG:25832.

  • Zwei Felder werden in Form von

    • "tbg_name" (der ursprüngliche Stringname der Polygone in den LUBW-Daten) und

    • "area" (ein echtes Nummernfeld, in dem der Polygonbereich unter m2 unter polygon.GetArea() berechnet wird).

  • Die Polygongeometrien stammen aus den WKT-formatierten Definitionen im Feld "wkt_geom" des pandas Dataframe-Objekts dreisam_inundation.

import pandas as pd

# get data from json file
dreisam_inundation = pd.read_json(r"" + os.path.abspath("") + "/geodata/json/hq100-dreisam.json")

# create shapefile
shp_dir = r"" + os.path.abspath('') + "/geodata/shapefiles/dreisam_hq100.shp"
dreisam_hq100 = create_shp(shp_dir, layer_name="basemap", layer_type="polygon")

# create .prj file for the shapefile for GIS map applications
with open(shp_dir.split(".shp")[0] + ".prj", "w+") as prj:
    prj.write(get_wkt(25832))

# get basemap layer
lyr = dreisam_hq100.GetLayer()

# add string field "tbg_name"
lyr.CreateField(ogr.FieldDefn("tbg_name", ogr.OFTString))

# add string field "area"
lyr.CreateField(ogr.FieldDefn("area", ogr.OFTReal))

for wkt, tbg in zip(dreisam_inundation["wkt_geom"], dreisam_inundation["TBG_NAME"]):
    # create Feature as child of the layer
    feature = ogr.Feature(lyr.GetLayerDefn())
    # assign tbg_name
    feature.SetField("tbg_name", tbg)
    # use WKT format to add a polygon geometry to the Feature
    polygon = ogr.CreateGeometryFromWkt(wkt)
    # define default value of 0 to the area field
    feature.SetField("area", polygon.GetArea())

    feature.SetGeometry(polygon)
    # append the new feature to the basemap layer
    lyr.CreateFeature(feature)

lyr = None
dreisam_hq100 = None

Auch GeoJSON Daten können verwendet werden, um eine ogr.Geometry mit ogr.createFromGeoJson(FILENAME) zu erstellen:

from osgeo import ogr
geojson_data = """{"type":"Point","coordinates":[1013452.282805,6231540.674235]}"""
point = ogr.CreateGeometryFromJson(geojson_data)
print("X=%d, Y=%d (EPSG:3857)" % (point.GetX(), point.GetY()))
X=1013452, Y=6231540 (EPSG:3857)

Geometrische Attribute berechnen

Der oben genannte Codeblock verdeutlicht die Nutzung von polygon.GetArea(), um den Polygonbereich in m2^2 zu berechnen. Die ogr Bibliothek bietet viele weitere Funktionen, um geometrische Attribute von Features zu berechnen und hier eine Zusammenfassung:

  • Mehrere Polygone
    wkt_... = ...
    polygon_a = ogr.CreateGeometryFromWkt(wkt_1)
    polygon_b = ogr.CreateGeometryFromWkt(wkt_2)
    polygon_union = polygon_a.Union(polygon_b)

  • Intersect zwei Polygone
    polygon_intersection = polygon_a.Intersection(polygon_b)

  • Envelope (minimale und maximale Ausdehnung) eines Polygons
    env = polygon.GetEnvelope()
    print("minX: %d, minY: %d, maxX: %d, maxY: %d" % (env[0],env[2],env[1],env[3])

  • Convex Rumpf (Ummantelungsfläche) mehrerer Geometrien (Punkte, Linien, Polygone)
    all_polygons = ogr.Geometry(ogr.wkbGeometryCollection)
    for feature in POLYGON-SOURCE-LAYER: all_polygons.AddGeometry(feature.GetGeometryRef())
    convexhull = all_polygons.ConvexHull()
    Save convexhull to shapefile (use the create_shp function as shown in the above examples or read more at pcjericks’ Github pages)
    Tip: To create a tight hull (e.g., of a point cloud), look for concavehull functions.

  • Länge (eine Zeile)
    wkt = "LINESTRING (415128.5 5320979.5, 415128.6 5320974.5, 415129.75 5320974.7)"
    line = ogr.CreateGeometryFromWkt(wkt)
    print("Length = %d" % line.Length())

  • Bereich (von einem Polygon): polygon.GetArea() (siehe oben Beispiel)

  • Beispiel für die Berechnung Zentrenkoordinaten von polygons.

Export in andere Formate

Die obigen Beispiele behandeln nur .shp-Dateien, aber andere Formate können nützlich sein (z.B. Web-Anwendungen erstellen oder auf Google Earth exportieren). Zu diesem Zweck illustrieren die folgenden Absätze die Erstellung von GeoJSON- und KML-Dateien. Mehrere andere Konvertierungen können durchgeführt werden, nicht nur zwischen Dateiformaten, sondern auch zwischen Merkmalstypen. So können beispielsweise Polygone aus Punktwolken erzeugt werden (u.a. mit der oben genannten ConvexHull-Methode). Interessierte Studenten können mehr über Umwandlungen in Michael Diener’s Python Geospatial Analysis Cookbook.

GeoJSON

GeoJSON-Dateien können wie zuvor einfach erstellt werden, auch ohne einen Treiber zu aktivieren:

triangle = ogr.Geometry(ogr.wkbLinearRing)
triangle.AddPoint(-11717151.498691, 2356192.894805)
triangle.AddPoint(-11717120.446149, 2355586.175893)
triangle.AddPoint(-11719392.059083, 2354012.050842)

polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(triangle)

with open(r"" + os.path.abspath('') + "/geodata/geojson/pitillal-triangle.geojson", "w+") as gjson:
    gjson.write(polygon.ExportToJson())

Für eine robustere Bearbeitung und Definition einer Projektion aktivieren Sie den Treiber ogr.GetDriverByName("GeoJSON"). So funktioniert die Erstellung und Manipulation von GeoJSON-Dateien ähnlich wie die oben gezeigten Shapefile-Handler.

gjson_driver = ogr.GetDriverByName("GeoJSON")

# make spatial reference
sr = osr.SpatialReference()
sr.ImportFromEPSG(3857)

# create GeoJSON file
gjson = gjson_driver.CreateDataSource("pitillal-full.geojson")
gjson_lyr = gjson.CreateLayer("pitillal-full.geojson", geom_type=ogr.wkbPolygon, srs=sr)

# get layer feature definitions
feature_def = gjson_lyr.GetLayerDefn()
# create new feature
new_feature = ogr.Feature(feature_def)
# assign the triangle from the above code block
new_feature.SetGeometry(polygon)
# add new feature to Layer
gjson_lyr.CreateFeature(new_feature)

# close links to data sources
gjson = None
gjson_lyr = None

KML (Google Earth)

Um Punkt-, Zeilen- oder Polygon-Features in Google Earth anzuzeigen, können Funktionen in Googles KML (Keyhole Markup Language), ähnlich der Erstellung einer GeoJSON-Datei, und mit der Funktion geometry.ExportToKML:

triangle = ogr.Geometry(ogr.wkbLinearRing)
triangle.AddPoint(-11717151.498691, 2356192.894805)
triangle.AddPoint(-11717120.446149, 2355586.175893)
triangle.AddPoint(-11719392.059083, 2354012.050842)

polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(triangle)

#geojson = poly.ExportToJson()
with open(r"" + os.path.abspath("") + "/geodata/pitillal-triangle.kml", "w+") as gjson:
    gjson.write(polygon.ExportToKML())

Darüber hinaus können ähnlich wie GeoJSON-Dateien und Formdateien KML-Dateien robuster generiert werden (z.B. mit einer definierten Projektion). Sie müssen nur den KML-Treiber (kml_driver = ogr.GetDriverByName("KML")) einleiten und eine KML-Datenquelle definieren (kml_file = kml_driver.CreateDataSource(FILENAME.KML)).