QGIS, ArcGIS, PostGIS, vb. Olmadan Python'a Daha Verimli Mekansal katılım


31

Buradaki örnekte olduğu gibi mekansal bir birleşme yapmaya çalışıyorum: "Konumlara göre niteliklere katılmak" için bir python seçeneği var mı?. Ancak, bu yaklaşım gerçekten yetersiz / yavaş görünüyor. Bunu mütevazı bir 250 puanla çalıştırmak bile 2 dakika sürüyor ve tamamen> 1000 puan olan şekil dosyalarında başarısız oluyor. Daha iyi bir yaklaşım var mı? Bunu ArcGIS, QGIS vb. Kullanmadan tamamen Python'da yapmak istiyorum.

Ayrıca bir çokgenin içine giren tüm noktaların SUM özniteliklerinin (yani popülasyonunun) TOPLANMASI mümkün olup olmadığını bilmek ve bu miktarı çokgen şekil dosyasına birleştirmenin mümkün olup olmadığını bilmek isterim.

İşte dönüştürmeye çalıştığım kod. 9. satırda hata alıyorum:

poly['properties']['score'] += point['properties']['score']

ki diyor ki:

TypeError: + =: 'NoneType' ve 'float' için desteklenmeyen işlenen türleri.

Eğer "+ =" yerine "=" koyarsam iyi çalışır ancak bu alanları toplamaz. Bunları tamsayı olarak yapmaya çalıştım ama bu da başarısız oldu.

with fiona.open(poly_shp, 'r') as n: 
  with fiona.open(point_shp,'r') as s:
    outSchema = {'geometry': 'Polygon','properties':{'region':'str','score':'float'}}
    with fiona.open (out_shp, 'w', 'ESRI Shapefile', outSchema, crs) as output:
        for point in s:
            for poly in n:
                if shape(point['geometry']).within(shape(poly['geometry'])):  
                    poly['properties']['score']) += point['properties']['score'])
                    output.write({
                        'properties':{
                            'region':poly['properties']['NAME'],
                            'score':poly['properties']['score']},
                        'geometry':poly['geometry']})

Sanırım ikinci sorunuzu buradan düzenlemelisiniz ki, bu soru sizin için daha önemli bir soru olduğunu farz ettim. Diğer ayrı ayrı araştırılabilir / sorulabilir.
PolyGeo

Yanıtlar:


37

Fiona Python sözlüklerini döndürür ve poly['properties']['score']) += point['properties']['score'])bir sözlükle kullanamazsınız .

Mike T tarafından verilen referansları kullanarak toplama özelliklerinin örneği:

görüntü tanımını buraya girin

# read the shapefiles 
import fiona
from shapely.geometry import shape
polygons = [pol for pol in fiona.open('poly.shp')]
points = [pt for pt in fiona.open('point.shp')]
# attributes of the polygons
for poly in polygons:
   print poly['properties'] 
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])

# attributes of the points
for pt in points:
    print i['properties']
 OrderedDict([(u'score', 1)]) 
 .... # (same for the 8 points)

Şimdi, uzamsal bir dizini olsun ya da olmasın iki yöntem kullanabiliriz:

1) olmadan

# iterate through points 
for i, pt in enumerate(points):
     point = shape(pt['geometry'])
     #iterate through polygons
     for j, poly in enumerate(polygons):
        if point.within(shape(poly['geometry'])):
             # sum of attributes values
             polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']

2) bir R-ağacı indeksi ile ( pyrtree veya rtree kullanabilirsiniz )

# Create the R-tree index and store the features in it (bounding box)
 from rtree import index
 idx = index.Index()
 for pos, poly in enumerate(polygons):
       idx.insert(pos, shape(poly['geometry']).bounds)

#iterate through points
for i,pt in enumerate(points):
  point = shape(pt['geometry'])
  # iterate through spatial index
  for j in idx.intersection(point.coords[0]):
      if point.within(shape(multi[j]['geometry'])):
            polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']

İki çözümle sonuçlandırın:

for poly in polygons:
   print poly['properties']    
 OrderedDict([(u'score', 2)]) # 2 points in the polygon
 OrderedDict([(u'score', 1)]) # 1 point in the polygon
 OrderedDict([(u'score', 1)]) # 1 point in the polygon

Fark ne ?

  • Dizin olmadan, tüm geometrileri yinelemelisiniz (çokgenler ve noktalar).
  • Sınırlayıcı bir uzamsal dizinle ( Spatial Index RTree ), yalnızca geçerli geometrinizle kesişme şansı olan geometrileri yinelersiniz (önemli miktarda hesaplama ve zaman kazandıran 'filtre' ...)
  • ancak Mekansal Bir Endeks sihirli bir değnek değildir. Veri setinin çok büyük bir kısmının alınması gerektiğinde, bir Mekansal Endeksi herhangi bir hız avantajı sağlayamaz.

