Skip to content

Commit 31c0a65

Browse files
committed
geom: rework the geo interface
Get rid of the ToCells and ToGeo trait. The API was quite bad tbh, even if it wasn't too visible on the surface. It was the result of me rushing the last mile to push out the 0.1 version. For example, we are always passing a `PolyfillConfig` but it only makes sense for Polygon and MultiPolygon... The worst offender here is the ugly hack that sweeps under the rug any errors generated by `Line`/`LineString`/`MultiLineString`, returning empty set of cells in that case. It was documented to give me good conscience, but let's be honnest here: nobody ever saw it (trait docs are not easily discoverable) and it's still a broken/flawed API to begin with... They also made evolution harder. Like it would make sense to have multithread implementation of some algorithm for `Polygon`, but retrofitting a trait that also handles `Point` is kinda ugly Of course, I could add another trait, but then I may tear down the whole things since I'm at it. So, let's revisit the bridging between H3 cell indexes and geometry types. - `Point` and `MultiPoint` can be handled by the caller by going through the `LatLng` type. - `Line`, `LineString` and `MultiLineString` are now convertible to set of cells using the `Plotter` object, which doesn't hide the fact that it's a faillible operation even on valid geometries (due to current implementation limitation) - `Polygon` and `MultiPolygon` are now convertible to set of cells using the `Tiler` object. - `Geometry` and `GeometryCollection` are no longer supported since it requires some trade-off that should be left upon the caller and not enforced by the library. - `GeoJSON` support is also gone, it was always a bit out of place to begin with (why this format and not others? the true reason is that made debugging easier during the initial development and I left it here after the release) Remove `geoJSON` support for output as well. The trait was also cumbersome to use (though not as bad as ToCells`) due to the mix of faillible and infallible implementation. Now the infallible versions are implemented as `From` and the complex case (set of cell indexes) has a dedicated interface through the new `Dissolver` entity. All those changes are a first step toward the stabilization of the API and the release of the 1.0 at some point.
1 parent e8bd43a commit 31c0a65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1536
-3774
lines changed

CHANGELOG.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,28 @@ Possible sections are:
1414
<!-- next-header -->
1515
## [Unreleased] - ReleaseDate
1616

17+
### Added
18+
19+
- `h3o::geom::Plotter` to compute the cells along lines.
20+
- `h3o::geom::Tiler` to compute the cell coverage of plane figures.
21+
1722
### Changed
1823

