การละลายรูปหลายเหลี่ยมขึ้นอยู่กับคุณลักษณะด้วย Python (หุ่นดีฟิโอน่า)?


15

ฉันพยายามสร้างฟังก์ชั่นที่ทำสิ่งเดียวกันกับที่ QGIS "ละลาย" ฟังก์ชั่น ฉันคิดว่ามันจะง่ายสุด ๆ แต่ก็ดูเหมือนจะไม่ดี ดังนั้นจากสิ่งที่ฉันรวบรวมได้การใช้ฟิโอน่ากับหุ่นดีควรเป็นตัวเลือกที่ดีที่สุดที่นี่ ฉันเพิ่งเริ่มยุ่งเกี่ยวกับไฟล์เวคเตอร์ดังนั้นโลกนี้ก็ค่อนข้างใหม่สำหรับฉันและไพ ธ อนเช่นกัน

สำหรับตัวอย่างเหล่านี้ฉันทำงานกับ County shapefile ก่อตั้งขึ้นที่นี่http://tinyurl.com/odfbanu ดังนั้นนี่คือรหัสบางส่วนที่ฉันรวบรวม แต่ไม่สามารถหาวิธีที่จะทำให้พวกเขาทำงานร่วมกันได้

สำหรับตอนนี้วิธีที่ดีที่สุดของฉันคือต่อไปนี้ขึ้นอยู่กับ: https://sgillies.net/2009/01/27/a-more-perfect-union-continued.html มันใช้งานได้ดีและฉันได้รับรายชื่อของ 52 รัฐเป็นรูปทรงเรขาคณิตที่ดี โปรดแสดงความคิดเห็นหากมีวิธีที่ตรงไปข้างหน้ามากขึ้นในการทำส่วนนี้

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 ยังใช้ฟิโอน่าในการอ่าน / เขียนรูปร่างไฟล์

ฉันไม่ได้ถามนี่ยูทิลิตี้ GeoPandas แต่คุณสามารถทำมันได้โดยตรงกับฟิโอน่าใช้โมดูลมาตรฐานitertoolsพิเศษกับคำสั่งGroupBy ( "สั้น, GroupBy ใช้เวลา iterator และแบ่งมันออกเป็นย่อย iterators ตามการเปลี่ยนแปลง ใน "คีย์" ของตัววนซ้ำหลักซึ่งแน่นอนว่าทำได้โดยไม่ต้องอ่านตัววนซ้ำต้นฉบับทั้งหมดลงในหน่วยความจำ ", itertools.groupby )

Shapefile ดั้งเดิมสีตามฟิลด์ 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]})

ผลลัพธ์

ป้อนคำอธิบายรูปภาพที่นี่


เป็นคนดีเช่นกันยากที่จะเลือกระหว่างทั้งสอง ยินดีที่ได้เห็นวิธีการที่แตกต่างกันขอบคุณ!
User18981898198119

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 จากไฟล์ต้นฉบับไปยังไฟล์ใหม่ สิ่งนี้จะเกิดขึ้นที่บรรทัดที่สร้าง GeoDataFrame ในสถานะฯ เกี่ยวกับ Schema ฉันขอแนะนำให้สร้างด้วยตนเอง (เช่นการใช้คุณสมบัติคอลัมน์ของบรรทัดเดียวกัน) ซึ่งจะถูกเขียนไปยังคุณสมบัติของไฟล์ใหม่ เช่นเมื่อคุณสร้างพจนานุกรมสถานะของคุณเพียงเพิ่มคุณสมบัติอื่น ๆ ตามที่คุณไปและกำหนดให้กับคอลัมน์ในกรอบข้อมูลใหม่
songololo

0

ในฐานะที่เป็นภาคผนวกของ @ คำตอบของยีนฉันจำเป็นต้องละลายมากกว่าหนึ่งช่องเพื่อแก้ไขโค้ดของเขาเพื่อจัดการหลายฟิลด์ รหัสด้านล่างใช้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.