Skip to main content

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}