kdtree 0.8.1

K-dimensional tree in Rust for fast geospatial indexing and nearest neighbors lookup
Documentation
mod __util__;

use __util__::{POINT_A, POINT_B, POINT_C, basic_tree};
use kdtree::distance::squared_euclidean;
use kdtree::{ErrorKind, KdTree};

fn assert_ordered_usize(results: Vec<(f64, &usize)>, expected: &[(f64, usize)]) {
    assert_eq!(results.into_iter().map(|(d, v)| (d, *v)).collect::<Vec<_>>(), expected);
}

#[test]
fn nearest_queries_match_expected() {
    let tree = basic_tree();
    assert_eq!(tree.size(), 4);

    assert_ordered_usize(tree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), &[]);
    assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[(0f64, 0)]);
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(),
        &[(0f64, 1), (2f64, 0), (2f64, 2), (8f64, 3)],
    );
}

#[test]
fn iter_nearest_matches_batch() {
    let tree = basic_tree();
    let collected: Vec<_> = tree.iter_nearest(&POINT_A.0, &squared_euclidean).unwrap().collect();
    assert_ordered_usize(collected, &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)]);
}

#[test]
fn iter_nearest_mut_applies_updates() {
    let mut tree = basic_tree();
    let (dist, value) = tree
        .iter_nearest_mut(&POINT_A.0, &squared_euclidean)
        .unwrap()
        .next()
        .unwrap();
    assert_eq!(dist, 0f64);
    *value = 10;

    let collected: Vec<_> = tree.iter_nearest(&POINT_A.0, &squared_euclidean).unwrap().collect();
    assert_ordered_usize(collected, &[(0f64, 10), (2f64, 1), (8f64, 2), (18f64, 3)]);
}

#[test]
fn nearest_within_radius_matches_unbounded_when_unlimited() {
    let tree = basic_tree();
    let unrestricted = tree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap();
    let bounded = tree
        .nearest_within_radius(&POINT_A.0, 4, None, &squared_euclidean)
        .unwrap();
    assert_eq!(unrestricted, bounded);
}

#[test]
fn nearest_within_radius_respects_limit() {
    let tree = basic_tree();
    let filtered = tree
        .nearest_within_radius(&POINT_A.0, 4, Some(1.5), &squared_euclidean)
        .unwrap();
    assert_eq!(filtered, vec![(0f64, &0)]);
}

#[test]
fn nearest_within_radius_returns_empty_for_zero_k() {
    let tree = basic_tree();
    let filtered = tree
        .nearest_within_radius(&POINT_A.0, 0, Some(1.5), &squared_euclidean)
        .unwrap();
    assert!(filtered.is_empty());
}

#[test]
fn iter_nearest_within_radius_collects_results() {
    let tree = basic_tree();
    let collected: Vec<_> = tree
        .iter_nearest_within_radius(&POINT_B.0, Some(3.0), &squared_euclidean)
        .unwrap()
        .collect();
    assert_ordered_usize(collected, &[(0f64, 1), (2f64, 0), (2f64, 2)]);
}

#[test]
fn iter_nearest_within_radius_mut_changes_only_within_radius() {
    let mut tree = basic_tree();
    {
        let mut iter = tree
            .iter_nearest_within_radius_mut(&POINT_A.0, Some(0.5), &squared_euclidean)
            .unwrap();
        let (dist, value) = iter.next().unwrap();
        assert_eq!(dist, 0f64);
        *value = 42;
        assert!(iter.next().is_none());
    }
    let collected = tree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap();
    assert_eq!(collected[0], (0f64, &42));
    assert_eq!(collected[1], (2f64, &1));
}

#[test]
fn nearest_supports_vec_points() {
    let dimensions = 2;
    let capacity_per_node = 2;
    let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);

    tree.add(vec![0.0; 2], 0).unwrap();
    tree.add(vec![1.0; 2], 1).unwrap();
    tree.add(vec![2.0; 2], 2).unwrap();
    tree.add(vec![3.0; 2], 3).unwrap();

    assert_ordered_usize(tree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), &[]);
    assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[(0f64, 0)]);
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(),
        &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)],
    );
    assert_ordered_usize(
        tree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(),
        &[(0f64, 1), (2f64, 0), (2f64, 2), (8f64, 3)],
    );
}

#[test]
fn handles_zero_capacity() {
    let mut tree = KdTree::with_capacity(2, 0);
    assert_eq!(tree.add(&POINT_A.0, POINT_A.1), Err(ErrorKind::ZeroCapacity));
    assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[]);
}

#[test]
fn handles_wrong_dimension() {
    let point = ([0f64], 0f64);
    let mut tree = KdTree::with_capacity(2, 1);

    assert_eq!(tree.add(&point.0, point.1), Err(ErrorKind::WrongDimension));
    assert_eq!(
        tree.nearest(&point.0, 1, &squared_euclidean),
        Err(ErrorKind::WrongDimension)
    );
}

#[test]
fn handles_non_finite_coordinate() {
    let point_a = ([f64::NAN, f64::NAN], 0f64);
    let point_b = ([f64::INFINITY, f64::INFINITY], 0f64);
    let mut tree = KdTree::with_capacity(2, 1);

    assert_eq!(tree.add(&point_a.0, point_a.1), Err(ErrorKind::NonFiniteCoordinate));
    assert_eq!(tree.add(&point_b.0, point_b.1), Err(ErrorKind::NonFiniteCoordinate));
    assert_eq!(
        tree.nearest(&point_a.0, 1, &squared_euclidean),
        Err(ErrorKind::NonFiniteCoordinate)
    );
    assert_eq!(
        tree.nearest(&point_b.0, 1, &squared_euclidean),
        Err(ErrorKind::NonFiniteCoordinate)
    );
}

#[test]
fn handles_singularity() {
    let mut tree = KdTree::with_capacity(2, 1);
    tree.add(&POINT_A.0, POINT_A.1).unwrap();
    tree.add(&POINT_A.0, POINT_A.1).unwrap();
    tree.add(&POINT_A.0, POINT_A.1).unwrap();
    tree.add(&POINT_B.0, POINT_B.1).unwrap();
    tree.add(&POINT_B.0, POINT_B.1).unwrap();
    tree.add(&POINT_B.0, POINT_B.1).unwrap();
    tree.add(&POINT_C.0, POINT_C.1).unwrap();
    tree.add(&POINT_C.0, POINT_C.1).unwrap();
    tree.add(&POINT_C.0, POINT_C.1).unwrap();
    assert_eq!(tree.size(), 9);
}

#[test]
fn handles_pending_order() {
    let item1 = ([0f64], 1);
    let item2 = ([100f64], 2);
    let item3 = ([45f64], 3);
    let item4 = ([55f64], 4);

    let mut tree = KdTree::with_capacity(1, 2);
    tree.add(&item1.0, item1.1).unwrap();
    tree.add(&item2.0, item2.1).unwrap();
    tree.add(&item3.0, item3.1).unwrap();
    tree.add(&item4.0, item4.1).unwrap();

    assert_ordered_usize(
        tree.nearest(&[50f64], 2, &squared_euclidean).unwrap(),
        &[(25f64, 3), (25f64, 4)],
    );
}