Sonra:

schema = fiona.open('poly.shp').schema
with fiona.open ('output.shp', 'w', 'ESRI Shapefile', schema) as output:
    for poly in polygons:
        output.write(poly)

Daha ileri gitmek için, OGR, Düzgün, Fiona ile Rtree Spatial Indexing'i Kullanma konusuna bakın.


15

Ek olarak - artık coğrafyalar isteğe bağlı olarak rtree olarak bir bağımlılık , github bakınız.

Bu nedenle, yukarıdaki tüm (çok güzel) kodları izlemek yerine, basitçe şöyle bir şey yapabilirsiniz:

import geopandas
from geopandas.tools import sjoin
point = geopandas.GeoDataFrame.from_file('point.shp') # or geojson etc
poly = geopandas.GeoDataFrame.from_file('poly.shp')
pointInPolys = sjoin(point, poly, how='left')
pointSumByPoly = pointInPolys.groupby('PolyGroupByField')['fields', 'in', 'grouped', 'output'].agg(['sum'])

Bu şık işlevselliği elde etmek için C-library'i kurduğunuzdan emin olun. libspatialindex ilk

EDIT: düzeltilmiş paket ithalatı


rtreeOpsiyonel olduğu izlenimi altındaydım . Bu rtree, libspatialindexC kütüphanesinin yanı sıra yüklemeniz gerektiği anlamına gelmiyor mu?
kuanb

Ben otomatik olarak eklenir github bu yüklemeden geopandas yaptım ama sanırım ederken bir oldu rtreeben ilk kurulduğunda ne zaman libspatialindexemin İşler biraz değişti ki ... onlar oldukça büyük sürümünde yaptık beri çok
claytonrsh

9

Daha hızlı birleşme işlemini gerçekleştirmek için Rtree'yi bir indeks olarak kullanın , ardından bir noktanın gerçekten çokgen içinde olup olmadığını belirlemek için uzamsal tahminleri yapmak için Shapely . Doğru şekilde yapılırsa, bu diğer çoğu CBS'den daha hızlı olabilir.

Buradaki veya buradan örneklere bakınız .

Sorunuzun 'SUM' ile ilgili ikinci bölümünde, dict , anahtar olarak bir çokgen kimliği kullanarak popülasyonları biriktirmek nesne kullanın. Yine de, bu tür bir şey PostGIS ile çok daha güzel yapılır.


Teşekkür ederim @Mike T ... dict nesnesini veya PostGIS'i kullanmak harika önerilerdir. Bununla birlikte, Rtree'yi koduma ekleyebileceğim kafam biraz karıştı (yukarıdaki kod dahil).
jburrfischer

1

Bu web sayfası, Shapely'in daha pahalı olan Mekansal sorgulamasından önce bir Poligon işaretinde bir Sınırlayıcı Kutunun nasıl kullanılacağını gösterir.

http://rexdouglass.com/fast-spatial-joins-in-python-with-a-spatial-index/


Teşekkürler @klewis ... ikinci bölüme yardım edebilme şansın var mı? Çokgenlerin içine giren nokta özelliklerini (örneğin popülasyonu) özetlemek için aşağıdaki koda benzer bir şey denedim, ancak bir hata attı. eğer şekil (okul ['geometri']). içinde (şekil (mahalle ['geometri'])): mahalle ['özellikler'] ['nüfus'] + = okullar ['özellikler'] ['nüfus']
jburrfischer

Mahalle 'r' modunda açarsanız, salt okunur olabilir. Her iki şekil dosyasının alan popülasyonu var mı? Hangi satır # hatayı atıyor? İyi şanslar.
klewis

Tekrar teşekkür ederim @klewis ... Yukarıdaki kodumu ekledim ve hatayı açıkladım. Ayrıca, rtree ile oynuyordum ve hala yukarıdaki koda ekleyeceğim kafam çok karıştı. Böyle bir rahatsızlık için özür dilerim.
jburrfischer

Bunu deneyin, bir int ekleyerek hataya neden oluyor gibi görünüyor. poly_score = poly ['özellik'] ['puan']) point_score = puan ['özellik'] ['puan']) eğer point_score: eğer poly_score poli ['özellik'] ['puan']) + = point_score başka: poly ['properties'] ['score']) = point_score
klewis
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.