1#![doc = include_str!("../README.md")]
2use std::{collections::HashSet, fmt::Debug, ops::Add};
3
4use euclid::{Transform2D, UnknownUnit};
5use geo::{
6 algorithm::{
7 coords_iter::CoordsIter,
8 map_coords::{MapCoords, MapCoordsInplace},
9 },
10 Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon,
11 Point, Polygon, Rect, Triangle,
12};
13use ndarray::s;
14use ndarray::Array2;
15use num_traits::{Num, NumCast};
16use thiserror::Error;
17
18mod poly;
19use poly::rasterize_polygon;
20mod line;
21use line::rasterize_line;
22#[cfg(test)]
23mod proptests;
24
25pub type Transform = Transform2D<f64, UnknownUnit, UnknownUnit>;
28type EuclidPoint = euclid::Point2D<f64, UnknownUnit>;
29
30#[derive(Error, Clone, Debug, PartialEq, Eq)]
32pub enum RasterizeError {
33 #[error("at least one coordinate of the supplied geometry is NaN or infinite")]
35 NonFiniteCoordinate,
36
37 #[error("`width` is required in builder")]
39 MissingWidth,
40
41 #[error("`height` is required in builder")]
43 MissingHeight,
44}
45
46pub type Result<T> = std::result::Result<T, RasterizeError>;
48
49#[derive(Debug, Clone, Default)]
60pub struct BinaryBuilder {
61 width: Option<usize>,
62 height: Option<usize>,
63 geo_to_pix: Option<Transform>,
64}
65
66impl BinaryBuilder {
67 pub fn new() -> Self {
68 BinaryBuilder::default()
69 }
70
71 pub fn width(mut self, width: usize) -> Self {
72 self.width = Some(width);
73 self
74 }
75
76 pub fn height(mut self, height: usize) -> Self {
77 self.height = Some(height);
78 self
79 }
80
81 pub fn geo_to_pix(mut self, geo_to_pix: Transform) -> Self {
82 self.geo_to_pix = Some(geo_to_pix);
83 self
84 }
85
86 pub fn build(self) -> Result<BinaryRasterizer> {
87 match (self.width, self.height) {
88 (None, _) => Err(RasterizeError::MissingWidth),
89 (_, None) => Err(RasterizeError::MissingHeight),
90 (Some(width), Some(height)) => BinaryRasterizer::new(width, height, self.geo_to_pix),
91 }
92 }
93}
94
95#[derive(Clone, Debug)]
136pub struct BinaryRasterizer {
137 inner: Rasterizer<u8>,
138}
139
140fn to_float<T>(coords: &(T, T)) -> (f64, f64)
141where
142 T: Into<f64> + Copy,
143{
144 (coords.0.into(), coords.1.into())
145}
146
147impl BinaryRasterizer {
148 pub fn new(width: usize, height: usize, geo_to_pix: Option<Transform>) -> Result<Self> {
149 let non_finite = geo_to_pix
150 .map(|geo_to_pix| geo_to_pix.to_array().iter().any(|param| !param.is_finite()))
151 .unwrap_or(false);
152 if non_finite {
153 Err(RasterizeError::NonFiniteCoordinate)
154 } else {
155 let inner = Rasterizer::new(width, height, geo_to_pix, MergeAlgorithm::Replace, 0);
156 Ok(BinaryRasterizer { inner })
157 }
158 }
159
160 pub fn geo_to_pix(&self) -> Option<Transform> {
162 self.inner.geo_to_pix
163 }
164
165 pub fn rasterize<Coord, InputShape, ShapeAsF64>(&mut self, shape: &InputShape) -> Result<()>
169 where
170 InputShape: MapCoords<Coord, f64, Output = ShapeAsF64>,
171 ShapeAsF64: Rasterize<u8> + for<'a> CoordsIter<'a, Scalar = f64> + MapCoordsInplace<f64>,
172 Coord: Into<f64> + Copy + Debug + Num + NumCast + PartialOrd,
173 {
174 self.inner.rasterize(shape, 1)
176 }
177
178 pub fn finish(self) -> Array2<bool> {
180 self.inner
181 .finish()
182 .mapv(|v| if v == 1u8 { true } else { false })
183 }
184}
185
186#[doc(hidden)]
187pub trait Rasterize<Label>
188where
189 Label: Copy + Add<Output = Label>,
190{
191 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>);
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub enum MergeAlgorithm {
198 Replace,
201
202 Add,
205}
206
207impl Default for MergeAlgorithm {
208 fn default() -> Self {
209 MergeAlgorithm::Replace
210 }
211}
212
213#[derive(Debug, Clone, Default)]
240pub struct LabelBuilder<Label> {
241 background: Label,
242 width: Option<usize>,
243 height: Option<usize>,
244 geo_to_pix: Option<Transform>,
245 algorithm: Option<MergeAlgorithm>,
246}
247
248impl<Label> LabelBuilder<Label>
249where
250 Label: Copy + Add<Output = Label>,
251{
252 pub fn background(background: Label) -> Self {
253 LabelBuilder {
254 background,
255 width: None,
256 height: None,
257 geo_to_pix: None,
258 algorithm: None,
259 }
260 }
261
262 pub fn width(mut self, width: usize) -> Self {
263 self.width = Some(width);
264 self
265 }
266
267 pub fn height(mut self, height: usize) -> Self {
268 self.height = Some(height);
269 self
270 }
271
272 pub fn geo_to_pix(mut self, geo_to_pix: Transform) -> Self {
273 self.geo_to_pix = Some(geo_to_pix);
274 self
275 }
276
277 pub fn algorithm(mut self, algorithm: MergeAlgorithm) -> Self {
278 self.algorithm = Some(algorithm);
279 self
280 }
281
282 pub fn build(self) -> Result<Rasterizer<Label>> {
283 match (self.width, self.height) {
284 (None, _) => Err(RasterizeError::MissingWidth),
285 (_, None) => Err(RasterizeError::MissingHeight),
286 (Some(width), Some(height)) => Ok(Rasterizer::new(
287 width,
288 height,
289 self.geo_to_pix,
290 self.algorithm.unwrap_or_default(),
291 self.background,
292 )),
293 }
294 }
295}
296
297#[derive(Clone, Debug)]
338pub struct Rasterizer<Label> {
339 pixels: Array2<Label>,
340 geo_to_pix: Option<Transform>,
341 algorithm: MergeAlgorithm,
342 foreground: Label,
343 previous_burnt_points: HashSet<(usize, usize)>,
344 current_burnt_points: HashSet<(usize, usize)>,
345}
346
347impl<Label> Rasterizer<Label>
348where
349 Label: Copy + Add<Output = Label>,
350{
351 pub fn new(
352 width: usize,
353 height: usize,
354 geo_to_pix: Option<Transform>,
355 algorithm: MergeAlgorithm,
356 background: Label,
357 ) -> Self {
358 let pixels = Array2::from_elem((height, width), background);
359 Rasterizer {
360 pixels,
361 geo_to_pix,
362 algorithm,
363 foreground: background,
364 previous_burnt_points: HashSet::new(),
365 current_burnt_points: HashSet::new(),
366 }
367 }
368
369 fn width(&self) -> usize {
370 self.pixels.shape()[1]
371 }
372
373 fn height(&self) -> usize {
374 self.pixels.shape()[0]
375 }
376
377 pub fn geo_to_pix(&self) -> Option<Transform> {
379 self.geo_to_pix
380 }
381
382 fn new_linestring(&mut self) {
400 self.previous_burnt_points.clear();
401 self.current_burnt_points.clear();
402 }
403
404 fn new_line(&mut self) {
405 std::mem::swap(
406 &mut self.previous_burnt_points,
407 &mut self.current_burnt_points,
408 );
409 self.current_burnt_points.clear();
410 }
411
412 fn fill_pixel(&mut self, ix: usize, iy: usize) {
413 debug_assert!(ix < self.width());
414 debug_assert!(iy < self.height());
415 let mut slice = self.pixels.slice_mut(s![iy, ix]);
416 match self.algorithm {
417 MergeAlgorithm::Replace => slice.fill(self.foreground),
418 MergeAlgorithm::Add => {
419 slice.mapv_inplace(|v| v + self.foreground);
420 }
421 }
422 }
423
424 fn fill_pixel_no_repeat(&mut self, ix: usize, iy: usize, use_current_too: bool) {
425 match self.algorithm {
426 MergeAlgorithm::Replace => {
427 self.fill_pixel(ix, iy);
428 }
429 MergeAlgorithm::Add => {
430 let point = (ix, iy);
431 let mut do_fill_pixel = !self.previous_burnt_points.contains(&point);
432 if use_current_too {
433 do_fill_pixel = do_fill_pixel && !self.current_burnt_points.contains(&point);
434 }
435 if do_fill_pixel {
436 self.fill_pixel(ix, iy);
437 self.current_burnt_points.insert(point);
438 }
439 }
440 }
441 }
442
443 fn fill_horizontal_line(&mut self, x_start: usize, x_end: usize, y: usize) {
452 let mut slice = self.pixels.slice_mut(s![y, x_start..x_end]);
453 match self.algorithm {
454 MergeAlgorithm::Replace => slice.fill(self.foreground),
455 MergeAlgorithm::Add => {
456 slice.mapv_inplace(|v| v + self.foreground);
457 }
458 }
459 }
460
461 fn fill_horizontal_line_no_repeat(&mut self, x_start: usize, x_end: usize, y: usize) {
462 for x in x_start..=x_end {
463 self.fill_pixel_no_repeat(x, y, true);
464 }
465 }
466
467 fn fill_vertical_line_no_repeat(&mut self, x: usize, y_start: usize, y_end: usize) {
468 for y in y_start..=y_end {
469 self.fill_pixel_no_repeat(x, y, false);
470 }
471 }
472
473 pub fn rasterize<Coord, InputShape, ShapeAsF64>(
477 &mut self,
478 shape: &InputShape,
479 foreground: Label,
480 ) -> Result<()>
481 where
482 InputShape: MapCoords<Coord, f64, Output = ShapeAsF64>,
483 ShapeAsF64: Rasterize<Label> + for<'a> CoordsIter<'a, Scalar = f64> + MapCoordsInplace<f64>,
484 Coord: Into<f64> + Copy + Debug + Num + NumCast + PartialOrd,
485 {
486 let mut float = shape.map_coords(to_float);
488
489 let all_finite = float
491 .coords_iter()
492 .all(|coordinate| coordinate.x.is_finite() && coordinate.y.is_finite());
493 if !all_finite {
494 return Err(RasterizeError::NonFiniteCoordinate);
495 }
496
497 self.foreground = foreground;
498
499 match self.geo_to_pix {
502 None => float,
503 Some(transform) => {
504 float.map_coords_inplace(|&(x, y)| {
505 transform.transform_point(EuclidPoint::new(x, y)).to_tuple()
506 });
507 float
508 }
509 }
510 .rasterize(self); Ok(())
513 }
514
515 pub fn finish(self) -> Array2<Label> {
517 self.pixels
518 }
519}
520
521impl<Label> Rasterize<Label> for Point<f64>
522where
523 Label: Copy + Add<Output = Label>,
524{
525 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
526 if self.x() >= 0. && self.y() >= 0. {
527 let x = self.x().floor() as usize;
528 let y = self.y().floor() as usize;
529 if x < rasterizer.width() && y < rasterizer.height() {
530 rasterizer.fill_pixel(x, y);
531 }
532 }
533 }
534}
535
536impl<Label> Rasterize<Label> for MultiPoint<f64>
537where
538 Label: Copy + Add<Output = Label>,
539{
540 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
541 self.iter().for_each(|point| point.rasterize(rasterizer));
542 }
543}
544
545impl<Label> Rasterize<Label> for Rect<f64>
546where
547 Label: Copy + Add<Output = Label>,
548{
549 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
550 self.to_polygon().rasterize(rasterizer);
556 }
557}
558
559impl<Label> Rasterize<Label> for Line<f64>
560where
561 Label: Copy + Add<Output = Label>,
562{
563 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
564 rasterizer.new_linestring();
565 rasterize_line(self, rasterizer);
566 }
567}
568
569impl<Label> Rasterize<Label> for LineString<f64>
570where
571 Label: Copy + Add<Output = Label>,
572{
573 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
574 rasterizer.new_linestring();
583 self.lines().for_each(|line| {
584 rasterizer.new_line();
585 rasterize_line(&line, rasterizer);
586 });
587 }
588}
589
590impl<Label> Rasterize<Label> for MultiLineString<f64>
591where
592 Label: Copy + Add<Output = Label>,
593{
594 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
595 self.iter()
596 .for_each(|line_string| line_string.rasterize(rasterizer));
597 }
598}
599
600impl<Label> Rasterize<Label> for Polygon<f64>
601where
602 Label: Copy + Add<Output = Label>,
603{
604 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
605 rasterize_polygon(self.exterior(), self.interiors(), rasterizer);
606 }
607}
608
609impl<Label> Rasterize<Label> for MultiPolygon<f64>
610where
611 Label: Copy + Add<Output = Label>,
612{
613 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
614 self.iter().for_each(|poly| poly.rasterize(rasterizer));
615 }
616}
617
618impl<Label> Rasterize<Label> for Triangle<f64>
619where
620 Label: Copy + Add<Output = Label>,
621{
622 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
623 self.to_polygon().rasterize(rasterizer)
624 }
625}
626
627impl<Label> Rasterize<Label> for Geometry<f64>
628where
629 Label: Copy + Add<Output = Label>,
630{
631 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
632 match self {
633 Geometry::Point(point) => point.rasterize(rasterizer),
634 Geometry::Line(line) => line.rasterize(rasterizer),
635 Geometry::LineString(ls) => ls.rasterize(rasterizer),
636 Geometry::Polygon(poly) => poly.rasterize(rasterizer),
637 Geometry::GeometryCollection(gc) => gc.rasterize(rasterizer),
638 Geometry::MultiPoint(points) => points.rasterize(rasterizer),
639 Geometry::MultiLineString(lines) => lines.rasterize(rasterizer),
640 Geometry::MultiPolygon(polys) => polys.rasterize(rasterizer),
641 Geometry::Rect(rect) => rect.rasterize(rasterizer),
642 Geometry::Triangle(tri) => tri.rasterize(rasterizer),
643 }
644 }
645}
646
647impl<Label> Rasterize<Label> for GeometryCollection<f64>
648where
649 Label: Copy + Add<Output = Label>,
650{
651 fn rasterize(&self, rasterizer: &mut Rasterizer<Label>) {
652 self.iter().for_each(|thing| thing.rasterize(rasterizer));
653 }
654}
655
656#[cfg(test)]
657mod tests;