Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade to h3o 0.7 #66

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ resolver = "2"
members = ["h3ronpy", "crates/h3arrow"]

[workspace.dependencies]
geo = "0.28"
geo = "0.29"
geo-types = "0.7"
h3o = { version = "0.6" }
h3o = { version = "0.7" }
rayon = "^1"
arrow = { version = "53" }

Expand Down
1 change: 1 addition & 0 deletions crates/h3arrow/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased (YYYY-MM-DD TBD)

* Update h3o to 0.7.
* Added H3ArrayBuilder type.
* Added LocalIj coordinate support.

Expand Down
111 changes: 94 additions & 17 deletions crates/h3arrow/src/array/from_geo.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use arrow::array::OffsetSizeTrait;
use geo::HasDimensions;
use geo_types::*;
use h3o::geom::{ContainmentMode, PolyfillConfig, ToCells};
use h3o::{CellIndex, Resolution};
use h3o::geom::{ContainmentMode, Plotter, PlotterBuilder, Tiler, TilerBuilder};
use h3o::{CellIndex, LatLng, Resolution};
#[cfg(feature = "rayon")]
use rayon::prelude::{IntoParallelIterator, ParallelIterator};

Expand All @@ -12,20 +12,16 @@ use crate::error::Error;

#[derive(Clone, Copy, Debug)]
pub struct ToCellsOptions {
pub(crate) polyfill_config: PolyfillConfig,
pub(crate) h3_resolution: Resolution,
pub(crate) containment_mode: ContainmentMode,
pub(crate) compact: bool,
}

impl From<PolyfillConfig> for ToCellsOptions {
fn from(polyfill_config: PolyfillConfig) -> Self {
Self::new(polyfill_config)
}
}

