Repository navigation
fix(analysis): keep polygon simplification monotonic at large tolerances #911
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1439,10 +1439,26 @@ fn publication(s: &mut Session, p: &Value) -> CmdResult { | |
| // Each polygon simplified to a tenth of a millimetre on the disc (its scale in metres), | ||
| // so the project keeps a sketch of the layers for it, not a second full copy of them. | ||
| let tolerance_m = globe * 1e-4; | ||
| // A polygon too small for that (an island) would collapse to a ring of under four | ||
| // points: it keeps its own shape, already small. | ||
| // On the locator, sub-tolerance rings keep their outline rather than becoming triangles. | ||
| let sketch = |g: &rustgis_data::Geometry| { | ||
| let simple = rustgis_analysis::simplify_geometry(g, tolerance_m); | ||
| let mut simple = rustgis_analysis::simplify_geometry(g, tolerance_m); | ||
| if let (rustgis_data::Geometry::Polygon(original), rustgis_data::Geometry::Polygon(reduced)) = (g, &mut simple) { | ||
| for (original, reduced) in original.iter().flatten().zip(reduced.iter_mut().flatten()) { | ||
| let mut extent = Extent::EMPTY; | ||
| // Keep the extent continuous across the antimeridian. | ||
| let mut longitude = original.iter().find(|c| c.is_finite()).map_or(0.0, |c| c.x); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Low-confidence concern: this zips the flattened rings of |
||
| for &c in original.iter().filter(|c| c.is_finite()) { | ||
| longitude += (c.x - longitude + 180.0).rem_euclid(360.0) - 180.0; | ||
| extent.expand(Coord::new(longitude, c.y)); | ||
| } | ||
| let projected = rustgis_geom::extent_to_merc(&extent); | ||
| let size_m = | ||
| projected.width().hypot(projected.height()) * extent.center().y.clamp(-85.0, 85.0).to_radians().cos().max(0.01); | ||
| if size_m < tolerance_m { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| *reduced = original.clone(); | ||
| } | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ring size is computed with an ad-hoc formula. Suggestion: compare the ring in the same units as The zip over |
||
| match &simple { | ||
| rustgis_data::Geometry::Polygon(polys) if polys.iter().flatten().all(|ring| ring.len() >= 4) => simple, | ||
| _ => g.clone(), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplify_ringpins three anchor vertices (first, farthest, max-area) in every ring. This guarantees at least a triangle, but it changes Douglas–Peucker semantics. Even a tolerance far larger than a ring leaves a triangle, and the anchors are kept even when they are within tolerance of the chord. Holes also collapse to triangles and can now cross the exterior or each other, which the docs acknowledge. Withmax_byand ties, the anchor choice is deterministic but arbitrary.Edge case:
ring.0.len() <= 4returns early, so rings with exactly 5 points, such as a closed quadrilateral, are simplified, but unclosed rings (first != last) are not special-cased. Thelen() - 1stop assumes closure. Input is probably closed byto_multipolygon, so this is low risk. Confidence: low.The
simplifytool previously reported 0 vertices for geometries that failed conversion and wrote aNonegeometry. It now clones the original (viasimplify_geometry's fallback). That seems better, but it is an observable behaviour change worth noting in the PR.