use crate::{Feature, Projection};
pub trait Layer<TFeature: Feature>: Send + Sync
{
fn projection(&self) -> &Projection;
fn features<'a>(
&'a self,
rect: geo::Rect,
zoom_level: f64,
) -> Box<dyn Iterator<Item = ccutils::containers::RefOrValue<'a, TFeature>> + 'a>;
}
#[allow(type_alias_bounds)]
pub(crate) type BoxedLayer<TFeature: Feature> = Box<dyn Layer<TFeature>>;
pub struct FeaturesVecLayer<TFeature: Feature>
{
projection: Projection,
features: Vec<TFeature>,
}
impl<TFeature: Feature> FeaturesVecLayer<TFeature>
{
pub fn features_iter(&self) -> impl Iterator<Item = &TFeature>
{
self.features.iter()
}
pub fn extent(&self) -> Option<geo::Rect>
{
use geo::BoundingRect as _;
self
.features
.iter()
.filter_map(|feature| {
feature
.geometry()
.and_then(|geometry| geometry.bounding_rect())
})
.reduce(|a, b| {
geo::Rect::new(
geo::coord! {
x: a.min().x.min(b.min().x),
y: a.min().y.min(b.min().y),
},
geo::coord! {
x: a.max().x.max(b.max().x),
y: a.max().y.max(b.max().y),
},
)
})
}
#[cfg(feature = "geojson")]
#[allow(clippy::result_large_err)]
pub fn read_from_geo_json<'de, T>(
feature_collection_reader: impl std::io::Read,
) -> geojson::Result<Self>
where
T: serde::Deserialize<'de> + Into<TFeature>,
{
let projection = Projection::wgs84();
let features =
geojson::de::deserialize_feature_collection_to_vec::<T>(feature_collection_reader)?;
let features = features.into_iter().map(|x| x.into()).collect();
Ok(Self {
projection,
features,
})
}
}
impl<TFeature: Feature> From<Vec<TFeature>> for FeaturesVecLayer<TFeature>
{
fn from(features: Vec<TFeature>) -> Self
{
Self {
projection: Projection::wgs84(),
features,
}
}
}
impl<TFeature, TVecElement> FromIterator<TVecElement> for FeaturesVecLayer<TFeature>
where
TFeature: Feature,
TVecElement: Into<TFeature>,
{
fn from_iter<T: IntoIterator<Item = TVecElement>>(iter: T) -> Self
{
Self {
projection: Projection::wgs84(),
features: iter.into_iter().map(Into::into).collect(),
}
}
}
impl<TFeature: Feature> Layer<TFeature> for FeaturesVecLayer<TFeature>
{
fn projection(&self) -> &Projection
{
&self.projection
}
fn features<'a>(
&'a self,
rect: geo::Rect,
_zoom_level: f64,
) -> Box<dyn Iterator<Item = crate::RefOrValue<'a, TFeature>> + 'a>
{
Box::new(
self
.features
.iter()
.filter(move |feat| feat.intersects(rect))
.map(Into::into),
)
}
}
pub trait IntoLayer<TFeature>
where
TFeature: Feature,
{
type Layer: Layer<TFeature>;
#[must_use]
fn into_layer(self) -> Self::Layer;
}
impl<TFeature, TOtherFeature> IntoLayer<TFeature> for Vec<TOtherFeature>
where
TFeature: From<TOtherFeature> + Feature,
{
type Layer = FeaturesVecLayer<TFeature>;
fn into_layer(self) -> Self::Layer
{
FeaturesVecLayer::from_iter(self.into_iter().map(Into::<TFeature>::into))
}
}
#[cfg(test)]
mod tests
{
use super::*;
use crate::GeometryRef;
struct DummyFeature(geo::Geometry);
impl GeometryRef for DummyFeature
{
fn geometry_ref(&self) -> &geo::Geometry
{
&self.0
}
}
fn point_feature(x: f64, y: f64) -> DummyFeature
{
DummyFeature(geo::Geometry::Point(geo::Point::new(x, y)))
}
#[test]
fn features_iter_returns_all_features()
{
let layer: FeaturesVecLayer<DummyFeature> = vec![
point_feature(0.0, 0.0),
point_feature(1.0, 1.0),
point_feature(2.0, 2.0),
]
.into();
assert_eq!(layer.features_iter().count(), 3);
}
#[test]
fn features_iter_empty_layer()
{
let layer: FeaturesVecLayer<DummyFeature> = vec![].into();
assert_eq!(layer.features_iter().count(), 0);
}
#[test]
fn features_iter_does_not_consume_layer()
{
let layer: FeaturesVecLayer<DummyFeature> = vec![point_feature(0.0, 0.0)].into();
let _ = layer.features_iter().count();
assert_eq!(layer.features_iter().count(), 1);
}
}