Розуміння використання просторових індексів за допомогою RTree?


13

У мене виникають проблеми з розумінням використання просторових індексів за допомогою RTree.

Приклад: У мене 300 буферизованих точок, і мені потрібно знати область перетину кожного буфера з форматом багатокутника. Формафайлу багатокутника налічується> 20000 багатокутників. Було запропоновано використати просторові індекси для прискорення процесу.

Так ... Якщо я створять просторовий індекс для свого формату багатокутника, він буде якимось чином "приєднаний" до файлу, чи індекс буде окремим? Тобто, після його створення я можу просто запустити функцію перетину на полігонному файлі та отримати швидші результати? Чи перехрестя "побачить", що є просторові індекси та знає, що робити? Або мені потрібно запустити його в індексі, а потім відновити ці результати назад до мого оригінального файла багатокутника через FID або щось подібне?

Документація RTree мені не дуже допомагає (можливо, тому, що я просто вивчаю програмування). Вони показують, як створити індекс, читаючи в створених вручну точках, а потім запитують його до інших створених вручну точок, що повертає ідентифікатори, що містяться у вікні. Має сенс. Але вони не пояснюють, як це стосуватиметься якогось оригінального файлу, з якого індекс взявся б.

Я думаю, що це має мати щось подібне:

  1. Витягніть скриньки для кожної функції багатокутника з мого файлу форм многокутника і помістіть їх у просторовий індекс, давши їм ідентифікатор, такий самий, як ідентифікатор у файлі форм.
  2. Запросіть цей індекс, щоб отримати ідентифікатори, які перетинаються.
  3. Потім перезапустіть мій перехрест лише в тих характеристиках мого оригінального файлу форм, які були ідентифіковані шляхом запиту мого індексу (не впевнений, як би я зробив цю останню частину).

Чи маю правильну ідею? Я щось пропускаю?


Зараз я намагаюся змусити цей код працювати над одним точковим файлом форми, який містить лише одну точкову функцію, і одним файлом форми многокутника, який містить> 20000 функцій багатокутника.

Я імпортую файли форм за допомогою Fiona, додаю просторовий індекс за допомогою RTree і намагаюся зробити перехрестя за допомогою Shapely.

Мій тестовий код виглядає приблизно так:

#point shapefile representing location of desired focal statistic
traps = fiona.open('single_pt_speed_test.shp', 'r') 

#polygon shapefile representing land cover of interest 
gl = MultiPolygon([shape(pol['geometry']) for pol in fiona.open('class3_aa.shp', 'r')]) 

#search area
areaKM2 = 20

#create empty spatial index
idx = index.Index()

#set initial search radius for buffer
areaM2 = areaKM2 * 1000000
r = (math.sqrt(areaM2/math.pi))

#create spatial index from gl
for i, shape in enumerate(gl):
    idx.insert(i, shape.bounds)

#query index for ids that intersect with buffer (will eventually have multiple points)
for point in traps:
        pt_buffer = shape(point['geometry']).buffer(r)
        intersect_ids = pt_buffer.intersection(idx)

Але я продовжую отримувати TypeError: "Полігон" об'єкт не можна викликати


1
Простірний індекс є інтегральним та прозорим для набору даних (містить, а не одну сутність з точки зору користувача) Програмне забезпечення, яке виконує перехрестя, обізнане і використовує просторові індекси для створення короткого списку для виконання реального перетину шляхом швидкого інформування програмне забезпечення, особливості якого слід враховувати для більш детального огляду і яке явно ніде не перетинається. Те, як ви створюєте його, залежить від вашого програмного забезпечення та типу даних ..., будь ласка, надайте більше інформації в цих пунктах для отримання конкретнішої допомоги. Для файлу форми це .shx файл.
Майкл Стімсон

4
.shx не є просторовим індексом. Це лише файл зміщення ширини запису змінної ширини. .sbn / .sbx є просторовою індексною парою архівної форми ArcGIS, хоча специфікація для них не була випущена.
Вінс

1
Також .qixє картсервер / GDAL / OGR / SpatiaLite квадрантів індекс
Mike T

Ваша ідея ідеально підходить для Spatialite, який не має реального просторового індексу. Більшість інших форматів, якщо вони взагалі підтримують просторові індекси, роблять це прозоро.
user30184

2
Ви продовжуєте отримувати TypeError: 'Polygon' object is not callableприклад оновлення, оскільки ви перезаписуєте shapeфункцію, яку ви імпортували з for i, shape in enumerate(gl):
формально,

Відповіді:


12

