셰이프 파일에 셰이프 형상을 작성하는 방법?


26

누군가 형상 데이터 구조를 형태에서 형태 파일로 작성하는 간단한 방법을 보여줄 수 있습니까? 특히 구멍과 선 스트링이있는 다각형에 관심이 있습니다. 또한 아크 피에서 멀리 떨어져있는 것이 좋습니다 (따라서 osgeo, pyshp 등이 모두 더 나을 것입니다).

답변:


44

잘 알려진 바이너리 는 Shapely 및 GDAL / OGR을 포함한 많은 GIS 소프트웨어와 교환 할 수있는 좋은 바이너리 교환 형식입니다.

이것은 다음 과 같은 워크 플로 의 작은 예입니다 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

업데이트 : 포스터가 GDAL / OGR 답변을 수락했지만 다음은 Fiona에 해당합니다.

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 사용자 : 변명의 여지가 없습니다 )


Fiona 라이브러리 대신이 방법을 선택한 이유에 관심이 있습니다.
Nathan W

1
글쎄, 포스터는 osgeo.ogr 예제를 찾고 있었고 비교는 흥미 롭습니다.
sgillies

@sgillies 명시 적 비교 추가
Mike T

3
솔직히 말해서 그것은 대부분 실용적이었습니다. 내 질문에 대한 응답으로 코드를 시연하는 노력에 감사하고 이미 osgeo에 대해 고민하고 있습니다. 나는 두 가지 방법을 모두 시도했지만 둘 다 충분한 답변입니다. 응답자의 정확하고 빠른 노력에 감사드립니다.
terra_matics

@Mike T osgeo.ogr 접근 방식과 관련하여 QGIS 용 Python 플러그인에서 사용하고 있습니다. 고려할 shapefile은 Line (Shapely의 LineString)입니다. "poly"변수를 정의한 경우 Qgs.Rectangle의 좌표로 "line"변수를 정의했습니다. 정확한 코드를 사용했지만 오류는 없지만 기능을 추가하지 않으며 기능이없는 shapefile을 제공합니다.
Akhil

28

Fiona 가 Shapely와 잘 작동 하도록 설계했습니다 . 다음은 셰이프 파일 기능을 "정리"하기 위해 함께 사용하는 매우 간단한 예입니다.

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'])

에서 https://github.com/Toblerity/Fiona/blob/master/examples/with-shapely.py .


6

PyShp를 사용하여 Shapely 도형을 작성할 수도 있습니다 (원래 포스터도 PyShp에 대해 요청한 이후).

한 가지 방법은 shapely geometry를 geojson으로 변환하고 (shapely.geometry.mapping 방법) 수정 된 PyShp 포크 를 사용하는 것입니다. 하여 shapefile에 쓸 때 geojson geometry 사전을 허용하는 Writer 메소드를 제공하는 것입니다.

기본 PyShp 버전을 사용하려는 경우 아래 변환 기능도 제공했습니다.

# 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

함수를 자신의 스크립트에 복사하여 붙여 넣기 만하면 모양이있는 도형을 pyshp 호환 모양으로 변환 할 수 있습니다. 이를 저장하기 위해 각 pyshp 모양을 shapefile.Writer 인스턴스의 ._shapes 목록에 추가하기 만하면됩니다 (예를 들어,이 게시물의 하단에있는 테스트 스크립트 참조).

그러나 내부 다각형 구멍이있는 경우이 함수는 처리하지 않으며 단순히 무시합니다. 해당 기능을 함수에 추가하는 것은 가능하지만 아직 귀찮게하지 않았습니다. 기능 개선을위한 제안이나 편집은 환영합니다 :)

완전한 독립형 테스트 스크립트는 다음과 같습니다.

### 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

카림의 대답은 꽤 오래되었지만 나는 그의 코드를 사용하여 그에게 감사하고 싶었습니다. 코드를 사용하여 알아 낸 한 가지 사소한 점 : 모양 유형이 다각형 또는 다중 다각형 인 경우 여전히 여러 부분 (구멍이 있음)이있을 수 있습니다. 따라서 그의 코드의 일부는

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.