19-
- Performance enhancement for aarch64, should not affect other platforms
20-
- Error types now derive `Error`, even in no-std mode.
24+
- small performance enhancement for aarch64, should not affect other platforms
25+
- error types now derive `Error`, even in no-std mode.
26+
27+
### Removed
28+
29+
- remove the geometry types wrapper (you can now use `geo` types directly).
30+
- remove the `ToCells` trait, now you can use:
31+
- `h3o::LatLng` for `Point` & `MultiPoint`.
32+
- `h3o::geom::Plotter` for `Line`, `LineString` and `MultiLineString`.
33+
- `h3o::geom::Tiler` for `Polygon`, `MultiPolygon`, `Rect` and `Triangle`.
34+
- `Geometry` and `GeometryCollection` are not directly supported
35+
- remove the support for `geoJSON`.
36+
- remove the `ToGeo` trait, now you can use:
37+
- `From` trait for `CellIndex`, `DirectedEdgeIndex` and `VertexIndex`
38+
` `geom::dissolve` for a set of cell indexes.
2139

2240
## [0.6.4] - 2024-05-10
2341

Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "h3o"
3-
version = "0.6.4"
3+
version = "0.7.0"
44
authors = ["Sylvain Laperche <[email protected]>"]
55
edition = "2021"
66
description = "A Rust implementation of the H3 geospatial indexing system."
@@ -26,7 +26,7 @@ pre-release-replacements = [
2626
[features]
2727
default = ["std"]
2828
std = ["dep:ahash"]
29-
geo = ["dep:geo", "dep:geojson"]
29+
geo = ["dep:geo"]
3030
serde = ["dep:serde", "dep:serde_repr"]
3131
tools = ["polyfit-rs"]
3232
typed_floats = ["dep:typed_floats"]
@@ -37,7 +37,6 @@ arbitrary = { version = "1.0", optional = true, default-features = false }
3737
either = { version = "1.0", default-features = false }
3838
float_eq = { version = "1.0", default-features = false }
3939
geo = { version = "0.29", optional = true, default-features = false }
40-
geojson = { version = "0.24", optional = true, default-features = false, features = ["geo-types"] }
4140
h3o-bit = { version = "0.1", default-features = false }
4241
libm = { version = "0.2", default-features = false }
4342
polyfit-rs = { version = "0.2", optional = true, default-features = false }
@@ -48,6 +47,7 @@ typed_floats = { version = "1.0", optional = true, default-features = false }
4847
[dev-dependencies]
4948
approx = { version = "0.5", default-features = false }
5049
criterion = { version = "0.5", default-features = false, features = ["plotters", "cargo_bench_support", "html_reports"] }
50+
geojson = { version = "0.24", default-features = false, features = ["geo-types"] }
5151
h3ron-h3-sys = { version = "0.17", default-features = false }
5252

5353
[lib]

benches/h3/h3_set_lo_linked_geo.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ahash::HashSet;
33
use criterion::{
44
black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion,
55
};
6-
use h3o::{geom::ToGeo, CellIndex};
6+
use h3o::CellIndex;
77
use std::os::raw::c_int;
88

99
pub fn bench_full(c: &mut Criterion) {
@@ -75,7 +75,7 @@ fn bench_h3o<T>(
7575
T: Measurement,
7676
{
7777
group.bench_with_input(BenchmarkId::new(name, k), &k, |b, _k| {
78-
b.iter(|| black_box(indexes.iter().copied().to_geom(false)))
78+
b.iter(|| h3o::geom::dissolve(black_box(indexes.iter().copied())))
7979
});
8080
}
8181

benches/h3/max_polygon_to_cells_size.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
use super::utils::load_polygon;
22
use criterion::{black_box, Criterion};
3-
use h3o::{
4-
geom::{PolyfillConfig, ToCells},
5-
Resolution,
6-
};
3+
use h3o::{geom::TilerBuilder, Resolution};
74
use std::os::raw::c_int;
85

96
const RESOLUTION: Resolution = Resolution::Nine;
107

118
pub fn bench(c: &mut Criterion) {
129
let mut group = c.benchmark_group("maxPolygonToCellsSize");
1310
let polygon = load_polygon("Paris");
14-
let config = PolyfillConfig::new(RESOLUTION);
1511

1612
group.bench_function("h3o", |b| {
17-
b.iter(|| black_box(&polygon).max_cells_count(black_box(config)))
13+
let mut tiler = TilerBuilder::new(RESOLUTION).build();
14+
tiler.add(polygon.clone()).expect("valid polygon");
15+
b.iter(|| black_box(&tiler).coverage_size_hint())
1816
});
1917
group.bench_function("h3", |b| {
20-
let mut coords = geo::Polygon::from(polygon.clone())
18+
let mut coords = polygon
2119
.exterior()
2220
.coords()
2321
.map(|coord| h3ron_h3_sys::LatLng {

benches/h3/polygon_to_cells.rs

+91-67
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use super::utils::load_polygon;
22
use criterion::{black_box, BatchSize, Bencher, BenchmarkId, Criterion};
3+
use geo::Polygon;
34
use h3o::{
4-
geom::{ContainmentMode, PolyfillConfig, Polygon, ToCells},
5+
geom::{ContainmentMode, TilerBuilder},
56
Resolution,
67
};
78
use std::os::raw::c_int;
@@ -50,88 +51,110 @@ pub fn bench_transmeridian(c: &mut Criterion) {
5051

5152
pub fn bench_polyfill_mode(c: &mut Criterion) {
5253
let mut group = c.benchmark_group("polyfillMode");
53-
let config = PolyfillConfig::new(Resolution::Eleven);
5454

5555
let polygon = load_polygon("Paris");
5656
group.bench_function("h3o/Centroid/Full", |b| {
57-
let config = config.containment_mode(ContainmentMode::ContainsCentroid);
58-
b.iter(|| {
59-
black_box(&polygon)
60-
.to_cells(black_box(config))
61-
.for_each(drop)
62-
})
57+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
58+
.containment_mode(ContainmentMode::ContainsCentroid)
59+
.build();
60+
tiler.add(polygon.clone()).expect("valid polygon");
61+
b.iter_batched(
62+
|| tiler.clone(),
63+
|tiler| black_box(tiler).into_coverage().for_each(drop),
64+
BatchSize::SmallInput,
65+
)
6366
});
6467
group.bench_function("h3o/Intersects/Full", |b| {
65-
let config =
66-
config.containment_mode(ContainmentMode::IntersectsBoundary);
67-
b.iter(|| {
68-
black_box(&polygon)
69-
.to_cells(black_box(config))
70-
.for_each(drop)
71-
})
68+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
69+
.containment_mode(ContainmentMode::IntersectsBoundary)
70+
.build();
71+
tiler.add(polygon.clone()).expect("valid polygon");
72+
b.iter_batched(
73+
|| tiler.clone(),
74+
|tiler| black_box(tiler).into_coverage().for_each(drop),
75+
BatchSize::SmallInput,
76+
)
7277
});
7378
group.bench_function("h3o/Contains/Full", |b| {
74-
let config = config.containment_mode(ContainmentMode::ContainsBoundary);
75-
b.iter(|| {
76-
black_box(&polygon)
77-
.to_cells(black_box(config))
78-
.for_each(drop)
79-
})
79+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
80+
.containment_mode(ContainmentMode::ContainsBoundary)
81+
.build();
82+
tiler.add(polygon.clone()).expect("valid polygon");
83+
b.iter_batched(
84+
|| tiler.clone(),
85+
|tiler| black_box(tiler).into_coverage().for_each(drop),
86+
BatchSize::SmallInput,
87+
)
8088
});
8189

8290
let polygon = load_polygon("Rabi");
8391
group.bench_function("h3o/Centroid/Transmeridian", |b| {
84-
let config = config.containment_mode(ContainmentMode::ContainsCentroid);
85-
b.iter(|| {
86-
black_box(&polygon)
87-
.to_cells(black_box(config))
88-
.for_each(drop)
89-
})
92+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
93+
.containment_mode(ContainmentMode::ContainsCentroid)
94+
.build();
95+
tiler.add(polygon.clone()).expect("valid polygon");
96+
b.iter_batched(
97+
|| tiler.clone(),
98+
|tiler| black_box(tiler).into_coverage().for_each(drop),
99+
BatchSize::SmallInput,
100+
)
90101
});
91102
group.bench_function("h3o/Intersects/Transmeridian", |b| {
92-
let config =
93-
config.containment_mode(ContainmentMode::IntersectsBoundary);
94-
b.iter(|| {
95-
black_box(&polygon)
96-
.to_cells(black_box(config))
97-
.for_each(drop)
98-
})
103+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
104+
.containment_mode(ContainmentMode::IntersectsBoundary)
105+
.build();
106+
tiler.add(polygon.clone()).expect("valid polygon");
107+
b.iter_batched(
108+
|| tiler.clone(),
109+
|tiler| black_box(tiler).into_coverage().for_each(drop),
110+
BatchSize::SmallInput,
111+
)
99112
});
100113
group.bench_function("h3o/Contains/Transmeridian", |b| {
101-
let config = config.containment_mode(ContainmentMode::ContainsBoundary);
102-
b.iter(|| {
103-
black_box(&polygon)
104-
.to_cells(black_box(config))
105-
.for_each(drop)
106-
})
114+
let mut tiler = TilerBuilder::new(Resolution::Eleven)
115+
.containment_mode(ContainmentMode::ContainsBoundary)
116+
.build();
117+
tiler.add(polygon.clone()).expect("valid polygon");
118+
b.iter_batched(
119+
|| tiler.clone(),
120+
|tiler| black_box(tiler).into_coverage().for_each(drop),
121+
BatchSize::SmallInput,
122+
)
107123
});
108124

109-
let config = PolyfillConfig::new(Resolution::Seven);
110125
let polygon = load_polygon("Holes");
111126
group.bench_function("h3o/Centroid/Holes", |b| {
112-
let config = config.containment_mode(ContainmentMode::ContainsCentroid);
113-
b.iter(|| {
114-
black_box(&polygon)
115-
.to_cells(black_box(config))
116-
.for_each(drop)
117-
})
127+
let mut tiler = TilerBuilder::new(Resolution::Seven)
128+
.containment_mode(ContainmentMode::ContainsCentroid)
129+
.build();
130+
tiler.add(polygon.clone()).expect("valid polygon");
131+
b.iter_batched(
132+
|| tiler.clone(),
133+
|tiler| black_box(tiler).into_coverage().for_each(drop),
134+
BatchSize::SmallInput,
135+
)
118136
});
119137
group.bench_function("h3o/Intersects/Holes", |b| {
120-
let config =
121-
config.containment_mode(ContainmentMode::IntersectsBoundary);
122-
b.iter(|| {
123-
black_box(&polygon)
124-
.to_cells(black_box(config))
125-
.for_each(drop)
126-
})
138+
let mut tiler = TilerBuilder::new(Resolution::Seven)
139+
.containment_mode(ContainmentMode::IntersectsBoundary)
140+
.build();
141+
tiler.add(polygon.clone()).expect("valid polygon");
142+
b.iter_batched(
143+
|| tiler.clone(),
144+
|tiler| black_box(tiler).into_coverage().for_each(drop),
145+
BatchSize::SmallInput,
146+
)
127147
});
128148
group.bench_function("h3o/Contains/Holes", |b| {
129-
let config = config.containment_mode(ContainmentMode::ContainsBoundary);
130-
b.iter(|| {
131-
black_box(&polygon)
132-
.to_cells(black_box(config))
133-
.for_each(drop)
134-
})
149+
let mut tiler = TilerBuilder::new(Resolution::Seven)
150+
.containment_mode(ContainmentMode::ContainsBoundary)
151+
.build();
152+
tiler.add(polygon.clone()).expect("valid polygon");
153+
b.iter_batched(
154+
|| tiler.clone(),
155+
|tiler| black_box(tiler).into_coverage().for_each(drop),
156+
BatchSize::SmallInput,
157+
)
135158
});
136159

137160
group.finish();
@@ -141,17 +164,18 @@ pub fn bench_polyfill_mode(c: &mut Criterion) {
141164

142165
fn bench_h3o(b: &mut Bencher<'_>, polygon: &Polygon, resolution: u8) {
143166
let resolution = Resolution::try_from(resolution).expect("resolution");
144-
let config = PolyfillConfig::new(resolution);
167+
let mut tiler = TilerBuilder::new(resolution).build();
168+
tiler.add(polygon.clone()).expect("valid polygon");
145169

146-
b.iter(|| {
147-
black_box(polygon)
148-
.to_cells(black_box(config))
149-
.for_each(drop)
150-
});
170+
b.iter_batched(
171+
|| tiler.clone(),
172+
|tiler| black_box(tiler).into_coverage().for_each(drop),
173+
BatchSize::SmallInput,
174+
)
151175
}
152176

153177
fn bench_h3(b: &mut Bencher<'_>, polygon: &Polygon, resolution: u8) {
154-
let mut coords = geo::Polygon::from(polygon.clone())
178+
let mut coords = polygon
155179
.exterior()
156180
.coords()
157181
.map(|coord| h3ron_h3_sys::LatLng {

benches/h3/utils.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use geo::{Geometry, Polygon};
12
use h3o::CellIndex;
23
use std::{
34
fs::File,
@@ -22,15 +23,15 @@ pub fn load_cells(resolution: u32) -> Vec<CellIndex> {
2223
.collect()
2324
}
2425

25-
pub fn load_polygon(name: &str) -> h3o::geom::Polygon {
26+
pub fn load_polygon(name: &str) -> Polygon {
2627
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
27-
let filepath = format!("dataset/{name}/shape.geojson");
28+
let filepath = format!("dataset/shapes/{name}.geojson");
2829
path.push(filepath);
2930

3031
let file = File::open(path).expect("open test dataset");
3132
let reader = BufReader::new(file);
3233

3334
let geojson = geojson::GeoJson::from_reader(reader).expect("GeoJSON");
34-
let geometry = h3o::geom::Geometry::try_from(&geojson).expect("geometry");
35-
h3o::geom::Polygon::try_from(geometry).expect("polygon")
35+
let geometry = Geometry::try_from(geojson).expect("geometry");
36+
Polygon::try_from(geometry).expect("polygon")
3637
}

dataset/shapes/Empty.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-122.40898669968212,37.81331899988944],[-122.40898669968784,37.81331899988944],[-122.40898669969356,37.81331899988944],[-122.40898669968212,37.81331899988944]]],"type":"Polygon"}

dataset/shapes/HalfWorld_1.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-180,-90],[-180,90],[0,90],[0,-90],[-180,-90]]],"type":"Polygon"}

dataset/shapes/HalfWorld_2.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[0,-90],[0,90],[180,90],[180,-90],[0,-90]]],"type":"Polygon"}
File renamed without changes.
File renamed without changes.

dataset/shapes/PrimeMeridian.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"Polygon","coordinates":[[[0.5729577951308232,0.5729577951308232],[-0.5729577951308232,0.5729577951308232],[-0.5729577951308232,-0.5729577951308232],[0.5729577951308232,-0.5729577951308232],[0.5729577951308232,0.5729577951308232]]]}
File renamed without changes.

dataset/shapes/SanFrancisco.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-122.40898669969356,37.81331899988944],[-122.38054369969613,37.78663019990699],[-122.35447369969584,37.719806199904276],[-122.51234369969448,37.70761319990403],[-122.52471869969825,37.783587199903444],[-122.47987669969707,37.81515719990604],[-122.40898669969356,37.81331899988944]]],"type":"Polygon"}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"Polygon","coordinates":[[[-122.40898669969356,37.81331899988944],[-122.38054369969613,37.78663019990699],[-122.35447369969584,37.719806199904276],[-122.51234369969448,37.70761319990403],[-122.52471869969825,37.783587199903444],[-122.47987669969707,37.81515719990604],[-122.40898669969356,37.81331899988944]],[[-122.44711969969569,37.786980199908015],[-122.45907769969834,37.76641019990431],[-122.41370969969519,37.77106819990672],[-122.44711969969569,37.786980199908015]]]}

dataset/shapes/Transmeridian.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"Polygon","coordinates":[[[-179.4270422048692,0.5729577951308232],[179.4270422048692,0.5729577951308232],[179.4270422048692,-0.5729577951308232],[-179.4270422048692,-0.5729577951308232],[-179.4270422048692,0.5729577951308232]]]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"Polygon","coordinates":[[[-179.99942704220487,5.729577951308233],[179.99942704220487,5.729577951308233],[168.5408440973835,2.8647889756541165],[179.99942704220487,-5.729577951308233],[-179.99942704220487,-5.729577951308233],[-168.5408440973835,-2.8647889756541165],[-179.99942704220487,5.729577951308233]]]}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"Polygon","coordinates":[[[-179.4270422048692,0.5729577951308232],[179.4270422048692,0.5729577951308232],[179.4270422048692,-0.5729577951308232],[-179.4270422048692,-0.5729577951308232],[-179.4270422048692,0.5729577951308232]],[[-179.71352110243458,0.2864788975654116],[179.71352110243458,0.2864788975654116],[179.71352110243458,-0.2864788975654116],[-179.71352110243458,-0.2864788975654116],[-179.71352110243458,0.2864788975654116]]]}

dataset/shapes/h3_issue136.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[51.1122595579185,5.76910652153309],[51.0844285870957,5.74843667987113],[51.0841156106137,5.74868565718131],[51.112087787177096,5.76939661923247],[51.1122595579185,5.76910652153309]]],"type":"Polygon"}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[11.147991,64.758157],[11.443969,64.758157],[11.443969,64.845147],[11.147991,64.845147],[11.147991,64.758157]]],"type":"Polygon"}

dataset/shapes/h3js_issue67_1.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-56.25,-33.13755119234615],[-56.25,-34.30714385628804],[-57.65625,-34.30714385628804],[-57.65625,-33.13755119234615],[-56.25,-33.13755119234615]]],"type":"Polygon"}

dataset/shapes/h3js_issue67_2.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-57.65625,-34.30714385628804],[-57.65625,-35.4606699514953],[-59.0625,-35.4606699514953],[-59.0625,-34.30714385628804],[-57.65625,-34.30714385628804]]],"type":"Polygon"}

dataset/shapes/h3o_issue21.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[156.0,6.0],[156.0,3.0],[159.0,3.0],[159.0,6.0],[156.0,6.0]]],"type":"Polygon"}

dataset/shapes/h3o_issue23.geojson

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[0.0,5.0],[5.0,5.0],[5.0,0.0],[-179.0,-90.0],[0.0,5.0]]],"type":"Polygon"}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-141.001,61.895],[-140.996,61.895],[-140.996,64.848],[-141.001,64.848],[-141.001,61.895]]],"type":"Polygon"}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"coordinates":[[[-141.001,61.895],[-140.996,61.895],[-140.996,65.848],[-141.001,65.848],[-141.001,61.895]]],"type":"Polygon"}

0 commit comments

Comments
 (0)