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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
use std::iter::Iterator;
use std::os::raw::c_int;

use geo_types::{LineString, Polygon};

use h3ron_h3_sys::{GeoCoord, GeoPolygon, Geofence, H3Index};
pub use to_geo::{
    to_linked_polygons, ToAlignedLinkedPolygons, ToCoordinate, ToLinkedPolygons, ToPolygon,
};

use crate::error::check_same_resolution;
pub use crate::error::Error;
pub use crate::index::Index;
pub use crate::index::ToIndex;
pub use crate::to_h3::ToH3Indexes;
use crate::util::linestring_to_geocoords;

#[macro_use]
mod util;
pub mod algorithm;
pub mod collections;
pub mod error;
pub mod experimental;
mod index;
mod to_geo;
mod to_h3;

pub const H3_MIN_RESOLUTION: u8 = 0_u8;
pub const H3_MAX_RESOLUTION: u8 = 15_u8;

/// marker trait for indexes, including conversion to a H3Index
pub trait HasH3Index {
    fn h3index(&self) -> H3Index;
}

impl HasH3Index for H3Index {
    fn h3index(&self) -> H3Index {
        *self
    }
}

pub trait FromH3Index {
    fn from_h3index(h3index: H3Index) -> Self;
}

impl FromH3Index for H3Index {
    fn from_h3index(h3index: H3Index) -> Self {
        h3index
    }
}

pub enum AreaUnits {
    M2,
    Km2,
    Radians2,
}

impl AreaUnits {
    pub fn hex_area_at_resolution(&self, resolution: u8) -> Result<f64, Error> {
        match self {
            AreaUnits::M2 => Ok(unsafe { h3ron_h3_sys::hexAreaM2(resolution as i32) }),
            AreaUnits::Km2 => Ok(unsafe { h3ron_h3_sys::hexAreaKm2(resolution as i32) }),
            _ => Err(Error::UnsupportedOperation),
        }
    }
}

unsafe fn to_geofence(ring: &mut Vec<GeoCoord>) -> Geofence {
    Geofence {
        numVerts: ring.len() as c_int,
        verts: ring.as_mut_ptr(),
    }
}

pub fn max_polyfill_size(poly: &Polygon<f64>, h3_resolution: u8) -> usize {
    unsafe {
        let mut exterior: Vec<GeoCoord> = linestring_to_geocoords(&poly.exterior());
        let mut interiors: Vec<Vec<GeoCoord>> = poly
            .interiors()
            .iter()
            .map(|ls| linestring_to_geocoords(ls))
            .collect();

        let mut holes: Vec<Geofence> = interiors.iter_mut().map(|ring| to_geofence(ring)).collect();

        let gp = GeoPolygon {
            geofence: to_geofence(&mut exterior),
            numHoles: holes.len() as c_int,
            holes: holes.as_mut_ptr(),
        };

        h3ron_h3_sys::maxPolyfillSize(&gp, h3_resolution as c_int) as usize
    }
}

pub fn polyfill(poly: &Polygon<f64>, h3_resolution: u8) -> Vec<H3Index> {
    let mut h3_indexes = unsafe {
        let mut exterior: Vec<GeoCoord> = linestring_to_geocoords(&poly.exterior());
        let mut interiors: Vec<Vec<GeoCoord>> = poly
            .interiors()
            .iter()
            .map(|ls| linestring_to_geocoords(ls))
            .collect();

        let mut holes: Vec<Geofence> = interiors.iter_mut().map(|ring| to_geofence(ring)).collect();

        let gp = GeoPolygon {
            geofence: to_geofence(&mut exterior),
            numHoles: holes.len() as c_int,
            holes: holes.as_mut_ptr(),
        };

        let num_hexagons = h3ron_h3_sys::maxPolyfillSize(&gp, h3_resolution as c_int);

        // pre-allocate for the expected number of hexagons
        let mut h3_indexes: Vec<H3Index> = vec![0; num_hexagons as usize];

        h3ron_h3_sys::polyfill(&gp, h3_resolution as c_int, h3_indexes.as_mut_ptr());
        h3_indexes
    };
    remove_zero_indexes_from_vec!(h3_indexes);
    h3_indexes
}

