Hòa tan đa giác dựa trên các thuộc tính với Python (shapely, fiona)?


15

Tôi đã cố gắng tạo ra một chức năng về cơ bản giống như chức năng "hòa tan" của QGIS. Tôi nghĩ rằng nó sẽ siêu dễ dàng nhưng rõ ràng là không. Vì vậy, từ những gì tôi đã thu thập được xung quanh, việc sử dụng fiona với hình dạng nên là lựa chọn tốt nhất ở đây. Tôi mới bắt đầu loay hoay với các tập tin vectơ nên thế giới này cũng khá mới đối với tôi và cả trăn.

Ví dụ này, tôi đang làm việc với một shapefile quận được thành lập tại đây http://tinyurl.com/odfbanu Vì vậy, đây là một số đoạn mã tôi đã thu thập được nhưng không thể tìm ra cách để chúng hoạt động cùng nhau

Hiện tại, phương pháp tốt nhất của tôi là như sau dựa trên: https://sgillies.net/2009/01/27/a-more-perinf-union-continued.html . Nó hoạt động tốt và tôi nhận được một danh sách 52 tiểu bang dưới dạng hình học Shapely. Xin vui lòng bình luận nếu có một cách thẳng thắn hơn để làm phần này.

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.

Vì vậy, văn bản thực sự không đi thẳng từ những gì tôi đã thấy, tôi thực sự chỉ muốn cùng một shapefile với đất nước tan thành các tiểu bang, tôi thậm chí không cần nhiều bảng thuộc tính nhưng tôi tò mò muốn xem làm thế nào bạn có thể vượt qua nó từ nguồn đến shapefile mới được tạo.

Tôi đã tìm thấy nhiều đoạn mã để viết bằng fiona nhưng tôi không bao giờ có thể làm cho nó hoạt động với dữ liệu của mình. Ví dụ từ Làm thế nào để viết hình học Shapely cho shapefiles? :

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

Vấn đề ở đây là làm thế nào để làm tương tự với một danh sách hình học và làm thế nào để tạo lại các thuộc tính tương tự như nguồn.

Câu trả lời:


22

Câu hỏi là về Fiona và Shapely và câu trả lời khác sử dụng GeoPandas đòi hỏi phải biết Pandas . Hơn nữa, GeoPandas sử dụng Fiona để đọc / ghi shapefiles.

Tôi không nghi ngờ đây các tiện ích của GeoPandas, nhưng bạn có thể làm điều đó trực tiếp với Fiona sử dụng các module chuẩn itertools , đặc biệt với các lệnh groupby ( "Tóm lại, groupby mất một iterator và phá vỡ nó lên thành các vòng lặp dựa trên những thay đổi trong "khóa" của trình lặp chính. Điều này tất nhiên được thực hiện mà không cần đọc toàn bộ trình lặp nguồn vào bộ nhớ ", itertools.groupby ).

Shapefile gốc được tô màu bởi trường STATEFP

nhập mô tả hình ảnh ở đây

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

Kết quả

nhập mô tả hình ảnh ở đây


Đẹp quá, khó chọn giữa cả hai. Rất vui khi thấy các phương pháp khác nhau cảm ơn bạn!
Người dùng18981898198119

11

Tôi đặc biệt khuyên dùng GeoPandas để xử lý các loại tính năng lớn và thực hiện các hoạt động hàng loạt.

Nó mở rộng các tệp dữ liệu Pandas và sử dụng hình dạng dưới mui xe.

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

Đó là cách thanh lịch hơn nhiều so với điều kỳ lạ hacky của tôi. cảm ơn ! Có cách nào để vượt qua hệ thống tham chiếu không gian?
Người dùng18981898198119

Tôi đã chỉnh sửa bài đăng của mình để hiển thị cách chuyển crs từ tệp nguồn sang tệp mới. Điều này xảy ra tại dòng nơi GeoDataFrame được tạo. Về Lược đồ, tôi khuyên bạn chỉ nên tạo một thủ công (nghĩa là sử dụng thuộc tính cột của cùng một dòng) sau đó được ghi vào thuộc tính của tệp mới. tức là khi bạn tạo từ điển trạng thái, chỉ cần thêm bất kỳ thuộc tính nào khác khi bạn đi và gán chúng vào một cột trong khung dữ liệu mới.
songololo

0

Là một phụ lục cho câu trả lời của @ gen , tôi cần phải giải thể bằng nhiều trường để sửa đổi mã của mình để xử lý nhiều trường. Mã dưới đây sử dụngoperator.itemgetter để nhóm theo nhiều trường:

# 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"))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.