use crate::{
CellIndex, LatLng, Resolution,
error::{InvalidGeometry, PlotterError},
index::GridPathCells,
};
use geo::{Line, ToRadians as _};
#[derive(Debug, Clone)]
pub struct Plotter {
resolution: Resolution,
convert_to_rads: bool,
paths: Vec<GridPathCells>,
}
impl Plotter {
pub fn add(&mut self, mut line: Line) -> Result<(), PlotterError> {
if self.convert_to_rads {
line.to_radians_in_place();
}
Self::check_coords(&line)?;
let start = LatLng::from_radians(line.start.y, line.start.x)
.expect("valid start")
.to_cell(self.resolution);
let end = LatLng::from_radians(line.end.y, line.end.x)
.expect("valid end")
.to_cell(self.resolution);
self.paths.push(GridPathCells::new(start, end)?);
Ok(())
}
pub fn add_batch(
&mut self,
lines: impl IntoIterator<Item = Line>,
) -> Result<(), PlotterError> {
for line in lines {
self.add(line)?;
}
Ok(())
}
pub fn plot(self) -> impl Iterator<Item = Result<CellIndex, PlotterError>> {
self.paths
.into_iter()
.flatten()
.map(|res| res.map_err(Into::into))
}
fn check_coords(line: &Line) -> Result<(), PlotterError> {
if !super::coord_is_valid(line.start)
|| !super::coord_is_valid(line.end)
{
return Err(InvalidGeometry::new(
"every coordinate of the line must be valid",
)
.into());
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub struct PlotterBuilder {
resolution: Resolution,
convert_to_rads: bool,
}
impl PlotterBuilder {
#[must_use]
pub const fn new(resolution: Resolution) -> Self {
Self {
resolution,
convert_to_rads: true,
}
}
#[must_use]
pub const fn disable_radians_conversion(mut self) -> Self {
self.convert_to_rads = false;
self
}
#[must_use]
pub const fn build(self) -> Plotter {
Plotter {
resolution: self.resolution,
convert_to_rads: self.convert_to_rads,
paths: Vec::new(),
}
}
}