파이썬의 속성을 기반으로 다각형을 푸는 (모양, fiona)?


15

기본적으로 QGIS가 "디졸브 (dissolve)"기능과 동일한 기능을 수행하려고했습니다. 나는 그것이 쉽지는 않을 것이라고 생각했다. 그래서 내가 모은 것에서, 피오나를 매끈하게 사용하는 것이 여기에 가장 좋은 옵션이어야합니다. 방금 벡터 파일을 엉망으로 만들기 시작했기 때문에이 세상은 나와 파이썬에도 새로 생겼습니다.

이 예제에서는 http://tinyurl.com/odfbanu에 있는 카운티 shapefile로 작업하고 있습니다. 여기에 내가 모은 코드가 있지만 함께 작동시키는 방법을 찾을 수 없습니다

현재 가장 좋은 방법은 다음을 기반으로합니다 : https://sgillies.net/2009/01/27/a-more-perfect-union-continued.html . 잘 작동하고 52 개의 상태 목록을 Shapely geometry로 얻습니다. 이 부분을 수행하는보다 직접적인 방법이 있으면 언제든지 의견을 말하십시오.

from osgeo import ogr
from shapely.wkb import loads
from numpy import asarray
from shapely.ops import cascaded_union

ds = ogr.Open('counties.shp')
layer = ds.GetLayer(0)

#create a list of unique states identifier to be able
#to loop through them later
STATEFP_list = []
for i in range(0 , layer.GetFeatureCount()) :
    feature = layer.GetFeature(i)
    statefp = feature.GetField('STATEFP')
    STATEFP_list.append(statefp)

STATEFP_list = set(STATEFP_list)

#Create a list of merged polygons = states 
#to be written to file
polygons = []

#do the actual dissolving based on STATEFP
#and append polygons
for i in STATEFP_list : 
    county_to_merge = []
    layer.SetAttributeFilter("STATEFP = '%s'" %i ) 
    #I am not too sure why "while 1" but it works 
    while 1:
        f = layer.GetNextFeature()
        if f is None: break
        g = f.geometry()
        county_to_merge.append(loads(g.ExportToWkb()))

    u = cascaded_union(county_to_merge)
    polygons.append(u)

#And now I am totally stuck, I have no idea how to write 
#this list of shapely geometry into a shapefile using the
#same properties that my source.

그래서 글은 실제로 내가 본 것에서 직접적이지 않습니다. 국가가 상태로 분해되는 동일한 모양 파일 만 원합니다. 속성 테이블이 많이 필요하지 않지만 어떻게 전달할 수 있는지 궁금합니다. 소스에서 새로 작성된 쉐이프 파일로 연결됩니다.

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},
    })

여기서 문제는 지오메트리 목록을 사용하여 동일한 작업을 수행하는 방법과 소스와 동일한 속성을 다시 만드는 방법입니다.

답변:


22

질문은 Fiona와 Shapely에 관한 것이며 GeoPandas를 사용하는 다른 대답Pandas 도 알아야 합니다. 또한 GeoPandas는 Fiona를 사용하여 모양 파일을 읽고 씁니다.