impl ToCellsOptions {
pub fn new(polyfill_config: PolyfillConfig) -> Self {
pub fn new(h3_resolution: Resolution) -> Self {
Self {
polyfill_config,
h3_resolution,
containment_mode: ContainmentMode::ContainsCentroid,
compact: false,
}
}
Expand All @@ -34,13 +30,26 @@ impl ToCellsOptions {
self.compact = compact;
self
}

pub fn containment_mode(mut self, containment_mode: ContainmentMode) -> Self {
self.containment_mode = containment_mode;
self
}

pub(crate) fn tiler(&self) -> Tiler {
TilerBuilder::new(self.h3_resolution)
.containment_mode(self.containment_mode)
.build()
}

pub(crate) fn plotter(&self) -> Plotter {
PlotterBuilder::new(self.h3_resolution).build()
}
}

impl From<Resolution> for ToCellsOptions {
fn from(resolution: Resolution) -> Self {
PolyfillConfig::new(resolution)
.containment_mode(ContainmentMode::ContainsCentroid)
.into()
Self::new(resolution)
}
}

Expand Down Expand Up @@ -301,9 +310,9 @@ pub fn geometry_to_cells(
if geom.is_empty() {
return Ok(vec![]);
}
let mut cells: Vec<_> = h3o::geom::Geometry::from_degrees(geom.clone())?
.to_cells(options.polyfill_config)
.collect();

let mut cells = vec![];
geometry_to_cells_internal(geom, options, &mut cells)?;

// deduplicate, in the case of overlaps or lines
cells.sort_unstable();
Expand All @@ -317,6 +326,74 @@ pub fn geometry_to_cells(
Ok(cells)
}

fn geometry_to_cells_internal(
geom: &Geometry,
options: &ToCellsOptions,
out_cells: &mut Vec<CellIndex>,
) -> Result<(), Error> {
match geom {
Geometry::Point(pt) => {
out_cells.push(LatLng::try_from(pt.0)?.to_cell(options.h3_resolution))
}
Geometry::Line(line) => {
let mut plotter = options.plotter();
plotter.add(*line)?;
push_plotter_contents(out_cells, plotter)?;
}
Geometry::LineString(line_string) => {
let mut plotter = options.plotter();
plotter.add_batch(line_string.lines())?;
push_plotter_contents(out_cells, plotter)?;
}
Geometry::Polygon(polygon) => {
let mut tiler = options.tiler();
tiler.add(polygon.clone())?;
out_cells.extend(tiler.into_coverage());
}
Geometry::MultiPoint(multi_point) => {
out_cells.reserve(multi_point.len());
for point in multi_point.iter() {
out_cells.push(LatLng::try_from(point.0)?.to_cell(options.h3_resolution))
}
}
Geometry::MultiLineString(multi_line_string) => {
let mut plotter = options.plotter();
for line_string in multi_line_string.iter() {
plotter.add_batch(line_string.lines())?;
}
push_plotter_contents(out_cells, plotter)?;
}
Geometry::MultiPolygon(multi_polygon) => {
let mut tiler = options.tiler();
tiler.add_batch(multi_polygon.iter().cloned())?;
out_cells.extend(tiler.into_coverage());
}
Geometry::GeometryCollection(geometry_collection) => geometry_collection
.iter()
.try_for_each(|g| geometry_to_cells_internal(g, options, out_cells))?,
Geometry::Rect(rect) => {
let mut tiler = options.tiler();
tiler.add(rect.to_polygon())?;
out_cells.extend(tiler.into_coverage());
}
Geometry::Triangle(triangle) => {
let mut tiler = options.tiler();
tiler.add(triangle.to_polygon())?;
out_cells.extend(tiler.into_coverage());
}
}
Ok(())
}

fn push_plotter_contents(out_cells: &mut Vec<CellIndex>, plotter: Plotter) -> Result<(), Error> {
let cell_iter = plotter.plot();
out_cells.reserve(cell_iter.size_hint().0);
for cell_result in cell_iter {
out_cells.push(cell_result?);
}
Ok(())
}

fn to_cells(
geom: Geometry,
options: &ToCellsOptions,
Expand Down
32 changes: 24 additions & 8 deletions crates/h3arrow/src/array/to_geo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use crate::array::{
VertexIndexArray,
};
use crate::error::Error;
use geo::CoordsIter;
use geo::{CoordsIter, ToRadians};
use geo_types::{Coord, Line, LineString, MultiPoint, MultiPolygon, Point, Polygon};
use h3o::geom::ToGeo;
use h3o::{CellIndex, DirectedEdgeIndex, LatLng, VertexIndex};
use std::convert::Infallible;
use std::iter::{repeat, Map, Repeat, Zip};
Expand All @@ -30,7 +29,15 @@ impl IterPolygons for CellIndexArray {
fn iter_polygons(&self, use_degrees: bool) -> Self::Iter<'_> {
self.iter()
.zip(repeat(use_degrees))
.map(|(v, use_degrees)| v.map(|cell| cell.to_geom(use_degrees)))
.map(|(v, use_degrees)| {
v.map(|cell| {
let mut poly = Polygon::new(LineString::from(cell.boundary()), vec![]);
if !use_degrees {
poly.to_radians_in_place();
}
Ok(poly)
})
})
}
}

Expand Down Expand Up @@ -141,7 +148,15 @@ impl IterLines for DirectedEdgeIndexArray {
fn iter_lines(&self, use_degrees: bool) -> Self::Iter<'_> {
self.iter()
.zip(repeat(use_degrees))
.map(|(v, use_degrees)| v.map(|cell| cell.to_geom(use_degrees)))
.map(|(v, use_degrees)| {
v.map(|edge| {
let mut line = Line::from(edge);
if !use_degrees {
line.to_radians_in_place();
}
Ok(line)
})
})
}
}

Expand Down Expand Up @@ -205,10 +220,11 @@ impl ToMultiPolygons for CellIndexArray {
type Output = MultiPolygon;

fn to_multipolygons(&self, use_degrees: bool) -> Result<Self::Output, Self::Error> {
self.iter()
.flatten()
.to_geom(use_degrees)
.map_err(Into::into)
let mut multi_polygons = h3o::geom::dissolve(self.iter().flatten())?;
if !use_degrees {
multi_polygons.to_radians_in_place();
}
Ok(multi_polygons)
}
}

Expand Down
5 changes: 4 additions & 1 deletion crates/h3arrow/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ pub enum Error {
CompactionError(#[from] h3o::error::CompactionError),

#[error(transparent)]
OutlinerError(#[from] h3o::error::OutlinerError),
PlotterError(#[from] h3o::error::PlotterError),

#[error(transparent)]
DissolutionError(#[from] h3o::error::DissolutionError),

#[error(transparent)]
LocalIJError(#[from] h3o::error::LocalIjError),
Expand Down
16 changes: 8 additions & 8 deletions crates/h3arrow/src/spatial_index.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use arrow::array::{Array, BooleanArray, BooleanBufferBuilder};
use geo::{BoundingRect, Intersects};
use geo_types::{Coord, MultiPolygon, Polygon, Rect};
use h3o::geom::ToGeo;
use geo_types::{Coord, Line, LineString, MultiPolygon, Point, Polygon, Rect};
use h3o::{CellIndex, DirectedEdgeIndex, LatLng, VertexIndex};
use rstar::primitives::{GeomWithData, Rectangle};
use rstar::{RTree, AABB};
Expand All @@ -15,14 +14,15 @@ pub trait RectIndexable {

impl RectIndexable for CellIndex {
fn spatial_index_rect(&self) -> Option<Rect> {
self.to_geom(true).unwrap().bounding_rect()
LineString::from(self.boundary()).bounding_rect()
}

fn intersects_with_polygon(&self, poly: &Polygon) -> bool {
// do a cheaper centroid containment check first before comparing the polygons
let centroid: Coord = LatLng::from(*self).into();
if poly.intersects(&centroid) {
poly.intersects(&self.to_geom(true).unwrap())
let self_poly = Polygon::new(LineString::from(self.boundary()), vec![]);
poly.intersects(&self_poly)
} else {
false
}
Expand All @@ -31,21 +31,21 @@ impl RectIndexable for CellIndex {

impl RectIndexable for DirectedEdgeIndex {
fn spatial_index_rect(&self) -> Option<Rect> {
Some(self.to_geom(true).unwrap().bounding_rect())
Some(Line::from(*self).bounding_rect())
}

fn intersects_with_polygon(&self, poly: &Polygon) -> bool {
poly.intersects(&self.to_geom(true).unwrap())
poly.intersects(&Line::from(*self))
}
}

impl RectIndexable for VertexIndex {
fn spatial_index_rect(&self) -> Option<Rect> {
Some(self.to_geom(true).unwrap().bounding_rect())
Some(Point::from(*self).bounding_rect())
}

fn intersects_with_polygon(&self, poly: &Polygon) -> bool {
poly.intersects(&self.to_geom(true).unwrap())
poly.intersects(&Point::from(*self))
}
}

Expand Down
1 change: 1 addition & 0 deletions h3ronpy/CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Versioning <https://semver.org/spec/v2.0.0.html>`__.
Unreleased
----------

- Upgrade to h3o 0.7
- The minimum supported python version is now 3.9.

0.21.1 - 2024-10-04
Expand Down
2 changes: 1 addition & 1 deletion h3ronpy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pyo3 = { version = "^0.22", features = [
"abi3-py39",
] }
pyo3-arrow = { version = "0.5.1", default-features = false }
rasterh3 = { git = "https://github.com/kylebarron/rasterh3", branch = "kyle/bump-ndarray", features = [
rasterh3 = { git = "https://github.com/nmandery/rasterh3", branch = "main", features = [
"rayon",
] }
rayon = { workspace = true }
2 changes: 1 addition & 1 deletion h3ronpy/python/h3ronpy/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def cells_to_wkb_polygons(arr, radians: bool = False, link_cells: bool = False)

:param: arr: The cell array
:param radians: Generate geometries using radians instead of degrees
:param link_cells: Combine neighboring cells into a single polygon geometry.
:param link_cells: Combine neighboring cells into a single polygon geometry. All cell indexes must have the same resolution.
"""
return vector.cells_to_wkb_polygons(_to_uint64_array(arr), radians=radians, link_cells=link_cells)

Expand Down
6 changes: 4 additions & 2 deletions h3ronpy/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ impl IntoPyErr for A3Error {
A3Error::InvalidLatLng(e) => e.into_pyerr(),
A3Error::InvalidGeometry(e) => e.into_pyerr(),
A3Error::CompactionError(e) => e.into_pyerr(),
A3Error::OutlinerError(e) => e.into_pyerr(),
A3Error::DissolutionError(e) => e.into_pyerr(),
A3Error::PlotterError(e) => e.into_pyerr(),
A3Error::LocalIJError(e) => e.into_pyerr(),
A3Error::Arrow2(e) => e.into_pyerr(),
A3Error::NotAUint64Array
Expand Down Expand Up @@ -63,7 +64,8 @@ impl_h3o_value_err!(
h3arrow::export::h3o::error::InvalidLatLng,
h3arrow::export::h3o::error::InvalidResolution,
h3arrow::export::h3o::error::InvalidVertexIndex,
h3arrow::export::h3o::error::OutlinerError,
h3arrow::export::h3o::error::DissolutionError,
h3arrow::export::h3o::error::PlotterError,
h3arrow::export::h3o::error::LocalIjError,
);

Expand Down
Loading