使用Python分解基于属性的多边形(形状为fiona)?


15

我一直在尝试创建一个功能,该功能基本上与QGIS的“溶解”功能相同。我认为这将超级容易,但显然并非如此。因此,从我收集到的信息来看,在这里使用fiona配合体形应该是最好的选择。我刚开始弄乱矢量文件,所以这个世界对我和python来说都是新的。

对于这些示例,我使用的是位于此处http://tinyurl.com/odfbanu的County shapefile, 所以这里是我收集的一些代码,但找不到使它们协同工作的方法

目前,我最好的方法是基于以下方法: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.

因此,从我所看到的内容来看,本书的内容确实不是直截了当的,我真的只想要同一个shapefile,而只是将国家分解为州,我什至不需要太多的属性表,但我很好奇,看看如何传递从源到新创建的shapefile。

我发现了许多用fiona编写的代码,但是我永远无法使其与我的数据一起使用。如何将Shapely几何图形写入shapefile中的示例

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

这里的问题是如何对几何列表进行同样的操作,以及如何重新创建与源相同的属性。

Answers:


22

问题是关于Fiona和Shapely的,而使用GeoPandas的其他答案还需要了解Pandas。此外,GeoPandas使用Fiona读取/写入shapefile。

我在这里不问问GeoPandas的实用程序,但是您可以使用标准模块itertools与Fiona一起直接使用它,特别是使用groupby命令(“概括地说,groupby接受一个迭代器,并根据更改将其分解为子迭代器。在主迭代器的“键”中。这当然不需要将整个源迭代器读入内存中,” itertools.groupby)。

由STATEFP字段着色的原始Shapefile

在此处输入图片说明

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

结果

在此处输入图片说明


太好了,两者之间很难选择。很高兴看到不同的方法,谢谢!
User18981898198119 2015年

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

这比我笨拙的古怪东西更优雅。谢谢 !有没有办法传递空间参照系?
User18981898198119 2015年

我已经编辑了帖子,以显示如何将crs从源文件传输到新文件。这发生在创建states_dissolved GeoDataFrame的行。关于模式,我建议只手动创建一个(即使用同一行的columns属性),然后将其写入新文件的属性。即,当您创建状态字典时,只需添加其他所有属性,然后将其分配给新数据框中的一列即可。
songololo 2015年

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"))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.