Şekil geometrilerine düzgün geometriler nasıl yazılır?


26

Birisi geometri veri yapılarını şekillendirmeden şekil dosyalarına yazmak için basit bir yol gösterebilir mi? Özellikle delikli ve kenarlı çokgenler ile ilgileniyorum. Arktikten uzak durmak da yararlı olacaktır (bu yüzden osgeo, pyshp, vs. hepsi daha iyi olurdu).

Yanıtlar:


44

İyi bilinen bir ikili , Shapely ve GDAL / OGR dahil olmak üzere, bol miktarda GIS yazılımıyla değiştirilebilen iyi bir ikili değişim formatıdır.

Bu şununla iş akışının küçük bir örneği osgeo.ogr:

from osgeo import ogr
from shapely.geometry import Polygon

# Here's an example Shapely geometry
poly = Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])

# Now convert it to a shapefile with OGR    
driver = ogr.GetDriverByName('Esri Shapefile')
ds = driver.CreateDataSource('my.shp')
layer = ds.CreateLayer('', None, ogr.wkbPolygon)
# Add one attribute
layer.CreateField(ogr.FieldDefn('id', ogr.OFTInteger))
defn = layer.GetLayerDefn()

## If there are multiple geometries, put the "for" loop here

# Create a new feature (attribute and geometry)
feat = ogr.Feature(defn)
feat.SetField('id', 123)

# Make a geometry, from Shapely object
geom = ogr.CreateGeometryFromWkb(poly.wkb)
feat.SetGeometry(geom)

layer.CreateFeature(feat)
feat = geom = None  # destroy these

# Save and close everything
ds = layer = feat = geom = None

Güncelleme : Poster GDAL / OGR cevabını kabul etmiş olsa da, işte Fiona eşdeğeri:

from shapely.geometry import mapping, Polygon
import fiona

# Here's an example Shapely geometry
poly = Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])

# Define a polygon feature geometry with one attribute
schema = {
    'geometry': 'Polygon',
    'properties': {'id': 'int'},
}

# Write a new Shapefile
with fiona.open('my_shp2.shp', 'w', 'ESRI Shapefile', schema) as c:
    ## If there are multiple geometries, put the "for" loop here
    c.write({
        'geometry': mapping(poly),
        'properties': {'id': 123},
    })

(Windows kullanıcıları için not: bahaneniz yok )


Neden bu yöntemi Fiona kütüphanesinden ziyade seçtin?
Nathan W,

1
Poster, bir osgeo.ogr örneği arıyordu ve karşılaştırma ilginç.
sgillies

@sgillies açık bir karşılaştırma eklendi
Mike T

3
Dürüst olmak gerekirse, çoğunlukla pragmatikti. Soruma cevap olarak kodu gösterme çabasını takdir ettim ve zaten osgeo ile dalga geçiyordum. O zamandan beri her iki yöntemi de denedim ve ikisi de yeterli cevaplar. Yanıt verenlerin hem doğru hem de hızlı olma çabalarını takdir ediyorum.
terra_matics

@Mike T osgeo.ogr yaklaşımıyla ilgili olarak, QGIS için bir Python eklentisinde kullanıyorum. Yazılması gereken şekil dosyası bir Satırdır (Düzgün Şekilde Satır) "Poly" değişkenini tanımladığınız yerde, Qgs.Rectangle'dan gelen koordinatları içeren bir "line" değişkeni tanımladım. Tam kodu kullandım, hata yok, ancak bir özellik eklemiyor ve bana özellik içermeyen bir şekil dosyası veriyor.
Akhil

28

Fiona'yı Shapely ile iyi çalışacak şekilde tasarladım . Onları bir arada kullanarak "temiz" shapefile özelliklerini basit bir örnek:

import logging
import sys

from shapely.geometry import mapping, shape

import fiona

logging.basicConfig(stream=sys.stderr, level=logging.INFO)

with fiona.open('docs/data/test_uk.shp', 'r') as source:

    # **source.meta is a shortcut to get the crs, driver, and schema
    # keyword arguments from the source Collection.
    with fiona.open(
            'with-shapely.shp', 'w',
            **source.meta) as sink:

        for f in source:

            try:
                geom = shape(f['geometry'])
                if not geom.is_valid:
                    clean = geom.buffer(0.0)
                    assert clean.is_valid
                    assert clean.geom_type == 'Polygon'
                    geom = clean
                f['geometry'] = mapping(geom)
                sink.write(f)

            except Exception, e:
                # Writing uncleanable features to a different shapefile
                # is another option.
                logging.exception("Error cleaning feature %s:", f['id'])

Gönderen https://github.com/Toblerity/Fiona/blob/master/examples/with-shapely.py .


6

Aynı zamanda, PyShp'i kullanarak da özgün geometriler yazabilirsiniz (çünkü orijinal poster PyShp hakkında da sorulmuştur).

Bunun bir yolu, düzgün geometrinizi geojson'a dönüştürmek (shapely.geometry.mapping yöntemiyle) ve daha sonra , bir shapefile yazarken geojson geometri sözlüklerini kabul eden bir Writer yöntemi sağlayan değiştirilmiş PyShp çatalımı kullanmaktır .

Ana PyShp sürümüne güvenmeyi tercih ediyorsanız, aşağıdaki dönüşüm işlevlerini de sağladım:

