Crates.io | geo-validity-check |
lib.rs | geo-validity-check |
version | 0.1.0 |
source | src |
created_at | 2023-04-11 15:19:49.649121 |
updated_at | 2023-04-11 15:19:49.649121 |
description | Expose a 'Valid' trait to check the validity of a geo-types geometry and report cause(s) of invalidity. |
homepage | https://github.com/mthh/geo-validity-check |
repository | https://github.com/mthh/geo-validity-check |
max_upload_size | |
id | 836119 |
size | 100,072 |
Expose a Valid
trait to check if rust geo-types geometries are valid.
Valid
trait has the following signature:
trait Valid {
fn is_valid(&self) -> bool;
fn explain_invalidity(&self) -> Option<ProblemReport>;
}
The result of the invalidity reason is provided in a ProblemReport
struct (it contains a Vec
of (Problem, ProblemPosition)
,
two enums that respectively represent the type of problem and the position of the problem in the tested geometry - having
this machine-readable information could be useful to try to fix the geometry).
This ProblemReport
result can also be formatted as a string as it implements the Display
trait.
Coord
and Point
use finite numbersMultiPoint
is made of valid pointsRect
, Line
and Triangle
are made of valid coordsTriangle
are not empty or degenerate (i.e. all points are different and not collinear)Line
is not of 0-length (i.e. both points are not the same)LineString
is made of valid pointsLineString
is not emptyLineString
has at least two different pointsMultiLineString
is made of valid linestringsPolygon
rings are made of valid pointsPolygon
rings have at least 4 points (including the closing point)Polygon
interior rings are contained in the exterior ring (but can touch it on a point)Polygon
interior rings don't cross each other (but can touch on a point)MultiPolygon
components don't cross each other (but can touch on a point)MultiPolygon
is made of valid polygonsGeometryCollection
is made of valid geometriesVerification is done against GEOS (any geometry invalid according to GEOS should be invalid according to this crate - the inverse doesn't have to be true since we are doing some extra check).
use geo_validity_check::Valid;
use geo_types::{Point, LineString, Polygon, MultiPolygon};
let line1 = LineString::from(vec![(0., 0.), (1., 1.)]);
let line2 = LineString::from(vec![(0., 0.), (0., 0.)]);
let line3 = LineString::from(vec![(0., 0.), (f64::NAN, f64::NAN), (1., 1.)]);
assert!(line1.is_valid());
assert!(!line2.is_valid());
assert!(!line3.is_valid());
println!("{}", line2.invalidity_reason().unwrap()); // "LineString has too few points at coordinate 0 of the LineString"
println!("{}", line3.invalidity_reason().unwrap()); // "Coordinate is not finite (NaN or infinite) at coordinate 1 of the LineString"
let polygon = Polygon::new(
LineString::from(vec![(0.5, 0.5), (3., 0.5), (3., 2.5), (0.5, 2.5), (0.5, 0.5)]),
vec![LineString::from(vec![(1., 1.), (1., 2.), (2.5, 2.), (3.5, 1.), (1., 1.)])],
);
assert!(!polygon.is_valid());
println!("{}", polygon.invalidity_reason().unwrap()); // "The interior ring of a Polygon is not contained in the exterior ring on the interior ring n°0"
let multipolygon = MultiPolygon(vec![
Polygon::new(
LineString::from(vec![(0.5, 0.5), (3., 0.5), (3., 2.5), (0.5, 2.5), (0.5, 0.5)]),
vec![LineString::from(vec![(1., 1.), (1., 2.), (2.5, 2.), (3.5, 1.), (1., 1.)])],
),
Polygon::new(
LineString::from(vec![(0.5, 0.5), (3., 0.5), (3., 2.5), (0.5, 2.5), (0.5, 0.5)]),
vec![LineString::from(vec![(1., 1.), (1., 2.), (2.5, 2.), (3.5, 1.), (1., 1.)])],
),
]);
assert!(!multipolygon.is_valid());
println!("{}", multipolygon.invalidity_reason().unwrap());
// "The interior ring of a Polygon is not contained in the exterior ring on the interior ring n°0 of the Polygon n°0 of the MultiPolygon
// Two Polygons of MultiPolygons are identical on the exterior ring of the Polygon n°0 of the MultiPolygon
// The interior ring of a Polygon is not contained in the exterior ring on the interior ring n°0 of the Polygon n°1 of the MultiPolygon
// Two Polygons of MultiPolygons are identical on the exterior ring of the Polygon n°1 of the MultiPolygon"
Improve the description of the invalidity reason (e.g. "Interior ring 0 intersects the exterior ring" could be "Interior ring 0 intersects the exterior ring at point (1.5, 1.5)")
Add a make_valid
or fix_invalidity
method to try to fix the geometry (e.g. by removing the invalid points ?)
Return the first invalidity reason found (instead of all of them) in invalidity_reason
method ? (because some other checks could fail because of the first invalidity reason)
Implement a rule that states that a Polygon
is valid if the polygon interior is simply connected (i.e. the rings must not touch in a way that splits the polygon into more than one part) ?
Licensed under either of
at your option.