use std::{collections::HashMap, hash::BuildHasherDefault};
use geojson::{feature::Id, Feature, Geometry, Value};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use twox_hash::XxHash64;
use crate::CoordinateSystem;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct SuperclusterOptions {
pub min_zoom: u8,
pub max_zoom: u8,
pub min_points: u8,
pub radius: f64,
pub extent: f64,
pub node_size: usize,
pub coordinate_system: CoordinateSystem,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct FeatureBuilder {
pub points: HashMap<String, Vec<f64>, BuildHasherDefault<XxHash64>>,
}
impl FeatureBuilder {
pub fn new() -> Self {
FeatureBuilder::default()
}
pub fn add_point(mut self, point: Vec<f64>) -> Self {
self.points.insert(self.points.len().to_string(), point);
self
}
pub fn add_points(mut self, points: Vec<Vec<f64>>) -> Self {
for point in points {
self.points.insert(self.points.len().to_string(), point);
}
self
}
pub fn build(self) -> Vec<Feature> {
self.points
.into_iter()
.map(|(id, point)| Feature {
id: Some(Id::String(id)),
geometry: Some(Geometry::new(Value::Point(point))),
bbox: None,
properties: None,
foreign_members: None,
})
.collect()
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct SuperclusterBuilder {
pub min_zoom: Option<u8>,
pub max_zoom: Option<u8>,
pub min_points: Option<u8>,
pub radius: Option<f64>,
pub extent: Option<f64>,
pub node_size: Option<usize>,
pub coordinate_system: Option<CoordinateSystem>,
}
impl SuperclusterBuilder {
pub fn new() -> Self {
SuperclusterBuilder::default()
}
pub fn min_zoom(mut self, min_zoom: u8) -> Self {
self.min_zoom = Some(min_zoom);
self
}
pub fn max_zoom(mut self, max_zoom: u8) -> Self {
self.max_zoom = Some(max_zoom);
self
}
pub fn min_points(mut self, min_points: u8) -> Self {
self.min_points = Some(min_points);
self
}
pub fn radius(mut self, radius: f64) -> Self {
self.radius = Some(radius);
self
}
pub fn extent(mut self, extent: f64) -> Self {
self.extent = Some(extent);
self
}
pub fn node_size(mut self, node_size: usize) -> Self {
self.node_size = Some(node_size);
self
}
pub fn coordinate_system(mut self, coordinate_system: CoordinateSystem) -> Self {
self.coordinate_system = Some(coordinate_system);
self
}
pub fn build(self) -> SuperclusterOptions {
SuperclusterOptions {
min_zoom: self.min_zoom.unwrap_or(0),
max_zoom: self.max_zoom.unwrap_or(16),
min_points: self.min_points.unwrap_or(2),
radius: self.radius.unwrap_or(40.0),
extent: self.extent.unwrap_or(512.0),
node_size: self.node_size.unwrap_or(64),
coordinate_system: self.coordinate_system.unwrap_or(CoordinateSystem::LatLng),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_builder_default() {
let features = FeatureBuilder::default()
.add_point(vec![0.0, 0.0])
.add_points(vec![vec![0.0, 1.0]])
.build();
assert_eq!(features.len(), 2);
}
#[test]
fn test_supercluster_builder_default() {
let options = SuperclusterBuilder::default().build();
assert_eq!(options.min_zoom, 0);
assert_eq!(options.max_zoom, 16);
assert_eq!(options.min_points, 2);
assert_eq!(options.radius, 40.0);
assert_eq!(options.extent, 512.0);
assert_eq!(options.node_size, 64);
assert_eq!(options.coordinate_system, CoordinateSystem::LatLng);
}
#[test]
fn test_supercluster_builder() {
let options = SuperclusterBuilder::new()
.min_zoom(1)
.max_zoom(10)
.min_points(5)
.radius(50.0)
.extent(1024.0)
.node_size(128)
.coordinate_system(CoordinateSystem::LatLng)
.build();
assert_eq!(options.min_zoom, 1);
assert_eq!(options.max_zoom, 10);
assert_eq!(options.min_points, 5);
assert_eq!(options.radius, 50.0);
assert_eq!(options.extent, 1024.0);
assert_eq!(options.node_size, 128);
assert_eq!(options.coordinate_system, CoordinateSystem::LatLng);
}
}