Skip to content

Commit

Permalink
BREAKING CHANGE: non-geospatial point clustering
Browse files Browse the repository at this point in the history
  • Loading branch information
slavik-pastushenko committed Jan 2, 2025
1 parent a765c89 commit b582fc5
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "supercluster"
description = "A very fast Rust crate for geospatial point clustering."
description = "Geospatial and non-geospatial point clustering."
version = "1.1.1"
edition = "2021"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Chargetrip
Copyright (c) 2025 Chargetrip

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Supercluster

A very fast Rust crate for geospatial point clustering.
A high-performance Rust crate for geospatial and non-geospatial point clustering.

This crate is inspired by Mapbox's supercluster [blog post](https://blog.mapbox.com/clustering-millions-of-points-on-a-map-with-supercluster-272046ec5c97).

Expand Down Expand Up @@ -30,14 +30,15 @@ This crate is inspired by Mapbox's supercluster [blog post](https://blog.mapbox.

## Options

| Option | Description |
|--------------|-------------------------------------------------------------------|
| `min_zoom` | Minimum zoom level at which clusters are generated. |
| `max_zoom` | Maximum zoom level at which clusters are generated. |
| `min_points` | Minimum number of points to form a cluster. |
| `radius` | Cluster radius, in pixels. |
| `extent` | (Tiles) Tile extent. Radius is calculated relative to this value. |
| `node_size` | Size of the KD-tree leaf node. Affects performance. |
| Option | Description |
|---------------------|-------------------------------------------------------------------|
| `min_zoom` | Minimum zoom level at which clusters are generated. |
| `max_zoom` | Maximum zoom level at which clusters are generated. |
| `min_points` | Minimum number of points to form a cluster. |
| `radius` | Cluster radius, in pixels. |
| `extent` | (Tiles) Tile extent. Radius is calculated relative to this value. |
| `node_size` | Size of the KD-tree leaf node. Affects performance. |
| `coordinate_system` | Type of coordinate system for clustering. |

## Safety

Expand Down Expand Up @@ -69,6 +70,7 @@ fn main() {
radius: 40.0,
node_size: 64,
extent: 512.0,
coordinate_system: CoordinateSystem::LatLng,
};

// Create a new instance with the specified configuration settings
Expand Down
35 changes: 18 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const OFFSET_NUM: usize = 5;
const OFFSET_PROP: usize = 6;

/// The range of the incoming data if choosing the cartesian coordinate system.
/// Applicable for non-geospatial data (i.e. microscopy, etc.).
#[derive(Clone, Debug)]
pub struct DataRange {
/// The minimum x-coordinate value.
Expand Down Expand Up @@ -98,8 +99,8 @@ pub enum CoordinateSystem {
/// Latitude and longitude coordinates. Used for geo-spatial data.
LatLng,

/// Cartesian coordinates. Used for non-geo-spatial (i.e. microscopy, etc.) data.
Cartesian { data_range: DataRange },
/// Cartesian coordinates. Used for non-geospatial (i.e. microscopy, etc.) data.
Cartesian { range: DataRange },
}

/// Supercluster configuration options.
Expand All @@ -123,7 +124,7 @@ pub struct Options {
/// Size of the KD-tree leaf node, affects performance.
pub node_size: usize,

/// The type of coordinate system for clustering: lat/lng or cartesian.
/// Type of coordinate system for clustering.
pub coordinate_system: CoordinateSystem,
}

Expand Down Expand Up @@ -200,12 +201,12 @@ impl Supercluster {
};

match &self.options.coordinate_system {
CoordinateSystem::Cartesian { data_range } => {
CoordinateSystem::Cartesian { range } => {
// X Coordinate
data.push(data_range.normalize_x(coordinates[0]));
data.push(range.normalize_x(coordinates[0]));

// Y Coordinate
data.push(data_range.normalize_y(coordinates[1]));
data.push(range.normalize_y(coordinates[1]));
}
CoordinateSystem::LatLng => {
// Longitude
Expand Down Expand Up @@ -257,11 +258,11 @@ impl Supercluster {
pub fn get_clusters(&self, bbox: [f64; 4], zoom: u8) -> Vec<Feature> {
let tree = &self.trees[self.limit_zoom(zoom)];
let ids = match &self.options.coordinate_system {
CoordinateSystem::Cartesian { data_range } => tree.range(
data_range.normalize_x(bbox[0]),
data_range.normalize_y(bbox[1]),
data_range.normalize_x(bbox[2]),
data_range.normalize_y(bbox[3]),
CoordinateSystem::Cartesian { range } => tree.range(
range.normalize_x(bbox[0]),
range.normalize_y(bbox[1]),
range.normalize_x(bbox[2]),
range.normalize_y(bbox[3]),
),
CoordinateSystem::LatLng => {
let mut min_lng = ((((bbox[0] + 180.0) % 360.0) + 360.0) % 360.0) - 180.0;
Expand Down Expand Up @@ -606,9 +607,9 @@ impl Supercluster {
Some(geometry) => {
if let Point(coordinates) = &geometry.value {
match &self.options.coordinate_system {
CoordinateSystem::Cartesian { data_range } => {
px = data_range.normalize_x(coordinates[0]);
py = data_range.normalize_y(coordinates[1]);
CoordinateSystem::Cartesian { range } => {
px = range.normalize_x(coordinates[0]);
py = range.normalize_y(coordinates[1]);
}
CoordinateSystem::LatLng => {
px = lng_x(coordinates[0]);
Expand Down Expand Up @@ -810,9 +811,9 @@ fn get_cluster_json(
coordinate_system: &CoordinateSystem,
) -> Feature {
let geometry = match coordinate_system {
CoordinateSystem::Cartesian { data_range } => Geometry::new(Point(vec![
data_range.denormalize_x(data[i]),
data_range.denormalize_y(data[i + 1]),
CoordinateSystem::Cartesian { range } => Geometry::new(Point(vec![
range.denormalize_x(data[i]),
range.denormalize_y(data[i + 1]),
])),
CoordinateSystem::LatLng => Geometry::new(Point(vec![x_lng(data[i]), y_lat(data[i + 1])])),
};
Expand Down
4 changes: 2 additions & 2 deletions tests/supercluster_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@ fn test_does_not_crash_on_weird_bbox_values() {
#[test]
fn test_cartesian_coordinates() {
let data = load_cartesian();
let data_range = get_data_range(&data).unwrap();
let range = get_data_range(&data).unwrap();

let mut cluster = Supercluster::new(get_options(
20.0,
512.0,
2,
16,
CoordinateSystem::Cartesian { data_range },
CoordinateSystem::Cartesian { range },
));
let index = cluster.load(data);

Expand Down

0 comments on commit b582fc5

Please sign in to comment.