cartography 0.10.0

Cartography is a map rendering library for Geographic features expressed using [georust](https://georust.org/) libraries.
Documentation
use std::{
  ops::Deref,
  sync::atomic::{AtomicU64, Ordering},
};

use geo::Intersects;

use crate::{Feature, FeaturesVecLayer, GeometryType};

#[cfg(feature = "image")]
pub use image_integration::{GeoImage, load_image_from_memory, open_image};

#[cfg(feature = "image")]
mod image_integration;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// Compute the next image counter
fn next_id() -> u64
{
  ID_COUNTER.fetch_add(1, Ordering::Relaxed)
}

/// Base trait for image data.
pub trait ImageData: Send + Sync
{
  /// Bounding box of the image.
  fn bounding_box(&self) -> geo::Rect;
  /// Width, height of the image in pixels.
  fn pixel_size(&self) -> (u32, u32);
  /// Pixel data in rgba 8bit format.
  fn rgba_data(&self) -> Vec<u8>;
}

/// Boxed reference to an image.
pub type BoxedImageDataRef<'a> = Box<&'a dyn ImageData>;

impl<'a> ImageData for BoxedImageDataRef<'a>
{
  fn bounding_box(&self) -> geo::Rect
  {
    self.deref().bounding_box()
  }
  fn pixel_size(&self) -> (u32, u32)
  {
    self.deref().pixel_size()
  }
  fn rgba_data(&self) -> Vec<u8>
  {
    self.deref().rgba_data()
  }
}

/// Alias to Image for implementing feature, to workaround rust limitation in term
/// of generic trait implementation.
pub struct ImageFeature<TImageData: ImageData>
{
  image_data: TImageData,
  id: u64,
}

impl<TImageData: ImageData> ImageFeature<TImageData>
{
  /// Create new feature
  pub fn new(image_data: TImageData) -> Self
  {
    Self {
      image_data,
      id: next_id(),
    }
  }
  /// Access to the image data
  pub fn image_data(&self) -> &TImageData
  {
    &self.image_data
  }
  /// Access to the unique id for the image
  pub fn id(&self) -> u64
  {
    self.id
  }
}

impl<TImageData: ImageData> From<TImageData> for ImageFeature<TImageData>
{
  fn from(value: TImageData) -> Self
  {
    Self::new(value)
  }
}

impl<TImageData: ImageData> Feature for ImageFeature<TImageData>
{
  fn geometry(&self) -> Option<geo::Geometry>
  {
    None
  }
  fn element_geometry_type(&self) -> GeometryType
  {
    GeometryType::Invalid
  }
  fn geometry_type(&self) -> crate::geometry::GeometryType
  {
    GeometryType::Invalid
  }
  fn image(&self) -> Option<ImageFeature<BoxedImageDataRef<'_>>>
  {
    Some(ImageFeature::<BoxedImageDataRef<'_>> {
      image_data: Box::new(&self.image_data),
      id: self.id,
    })
  }
  fn intersects(&self, rect: geo::Rect) -> bool
  {
    self.image_data.bounding_box().intersects(&rect)
  }
}

/// Layer for a vector of boxed images
#[allow(type_alias_bounds)]
pub type VecLayer<TImageData: ImageData> = FeaturesVecLayer<ImageFeature<TImageData>>;

impl<TImageData: ImageData> VecLayer<TImageData>
{
  /// From a vector images
  pub fn from_images(images: Vec<TImageData>) -> VecLayer<TImageData>
  {
    let images: Vec<_> = images.into_iter().map(|x| ImageFeature::new(x)).collect();
    VecLayer::<TImageData>::from(images)
  }
}