# THIS FUNCTION CONVERTS A GEOJSON GEOMETRY DICTIONARY TO A PYSHP SHAPE OBJECT
def shapely_to_pyshp(shapelygeom):
    # first convert shapely to geojson
    try:
        shapelytogeojson = shapely.geometry.mapping
    except:
        import shapely.geometry
        shapelytogeojson = shapely.geometry.mapping
    geoj = shapelytogeojson(shapelygeom)
    # create empty pyshp shape
    record = shapefile._Shape()
    # set shapetype
    if geoj["type"] == "Null":
        pyshptype = 0
    elif geoj["type"] == "Point":
        pyshptype = 1
    elif geoj["type"] == "LineString":
        pyshptype = 3
    elif geoj["type"] == "Polygon":
        pyshptype = 5
    elif geoj["type"] == "MultiPoint":
        pyshptype = 8
    elif geoj["type"] == "MultiLineString":
        pyshptype = 3
    elif geoj["type"] == "MultiPolygon":
        pyshptype = 5
    record.shapeType = pyshptype
    # set points and parts
    if geoj["type"] == "Point":
        record.points = geoj["coordinates"]
        record.parts = [0]
    elif geoj["type"] in ("MultiPoint","Linestring"):
        record.points = geoj["coordinates"]
        record.parts = [0]
    elif geoj["type"] in ("Polygon"):
        record.points = geoj["coordinates"][0]
        record.parts = [0]
    elif geoj["type"] in ("MultiPolygon","MultiLineString"):
        index = 0
        points = []
        parts = []
        for eachmulti in geoj["coordinates"]:
            points.extend(eachmulti[0])
            parts.append(index)
            index += len(eachmulti[0])
        record.points = points
        record.parts = parts
    return record

Fonksiyonu kopyalayıp kendi betiğinize yapıştırın ve onu herhangi bir düzgün geometrinizi pyshp uyumlu bir şekle dönüştürmek için arayın. Onları kaydetmek için, daha sonra ortaya çıkan her pyshp şeklini, shapefile.Writer örneğinin ._shapes listesine ekleyin (örneğin bu yazının altındaki test komut dosyasına bakın).

Ancak not: eğer varsa iç fonksiyon poligon deliklerini KULLANMAYACAK, fonksiyon onları görmezden gelir. Bu işlevselliği işleve eklemek kesinlikle mümkündür, ancak henüz rahatsız etmedim. Fonksiyonu geliştirmek için öneriler veya düzenlemeler kabul edilir :)

İşte tam bir bağımsız test betiği:

### HOW TO SAVE SHAPEFILE FROM SHAPELY GEOMETRY USING PYSHP

# IMPORT STUFF
import shapefile
import shapely, shapely.geometry

# CREATE YOUR SHAPELY TEST INPUT
TEST_SHAPELYSHAPE = shapely.geometry.Polygon([(133,822),(422,644),(223,445),(921,154)])

#########################################################
################## END OF USER INPUT ####################
#########################################################

# DEFINE/COPY-PASTE THE SHAPELY-PYSHP CONVERSION FUNCTION
def shapely_to_pyshp(shapelygeom):
    # first convert shapely to geojson
    try:
        shapelytogeojson = shapely.geometry.mapping
    except:
        import shapely.geometry
        shapelytogeojson = shapely.geometry.mapping
    geoj = shapelytogeojson(shapelygeom)
    # create empty pyshp shape
    record = shapefile._Shape()
    # set shapetype
    if geoj["type"] == "Null":
        pyshptype = 0
    elif geoj["type"] == "Point":
        pyshptype = 1
    elif geoj["type"] == "LineString":
        pyshptype = 3
    elif geoj["type"] == "Polygon":
        pyshptype = 5
    elif geoj["type"] == "MultiPoint":
        pyshptype = 8
    elif geoj["type"] == "MultiLineString":
        pyshptype = 3
    elif geoj["type"] == "MultiPolygon":
        pyshptype = 5
    record.shapeType = pyshptype
    # set points and parts
    if geoj["type"] == "Point":
        record.points = geoj["coordinates"]
        record.parts = [0]
    elif geoj["type"] in ("MultiPoint","Linestring"):
        record.points = geoj["coordinates"]
        record.parts = [0]
    elif geoj["type"] in ("Polygon"):
        record.points = geoj["coordinates"][0]
        record.parts = [0]
    elif geoj["type"] in ("MultiPolygon","MultiLineString"):
        index = 0
        points = []
        parts = []
        for eachmulti in geoj["coordinates"]:
            points.extend(eachmulti[0])
            parts.append(index)
            index += len(eachmulti[0])
        record.points = points
        record.parts = parts
    return record

# WRITE TO SHAPEFILE USING PYSHP
shapewriter = shapefile.Writer()
shapewriter.field("field1")
# step1: convert shapely to pyshp using the function above
converted_shape = shapely_to_pyshp(TEST_SHAPELYSHAPE)
# step2: tell the writer to add the converted shape
shapewriter._shapes.append(converted_shape)
# add a list of attributes to go along with the shape
shapewriter.record(["empty record"])
# save it
shapewriter.save("test_shapelytopyshp.shp")

5

Karim'in cevabı oldukça eski ama kodunu kullandım ve ona teşekkür etmek istedim. Kodunu kullanarak çözdüğüm küçük bir şey: Eğer şekil tipi Çokgen ya da Çokyollu ise, hala içinde birden fazla parça (delikler) olabilir. Bu nedenle, kodunun bir kısmı olarak değiştirilmelidir.

elif geoj["type"] == "Polygon":
    index = 0
    points = []
    parts = []
    for eachmulti in geoj["coordinates"]:
        points.extend(eachmulti)
        parts.append(index)
        index += len(eachmulti)
    record.points = points
    record.parts = parts
elif geoj["type"] in ("MultiPolygon", "MultiLineString"):
    index = 0
    points = []
    parts = []
    for polygon in geoj["coordinates"]:
        for part in polygon:
            points.extend(part)
            parts.append(index)
            index += len(part)
    record.points = points
    record.parts = parts
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.