1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::{CellIndex, Resolution};
use alloc::boxed::Box;

/// A trait to convert a geometry (or a collection of geometries) into a list of
/// cell indexes of the specified resolution.
pub trait ToCells {
    /// Returns an upper bound to the number of cells returned by `to_cells`.
    ///
    /// Can be used to preallocate memory for [`Self::to_cells`].
    ///
    /// # Example
    ///
    /// ```
    /// use h3o::{Resolution, geom::{Point, PolyfillConfig, ToCells}};
    ///
    /// let p = geo::point!(x: 2.349014, y: 48.864716);
    /// let point = Point::from_degrees(p)?;
    /// let count = point.max_cells_count(PolyfillConfig::new(Resolution::Nine));
    /// # Ok::<(), h3o::error::InvalidGeometry>(())
    /// ```
    fn max_cells_count(&self, config: PolyfillConfig) -> usize;

    /// Computes the coverage of the input using cell indexes of the specified
    /// resolution.
    ///
    /// The output may contain duplicate indexes in case of overlapping input
    /// geometries/depending on the selected containment mode.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use geojson::GeoJson;
    /// use h3o::{Resolution, geom::{Geometry, Polygon, ToCells, PolyfillConfig}};
    /// use std::{fs::File, io::BufReader};
    ///
    /// let file = File::open("foo.geojson")?;
    /// let reader = BufReader::new(file);
    /// let geojson = GeoJson::from_reader(reader)?;
    /// let geometry = Geometry::try_from(&geojson)?;
    /// let polygon = Polygon::try_from(geometry)?;
    /// let cells = polygon.to_cells(PolyfillConfig::new(Resolution::Seven)).collect::<Vec<_>>();
    /// # Ok::<(), Box<dyn std::error::Error>>(())
    /// ```
    // TODO: use `impl Iterator` when RPITIT are stabilized.
    fn to_cells(
        &self,
        config: PolyfillConfig,
    ) -> Box<dyn Iterator<Item = CellIndex> + '_>;
}

// -----------------------------------------------------------------------------

/// Polyfill configuration.
#[derive(Clone, Copy, Debug)]
pub struct PolyfillConfig {
    pub(crate) resolution: Resolution,
    pub(crate) containment: ContainmentMode,
}

impl PolyfillConfig {
    /// Instanciate a default configuration.
    #[must_use]
    pub const fn new(resolution: Resolution) -> Self {
        Self {
            resolution,
            containment: ContainmentMode::ContainsCentroid,
        }
    }

    /// Set the containment mode defining if a cell is in a polygon or not.
    #[must_use]
    pub const fn containment_mode(mut self, mode: ContainmentMode) -> Self {
        self.containment = mode;
        self
    }
}

// -----------------------------------------------------------------------------

/// Containment mode used to decide if a cell is contained in a polygon or not.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ContainmentMode {
    /// This mode will select every cells whose centroid are contained inside
    /// the polygon.
    ///
    /// This is the fasted option and ensures that every cell is uniquely
    /// assigned (e.g. two adjacent polygon with zero overlap also have zero
    /// overlapping cells).
    ///
    /// On the other hand, some cells may cover area outside of the polygon
    /// (overshooting) and some parts of the polygon may be left uncovered.
    ContainsCentroid,

    /// This mode will select every cells whose boundaries are entirely within
    /// the polygon.
    ///
    /// This ensures that every cell is uniquely assigned  (e.g. two adjacent
    /// polygon with zero overlap also have zero overlapping cells) and avoids
    /// any coverage overshooting.
    ///
    /// Some parts of the polygon may be left uncovered (more than with
    /// `ContainsCentroid`).
    ContainsBoundary,

    /// This mode will select every cells whose boundaries are within the
    /// polygon, even partially.
    ///
    /// This guarantees a complete coverage of the polygon, but some cells may
    /// belong to two different polygons if they are adjacent/close enough. Some
    /// cells may cover area outside of the polygon.
    ///
    /// Note that if the geometry is fully contained within a cell, this mode
    /// returns nothing (because there are no boundaries intersection).
    IntersectsBoundary,

    /// This mode behaves the same as IntersectsBoundary, but also handles the
    /// case where the geometry is being covered by a cell without intersecting
    /// with its boundaries. In such cases, the covering cell is returned.
    Covers,
}