เข้าใจการใช้ดัชนีเชิงพื้นที่กับ RTree หรือไม่?


13

ฉันมีปัญหาในการทำความเข้าใจการใช้ดัชนีเชิงพื้นที่กับ RTree

ตัวอย่าง: ฉันมี 300 คะแนนบัฟเฟอร์และฉันจำเป็นต้องรู้พื้นที่จุดตัดบัฟเฟอร์แต่ละแห่งด้วยไฟล์รูปหลายเหลี่ยม ไฟล์รูปหลายเหลี่ยมมีมากกว่า 20,000 รูป มันแนะนำให้ฉันใช้ดัชนีเชิงพื้นที่เพื่อเร่งกระบวนการ

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

เอกสาร RTree ไม่ได้ช่วยฉันมาก (อาจเป็นเพราะฉันเพิ่งเรียนรู้การเขียนโปรแกรม) พวกเขาแสดงวิธีการสร้างดัชนีโดยการอ่านในจุดที่สร้างขึ้นด้วยตนเองแล้วทำการสอบถามกับจุดที่สร้างขึ้นด้วยตนเองอื่น ๆ ซึ่งจะส่งกลับรหัสที่มีอยู่ภายในหน้าต่าง มีเหตุผล. แต่พวกเขาไม่ได้อธิบายว่ามันจะเกี่ยวข้องกับไฟล์ต้นฉบับที่ดัชนีมาจากไหน

ฉันคิดว่ามันต้องเป็นอย่างนี้:

  1. ดึงกล่องสี่เหลี่ยมสำหรับคุณลักษณะรูปหลายเหลี่ยมแต่ละรูปจากแฟ้มรูปหลายเหลี่ยมของฉันและวางไว้ในดัชนีเชิงพื้นที่โดยให้รหัสที่เหมือนกันกับรหัสในรูปแบบไฟล์
  2. ค้นหาดัชนีนั้นเพื่อรับรหัสที่ตัดกัน
  3. จากนั้นเรียกใช้จุดแยกของฉันอีกครั้งบนคุณสมบัติเฉพาะในไฟล์รูปร่างดั้งเดิมที่ระบุโดยการสืบค้นดัชนีของฉัน (ไม่แน่ใจว่าฉันจะทำส่วนสุดท้ายนี้อย่างไร)

ฉันมีความคิดที่ถูกต้องหรือไม่? ฉันไม่มีอะไรเลยหรือ


ตอนนี้ฉันกำลังพยายามทำให้โค้ดนี้ทำงานบนหนึ่งไฟล์ shapefile ที่มีคุณสมบัติจุดเดียวเท่านั้นและหนึ่งไฟล์รูปหลายเหลี่ยมที่มี> 20,000 รูปหลายเหลี่ยมคุณสมบัติ

ฉันกำลังนำเข้า Shapefiles โดยใช้ 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
Michael Stimson

4
.shx ไม่ใช่ดัชนีเชิงพื้นที่ มันเป็นเพียงไฟล์ออฟเซ็ตการเข้าถึงแบบไดนามิกบันทึกความกว้างตัวแปร .sbn / .sbx เป็นคู่ของ ArcGIS Shapefile spatial index pair แม้ว่าข้อมูลจำเพาะสำหรับสิ่งเหล่านั้นจะไม่ถูกปล่อย
วินซ์

1
นอกจากนี้ยัง.qixเป็นดัชนีMapServer / GDAL / OGR / SpatiaLite quadtree
Mike T

ความคิดของคุณอย่างถูกต้องเหมาะสมสำหรับ Spatialite ซึ่งไม่มีดัชนีเชิงพื้นที่ที่แท้จริง รูปแบบอื่น ๆ ส่วนใหญ่หากพวกเขาสนับสนุนดัชนีเชิงพื้นที่เลยทำมันโปร่งใส
user30184

2
คุณได้รับTypeError: 'Polygon' object is not callableกับตัวอย่างการปรับปรุงของคุณเพราะคุณเขียนทับshapeฟังก์ชั่นที่คุณนำเข้าจากหุ่นดีด้วยวัตถุรูปหลายเหลี่ยมที่คุณสร้างด้วยบรรทัดนี้:for i, shape in enumerate(gl):
2856

คำตอบ:


12

นั่นคือส่วนสำคัญของมัน ต้นไม้ R ช่วยให้คุณผ่านอย่างรวดเร็วก่อนและให้ชุดผลลัพธ์ที่จะมี "ผลบวกปลอม" (กล่องที่ล้อมรอบอาจตัดกันเมื่อรูปทรงเรขาคณิตไม่ถูกต้อง) จากนั้นคุณก็ไปที่กลุ่มของผู้สมัคร (ดึงข้อมูลพวกเขาจาก shapefile ตามดัชนีของพวกเขา) และทำการทดสอบทางแยกที่แม่นยำทางคณิตศาสตร์โดยใช้เช่น Shapely นี่เป็นกลยุทธ์เดียวกับที่ใช้ในฐานข้อมูลเชิงพื้นที่เช่น PostGIS


1
Nice pun (GiST)! โดยทั่วไปแล้ว GiST จะอธิบายว่าเป็นตัวแปร B-Tree แต่ Postgresql มีการนำ GST ไปใช้กับ R-Tree ถึงแม้ว่าวิกินั้นไม่จำเป็นต้องมีการอ้างอิงที่ดีที่สุดในการอ้างอิง แต่ก็มีไดอะแกรมที่ดีในการอธิบายการค้นหาช่องว่าง
MappaGnosis

มันอาจจะคุ้มค่าที่จะเรียนรู้วิธีใช้คู่มือสำหรับการใช้ดัชนี R-tree ในขั้นตอนที่ 2 และ 3 บล็อกของคุณเกี่ยวกับ OGC GeoPackage ซึ่งรองรับ R-tree ด้วยตารางฐานข้อมูลที่แยกจากกันแสดง 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-tree ใน Pythonโดยใช้หุ่นดี, Fiona และ geopandas:

r-tree หมายถึงวัตถุเดี่ยว ๆ และกล่อง bounding ("r" ใช้สำหรับ "rectangle") เป็นระดับต่ำสุดของดัชนีอวกาศ จากนั้นจะรวมวัตถุที่อยู่ใกล้เคียงและแสดงวัตถุเหล่านั้นด้วยกล่องรวมขอบเขตในระดับที่สูงขึ้นของดัชนี ในระดับที่สูงขึ้น r-tree จะรวมกล่อง bounding และแสดงโดยกล่อง bounding ของมันซ้ำ ๆ จนกระทั่งทุกอย่างซ้อนกันเป็น box bounding ระดับบนสุด ในการค้นหา r-tree จะใช้กล่องแบบสอบถามและเริ่มต้นที่ระดับบนสุดเพื่อดูว่ามีช่องใด (ถ้ามี) ที่ตัดกัน จากนั้นจะขยายแต่ละกล่องการข้ามจุดตัดและดูว่ากล่องลูกเชื่อมโยงใดที่อยู่ข้างในนั้นตัดกล่องแบบสอบถาม การดำเนินการนี้จะเกิดขึ้นซ้ำ ๆ จนกว่ากล่องที่ตัดกันทั้งหมดจะถูกค้นหาลงสู่ระดับต่ำสุดและส่งคืนวัตถุที่ตรงกันจากระดับต่ำสุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.