ezu_features/lib.rs
1//! GIS feature parsing for ezu.
2//!
3//! Parses tile / feature formats into a flat, owned representation that
4//! downstream paint code can iterate over without implementing
5//! format-specific traits. Remote fetching is intentionally *not* in
6//! scope — input is always raw bytes / strings from the caller.
7//!
8//! Submodules:
9//!
10//! - [`mvt`] — Mapbox Vector Tile protobuf decode
11//! - [`geojson`] — GeoJSON FeatureCollection parse
12//!
13//! All decoders produce the same shared [`Feature`] / [`Geometry`] /
14//! [`Polygon`] / [`Value`] types defined at the crate root.
15
16use std::collections::HashMap;
17
18pub mod geojson;
19pub mod mvt;
20pub mod ops;
21
22/// A single tile-local layer of decoded features. The producer (MVT
23/// decoder, GeoJSON loader, host glue, …) normalizes coordinates into
24/// `[0, extent]` with y-down before constructing this. Consumers in
25/// `ezu-paint` read it via the unified `AssetLoader` under
26/// `tile.<name>` bindings.
27#[derive(Debug)]
28pub struct FeatureLayer {
29 pub name: String,
30 /// Tile coordinate extent (typically 4096 for MVT). GeoJSON
31 /// producers pick a convention and pre-project into it.
32 pub extent: u32,
33 pub features: Vec<Feature>,
34}
35
36/// One decoded feature: geometry plus a properties bag.
37#[derive(Debug)]
38pub struct Feature {
39 pub id: Option<u64>,
40 pub geometry: Geometry,
41 pub properties: HashMap<String, Value>,
42}
43
44/// Geometry in source-specific tile-local coordinates. For MVT this is
45/// `[0, extent]` with y-down; for GeoJSON it's whatever the caller
46/// chose to project into before parsing.
47///
48/// All three layers (points / lines / polygons) co-exist on a single
49/// feature. Single-vs-multi (`Point` vs `MultiPoint`, etc.) is
50/// expressed by element count, and a GeoJSON `GeometryCollection` maps
51/// onto whichever combination of the three vecs its children produce.
52#[derive(Debug, Default, Clone)]
53pub struct Geometry {
54 pub points: Vec<(i32, i32)>,
55 pub lines: Vec<Vec<(i32, i32)>>,
56 pub polygons: Vec<Polygon>,
57}
58
59impl Geometry {
60 pub fn is_empty(&self) -> bool {
61 self.points.is_empty() && self.lines.is_empty() && self.polygons.is_empty()
62 }
63
64 /// Merge `other`'s vertices into `self`. Used when flattening a
65 /// GeoJSON `GeometryCollection`.
66 pub fn extend(&mut self, other: Geometry) {
67 self.points.extend(other.points);
68 self.lines.extend(other.lines);
69 self.polygons.extend(other.polygons);
70 }
71}
72
73/// A polygon with one exterior ring and zero or more interior holes.
74#[derive(Debug, Clone)]
75pub struct Polygon {
76 pub exterior: Vec<(i32, i32)>,
77 pub holes: Vec<Vec<(i32, i32)>>,
78}
79
80/// Untyped property value carried alongside a feature.
81#[derive(Debug, Clone)]
82pub enum Value {
83 String(String),
84 Float(f32),
85 Double(f64),
86 Int(i64),
87 UInt(u64),
88 SInt(i64),
89 Bool(bool),
90 Null,
91}