Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion a5_rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ name = "a5_rust"
crate-type = ["staticlib"]

[dependencies]
a5 = "0.8.0"
a5 = "0.9.0"
31 changes: 27 additions & 4 deletions a5_rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,35 @@ pub extern "C" fn a5_line_string_to_cells(points: *const LonLatDegrees, len: usi
cell_vec_result_to_c(a5::line_string_to_cells(&lonlats, resolution))
}

// GeoJSON-style polygon: ring 0 is the outer ring, rings 1.. are holes. The
// rings are passed flattened into a single `points` buffer, with `ring_lengths`
// giving the vertex count of each ring (so the offsets can be reconstructed).
// Holes are excluded by the a5 crate itself - the caller does no hole handling.
#[no_mangle]
pub extern "C" fn a5_polygon_to_cells(points: *const LonLatDegrees, len: usize, resolution: i32) -> CellArray {
if points.is_null() || len == 0 {
pub extern "C" fn a5_polygon_to_cells(
points: *const LonLatDegrees,
ring_lengths: *const usize,
ring_count: usize,
resolution: i32,
) -> CellArray {
if points.is_null() || ring_lengths.is_null() || ring_count == 0 {
return CellArray { data: std::ptr::null_mut(), len: 0, error: std::ptr::null_mut() };
}
let lonlats = lonlat_slice_to_vec(points, len);
cell_vec_result_to_c(a5::polygon_to_cells(&lonlats, resolution))
let lengths = unsafe { std::slice::from_raw_parts(ring_lengths, ring_count) };
let total: usize = lengths.iter().sum();
let flat = unsafe { std::slice::from_raw_parts(points, total) };

let mut rings: Vec<Vec<a5::LonLat>> = Vec::with_capacity(ring_count);
let mut offset = 0;
for &len in lengths {
rings.push(
flat[offset..offset + len]
.iter()
.map(|p| a5::LonLat::new(p.lon, p.lat))
.collect(),
);
offset += len;
}
cell_vec_result_to_c(a5::polygon_to_cells(&rings, resolution))
}

74 changes: 17 additions & 57 deletions src/a5_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,74 +641,34 @@ static vector<LonLatDegrees> WkbReadRing(WkbCursor &cur, idx_t dims) {

// Fill a polygon (outer ring minus any holes) into the accumulator.
//
// A5's polygon_to_cells returns a *compacted* (mixed-resolution) covering, so the outer
// covering and a hole's covering generally share no cell IDs and cannot be differenced
// directly. To subtract holes we uncompact both to the target resolution, take the set
// difference at that uniform resolution, then re-compact for output. The (common) no-hole
// case skips all of this and passes the crate's compacted covering through unchanged.
// Holes are excluded by the a5 crate itself: we flatten all rings (outer first, then
// holes) into a single point buffer plus a per-ring length array and hand them to
// a5_polygon_to_cells, which returns the compacted covering of the outer ring with the
// holes already removed. Empty rings are dropped so a degenerate ring never shifts the
// outer-ring-is-first convention.
static void PolygonRingsToCells(const vector<vector<LonLatDegrees>> &rings, int32_t resolution, CellAccumulator &acc,
const char *function_name) {
if (rings.empty() || rings[0].empty()) {
return;
}
auto outer = a5_polygon_to_cells(rings[0].data(), rings[0].size(), resolution);
ThrowCellArrayError(outer, function_name);

bool has_holes = false;
for (size_t r = 1; r < rings.size(); r++) {
if (!rings[r].empty()) {
has_holes = true;
break;
}
}
if (!has_holes) {
for (size_t i = 0; i < outer.len; i++) {
acc.Add(outer.data[i]);
}
a5_free_cell_array(outer);
return;
}

// Expand the outer covering to a uniform resolution.
auto outer_uniform = a5_uncompact(outer.data, outer.len, resolution);
ThrowCellArrayError(outer_uniform, function_name);
a5_free_cell_array(outer);

// Collect the uniform-resolution cells of every hole.
std::unordered_set<uint64_t> holes;
for (size_t r = 1; r < rings.size(); r++) {
if (rings[r].empty()) {
vector<LonLatDegrees> points;
vector<uintptr_t> ring_lengths;
ring_lengths.reserve(rings.size());
for (const auto &ring : rings) {
if (ring.empty()) {
continue;
}
auto hole = a5_polygon_to_cells(rings[r].data(), rings[r].size(), resolution);
ThrowCellArrayError(hole, function_name);
auto hole_uniform = a5_uncompact(hole.data, hole.len, resolution);
ThrowCellArrayError(hole_uniform, function_name);
a5_free_cell_array(hole);
for (size_t i = 0; i < hole_uniform.len; i++) {
holes.insert(hole_uniform.data[i]);
}
a5_free_cell_array(hole_uniform);
ring_lengths.push_back(ring.size());
points.insert(points.end(), ring.begin(), ring.end());
}

// Difference, then re-compact so the output matches the no-hole convention.
vector<uint64_t> kept;
kept.reserve(outer_uniform.len);
for (size_t i = 0; i < outer_uniform.len; i++) {
if (holes.find(outer_uniform.data[i]) == holes.end()) {
kept.push_back(outer_uniform.data[i]);
}
}
a5_free_cell_array(outer_uniform);
if (kept.empty()) {
return;
}
auto compacted = a5_compact(kept.data(), kept.size());
ThrowCellArrayError(compacted, function_name);
for (size_t i = 0; i < compacted.len; i++) {
acc.Add(compacted.data[i]);
auto cells = a5_polygon_to_cells(points.data(), ring_lengths.data(), ring_lengths.size(), resolution);
ThrowCellArrayError(cells, function_name);
for (size_t i = 0; i < cells.len; i++) {
acc.Add(cells.data[i]);
}
a5_free_cell_array(compacted);
a5_free_cell_array(cells);
}

// Recursively read a (possibly multi-part) geometry and accumulate its A5 cells.
Expand Down
3 changes: 2 additions & 1 deletion src/include/rust.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ ResultU64 a5_spherical_to_cell(double theta, double phi, int32_t resolution);

CellArray a5_line_string_to_cells(const LonLatDegrees *points, uintptr_t len, int32_t resolution);

CellArray a5_polygon_to_cells(const LonLatDegrees *points, uintptr_t len, int32_t resolution);
CellArray a5_polygon_to_cells(const LonLatDegrees *points, const uintptr_t *ring_lengths, uintptr_t ring_count,
int32_t resolution);

} // extern "C"
Loading