#[derive(Debug, Clone, PartialEq)]
pub struct Position(pub f64, pub f64);
#[derive(Debug, Clone, PartialEq)]
pub enum Geometry {
Point(Position),
LineString(Vec<Position>),
Polygon(Vec<Vec<Position>>),
MultiPoint(Vec<Position>),
MultiLineString(Vec<Vec<Position>>),
MultiPolygon(Vec<Vec<Vec<Position>>>),
}
impl Geometry {
pub fn bbox(&self) -> Option<(f64, f64, f64, f64)> {
let mut pts: Vec<&Position> = Vec::new();
fn collect<'a>(g: &'a Geometry, out: &mut Vec<&'a Position>) {
match g {
Geometry::Point(p) => out.push(p),
Geometry::LineString(v) | Geometry::MultiPoint(v) => out.extend(v),
Geometry::Polygon(v) | Geometry::MultiLineString(v) => {
for ring in v {
out.extend(ring)
}
}
Geometry::MultiPolygon(v) => {
for poly in v {
for ring in poly {
out.extend(ring)
}
}
}
}
}
collect(self, &mut pts);
if pts.is_empty() {
return None;
}
let mut w = f64::INFINITY;
let mut s = f64::INFINITY;
let mut e = f64::NEG_INFINITY;
let mut n = f64::NEG_INFINITY;
for p in pts {
w = w.min(p.0);
e = e.max(p.0);
s = s.min(p.1);
n = n.max(p.1);
}
Some((w, s, e, n))
}
}
#[derive(Debug, Clone)]
pub struct Feature {
pub geometry: Geometry,
pub properties: Vec<(String, String)>,
}
impl Feature {
pub fn new(geometry: Geometry) -> Self {
Feature {
geometry,
properties: Vec::new(),
}
}
pub fn prop(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
self.properties.push((k.into(), v.into()));
self
}
pub fn get(&self, k: &str) -> Option<&str> {
self.properties
.iter()
.find(|(key, _)| key == k)
.map(|(_, v)| v.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bbox_of_polygon() {
let g = Geometry::Polygon(vec![vec![
Position(0.0, 0.0),
Position(2.0, 0.0),
Position(2.0, 3.0),
Position(0.0, 3.0),
]]);
assert_eq!(g.bbox(), Some((0.0, 0.0, 2.0, 3.0)));
}
#[test]
fn feature_properties() {
let f = Feature::new(Geometry::Point(Position(105.85, 21.02))).prop("name", "Hà Nội");
assert_eq!(f.get("name"), Some("Hà Nội"));
assert_eq!(f.get("missing"), None);
}
}