georust / rstar

R*-tree spatial index for the Rust ecosystem
https://docs.rs/rstar
Apache License 2.0
414 stars 68 forks source link

locate_in_envelope not working? #173

Open mztlive opened 4 months ago

mztlive commented 4 months ago

The following codes should supposedly be able to return data consistent with expectations, but the results cannot be obtained correctly after using locate_in_envlope.

If you are traversing all products and using geo's contains method, you can get the correct result.

Is my usage method wrong? Please teach me, thank you!

use geo::{BoundingRect, Contains, LineString, Point, Polygon};
use rayon::prelude::*;
use rstar::{RTree, RTreeObject, AABB};
use std::time::Instant;

#[derive(Debug, Clone)]
struct Product {
    id: u32,
    polygon: Polygon<f64>,
}

impl RTreeObject for Product {
    type Envelope = AABB<[f64; 2]>;

    fn envelope(&self) -> Self::Envelope {
        let bbox = self.polygon.bounding_rect().unwrap();
        AABB::from_corners([bbox.min().x, bbox.min().y], [bbox.max().x, bbox.max().y])
    }
}

fn main() {
    // Example product data
    let products = vec![
        Product {
            id: 1,
            polygon: Polygon::new(
                LineString::from(vec![
                    (116.0, 39.0),
                    (116.0, 40.0),
                    (117.0, 40.0),
                    (117.0, 39.0),
                    (116.0, 39.0), // Ensure the polygon is closed
                ]),
                vec![],
            ),
        },
        Product {
            id: 2,
            polygon: Polygon::new(
                LineString::from(vec![
                    (118.0, 39.0),
                    (118.0, 40.0),
                    (119.0, 40.0),
                    (119.0, 39.0),
                    (118.0, 39.0), // Ensure the polygon is closed
                ]),
                vec![],
            ),
        },
    ];

    // Create R-tree index
    let rtree = RTree::bulk_load(products.clone());

    // Print the bounding box and polygon points of each product
    for product in &products {
        let bbox = product.polygon.bounding_rect().unwrap();
        println!(
            "Product ID: {}, BBox: (({}, {}), ({}, {})), Polygon: {:?}",
            product.id,
            bbox.min().x,
            bbox.min().y,
            bbox.max().x,
            bbox.max().y,
            product.polygon
        );
    }

    // Parallel query function
    let query_point = |point: Point<f64>| -> Vec<u32> {
        let aabb = AABB::from_point([point.x(), point.y()]);
        println!(
            "Query Point: ({}, {}), AABB: (({}, {}), ({}, {}))",
            point.x(),
            point.y(),
            aabb.lower()[0],
            aabb.lower()[1],
            aabb.upper()[0],
            aabb.upper()[1]
        );

        let results: Vec<u32> = rtree
            .locate_in_envelope(&aabb)
            .filter(|product| {
                println!(
                    "Checking Product ID: {}, Polygon: {:?}",
                    product.id, product.polygon
                );
                let contains = product.polygon.contains(&point);
                println!(
                    "Checking Product ID: {}, Point: ({}, {}), Contains: {}",
                    product.id,
                    point.x(),
                    point.y(),
                    contains
                );
                contains
            })
            .map(|product| product.id)
            .collect();

        println!(
            "Query Point: ({}, {}), Results: {:?}",
            point.x(),
            point.y(),
            results
        );
        results
    };

    // Example multiple query points
    let points = vec![
        Point::new(116.5, 39.5), // Should match product ID 1
        Point::new(118.5, 39.5), // Should match product ID 2
        Point::new(117.5, 39.5), // Should not match any product
    ];

    let start = Instant::now();

    // Parallel processing of queries
    let results: Vec<Vec<u32>> = points.par_iter().map(|&point| query_point(point)).collect();

    let duration = start.elapsed();

    println!("{:?}", results); // Output the list of product IDs for each point
    println!("Time elapsed in expensive_function() is: {:?}", duration);
}
grovesNL commented 2 months ago

If I'm reading this correctly, it looks like this this is checking which products are fully contained in each point's envelope/bounding box (which is nothing), but you're trying to do the opposite.

Maybe locate_in_envelope_intersecting would be a better fit?