///
/// the input vec must be deduplicated
pub fn compact(h3_indexes: &[H3Index]) -> Vec<H3Index> {
    let mut h3_indexes_out: Vec<H3Index> = vec![0; h3_indexes.len()];
    unsafe {
        h3ron_h3_sys::compact(
            h3_indexes.as_ptr(),
            h3_indexes_out.as_mut_ptr(),
            h3_indexes.len() as c_int,
        );
    }
    remove_zero_indexes_from_vec!(h3_indexes_out);
    h3_indexes_out
}

pub fn max_k_ring_size(k: u32) -> usize {
    unsafe { h3ron_h3_sys::maxKringSize(k as c_int) as usize }
}

/// Number of indexes in a line connecting two indexes
pub fn line_size(start: H3Index, end: H3Index) -> Result<usize, Error> {
    check_same_resolution(start, end)?;
    line_size_not_checked(start, end)
}

fn line_size_not_checked(start: H3Index, end: H3Index) -> Result<usize, Error> {
    let size = unsafe { h3ron_h3_sys::h3LineSize(start, end) };
    if size < 0 {
        Err(Error::LineNotComputable)
    } else {
        Ok(size as usize)
    }
}

fn line_between_indexes_not_checked(start: H3Index, end: H3Index) -> Result<Vec<H3Index>, Error> {
    let num_indexes = line_size_not_checked(start, end)?;
    let mut h3_indexes_out: Vec<H3Index> = vec![0; num_indexes];
    let retval = unsafe { h3ron_h3_sys::h3Line(start, end, h3_indexes_out.as_mut_ptr()) };
    if retval != 0 {
        return Err(Error::LineNotComputable);
    }
    remove_zero_indexes_from_vec!(h3_indexes_out);
    Ok(h3_indexes_out)
}

/// Line of h3 indexes connecting two indexes
pub fn line_between_indexes(start: H3Index, end: H3Index) -> Result<Vec<H3Index>, Error> {
    check_same_resolution(start, end)?;
    line_between_indexes_not_checked(start, end)
}

/// Generate h3 indexes along the given linestring
///
/// The returned indexes are ordered sequentially, there may
/// be duplicates caused by multiple line seqments
pub fn line(linestring: &LineString<f64>, h3_resolution: u8) -> Result<Vec<H3Index>, Error> {
    let mut h3_indexes_out = vec![];
    for coords in linestring.0.windows(2) {
        let start_index = Index::from_coordinate(&coords[0], h3_resolution)?;
        let end_index = Index::from_coordinate(&coords[1], h3_resolution)?;

        let mut segment_indexes =
            line_between_indexes_not_checked(start_index.h3index(), end_index.h3index())?;
        if segment_indexes.is_empty() {
            continue;
        }
        if !h3_indexes_out.is_empty()
            && h3_indexes_out[h3_indexes_out.len() - 1] == segment_indexes[0]
        {
            h3_indexes_out.remove(h3_indexes_out.len() - 1);
        }
        h3_indexes_out.append(&mut segment_indexes);
    }
    Ok(h3_indexes_out)
}

#[cfg(test)]
mod tests {
    use geo::Coordinate;
    use geo_types::LineString;

    use crate::{line, line_between_indexes};

    #[test]
    fn line_across_multiple_faces() {
        // ported from H3s testH3Line.c
        let start = 0x85285aa7fffffff_u64;
        let end = 0x851d9b1bfffffff_u64;

        // Line not computable across multiple icosa faces
        assert!(line_between_indexes(start, end).is_err());
    }

    #[test]
    fn linestring() {
        let ls = LineString::from(vec![
            Coordinate::from((11.60, 37.16)),
            Coordinate::from((3.86, 39.63)),
            Coordinate::from((-4.57, 35.17)),
            Coordinate::from((-20.74, 34.88)),
            Coordinate::from((-23.55, 48.92)),
        ]);
        assert!(line(&ls, 5).unwrap().len() > 200)
    }
}