Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissions:
contents: read

env:
PYTHON_VERSION: "3.9"
PYTHON_VERSION: "3.12"
RUST_BACKTRACE: "1"

jobs:
Expand Down
9 changes: 5 additions & 4 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ build:
os: "ubuntu-lts-latest"
tools:
python: "3.13"
rust: "1.96"

jobs:
post_checkout:
- (git --no-pager log --pretty="tformat:%s" -1 | grep -vqF "[skip-rtd]") || exit 183
- git fetch --unshallow || true
pre_create_environment:
# install rust manually. https://github.com/readthedocs/readthedocs.org/issues/11488
- asdf install rust 1.91
- asdf global rust 1.91
#pre_create_environment:
# # install rust manually. https://github.com/readthedocs/readthedocs.org/issues/11488
# - asdf install rust 1.96
# - asdf global rust 1.96

python:
install:
Expand Down
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ resolver = "2"
members = ["h3ronpy", "crates/h3arrow"]

[workspace.dependencies]
geo = "0.29"
geo = "0.33"
geo-types = "0.7"
h3o = { version = "0.7" }
h3o = { version = "0.10" }
rayon = "^1"
arrow = { version = "53" }
arrow = { version = "58" }
geoarrow-array = { version = "0.8" }
geoarrow = { version = "0.8" }

[profile.release]
lto = "thin"
Expand Down
27 changes: 17 additions & 10 deletions crates/h3arrow/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
[package]
name = "h3arrow"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
authors = ["Nico Mandery <nico@nmandery.net>"]
description = "Integration of the H3 geospatial grid with the arrow memory model"
license = "MIT"
keywords = ["geo", "spatial", "h3", "arrow"]
readme = "README.md"
homepage = "https://github.com/nmandery/h3arrow"
repository = "https://github.com/nmandery/h3arrow"
homepage = "https://github.com/nmandery/h3ronpy"
repository = "https://github.com/nmandery/h3ronpy"

[package.metadata.docs.rs]
all-features = true

[features]
geoarrow = ["dep:geoarrow", "dep:geozero"]
rayon = ["dep:rayon", "geoarrow/rayon"]
geoarrow = [
"dep:geoarrow",
"dep:geozero",
"dep:geoarrow-array",
"dep:geo-traits",
]
rayon = ["dep:rayon"]
spatial_index = ["dep:rstar"]