여기 GeoPandas의 유용성에 의문을 제기하지 않는,하지만 당신은 표준 모듈 사용 피오나와 직접 그것을 할 수 itertools을 특별히 명령으로, GROUPBY ( "간단히 말해서, GROUPBY 변경에 따라 반복자 및 하위 반복자에 휴식 그것을 소요 메인 이터레이터의 "키"에 있습니다. 물론 이것은 전체 소스 이터레이터를 메모리로 읽지 않고 수행됩니다 "( itertools.groupby ).

STATEFP 필드로 색상이 지정된 원래 모양 파일

여기에 이미지 설명을 입력하십시오

from shapely.geometry import shape, mapping
from shapely.ops import unary_union
import fiona
import itertools
with fiona.open('cb_2013_us_county_20m.shp') as input:
    # preserve the schema of the original shapefile, including the crs
    meta = input.meta
    with fiona.open('dissolve.shp', 'w', **meta) as output:
        # groupby clusters consecutive elements of an iterable which have the same key so you must first sort the features by the 'STATEFP' field
        e = sorted(input, key=lambda k: k['properties']['STATEFP'])
        # group by the 'STATEFP' field 
        for key, group in itertools.groupby(e, key=lambda x:x['properties']['STATEFP']):
            properties, geom = zip(*[(feature['properties'],shape(feature['geometry'])) for feature in group])
            # write the feature, computing the unary_union of the elements in the group with the properties of the first element in the group
            output.write({'geometry': mapping(unary_union(geom)), 'properties': properties[0]})

결과

여기에 이미지 설명을 입력하십시오


둘 중 하나를 선택하기 어려운 것도 좋은 것입니다. 다른 방법으로 감사합니다 감사합니다!
18981898198119

11

다양한 기능을 처리하고 대량 작업을 수행 할 때는 GeoPanda를 적극 권장합니다.

Pandas 데이터 프레임을 확장하고 후드 아래에서 매끄럽게 사용합니다.

from geopandas import GeoSeries, GeoDataFrame

# define your directories and file names
dir_input = '/path/to/your/file/'
name_in = 'cb_2013_us_county_20m.shp'
dir_output = '/path/to/your/file/'
name_out = 'states.shp'

# create a dictionary
states = {}
# open your file with geopandas
counties = GeoDataFrame.from_file(dir_input + name_in)

for i in range(len(counties)):
    state_id = counties.at[i, 'STATEFP']
    county_geometry = counties.at[i, 'geometry']
    # if the feature's state doesn't yet exist, create it and assign a list
    if state_id not in states:
        states[state_id] = []
    # append the feature to the list of features
    states[state_id].append(county_geometry)

# create a geopandas geodataframe, with columns for state and geometry
states_dissolved = GeoDataFrame(columns=['state', 'geometry'], crs=counties.crs)

# iterate your dictionary
for state, county_list in states.items():
    # create a geoseries from the list of features
    geometry = GeoSeries(county_list)
    # use unary_union to join them, thus returning polygon or multi-polygon
    geometry = geometry.unary_union
    # set your state and geometry values
    states_dissolved.set_value(state, 'state', state)
    states_dissolved.set_value(state, 'geometry', geometry)

# save to file
states_dissolved.to_file(dir_output + name_out, driver="ESRI Shapefile")

그것은 내 해키 이상한 것보다 더 우아합니다. 감사 ! 공간 참조 시스템을 전달하는 방법이 있습니까?
18981898198119

소스 파일에서 새 파일로 crs를 전송하는 방법을 보여주기 위해 게시물을 편집했습니다. states_dissolved GeoDataFrame이 생성되는 라인에서 발생합니다. 스키마와 관련하여 새 파일의 속성에 기록되는 수동으로 (즉, 동일한 줄의 열 속성 사용) 생성하는 것이 좋습니다. 즉, 상태 사전을 만들 때 다른 속성을 추가하고 새 데이터 프레임의 열에 할당하십시오.
songololo

0

@ gene 's answer 의 부록으로 , 여러 필드를 처리하도록 코드를 수정 한 두 개 이상의 필드를 해산해야했습니다. 아래 코드는 operator.itemgetter여러 필드로 그룹화하는 데 활용 됩니다.

# Modified from /gis//a/150001/2856
from shapely.geometry import shape, mapping
from shapely.ops import unary_union
import fiona
import itertools
from operator import itemgetter


def dissolve(input, output, fields):
    with fiona.open(input) as input:
        with fiona.open(output, 'w', **input.meta) as output:
            grouper = itemgetter(*fields)
            key = lambda k: grouper(k['properties'])
            for k, group in itertools.groupby(sorted(input, key=key), key):
                properties, geom = zip(*[(feature['properties'], shape(feature['geometry'])) for feature in group])
                output.write({'geometry': mapping(unary_union(geom)), 'properties': properties[0]})


if __name__ == '__main__':
    dissolve('input.shp', 'input_dissolved.shp', ["FIELD1", "FIELD2", "FIELDN"))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.