1use std::collections::HashMap;
2
3use geo_index::kdtree::KDTreeIndex;
4
5use crate::cluster::{ClusterId, ClusterInfo};
6use crate::error::SuperclusterError;
7use crate::options::SuperclusterOptions;
8use crate::tree::TreeWithData;
9use crate::util::{latitude_to_y, longitude_to_x};
10
11#[derive(Debug, Clone)]
13pub struct Supercluster {
14 options: SuperclusterOptions,
15
16 trees: HashMap<usize, TreeWithData>,
18
19 points: Vec<(f64, f64)>,
21}
22
23impl Supercluster {
24 pub(crate) fn new(
25 points: Vec<(f64, f64)>,
26 trees: HashMap<usize, TreeWithData>,
27 options: SuperclusterOptions,
28 ) -> Self {
29 Self {
30 options,
31 trees,
32 points,
33 }
34 }
35
36 pub fn get_clusters(
40 &self,
41 min_lng: f64,
42 min_lat: f64,
43 max_lng: f64,
44 max_lat: f64,
45 zoom: usize,
46 ) -> Vec<ClusterInfo> {
47 let mut min_lng = ((min_lng + 180.0) % 360.0 + 360.0) % 360.0 - 180.0;
48 let min_lat = min_lat.clamp(-90.0, 90.0);
49 let mut max_lng = if max_lng == 180.0 {
50 180.0
51 } else {
52 ((max_lng + 180.0) % 360.0 + 360.0) % 360.0 - 180.0
53 };
54 let max_lat = max_lat.clamp(-90.0, 90.0);
55
56 if max_lng - min_lng >= 360.0 {
57 min_lng = -180.0;
58 max_lng = 180.0;
59 } else if min_lng > max_lng {
60 let mut eastern_hem = self.get_clusters(min_lng, min_lat, 180.0, max_lat, zoom);
61 let mut western_hem = self.get_clusters(-180.0, min_lat, max_lng, max_lat, zoom);
62 eastern_hem.append(&mut western_hem);
63 return eastern_hem;
64 }
65
66 let tree_with_data = self.trees.get(&self.clamp_zoom(zoom)).unwrap();
67
68 let ids = tree_with_data.tree.as_ref().range(
71 longitude_to_x(min_lng),
72 latitude_to_y(max_lat),
73 longitude_to_x(max_lng),
74 latitude_to_y(min_lat),
75 );
76
77 let data = tree_with_data.data();
78
79 let mut clusters = Vec::with_capacity(ids.len());
80 for id in ids {
81 let cluster_data = &data[id];
82
83 if cluster_data.num_points > 1 {
85 clusters.push(ClusterInfo::new_cluster(
86 cluster_data.source_id,
87 cluster_data.x,
88 cluster_data.y,
89 cluster_data.num_points,
90 ));
91 } else {
92 let (x, y) = self.points[id];
93 clusters.push(ClusterInfo::new_leaf(cluster_data.source_id, x, y))
94 }
95 }
96
97 clusters
98 }
99
100 pub fn get_children(
104 &self,
105 cluster_id: ClusterId,
106 ) -> Result<Vec<ClusterInfo>, SuperclusterError> {
107 let origin_id = self.get_origin_idx(cluster_id);
108 let origin_zoom = self.get_origin_zoom(cluster_id);
109
110 let tree_with_data = match self.trees.get(&origin_zoom) {
111 Some(tree_with_data) => tree_with_data,
112 None => return Err(SuperclusterError::NoClusterFound),
113 };
114
115 let data = tree_with_data.data();
116 let tree = tree_with_data.tree();
117 if origin_id >= data.len() {
118 return Err(SuperclusterError::NoClusterFound);
119 }
120
121 let r = self.options.radius
122 / (self.options.extent * usize::pow(2, (origin_zoom - 1).try_into().unwrap()) as f64);
123 let x = data[origin_id].x;
124 let y = data[origin_id].y;
125 let ids = tree.as_ref().within(x, y, r);
126 let mut children = vec![];
127
128 for id in ids {
129 let cluster_data = &data[id];
130
131 if cluster_data
132 .parent_id
133 .is_some_and(|parent_id| parent_id == cluster_id)
134 {
135 if cluster_data.num_points > 1 {
136 children.push(ClusterInfo::new_cluster(
137 cluster_data.source_id,
138 cluster_data.x,
139 cluster_data.y,
140 cluster_data.num_points,
141 ));
142 } else {
143 let (x, y) = self.points[id];
144 children.push(ClusterInfo::new_leaf(cluster_data.source_id, x, y))
145 }
146 }
147 }
148
149 if children.is_empty() {
150 return Err(SuperclusterError::NoClusterFound);
151 }
152
153 Ok(children)
154 }
155
156 pub fn get_leaves(
164 &self,
165 cluster_id: ClusterId,
166 limit: Option<usize>,
167 offset: Option<usize>,
168 ) -> Result<Vec<ClusterInfo>, SuperclusterError> {
169 let limit = limit.unwrap_or(10);
170 let offset = offset.unwrap_or(0);
171
172 let mut leaves = vec![];
173 self.append_leaves(&mut leaves, cluster_id, limit, offset, 0)?;
174
175 Ok(leaves)
176 }
177
178 pub fn get_cluster_expansion_zoom(
193 &self,
194 cluster_id: ClusterId,
195 ) -> Result<usize, SuperclusterError> {
196 let mut cluster_id = cluster_id;
197 let mut expansion_zoom = cluster_id.get_origin_zoom(self.points.len()) - 1;
198 while expansion_zoom <= self.options.max_zoom {
199 let children = self.get_children(cluster_id)?;
200 expansion_zoom += 1;
201 if children.len() != 1 {
202 break;
203 }
204 cluster_id = children[0].id();
205 }
206
207 Ok(expansion_zoom)
208 }
209
210 fn append_leaves(
211 &self,
212 result: &mut Vec<ClusterInfo>,
213 cluster_id: ClusterId,
214 limit: usize,
215 offset: usize,
216 skipped: usize,
217 ) -> Result<usize, SuperclusterError> {
218 let children = self.get_children(cluster_id)?;
219
220 let mut skipped = skipped;
221
222 for child in children {
223 if child.is_cluster() {
224 if skipped + child.count() <= offset {
225 skipped += child.count();
227 } else {
228 skipped = self.append_leaves(result, child.id(), limit, offset, skipped)?;
230 }
232 skipped += 1;
233 } else if skipped < offset {
234 skipped += 1;
236 } else {
237 result.push(child);
239 }
240
241 if result.len() == limit {
242 break;
243 }
244 }
245
246 Ok(skipped)
247 }
248
249 fn clamp_zoom(&self, zoom: usize) -> usize {
250 zoom.clamp(self.options.min_zoom, self.options.max_zoom + 1)
251 }
252
253 fn get_origin_idx(&self, cluster_id: ClusterId) -> usize {
255 cluster_id.get_origin_idx(self.points.len())
256 }
257
258 fn get_origin_zoom(&self, cluster_id: ClusterId) -> usize {
260 cluster_id.get_origin_zoom(self.points.len())
261 }
262}
263
264#[cfg(test)]
265mod test {
266 use crate::test::load_fixture::load_places;
267 use crate::SuperclusterBuilder;
268
269 #[test]
270 fn test_builder() {
271 let coords = load_places();
272 let mut builder = SuperclusterBuilder::new(coords.len());
273 for coord in coords {
274 builder.add(coord[0], coord[1]);
275 }
276 let supercluster = builder.finish();
277 let clusters = supercluster.get_clusters(-50., -50., 50., 50., 0);
278 dbg!(clusters.len());
279 dbg!(&clusters);
280 }
282}