1use crate::{
2 Error,
3 engine::{
4 cluster::bits,
5 index::{self, Index},
6 },
7};
8use geo::{BoundingRect, CoordsIter, Geometry, LineString, Point, Polygon, Rect};
9use serde::{Deserialize, Serialize};
10use std::{collections::HashSet, sync::OnceLock};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
15#[serde(transparent)]
16pub struct LayerId(pub(crate) usize);
17impl LayerId {
18 #[must_use]
20 pub fn index(self) -> usize {
21 self.0
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
27#[non_exhaustive]
28pub struct FeatureRef {
29 pub layer: LayerId,
31 pub fid: i64,
33 pub part: Option<u32>,
36}
37impl FeatureRef {
38 pub(crate) fn feature(self) -> (LayerId, i64) {
39 (self.layer, self.fid)
40 }
41 pub(crate) fn aggregate(&mut self, other: Self) {
42 if self.part != other.part {
43 self.part = None;
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy)]
54pub(crate) struct PartRef {
55 pub fid: i64,
56 pub part: Option<u32>,
57}
58impl PartRef {
59 pub(crate) fn at(self, layer: LayerId) -> FeatureRef {
60 FeatureRef {
61 layer,
62 fid: self.fid,
63 part: self.part,
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub(crate) enum Invalid {
71 DuplicateFid(i64),
72 NonFinite(i64),
73}
74impl Invalid {
75 pub(crate) fn at(self, layer: LayerId, name: &str) -> Error {
76 let name = name.to_string();
77 match self {
78 Self::DuplicateFid(fid) => Error::DuplicateFid { layer, name, fid },
79 Self::NonFinite(fid) => Error::NonFinite { layer, name, fid },
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
87#[serde(rename_all = "lowercase")]
88#[non_exhaustive]
89pub enum GeometryKind {
90 Point,
92 Line,
94 Polygon,
96 Collection,
98 Any,
100}
101impl GeometryKind {
102 #[must_use]
104 pub fn of(g: &Geometry) -> Self {
105 match g {
106 Geometry::Point(_) | Geometry::MultiPoint(_) => Self::Point,
107 Geometry::Line(_) | Geometry::LineString(_) | Geometry::MultiLineString(_) => {
108 Self::Line
109 }
110 Geometry::Polygon(_)
111 | Geometry::MultiPolygon(_)
112 | Geometry::Rect(_)
113 | Geometry::Triangle(_) => Self::Polygon,
114 Geometry::GeometryCollection(_) => Self::Collection,
115 }
116 }
117 #[must_use]
120 pub fn satisfies(self, required: Self) -> bool {
121 required == Self::Any || self == required
122 }
123}
124
125#[derive(Debug)]
139pub struct Layer {
140 name: String,
141 fids: Vec<i64>,
142 geometries: Vec<Geometry>,
143 parts: OnceLock<Result<Parts, Invalid>>,
144}
145impl Layer {
146 pub fn from_geometries(
149 name: impl Into<String>,
150 geometries: impl IntoIterator<Item = Geometry>,
151 ) -> Self {
152 Self::from_features(
153 name,
154 geometries
155 .into_iter()
156 .enumerate()
157 .map(|(i, g)| (i as i64, g)),
158 )
159 }
160 pub fn from_features(
163 name: impl Into<String>,
164 features: impl IntoIterator<Item = (i64, Geometry)>,
165 ) -> Self {
166 let (fids, geometries) = features.into_iter().unzip();
167 Self {
168 name: name.into(),
169 fids,
170 geometries,
171 parts: OnceLock::new(),
172 }
173 }
174 #[must_use]
176 pub fn name(&self) -> &str {
177 &self.name
178 }
179 pub fn features(&self) -> impl ExactSizeIterator<Item = (i64, &Geometry)> {
181 self.fids.iter().copied().zip(&self.geometries)
182 }
183 #[must_use]
185 pub fn feature(&self, index: usize) -> Option<(i64, &Geometry)> {
186 Some((*self.fids.get(index)?, self.geometries.get(index)?))
187 }
188 #[must_use]
190 pub fn len(&self) -> usize {
191 self.fids.len()
192 }
193 #[must_use]
195 pub fn is_empty(&self) -> bool {
196 self.fids.is_empty()
197 }
198 pub(crate) fn check_kind(&self, id: LayerId, expected: GeometryKind) -> Result<(), Error> {
199 fn check(g: &Geometry, expected: GeometryKind) -> Option<GeometryKind> {
200 if expected == GeometryKind::Any {
201 return None;
202 }
203 if let Geometry::GeometryCollection(c) = g {
204 return c.iter().find_map(|g| check(g, expected));
205 }
206 let found = GeometryKind::of(g);
207 (!found.satisfies(expected)).then_some(found)
208 }
209 for (fid, g) in self.features() {
210 if let Some(found) = check(g, expected) {
211 return Err(Error::GeometryKind {
212 layer: id,
213 name: self.name.clone(),
214 fid,
215 expected,
216 found,
217 });
218 }
219 }
220 Ok(())
221 }
222 pub(crate) fn parts(&self) -> Result<&Parts, Invalid> {
223 self.parts
224 .get_or_init(|| self.make_parts())
225 .as_ref()
226 .map_err(|e| *e)
227 }
228 fn make_parts(&self) -> Result<Parts, Invalid> {
229 let mut out = Parts::default();
230 let mut seen = HashSet::new();
231 for (fid, g) in self.features() {
232 if !seen.insert(fid) {
233 return Err(Invalid::DuplicateFid(fid));
234 }
235 if g.coords_iter()
236 .any(|c| !c.x.is_finite() || !c.y.is_finite())
237 {
238 return Err(Invalid::NonFinite(fid));
239 }
240 out.add(g, PartRef { fid, part: Some(0) }, &mut 0);
241 }
242 out.point_index = index::build(
243 out.points
244 .iter()
245 .enumerate()
246 .map(|(i, p)| (i, Rect::new(p.geometry.0, p.geometry.0))),
247 );
248 out.line_index = index::build(
249 out.lines
250 .iter()
251 .enumerate()
252 .filter_map(|(i, p)| p.geometry.bounding_rect().map(|r| (i, r))),
253 );
254 out.polygon_index = index::build(
255 out.polygons
256 .iter()
257 .enumerate()
258 .filter_map(|(i, p)| p.geometry.bounding_rect().map(|r| (i, r))),
259 );
260 Ok(out)
261 }
262}
263#[derive(Debug)]
264pub(crate) struct Part<G> {
265 pub reference: PartRef,
266 pub geometry: G,
267}
268#[derive(Debug, Default)]
269pub(crate) struct Parts {
270 pub points: Vec<Part<Point>>,
271 pub lines: Vec<Part<LineString>>,
272 pub polygons: Vec<Part<Polygon>>,
273 pub point_index: Index,
274 pub line_index: Index,
275 pub polygon_index: Index,
276 pub union: OnceLock<crate::engine::polygons::Union>,
277 pub cross: OnceLock<crate::engine::cross::Views>,
278 pub skipped: usize,
279}
280fn clean_line(line: &LineString) -> LineString {
281 let mut coords = line.0.clone();
282 coords.dedup();
283 LineString(coords)
284}
285fn ring_valid(ring: &LineString) -> bool {
286 ring.0.len() >= 4
287 && ring
288 .0
289 .iter()
290 .map(|c| (bits(c.x), bits(c.y)))
291 .collect::<HashSet<_>>()
292 .len()
293 >= 3
294}
295impl Parts {
296 fn next_reference(mut reference: PartRef, part: &mut u32) -> PartRef {
297 reference.part = Some(*part);
298 *part += 1;
299 reference
300 }
301
302 fn add_point(&mut self, p: &Point, reference: PartRef, part: &mut u32) {
303 self.points.push(Part {
304 reference: Self::next_reference(reference, part),
305 geometry: *p,
306 });
307 }
308
309 fn add_line(&mut self, l: &LineString, reference: PartRef, part: &mut u32) {
310 let reference = Self::next_reference(reference, part);
311 let geometry = clean_line(l);
312 if geometry.0.len() < 2 {
313 self.skipped += 1;
314 } else {
315 self.lines.push(Part {
316 reference,
317 geometry,
318 });
319 }
320 }
321
322 fn add_polygon(&mut self, p: &Polygon, reference: PartRef, part: &mut u32) {
323 let reference = Self::next_reference(reference, part);
324 let exterior = clean_line(p.exterior());
325 if !ring_valid(&exterior) {
326 self.skipped += 1;
327 return;
328 }
329 let holes = p
330 .interiors()
331 .iter()
332 .map(clean_line)
333 .filter(|r| {
334 let valid = ring_valid(r);
335 if !valid {
336 self.skipped += 1;
337 }
338 valid
339 })
340 .collect();
341 self.polygons.push(Part {
342 reference,
343 geometry: Polygon::new(exterior, holes),
344 });
345 }
346
347 fn add(&mut self, g: &Geometry, reference: PartRef, part: &mut u32) {
348 match g {
349 Geometry::MultiPoint(m) => {
350 for p in m {
351 self.add_point(p, reference, part);
352 }
353 }
354 Geometry::MultiLineString(m) => {
355 for p in m {
356 self.add_line(p, reference, part);
357 }
358 }
359 Geometry::MultiPolygon(m) => {
360 for p in m {
361 self.add_polygon(p, reference, part);
362 }
363 }
364 Geometry::GeometryCollection(m) => {
365 for p in m {
366 self.add(p, reference, part);
367 }
368 }
369 Geometry::Line(l) => self.add_line(&LineString(vec![l.start, l.end]), reference, part),
370 Geometry::Rect(r) => self.add_polygon(&r.to_polygon(), reference, part),
371 Geometry::Triangle(t) => self.add_polygon(&t.to_polygon(), reference, part),
372 Geometry::Point(p) => self.add_point(p, reference, part),
373 Geometry::LineString(l) => self.add_line(l, reference, part),
374 Geometry::Polygon(p) => self.add_polygon(p, reference, part),
375 }
376 }
377}
378pub(crate) fn part_count(g: &Geometry) -> usize {
379 match g {
380 Geometry::MultiPoint(m) => m.len(),
381 Geometry::MultiLineString(m) => m.0.len(),
382 Geometry::MultiPolygon(m) => m.0.len(),
383 Geometry::GeometryCollection(m) => m.iter().map(part_count).sum(),
384 _ => 1,
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use geo::{GeometryCollection, MultiLineString, MultiPolygon, line_string, polygon};
392
393 #[test]
394 fn multipart_cleaning_keeps_original_positions_and_counts_skipped_rings() {
395 let line = line_string![(x:0.,y:0.),(x:0.,y:0.),(x:1.,y:0.)];
396 let mut polygon = polygon![(x:0.,y:0.),(x:1.,y:0.),(x:1.,y:1.),(x:0.,y:0.)];
397 polygon.interiors_push(LineString::from(vec![(0.5, 0.5); 4]));
398 let geometry = Geometry::GeometryCollection(GeometryCollection(vec![
399 MultiLineString(vec![LineString::empty(), line.clone()]).into(),
400 MultiPolygon(vec![Polygon::empty(), polygon.clone()]).into(),
401 Point::new(4., 5.).into(),
402 ]));
403 let layer = Layer::from_features("parts", [(-12, geometry.clone())]);
404 let parts = layer.parts().unwrap();
405 assert_eq!(parts.skipped, 3);
406 assert_eq!(
407 parts.lines[0].geometry,
408 line_string![(x:0.,y:0.),(x:1.,y:0.)]
409 );
410 assert_eq!(parts.polygons[0].geometry.exterior(), polygon.exterior());
411 assert!(parts.polygons[0].geometry.interiors().is_empty());
412 for (reference, part) in [
413 (parts.lines[0].reference, 1),
414 (parts.polygons[0].reference, 3),
415 (parts.points[0].reference, 4),
416 ] {
417 assert_eq!(
418 reference.at(LayerId(3)),
419 FeatureRef {
420 layer: LayerId(3),
421 fid: -12,
422 part: Some(part)
423 }
424 );
425 }
426 assert_eq!(layer.features().next().unwrap().1, &geometry);
427 }
428
429 #[test]
430 fn multipart_nonfinite_coordinates_are_rejected_before_skipping_degeneracies() {
431 let layer = Layer::from_features(
432 "bad",
433 [(
434 42,
435 MultiLineString(vec![LineString::from(vec![(f64::NAN, 0.)])]).into(),
436 )],
437 );
438 assert_eq!(layer.parts().err(), Some(Invalid::NonFinite(42)));
439 }
440}