GeoJSON mit Python generieren

16

Ich möchte programmgesteuert eine GeoJSON-Datei mit Polygonen aus einem Shapefile erstellen, aber Attribute aus meiner eigenen Anwendung hinzufügen.

Dies ist für ein Shapefile einfach durchzuführen:

def create_data_dayer(self,varlist, data):
    """
    Creates a new shape to contain data about nodes.
    varlist is the list of fields names associated with
    the nodes.
    data is a list of lists whose first element is the geocode
    and the remaining elements are values of the fields, in the
    same order as they appear in varlist.
    """
    if os.path.exists(os.path.join(self.outdir,'Data.shp')):
        os.remove(os.path.join(self.outdir,'Data.shp'))
        os.remove(os.path.join(self.outdir,'Data.shx'))
        os.remove(os.path.join(self.outdir,'Data.dbf'))
    # Creates a new shape file to hold the data
    if not self.datasource:
        dsd = self.driver.CreateDataSource(os.path.join(self.outdir,'Data.shp'))
        self.datasource = dsd
        dl = dsd.CreateLayer("sim_results",geom_type=ogr.wkbPolygon)
    #Create the fields
    fi1 = ogr.FieldDefn("geocode",field_type=ogr.OFTInteger)
    dl.CreateField(fi1)
    for v in varlist:
        #print "creating data fields"
        fi = ogr.FieldDefn(v,field_type=ogr.OFTString)
        fi.SetPrecision(12)
        dl.CreateField(fi)

    #Add the features (points)
    for n,l in enumerate(data):
        #Iterate over the lines of the data matrix.
        gc = l[0]
        try:
            geom = self.geomdict[gc]
            if geom.GetGeometryType() != 3: continue
            #print geom.GetGeometryCount()
            fe = ogr.Feature(dl.GetLayerDefn())
            fe.SetField('geocode',gc)
            for v,d in zip (varlist,l[1:]):
                #print v,d
                fe.SetField(v,str(d))
            #Add the geometry
            #print "cloning geometry"
            clone = geom.Clone()
            #print geom
            #print "setting geometry"
            fe.SetGeometry(clone)
            #print "creating geom"
            dl.CreateFeature(fe)
        except: #Geocode not in polygon dictionary
            pass
        dl.SyncToDisk()

da ich alle geometrien in einem wörterbuch per geocode habe (self.geomdict), erstelle ich einfach die merkmale, setze die felder und klone die geometrien von einer bereits vorhandenen ebene (code, der diese ebene lädt, der einfachheit halber weggelassen). Alles, was ich jetzt brauche, ist eine Möglichkeit, das GeoJSON aus der Kombination von Feldern und Geometrien zu generieren, natürlich mit Hilfe von OGR, um den Rest der Datei richtig zu machen (CRS usw. aus der Quellkarte).

Wie exportiere ich die Feature-Sammlung, die wie oben generiert wurde?

fccoelho
quelle

Antworten:

14

Glücklicherweise kann OGR dies für Sie tun, da beide ogr.Featureund ogr.GeometryObjekte ExportToJson()Methoden haben. In Ihrem Code;

fe.ExportToJson()

Und da Geojson FeatureCollection- Objekte einfach Wörterbücher mit einem typevon FeatureCollectionund einem featuresObjekt sind, die eine Liste von Feature-Objekten enthalten.

feature_collection = {"type": "FeatureCollection",
                      "features": []
                      }

feature_collection["features"].append(fe.ExportToJson())

Es gibt zwei Arten von CRS-Objekten in einer Feature-Sammlung:

  • Ein benanntes CRS (zB ein OGC URN oder ein EPSG Code)
  • Ein Verknüpfungsobjekt mit einer URI und einem Typ wie "proj4"

Abhängig von Ihrem Datenformat ist es sehr wahrscheinlich, dass der Name von OGR schwer zu bekommen sein wird. Schreiben wir stattdessen die Projektion in eine Datei auf der Festplatte, auf die wir mit der URI verweisen können. Wir können die Projektion vom Ebenenobjekt (das mehrere Exportfunktionen hat) abrufen.

spatial_reference = dl.GetSpatialRef()

with open("data.crs", "wb") as f:
    f.write(spatial_reference.ExportToProj4())

feature_collection["crs"] = {"type": "link",
                             "properties": {
                                 "href": "data.crs",
                                 "type": "proj4"
                                 }
                             }
om_henners
quelle
Dies ist eine gute Lösung, da es meinem Projekt keine zusätzliche Abhängigkeit hinzufügt, wie die (nette) Lösung von @sgillies
fccoelho
Ich habe meine Tests mit dieser Lösung beendet und es hat gut funktioniert. Allerdings musste ich manuell vorgehen, wenn Features Unicode-Zeichen in Feldnamen hatten, da ogr.py diese nicht richtig behandelte.
Fccoelho
Ich weiß nicht, ob sich die Funktionalität seitdem geändert hat, aber es wird fe.ExportToJson()ein String zurückgegeben, mit dem Sie sich einschließen müssen json.loads(...). Ansonsten ist das super hilfreich!
jon_two
35

Wenn Sie eine GDAL / OGR-Entwicklungsumgebung (Header, Bibliotheken) haben, können Sie Ihren Code mithilfe von Fiona radikal vereinfachen . Um Features aus einem Shapefile zu lesen, fügen Sie neue Attribute hinzu und schreiben Sie sie aus, da GeoJSON nur eine Handvoll Zeilen enthält:

import fiona
import json

features = []
crs = None
with fiona.collection("docs/data/test_uk.shp", "r") as source:
    for feat in source:
        feat['properties'].update(...) # with your attributes
        features.append(feat)
    crs = " ".join("+%s=%s" % (k,v) for k,v in source.crs.items())

my_layer = {
    "type": "FeatureCollection",
    "features": features,
    "crs": {
        "type": "link", 
        "properties": {"href": "my_layer.crs", "type": "proj4"} }}

with open("my_layer.json", "w") as f:
    f.write(json.dumps(my_layer))
with open("my_layer.crs", "w") as f:
    f.write(crs)
sgillies
quelle
4
Fiona-Docs sind Killer!
Chad Cooper
1
Würde mehr als einmal abstimmen, wenn ich könnte!
Om_henners
2
Gibt es keine Möglichkeit, die crs-Definition in GeoJSON aufzunehmen?
Fccoelho
2

Dies ist die einfachste und einfachste in Fiona. Sie können das SRS für die Ausgabe von GeoJSON festlegen.

import fiona
from fiona.crs import from_epsg

source= fiona.open('shp/second_shp.shp', 'r', encoding = 'utf-8')

with fiona.open('tool_shp_geojson/geojson_fiona.json','w',  driver ="GeoJSON", schema=source.schema, encoding = 'utf-8', crs=fiona.crs.from_epsg(4326)) as geojson:
     geojson.write(feat)
Muhammad Imran Siddique
quelle