#![no_std]
#![deny(missing_docs)]
extern crate alloc;
pub mod geometry;
pub mod map;
pub mod value;
pub mod value_impl;
pub mod vector_point;
pub use geometry::*;
pub use map::*;
pub use value::*;
pub use vector_point::*;
use serde::{Deserialize, Serialize};
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Projection {
#[default]
S2,
WG,
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Face {
#[default]
Face0 = 0,
Face1 = 1,
Face2 = 2,
Face3 = 3,
Face4 = 4,
Face5 = 5,
}
impl From<Face> for u8 {
fn from(face: Face) -> Self {
face as u8
}
}
impl From<u8> for Face {
fn from(face: u8) -> Self {
match face {
1 => Face::Face1,
2 => Face::Face2,
3 => Face::Face3,
4 => Face::Face4,
5 => Face::Face5,
_ => Face::Face0,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct FeatureCollection<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue>
{
#[serde(rename = "type")]
pub _type: String,
pub features: Vec<WMFeature<M, P, D>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributions: Option<Attributions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bbox: Option<BBox>,
}
impl<M, P: MValueCompatible, D: MValueCompatible> FeatureCollection<M, P, D> {
pub fn new(attributions: Option<Attributions>) -> Self {
Self {
_type: "FeatureCollection".to_string(),
features: Vec::new(),
attributions,
bbox: None,
}
}
pub fn update_bbox(&mut self, bbox: BBox) {
let mut self_bbox = self.bbox.unwrap_or_default();
self_bbox = self_bbox.merge(&bbox);
self.bbox = Some(self_bbox);
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct S2FeatureCollection<
M = (),
P: MValueCompatible = Properties,
D: MValueCompatible = MValue,
> {
#[serde(rename = "type")]
pub _type: String,
pub features: Vec<VectorFeature<M, P, D>>,
pub faces: Vec<Face>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributions: Option<Attributions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bbox: Option<BBox>,
}
impl<M, P: MValueCompatible, D: MValueCompatible> S2FeatureCollection<M, P, D> {
pub fn new(attributions: Option<Attributions>) -> Self {
Self {
_type: "S2FeatureCollection".to_string(),
features: Vec::new(),
faces: Vec::new(),
attributions,
bbox: None,
}
}
pub fn update_bbox(&mut self, bbox: BBox) {
let mut self_bbox = self.bbox.unwrap_or_default();
self_bbox = self_bbox.merge(&bbox);
self.bbox = Some(self_bbox);
}
pub fn add_face(&mut self, face: Face) {
if !self.faces.contains(&face) {
self.faces.push(face);
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct Feature<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue> {
#[serde(rename = "type")]
pub _type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
pub properties: P,
pub geometry: Geometry<D>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<M>,
}
impl<M, P: MValueCompatible, D: MValueCompatible> Feature<M, P, D> {
pub fn new(id: Option<u64>, properties: P, geometry: Geometry<D>, metadata: Option<M>) -> Self {
Self { _type: "Feature".to_string(), id, properties, geometry, metadata }
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct VectorFeature<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue> {
#[serde(rename = "type")]
pub _type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
pub face: Face,
pub properties: P,
pub geometry: VectorGeometry<D>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<M>,
}
impl<M, P: MValueCompatible, D: MValueCompatible> VectorFeature<M, P, D> {
pub fn new_wm(
id: Option<u64>,
properties: P,
geometry: VectorGeometry<D>,
metadata: Option<M>,
) -> Self {
Self {
_type: "VectorFeature".to_string(),
face: 0.into(),
id,
properties,
geometry,
metadata,
}
}
pub fn new_s2(
id: Option<u64>,
face: Face,
properties: P,
geometry: VectorGeometry<D>,
metadata: Option<M>,
) -> Self {
Self { _type: "S2Feature".to_string(), face, id, properties, geometry, metadata }
}
pub fn from_vector_feature(
feature: &VectorFeature<M, P, D>,
geometry: Option<VectorGeometry<D>>,
) -> Self
where
M: Clone,
{
Self {
_type: feature._type.clone(),
id: feature.id,
face: feature.face,
properties: feature.properties.clone(),
geometry: geometry.unwrap_or(feature.geometry.clone()),
metadata: feature.metadata.clone(),
}
}
}
pub type Attributions = Map<String, String>;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum FeatureCollections<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue>
{
FeatureCollection(FeatureCollection<M, P, D>),
S2FeatureCollection(S2FeatureCollection<M, P, D>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Features<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue> {
Feature(Feature<M, P, D>),
VectorFeature(VectorFeature<M, P, D>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum WMFeature<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue> {
Feature(Feature<M, P, D>),
VectorFeature(VectorFeature<M, P, D>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum JSONCollection<M = (), P: MValueCompatible = Properties, D: MValueCompatible = MValue> {
FeatureCollection(FeatureCollection<M, P, D>),
S2FeatureCollection(S2FeatureCollection<M, P, D>),
Feature(Feature<M, P, D>),
VectorFeature(VectorFeature<M, P, D>),
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn face() {
let face = Face::Face0;
assert_eq!(u8::from(face), 0);
let face = Face::Face1;
assert_eq!(u8::from(face), 1);
let face = Face::Face2;
assert_eq!(u8::from(face), 2);
let face = Face::Face3;
assert_eq!(u8::from(face), 3);
let face = Face::Face4;
assert_eq!(u8::from(face), 4);
let face = Face::Face5;
assert_eq!(u8::from(face), 5);
assert_eq!(Face::Face0, Face::from(0));
assert_eq!(Face::Face1, Face::from(1));
assert_eq!(Face::Face2, Face::from(2));
assert_eq!(Face::Face3, Face::from(3));
assert_eq!(Face::Face4, Face::from(4));
assert_eq!(Face::Face5, Face::from(5));
}
#[test]
fn feature_collection_new() {
let mut attributions = Attributions::new();
attributions.insert("Open S2".to_string(), "https://opens2.com/legal/data".to_string());
let mut fc = FeatureCollection::<()>::new(Some(attributions.clone()));
assert_eq!(fc._type, "FeatureCollection");
assert_eq!(fc.features.len(), 0);
assert_eq!(fc.attributions, Some(attributions.clone()));
fc.update_bbox(BBox::new(5., -2., 35., 2.2));
assert_eq!(fc.bbox, Some(BBox::new(5., -2., 35., 2.2)));
let string = serde_json::to_string(&fc).unwrap();
assert_eq!(string, "{\"type\":\"FeatureCollection\",\"features\":[],\"attributions\":{\"Open S2\":\"https://opens2.com/legal/data\"},\"bbox\":[5.0,-2.0,35.0,2.2]}");
let back_to_fc: FeatureCollection = serde_json::from_str(&string).unwrap();
assert_eq!(back_to_fc, fc);
}
#[test]
fn s2_feature_collection_new() {
let mut attributions = Attributions::new();
attributions.insert("Open S2".to_string(), "https://opens2.com/legal/data".to_string());
let mut fc = S2FeatureCollection::<()>::new(Some(attributions.clone()));
assert_eq!(fc._type, "S2FeatureCollection");
assert_eq!(fc.features.len(), 0);
assert_eq!(fc.attributions, Some(attributions.clone()));
fc.update_bbox(BBox::new(5., -2., 35., 2.2));
assert_eq!(fc.bbox, Some(BBox::new(5., -2., 35., 2.2)));
fc.add_face(0.into());
fc.add_face(3.into());
assert_eq!(fc.faces, vec![0.into(), 3.into()]);
let string = serde_json::to_string(&fc).unwrap();
assert_eq!(string, "{\"type\":\"S2FeatureCollection\",\"features\":[],\"faces\":[\"Face0\",\"Face3\"],\"attributions\":{\"Open S2\":\"https://opens2.com/legal/data\"},\"bbox\":[5.0,-2.0,35.0,2.2]}");
let back_to_fc: S2FeatureCollection = serde_json::from_str(&string).unwrap();
assert_eq!(back_to_fc, fc);
}
#[test]
fn feature_new() {
let fc: Feature = Feature::new(
Some(22),
Properties::new(),
Geometry::Point(PointGeometry {
_type: "Point".into(),
coordinates: (0.0, 0.0),
m_values: None,
bbox: None,
}),
None,
);
assert_eq!(fc.id, Some(22));
assert_eq!(fc._type, "Feature");
assert_eq!(
fc.geometry,
Geometry::Point(PointGeometry {
_type: "Point".into(),
coordinates: (0.0, 0.0),
m_values: None,
bbox: None,
})
);
assert_eq!(fc.properties, Properties::new());
assert_eq!(fc.metadata, None);
}
#[test]
fn s2_feature_new() {
let fc: VectorFeature = VectorFeature::new_wm(
Some(55),
Properties::new(),
VectorGeometry::Point(VectorPointGeometry {
_type: "Point".into(),
coordinates: VectorPoint { x: 0.0, y: 1.0, z: Some(3.), m: None, t: None },
bbox: None,
is_3d: true,
offset: None,
vec_bbox: None,
indices: None,
tesselation: None,
}),
None,
);
assert_eq!(fc.id, Some(55));
assert_eq!(fc._type, "VectorFeature");
assert_eq!(
fc.geometry,
VectorGeometry::Point(VectorPointGeometry {
_type: "Point".into(),
coordinates: VectorPoint { x: 0.0, y: 1.0, z: Some(3.), m: None, t: None },
bbox: None,
is_3d: true,
offset: None,
vec_bbox: None,
indices: None,
tesselation: None,
})
);
assert_eq!(fc.properties, Properties::new());
assert_eq!(fc.metadata, None);
assert_eq!(fc.face, 0.into());
#[derive(PartialEq, Clone, Debug)]
struct MetaTest {
name: String,
value: String,
}
let fc = VectorFeature::<MetaTest>::new_s2(
Some(55),
3.into(),
Properties::new(),
VectorGeometry::Point(VectorPointGeometry {
_type: "Point".into(),
coordinates: VectorPoint { x: 0.0, y: 1.0, z: Some(3.), m: None, t: None },
bbox: None,
is_3d: true,
offset: None,
vec_bbox: None,
indices: None,
tesselation: None,
}),
Some(MetaTest { name: "test".to_string(), value: "value".to_string() }),
);
assert_eq!(fc.id, Some(55));
assert_eq!(fc._type, "S2Feature");
assert_eq!(
fc.geometry,
VectorGeometry::Point(VectorPointGeometry {
_type: "Point".into(),
coordinates: VectorPoint { x: 0.0, y: 1.0, z: Some(3.), m: None, t: None },
bbox: None,
is_3d: true,
offset: None,
vec_bbox: None,
indices: None,
tesselation: None,
})
);
assert_eq!(fc.properties, Properties::new());
assert_eq!(
fc.metadata,
Some(MetaTest { name: "test".to_string(), value: "value".to_string() })
);
assert_eq!(fc.face, 3.into());
let new_geo = VectorGeometry::Point(VectorPointGeometry {
_type: "Point".into(),
coordinates: VectorPoint { x: 5.0, y: 4.0, z: Some(-3.), m: None, t: None },
bbox: None,
is_3d: true,
offset: None,
vec_bbox: None,
indices: None,
tesselation: None,
});
let fc_clone_new_geometry =
VectorFeature::<MetaTest>::from_vector_feature(&fc, Some(new_geo.clone()));
assert_eq!(fc_clone_new_geometry.geometry, new_geo);
}
}