use crate::{bounds::Bounds, lat_to_y, lon_to_x, tools::Tool, x_to_lon, y_to_lat, Error, Result};
use tiny_skia::{Pixmap, PixmapMut, PixmapPaint, Transform};
pub struct Icon {
lat_coordinate: f64,
lon_coordinate: f64,
x_offset: f64,
y_offset: f64,
icon: Pixmap,
}
#[derive(Default)]
pub struct IconBuilder {
lat_coordinate: Option<f64>,
lon_coordinate: Option<f64>,
x_offset: f64,
y_offset: f64,
icon: Option<Pixmap>,
}
impl IconBuilder {
pub fn new() -> Self {
Default::default()
}
pub fn lat_coordinate(mut self, coordinate: f64) -> Self {
self.lat_coordinate = Some(coordinate);
self
}
pub fn lon_coordinate(mut self, coordinate: f64) -> Self {
self.lon_coordinate = Some(coordinate);
self
}
pub fn x_offset(mut self, offset: f64) -> Self {
self.x_offset = offset;
self
}
pub fn y_offset(mut self, offset: f64) -> Self {
self.y_offset = offset;
self
}
pub fn path<P: AsRef<std::path::Path>>(mut self, path: P) -> Result<Self> {
self.icon = Some(Pixmap::load_png(path)?);
Ok(self)
}
pub fn data<D: AsRef<[u8]>>(mut self, data: D) -> Result<Self> {
self.icon = Some(Pixmap::decode_png(data.as_ref())?);
Ok(self)
}
pub fn build(self) -> Result<Icon> {
Ok(Icon {
lat_coordinate: self
.lat_coordinate
.ok_or(Error::BuildError("Latitude coordinate not supplied."))?,
lon_coordinate: self
.lon_coordinate
.ok_or(Error::BuildError("Longitude coordinate not supplied."))?,
x_offset: self.x_offset,
y_offset: self.y_offset,
icon: self
.icon
.ok_or(Error::BuildError("Icon image not supplied."))?,
})
}
}
impl Tool for Icon {
fn extent(&self, zoom: u8, tile_size: f64) -> (f64, f64, f64, f64) {
let (width, height): (f64, f64) = (self.icon.width().into(), self.icon.height().into());
let extent = (
self.x_offset,
height - self.y_offset,
width - self.x_offset,
self.y_offset,
);
let x = lon_to_x(self.lon_coordinate, zoom);
let y = lat_to_y(self.lat_coordinate, zoom);
let lon_min = x_to_lon(x - extent.0 / tile_size, zoom);
let lat_min = y_to_lat(y + extent.1 / tile_size, zoom);
let lon_max = x_to_lon(x + extent.2 / tile_size, zoom);
let lat_max = y_to_lat(y - extent.3 / tile_size, zoom);
(lon_min, lat_min, lon_max, lat_max)
}
fn draw(&self, bounds: &Bounds, mut pixmap: PixmapMut) {
let x = bounds.x_to_px(lon_to_x(self.lon_coordinate, bounds.zoom)) - self.x_offset;
let y = bounds.y_to_px(lat_to_y(self.lat_coordinate, bounds.zoom)) - self.y_offset;
pixmap.draw_pixmap(
x as i32,
y as i32,
self.icon.as_ref(),
&PixmapPaint::default(),
Transform::default(),
None,
);
}
}