use std::any::Any;
use std::marker::PhantomData;
use std::ops::Deref;
use galileo_types::cartesian::{
CartesianPoint2d, NewCartesianPoint2d, NewCartesianPoint3d, Point2, Point3, Rect,
};
use galileo_types::geo::impls::projection::{AddDimensionProjection, IdentityProjection};
use galileo_types::geo::impls::GeoPoint2d;
use galileo_types::geo::{ChainProjection, Crs, InvertedProjection, NewGeoPoint, Projection};
use galileo_types::geometry::{CartesianGeometry2d, Geometry};
use galileo_types::geometry_type::{CartesianSpace2d, CartesianSpace3d, GeoSpace2d};
use maybe_sync::{MaybeSend, MaybeSync};
use num_traits::AsPrimitive;
use parking_lot::{Mutex, RwLock};
use crate::layer::attribution::Attribution;
use crate::layer::Layer;
use crate::messenger::Messenger;
use crate::render::{Canvas, RenderOptions};
use crate::view::MapView;
mod feature;
mod feature_store;
pub mod symbol;
mod bundle_store;
use bundle_store::{BundleStore, UpdateType};
pub use feature::Feature;
use feature_store::VecFeatureStore;
pub use feature_store::{FeatureId, FeatureStore};
pub use symbol::Symbol;
pub struct FeatureLayer<P, F, S, Space>
where
F: Feature,
F::Geom: Geometry<Point = P>,
{
features: Box<dyn FeatureStore<F>>,
symbol: S,
crs: Crs,
lods: Vec<Lod>,
messenger: RwLock<Option<Box<dyn Messenger>>>,
options: FeatureLayerOptions,
space: PhantomData<Space>,
}
#[derive(Debug, Copy, Clone)]
pub struct FeatureLayerOptions {
pub sort_by_depth: bool,
pub buffer_size_limit: usize,
pub use_antialiasing: bool,
}
impl Default for FeatureLayerOptions {
fn default() -> Self {
Self {
sort_by_depth: false,
buffer_size_limit: 10_000_000,
use_antialiasing: true,
}
}
}
struct Lod {
min_resolution: f64,
bundles: Mutex<BundleStore>,
}
impl Lod {
fn new(min_resolution: f64, bundle_size_limit: usize) -> Self {
Self {
min_resolution,
bundles: Mutex::new(BundleStore::new(bundle_size_limit)),
}
}
}
impl<P, F, S, Space> FeatureLayer<P, F, S, Space>
where
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F>,
{
pub fn new(features: Vec<F>, style: S, crs: Crs) -> Self {
let options = FeatureLayerOptions::default();
Self {
features: Box::new(VecFeatureStore::new(features)),
symbol: style,
crs,
messenger: RwLock::new(None),
lods: vec![Lod::new(1.0, options.buffer_size_limit)],
options,
space: Default::default(),
}
}
pub fn with_lods(features: Vec<F>, style: S, crs: Crs, lods: &[f64]) -> Self {
let options = FeatureLayerOptions::default();
let mut lods: Vec<_> = lods
.iter()
.map(|&min_resolution| Lod::new(min_resolution, options.buffer_size_limit))
.collect();
lods.sort_by(|a, b| b.min_resolution.total_cmp(&a.min_resolution));
Self {
features: Box::new(VecFeatureStore::new(features)),
symbol: style,
crs,
messenger: RwLock::new(None),
lods,
options,
space: Default::default(),
}
}
pub fn with_options(mut self, options: FeatureLayerOptions) -> Self {
self.options = options;
for lod in &self.lods {
let mut store = lod.bundles.lock();
store.set_bundle_size_limit(options.buffer_size_limit);
}
self
}
pub fn features(&self) -> &dyn FeatureStore<F> {
&*self.features
}
pub fn features_mut(&mut self) -> &mut dyn FeatureStore<F> {
&mut *self.features
}
pub fn crs(&self) -> &Crs {
&self.crs
}
pub fn set_symbol(&mut self, symbol: S) {
self.symbol = symbol;
self.drop_render_cache();
self.request_redraw();
}
pub fn update_feature(&self, feature_id: FeatureId) {
for lod in &self.lods {
lod.bundles.lock().reset_feature(feature_id);
}
}
pub fn update_all_features(&mut self) {
self.drop_render_cache();
}
fn drop_render_cache(&mut self) {
for lod in &mut self.lods {
let mut bundles = lod.bundles.lock();
bundles.clear();
}
}
fn request_redraw(&self) {
if let Some(messenger) = self.messenger.read().as_ref() {
messenger.request_redraw();
}
}
fn select_lod(&self, resolution: f64) -> &Lod {
debug_assert!(!self.lods.is_empty());
for lod in &self.lods {
if lod.min_resolution < resolution {
return lod;
}
}
&self.lods[self.lods.len() - 1]
}
fn render_with_projection<Proj: Projection<InPoint = P, OutPoint = Point3> + ?Sized>(
&self,
view: &MapView,
canvas: &mut dyn Canvas,
projection: impl Deref<Target = Proj>,
) {
let lod = self.select_lod(view.resolution());
let mut store = lod.bundles.lock();
match store.required_update() {
UpdateType::All => {
for (id, feature) in self.features.iter() {
store.with_bundle(|bundle| {
if let Some(projected) = feature.geometry().project(&*projection) {
self.symbol
.render(feature, &projected, lod.min_resolution, bundle);
}
id
});
}
}
UpdateType::Selected(ids) => {
for id in ids {
let Some(feature) = self.features.get(id) else {
continue;
};
store.with_bundle(|bundle| {
if let Some(projected) = feature.geometry().project(&*projection) {
self.symbol
.render(feature, &projected, lod.min_resolution, bundle);
}
id
});
}
}
UpdateType::None => {}
}
store.pack(canvas);
canvas.draw_bundles(
&store.packed(),
RenderOptions {
antialias: self.options.use_antialiasing,
},
);
}
}
impl<P, F, S> FeatureLayer<P, F, S, GeoSpace2d>
where
P: NewGeoPoint + 'static,
F: Feature + MaybeSync + MaybeSend,
F::Geom: Geometry<Point = P>,
{
pub fn extent_projected(&self, crs: &Crs) -> Option<Rect> {
let projection = crs.get_projection::<P, Point2>()?;
self.features
.iter()
.filter_map(|(_, f)| f.geometry().project(&*projection))
.filter_map(|g| g.bounding_rectangle())
.collect()
}
}
impl<P, F, S> FeatureLayer<P, F, S, CartesianSpace2d>
where
P: CartesianPoint2d,
F: Feature + MaybeSync + MaybeSend,
F::Geom: Geometry<Point = P>,
{
pub fn get_features_at<'a>(
&'a self,
point: &'a impl CartesianPoint2d<Num = P::Num>,
tolerance: P::Num,
) -> impl Iterator<Item = (FeatureId, &'a F)> + 'a
where
F::Geom: CartesianGeometry2d<P>,
{
self.features
.iter()
.filter(move |(_, f)| f.geometry().is_point_inside(point, tolerance))
}
pub fn get_features_at_mut<'a>(
&'a mut self,
point: &'a impl CartesianPoint2d<Num = P::Num>,
tolerance: P::Num,
) -> impl Iterator<Item = (FeatureId, &'a mut F)> + 'a
where
F::Geom: CartesianGeometry2d<P>,
{
self.features
.iter_mut()
.filter(move |(_, f)| f.geometry().is_point_inside(point, tolerance))
}
}
impl<P, F, S> FeatureLayer<P, F, S, GeoSpace2d>
where
P: NewGeoPoint + 'static,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn get_projection(&self, crs: &Crs) -> Option<impl Projection<InPoint = P, OutPoint = Point3>> {
Some(ChainProjection::new(
crs.get_projection::<P, Point2>()?,
Box::new(AddDimensionProjection::new(0.0)),
))
}
}
impl<P, F, S> Layer for FeatureLayer<P, F, S, GeoSpace2d>
where
P: NewGeoPoint + 'static,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn render(&self, view: &MapView, canvas: &mut dyn Canvas) {
let Some(projection) = self.get_projection(view.crs()) else {
return;
};
self.render_with_projection(view, canvas, &projection);
}
fn prepare(&self, _view: &MapView) {
}
fn set_messenger(&mut self, messenger: Box<dyn Messenger>) {
*self.messenger.write() = Some(messenger);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn attribution(&self) -> Option<Attribution> {
None
}
}
impl<P, F, S> FeatureLayer<P, F, S, CartesianSpace2d>
where
P: NewCartesianPoint2d + Clone + 'static,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn get_projection(
&self,
crs: &Crs,
) -> Option<Box<dyn Projection<InPoint = P, OutPoint = Point3>>> {
if crs == &self.crs {
Some(Box::new(AddDimensionProjection::new(0.0)))
} else {
let self_proj = self.crs.get_projection::<GeoPoint2d, P>()?;
let view_proj: Box<dyn Projection<InPoint = _, OutPoint = Point2>> =
crs.get_projection()?;
Some(Box::new(ChainProjection::new(
Box::new(ChainProjection::new(
Box::new(InvertedProjection::new(self_proj)),
view_proj,
)),
Box::new(AddDimensionProjection::new(0.0)),
)))
}
}
}
impl<P, F, S> Layer for FeatureLayer<P, F, S, CartesianSpace2d>
where
P: NewCartesianPoint2d + Clone + 'static,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn render(&self, view: &MapView, canvas: &mut dyn Canvas) {
let Some(projection) = self.get_projection(view.crs()) else {
return;
};
self.render_with_projection(view, canvas, projection);
}
fn prepare(&self, _view: &MapView) {
}
fn set_messenger(&mut self, messenger: Box<dyn Messenger>) {
*self.messenger.write() = Some(messenger);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn attribution(&self) -> Option<Attribution> {
None
}
}
impl<P, F, S> FeatureLayer<P, F, S, CartesianSpace3d>
where
P: NewCartesianPoint3d + 'static,
P::Num: AsPrimitive<f32>,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn get_projection(&self) -> IdentityProjection<P, Point3, CartesianSpace3d> {
IdentityProjection::new()
}
}
impl<P, F, S> Layer for FeatureLayer<P, F, S, CartesianSpace3d>
where
P: NewCartesianPoint3d + 'static,
P::Num: AsPrimitive<f32>,
F: Feature + MaybeSend + MaybeSync + 'static,
F::Geom: Geometry<Point = P>,
S: Symbol<F> + MaybeSend + MaybeSync + 'static,
{
fn render(&self, view: &MapView, canvas: &mut dyn Canvas) {
if view.crs() != &self.crs {
return;
}
let projection = self.get_projection();
self.render_with_projection(view, canvas, &projection);
}
fn prepare(&self, _view: &MapView) {
}
fn set_messenger(&mut self, messenger: Box<dyn Messenger>) {
*self.messenger.write() = Some(messenger);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn attribution(&self) -> Option<Attribution> {
None
}
}