Python(shapely、fiona)を使用して属性に基づいてポリゴンを溶解しますか?


15

私は、QGISが機能を「溶解」するのと基本的に同じことを行う関数を作成しようとしています。私はそれは非常に簡単だと思ったが、明らかにそうではない。だから、私が集めたものから、フィオナとshapelyの使用がここでの最良の選択肢であるはずです。ベクターファイルをいじり始めたばかりなので、この世界は私にとってもPythonにとっても新しいものです。

これらの例では、http: //tinyurl.com/odfbanuでここに設立された郡のシェープファイルを使用して います

:今の私の最善の方法は、に基づいて、以下の通りであるhttps://sgillies.net/2009/01/27/a-more-perfect-union-continued.html。それはうまく機能し、52の状態のリストをShapelyジオメトリとして取得します。この部分を行うためのより簡単な方法がある場合は、お気軽にコメントしてください。

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で書くためのコードがたくさん見つかりましたが、自分のデータで動作させることはできません。Shapelyジオメトリをシェープファイルに書き込む方法の例 :

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

質問はフィオナとシェイプリーに関するものであり GeoPandasを使用する他の答えパンダについても知っている必要があります。さらに、GeoPandasはFionaを使用してシェープファイルを読み書きします。

ここではGeoPandasのユーティリティについては質問しませんが、標準モジュールitertoolsを使用して、特にコマンドgroupbyを使用して直接Fionaで実行できます(「簡単に言うと、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

さまざまな機能を扱い、一括操作を実行するには、GeoPandasを強くお勧めします。

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の回答の補遺として、複数のフィールドでディゾルブする必要があるため、複数のフィールドを処理するようにコードを修正しました。以下のコードは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.