[dependencies]
ahash = "0.8"
arrow = { workspace = true }
geoarrow = { git = "https://github.com/geoarrow/geoarrow-rs", rev = "3ecf7dfc1816261b84f813eaf2a0174f2b5752d8", optional = true }
geoarrow = { workspace = true, optional = true }
geoarrow-array = { workspace = true, optional = true }
geo-traits = { version = "0.3", optional = true }
geo-types = { workspace = true }
geo = { workspace = true }
geozero = { version = "^0.14", default-features = false, features = [
geozero = { version = "^0.15", default-features = false, features = [
"with-geo",
"with-wkb",
], optional = true }
h3o = { workspace = true, features = ["geo"] }
nom = "7"
nom = "8"
rayon = { workspace = true, optional = true }
rstar = { version = "0.12", optional = true }
thiserror = "1"
rstar = { version = "0.13", optional = true }
thiserror = "2"
4 changes: 2 additions & 2 deletions crates/h3arrow/src/algorithm/change_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::array::{CellIndexArray, H3ListArray, H3ListArrayBuilder};
use crate::error::Error;
use h3o::{CellIndex, Resolution};
use std::cmp::Ordering;
use std::iter::repeat;
use std::iter::repeat_n;

pub struct ChangedResolutionPair<T> {
/// values before the resolution change
Expand Down Expand Up @@ -109,7 +109,7 @@ impl ChangeResolutionOp for CellIndexArray {
self.iter().flatten().for_each(|cell| {
let len_before = after_vec.len();
extend_with_cell(&mut after_vec, cell, resolution);
before_vec.extend(repeat(cell).take(after_vec.len() - len_before));
before_vec.extend(repeat_n(cell, after_vec.len() - len_before));
});

Ok(ChangedResolutionPair {
Expand Down
8 changes: 6 additions & 2 deletions crates/h3arrow/src/algorithm/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ where

impl CompactOp for CellIndexArray {
fn compact(&self) -> Result<Self, Error> {
Ok(CellIndex::compact(self.iter().flatten())?.collect())
// TODO: find a way to avoid allocation of a new vec
let mut cells: Vec<_> = self.iter().flatten().collect();
CellIndex::compact(&mut cells)?;
Ok(cells.into())
}

fn compact_mixed_resolutions(&self) -> Result<Self, Error> {
Expand Down Expand Up @@ -76,7 +79,8 @@ impl CellSet {
let mut compacted_in = std::mem::take(&mut self.cells_by_resolution[r_idx]);
compacted_in.sort_unstable();
compacted_in.dedup();
for cell in CellIndex::compact(compacted_in.into_iter())? {
CellIndex::compact(&mut compacted_in)?;
for cell in compacted_in {
self.insert(cell);
}
res = h3_res.pred();
Expand Down
35 changes: 28 additions & 7 deletions crates/h3arrow/src/algorithm/concave_hull.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,50 @@ use crate::array::to_geo::{
vertexindexarray_to_multipoint,
};
use crate::array::{CellIndexArray, DirectedEdgeIndexArray, VertexIndexArray};
use geo::concave_hull::ConcaveHull;
use geo::concave_hull::{ConcaveHull, ConcaveHullOptions};
use geo_types::Polygon;

impl ConcaveHull for CellIndexArray {
type Scalar = f64;

fn concave_hull(&self, concavity: Self::Scalar) -> Polygon<Self::Scalar> {
cellindexarray_to_multipolygon(self).concave_hull(concavity)
fn concave_hull(&self) -> Polygon<Self::Scalar> {
cellindexarray_to_multipolygon(self).concave_hull()
}

fn concave_hull_with_options(
&self,
concave_hull_options: ConcaveHullOptions<Self::Scalar>,
) -> Polygon<Self::Scalar> {
cellindexarray_to_multipolygon(self).concave_hull_with_options(concave_hull_options)
}
}

impl ConcaveHull for VertexIndexArray {
type Scalar = f64;

fn concave_hull(&self, concavity: Self::Scalar) -> Polygon<Self::Scalar> {
vertexindexarray_to_multipoint(self).concave_hull(concavity)
fn concave_hull(&self) -> Polygon<Self::Scalar> {
vertexindexarray_to_multipoint(self).concave_hull()
}

fn concave_hull_with_options(
&self,
concave_hull_options: ConcaveHullOptions<Self::Scalar>,
) -> Polygon<Self::Scalar> {
vertexindexarray_to_multipoint(self).concave_hull_with_options(concave_hull_options)
}
}

impl ConcaveHull for DirectedEdgeIndexArray {
type Scalar = f64;

fn concave_hull(&self, concavity: Self::Scalar) -> Polygon<Self::Scalar> {
directededgeindexarray_to_multipoint(self).concave_hull(concavity)
fn concave_hull(&self) -> Polygon<Self::Scalar> {
directededgeindexarray_to_multipoint(self).concave_hull()
}

fn concave_hull_with_options(
&self,
concave_hull_options: ConcaveHullOptions<Self::Scalar>,
) -> Polygon<Self::Scalar> {
directededgeindexarray_to_multipoint(self).concave_hull_with_options(concave_hull_options)
}
}
4 changes: 2 additions & 2 deletions crates/h3arrow/src/algorithm/localij.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::array::{CellIndexArray, H3ArrayBuilder};
use crate::error::Error;
use arrow::array::Int32Array;
use h3o::{CellIndex, CoordIJ, LocalIJ};
use std::iter::repeat;
use std::iter::repeat_n;

pub struct LocalIJArrays {
pub anchors: CellIndexArray,
Expand Down Expand Up @@ -97,7 +97,7 @@ impl ToLocalIJOp for CellIndexArray {
anchor: CellIndex,
set_failing_to_invalid: bool,
) -> Result<LocalIJArrays, Error> {
let anchors = CellIndexArray::from_iter(repeat(anchor).take(self.len()));
let anchors = CellIndexArray::from_iter(repeat_n(anchor, self.len()));
self.to_local_ij_array(anchors, set_failing_to_invalid)
}

Expand Down
12 changes: 7 additions & 5 deletions crates/h3arrow/src/algorithm/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use nom::branch::alt;
use nom::bytes::complete::{tag, take_while, take_while_m_n};
use nom::combinator::map_res;
use nom::number::complete::double;
use nom::IResult;
use nom::{IResult, Parser};

use crate::error::Error;

Expand Down Expand Up @@ -67,13 +67,15 @@ fn is_whitespace(c: char) -> bool {
}

fn seperator(s: &str) -> IResult<&str, &str> {
alt((tag(","), (tag(";"))))(s)
alt((tag(","), (tag(";")))).parse(s)
}

fn u8_str(s: &str) -> IResult<&str, u8> {
map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |u8s| {
u8::from_str(u8s)
})(s)
map_res(
take_while_m_n(1, 2, |c: char| c.is_ascii_digit()),
|u8s: &str| u8::from_str(u8s),
)
.parse(s)
}

fn parse_coordinate_and_resolution(s: &str) -> IResult<&str, (Coord, u8)> {
Expand Down
8 changes: 3 additions & 5 deletions crates/h3arrow/src/array/from_geo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,9 @@ pub fn geometry_to_cells(
cells.sort_unstable();
cells.dedup();

let cells = if options.compact {
CellIndex::compact(cells)?.collect()
} else {
cells
};
if options.compact {
CellIndex::compact(&mut cells)?
}
Ok(cells)
}

Expand Down
44 changes: 29 additions & 15 deletions crates/h3arrow/src/array/from_geoarrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use crate::array::from_geo::geometry_to_cells;
use crate::array::{CellIndexArray, H3ListArray};
use crate::error::Error;
use arrow::array::OffsetSizeTrait;
use geo_traits::to_geo::ToGeoGeometry;
use geo_types::Geometry;
use geoarrow::array::WKBArray;
use geoarrow::trait_::ArrayAccessor;
use geoarrow::ArrayBase;
use geoarrow::array::{GenericWkbArray, GeometryArray};
use geoarrow_array::{GeoArrowArray, GeoArrowArrayAccessor};
use h3o::CellIndex;
#[cfg(feature = "rayon")]
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
Expand All @@ -22,17 +22,29 @@ macro_rules! impl_to_cells {
&self,
options: &ToCellsOptions,
) -> Result<H3ListArray<CellIndex, O>, Error> {
self.iter_geo()
.map(|v| v.map(Geometry::from))
.to_celllistarray(options)
let geom_array: GeometryArray = self.clone().into();
let geoms: Vec<Option<Geometry>> = geom_array
.iter()
.map(|opt| match opt {
None => Ok(None),
Some(r) => Ok(r.map_err(Error::from)?.try_to_geometry()),
})
.collect::<Result<_, Error>>()?;
geoms.into_iter().to_celllistarray(options)
}
}

impl ToCellIndexArray for $array_type {
fn to_cellindexarray(&self, options: &ToCellsOptions) -> Result<CellIndexArray, Error> {
self.iter_geo()
.map(|v| v.map(Geometry::from))
.to_cellindexarray(options)
let geom_array: GeometryArray = self.clone().into();
let geoms: Vec<Option<Geometry>> = geom_array
.iter()
.map(|opt| match opt {
None => Ok(None),
Some(r) => Ok(r.map_err(Error::from)?.try_to_geometry()),
})
.collect::<Result<_, Error>>()?;
geoms.into_iter().to_cellindexarray(options)
}
}
};
Expand All @@ -45,7 +57,7 @@ impl_to_cells!(geoarrow::array::MultiPolygonArray);
impl_to_cells!(geoarrow::array::PointArray);
impl_to_cells!(geoarrow::array::PolygonArray);

impl<O: OffsetSizeTrait> ToCellListArray<O> for WKBArray<O> {
impl<O: OffsetSizeTrait> ToCellListArray<O> for GenericWkbArray<O> {
fn to_celllistarray(
&self,
options: &ToCellsOptions,
Expand All @@ -57,18 +69,20 @@ impl<O: OffsetSizeTrait> ToCellListArray<O> for WKBArray<O> {
let pos_iter = (0..self.len()).into_par_iter();

let cell_vecs = pos_iter
.map(|pos| {
self.get_as_geo(pos)
.map(|geom| geometry_to_cells(&geom, options))
.transpose()
.map(|pos| match self.get(pos).map_err(Error::from)? {
None => Ok(None),
Some(wkb) => match wkb.try_to_geometry() {
Some(geom) => geometry_to_cells(&geom, options).map(Some),
None => Ok(None),
},
})
.collect::<Result<Vec<_>, _>>()?;

cell_vecs_to_h3listarray(cell_vecs)
}
}

impl<O: OffsetSizeTrait> ToCellIndexArray for WKBArray<O> {
impl<O: OffsetSizeTrait> ToCellIndexArray for GenericWkbArray<O> {
fn to_cellindexarray(&self, options: &ToCellsOptions) -> Result<CellIndexArray, Error> {
let cellindexarray = self.to_celllistarray(options)?.into_flattened()?;

Expand Down
2 changes: 1 addition & 1 deletion crates/h3arrow/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ where

/// Returns an iterator over the values and validity as Option.
#[allow(clippy::type_complexity)]
pub fn iter(&self) -> PrimitiveArrayH3IndexIter<IX> {
pub fn iter(&self) -> PrimitiveArrayH3IndexIter<'_, IX> {
// as the array contents have been validated upon construction, we just transmute to the h3o type
PrimitiveArrayH3IndexIter {
primitive_array_iter: self.primitive_array.iter(),
Expand Down
14 changes: 5 additions & 9 deletions crates/h3arrow/src/array/to_geo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::array::{
use crate::error::Error;
use geo::{CoordsIter, ToRadians};
use geo_types::{Coord, Line, LineString, MultiPoint, MultiPolygon, Point, Polygon};
use h3o::geom::SolventBuilder;
use h3o::{CellIndex, DirectedEdgeIndex, LatLng, VertexIndex};
use std::convert::Infallible;
use std::iter::{repeat, Map, Repeat, Zip};
Expand Down Expand Up @@ -202,14 +203,8 @@ impl ToMultiPolygons for H3ListArray<CellIndex> {
fn to_multipolygons(&self, use_degrees: bool) -> Result<Self::Output, Self::Error> {
self.iter_arrays()
.map(|opt| {
opt.map(|res| {
res.and_then(|array| {
array
.to_multipolygons(use_degrees)
.map_err(Self::Error::from)
})
})
.transpose()
opt.map(|res| res.and_then(|array| array.to_multipolygons(use_degrees)))
.transpose()
})
.collect()
}
Expand All @@ -220,7 +215,8 @@ impl ToMultiPolygons for CellIndexArray {
type Output = MultiPolygon;

fn to_multipolygons(&self, use_degrees: bool) -> Result<Self::Output, Self::Error> {
let mut multi_polygons = h3o::geom::dissolve(self.iter().flatten())?;
let solvent = SolventBuilder::new().build();
let mut multi_polygons = solvent.dissolve(self.iter().flatten())?;
if !use_degrees {
multi_polygons.to_radians_in_place();
}
Expand Down
Loading
Loading