Generazione di GeoJSON con Python


16

Voglio creare a livello di codice un file GeoJSON usando poligoni da un file di forma ma aggiungendo attributi dalla mia stessa applicazione.

Questo viene fatto facilmente per un file di forma:

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()

dal momento che ho tutte le geometrie su un dizionario per geocodice (self.geomdict) creo semplicemente le caratteristiche, imposto i campi e clonano le geometrie da un livello preesistente (il codice carica quel livello omesso per semplicità). Tutto ciò di cui ho bisogno ora è un modo per generare il GeoJSON dalla combinazione di campi e geometrie, naturalmente con l'aiuto di OGR per ottenere il resto del file giusto (CRS, ecc. Come dalla mappa di origine)

Come esportare la raccolta di funzioni generata come sopra?

Risposte:


14

Fortunatamente OGR può fare questo per voi sia come ogr.Featuree ogr.Geometryoggetti hanno ExportToJson()metodi. Nel tuo codice;

fe.ExportToJson()

E poiché GeoJSON FeatureCollection oggetti sono semplicemente dizionari con una typedi FeatureCollectione un featuresoggetto contenente una lista di oggetti Feature.

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

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

L'oggetto CRS in una raccolta di funzionalità può essere di due tipi:

  • Un CRS denominato (ad esempio un URC OGC o un codice EPSG)
  • Un oggetto collegamento con un URI e un tipo come "proj4"

A seconda del formato dei dati è abbastanza probabile che il nome sarà un problema da ottenere da OGR. Invece se scriviamo la proiezione su un file su disco a cui possiamo fare riferimento con l'URI. Possiamo catturare la proiezione dall'oggetto layer (che ha diverse funzioni di esportazione)

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"
                                 }
                             }

Questa è una buona soluzione, perché non aggiunge una dipendenza extra al mio progetto come la (bella) soluzione di @sgillies
fccoelho,

Ho appena finito i test con questa soluzione e ha funzionato bene. Tuttavia, ho dovuto gestire manualmente quando le caratteristiche avevano caratteri unicode nei nomi dei campi, poiché ogr.py non li gestiva correttamente.
fccoelho,

Non so se la funzionalità sia cambiata da allora, ma fe.ExportToJson()restituisce una stringa, quindi è necessario concludere json.loads(...). Altrimenti, questo è super utile!
jon_two

35

Se hai un ambiente di sviluppo GDAL / OGR (header, libs), puoi semplificare radicalmente il tuo codice usando Fiona . Per leggere funzionalità da un file di forma, aggiungere nuovi attributi e scriverli poiché GeoJSON è solo una manciata di righe:

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)

4
I documenti di Fiona sono killer!
Chad Cooper,

1
Vorrei votare più di una volta se potessi!
om_henners,

2
Non esiste un modo per includere la definizione crs in GeoJSON?
fccoelho,

2

Questo è il più semplice e facile di Fiona. è possibile impostare SRS per l'output di GeoJSON.

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.