Çokgenleri Python (şekilli, fiona) ile özniteliklere göre çözme?


15

Temelde QGIS "çözülür" işlevi aynı şeyi yapan bir işlev oluşturmaya çalışıyorum. Süper kolay olacağını düşündüm ama görünüşe göre değil. Bu yüzden topladığım şeyden, fiona'nin düzgün bir şekilde kullanılması burada en iyi seçenek olmalı. Sadece vektör dosyaları ile uğraşmaya başladım, bu yüzden bu dünya benim ve python için de oldukça yeni.

Bu örnek için, burada kurulmuş bir ilçe şekil dosyası ile çalışıyorum http://tinyurl.com/odfbanu İşte burada topladığım ancak birlikte çalışmasını sağlamak için bir yol bulamıyorum

Şimdilik en iyi yöntemim şu şekildedir: https://sgillies.net/2009/01/27/a-more-perfect-union-continued.html . İyi çalışıyor ve 52 durumun bir listesini Shapely geometry olarak alıyorum. Bu bölümü yapmanın daha basit bir yolu varsa lütfen yorum yapmaktan çekinmeyin.

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.

Bu yüzden yazı gerçekten gördüğümden çok ileri değil, sadece aynı ülke sadece devletlerin çözülmesiyle birlikte istiyorum, özellik tablosunun çoğuna bile ihtiyacım yok ama nasıl geçebileceğinizi merak ediyorum kaynaktan yeni oluşturulan şekil dosyasına aktarır.

Fiona ile yazmak için birçok kod parçası buldum ama asla verilerimle çalışmasını sağlayamıyorum. Şekil dosyalarına düzgün geometriler nasıl yazılır? :

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

Buradaki problem, geometri listesiyle aynı şeyin nasıl yapılacağı ve kaynaktan aynı özelliklerin nasıl yeniden yaratılacağıdır.

Yanıtlar:


22

Soru Fiona ve düzgün ilgilidir ve GeoPandas kullanan diğer cevabı da bilmek gerekir pandalar . Ayrıca GeoPandas şekil dosyalarını okumak / yazmak için Fiona'yı kullanır.

Burada GeoPandas'ın faydasını sorgulamıyorum, ancak standart modül itertools'u kullanarak doğrudan Fiona ile yapabilirsiniz , özellikle komut groupby komutuyla ("Özetle, groupby bir yineleyici alır ve değişikliklere bağlı olarak alt yineleyicilere ayırır ana yineleyicinin "anahtarında" Bu, elbette tüm kaynak yineleyiciyi belleğe okumadan yapılır " itertools.groupby ).

STATEFP alanı ile renklendirilmiş orijinal Shapefile

resim açıklamasını buraya girin

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

Sonuç

resim açıklamasını buraya girin


Güzel bir de, ikisi arasında seçim zor. Farklı yöntemler görmek güzel teşekkür ederim!
Kullanıcı18981898198119

11

GeoPandas'ı çok çeşitli özelliklerle uğraşmak ve toplu işlemler yapmak için tavsiye ederim.

Pandas veri çerçevelerini genişletir ve kaputun altında düzgün bir şekilde kullanır.

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

Bu acayip garip şeyimden daha şık. Teşekkürler ! Mekansal referans sistemini aktarmanın bir yolu var mı?
Kullanıcı18981898198119

Yazımı, kaynak dosyadan yeni dosyaya nasıl aktarılacağını göstermek için düzenledim. Bu, states_dissolved GeoDataFrame'in oluşturulduğu satırda gerçekleşir. Şema ile ilgili olarak, daha sonra yeni dosyanın özelliklerine yazılan yalnızca bir tane (yani aynı satırın column özelliğini kullanarak) oluşturmanızı öneririm. Örneğin, durum sözlüğünüzü oluştururken, diğer özellikleri ilerledikçe ekleyin ve yeni veri çerçevesindeki bir sütuna atayın.
songololo

0

@ Gene'nin cevabına bir zeyilname olarak , birden fazla alanı çözmem gerekiyordu, bu yüzden kodunu birden fazla alanı işlemek için değiştirdim. Aşağıdaki kod operator.itemgetter, birden çok alana göre gruplandırmak için kullanılır:

# 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"))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.