#![no_std]
#![deny(missing_docs)]
extern crate alloc;
pub mod geometry;
pub mod value;
pub mod value_impl;
pub mod vector_point;
pub use geometry::*;
pub use value::*;
pub use vector_point::*;
use serde::{Deserialize, Serialize};
use alloc::collections::BTreeMap;
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 = ()> {
#[serde(rename = "type")]
pub _type: String,
pub features: Vec<WMFeature<M>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributions: Option<Attributions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bbox: Option<BBox>,
}
impl<M> FeatureCollection<M> {
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 = ()> {
#[serde(rename = "type")]
pub _type: String,
pub features: Vec<VectorFeature<M>>,
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> S2FeatureCollection<M> {
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 = ()> {
#[serde(rename = "type")]
pub _type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
pub properties: Properties,
pub geometry: Geometry,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<M>,
}
impl<M> Feature<M> {
pub fn new(
id: Option<u64>,
properties: Properties,
geometry: Geometry,
metadata: Option<M>,
) -> Self {
Self { _type: "Feature".to_string(), id, properties, geometry, metadata }
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct VectorFeature<M = ()> {
#[serde(rename = "type")]
pub _type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
pub face: Face,
pub properties: Properties,
pub geometry: VectorGeometry,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<M>,
}
impl<M> VectorFeature<M> {
pub fn new_wm(
id: Option<u64>,
properties: Properties,
geometry: VectorGeometry,
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: Properties,
geometry: VectorGeometry,
metadata: Option<M>,
) -> Self {
Self { _type: "S2Feature".to_string(), face, id, properties, geometry, metadata }
}
pub fn from_vector_feature(feature: &VectorFeature<M>, geometry: Option<VectorGeometry>) -> 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 = BTreeMap<String, String>;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum FeatureCollections<M = ()> {
FeatureCollection(FeatureCollection<M>),
S2FeatureCollection(S2FeatureCollection<M>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Features<M = ()> {
Feature(Feature<M>),
VectorFeature(VectorFeature<M>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum WMFeature<M = ()> {
Feature(Feature<M>),
VectorFeature(VectorFeature<M>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum JSONCollection<M = ()> {
FeatureCollection(FeatureCollection<M>),
S2FeatureCollection(S2FeatureCollection<M>),
Feature(Feature<M>),
VectorFeature(VectorFeature<M>),
}
#[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)));
}
#[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()]);
}
#[test]
fn feature_new() {
let fc = 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);
}
}