У цьому суть. R-дерево дозволяє зробити перший швидкий прохід дуже швидко і дає набір результатів, які матимуть "помилкові позитиви" (обмежувальні поля можуть перетинатися, коли геометрії точно не роблять). Потім ви переходите через набір кандидатів (виймаєте їх з формфайлу за їх індексом) і робите математично точний тест перетину, використовуючи, наприклад, Shapely. Це та сама стратегія, яка використовується в просторових базах даних, як PostGIS.


1
Гарний каламбур (Гіст)! GiST зазвичай описується як B-Tree варіант, але Postgresql має реалізацію GiST R-Tree. Хоча wiki не обов'язково є найкращим посиланням на те, що цитується, він має приємну схему, яка пояснює пошук обмежувальних вікон.
MappaGnosis

Можливо, варто вивчити ручний спосіб використання індексу R-дерева, як у кроках 2 та 3. Цей блог про OGC GeoPackage, який також підтримує R-дерево, оскільки окремі таблиці баз даних показують деякі SQL і екранні захоплення openjump.blogspot.fi / 2014/02 /… .
user30184

9

Ви майже отримали це, але ви зробили невелику помилку. Вам потрібно використовувати intersectionметод на просторовому індексі, а не передавати індекс intersectionметоду в буферній точці. Після того, як ви знайшли перелік функцій, де обмежувальні рамки перекриваються, вам потрібно перевірити, чи справді ваша буферна точка перетинає геометрії.

import fiona
from shapely.geometry import mapping
import rtree
import math

areaM2 = areaKM2 * 1000000
r = (math.sqrt(areaM2/math.pi))

# open both layers
with fiona.open('single_pt_speed_test.shp', 'r') as layer_pnt:
    with fiona.open('class3_aa.shp', 'r') as layer_land:

        # create an empty spatial index object
        index = rtree.index.Index()

        # populate the spatial index
        for fid, feature in layer_land.items():
            geometry = shape(feature['geometry'])
            idx.insert(fid, geometry.bounds)

        for feature in layer_pnt:
            # buffer the point
            geometry = shape(feature['geometry'])
            geometry_buffered = geometry.buffer(r)

            # get list of fids where bounding boxes intersect
            fids = [int(i) for i in index.intersection(geometry_buffered.bounds)]

            # access the features that those fids reference
            for fid in fids:
                feature_land = layer_land[fid]
                geometry_land = shape(feature_land['geometry'])

                # check the geometries intersect, not just their bboxs
                if geometry.intersects(geometry_land):
                    print('Found an intersection!')  # do something useful here

Якщо вам цікаво знайти точки, які знаходяться на мінімальній відстані від вашого наземного класу, ви можете скористатися distanceметодом (замініть відповідний розділ з попереднього).

for feature in layer_pnt:
    geometry = shape(feature['geometry'])

    # expand bounds by r in all directions
    bounds = [a+b*r for a,b in zip(geometry.bounds, [-1, -1, 1, 1])]

    # get list of fids where bounding boxes intersect
    fids = [int(i) for i in index.intersection(geometry_buffered.bounds)]

    for fid in fids:
        feature_land = layer_land[fid]
        geometry_land = shape(feature_land['geometry'])

        # check the geometries are within r metres
        if geometry.distance(geometry_land) <= r:
            print('Found a match!')

Якщо для побудови просторового індексу потрібно багато часу, і ви збираєтеся це робити більше декількох разів, вам слід розібратися в послідовній індексації до файлу. Документація описує, як це зробити: http://toblerity.org/rtree/tutorial.html#serializing-your-index-to-a-file

Ви також можете подивитися на групове завантаження обмежувальних коробок у rtree за допомогою генератора, як це:

def gen(collection):
    for fid, feature in collection.items():
        geometry = shape(feature['geometry'])
        yield((fid, geometry.bounds, None))
index = rtree.index.Index(gen(layer_land))

2

Так, це ідея. Ось уривок із цього підручника про використання просторового індексу r-дерева в Python , використовуючи стройні, Fiona та геопанди:

R-дерево представляє окремі об'єкти та їх обмежувальні поля ("r" - "прямокутник") як найнижчий рівень просторового індексу. Потім він агрегує об'єкти, що знаходяться поблизу, і представляє їх з їх сукупним обмежувальним вікном на наступному більш високому рівні індексу. На ще більш високих рівнях r-дерево агрегує обмежувальні коробки і представляє їх своєю обмежувальною коробкою ітеративно, поки все не вкладається в один обмежувальний ящик верхнього рівня. Для пошуку r-дерево бере поле запиту і, починаючи з верхнього рівня, бачить, які (якщо такі є) обмежувальні поля перетинають його. Потім він розширює кожне пересічне обмежувальне поле і бачить, який із дочірніх обмежувальних коробок всередині нього перетинає поле запиту. Це триває рекурсивно, поки всі пересічні поля не будуть знайдені до найнижчого рівня і не повернуть відповідні об'єкти з найнижчого рівня.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.