Warning
Note that validation has been added directly to geo in december 2024 (see #1279)
on the basis of this crate, plus improvements by the geo maintainers.
You can now find a Validation trait in the geo crate that provides the same functionality as this crate.
I therefore do not plan to maintain this crate anymore and this repository is now archived.
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.
-
CoordandPointuse finite numbers -
MultiPointis made of valid points -
Rect,LineandTriangleare made of valid coords -
Triangleare not empty or degenerate (i.e. all points are different and not collinear) -
Lineis not of 0-length (i.e. both points are not the same) -
LineStringis made of valid points -
LineStringis not empty -
LineStringhas at least two different points -
MultiLineStringis made of valid linestrings -
Polygonrings are made of valid points -
Polygonrings have at least 4 points (including the closing point) -
Polygoninterior rings are contained in the exterior ring (but can touch it on a point) -
Polygoninterior rings don't cross each other (but can touch on a point) -
MultiPolygoncomponents don't cross each other (but can touch on a point) -
MultiPolygonis made of valid polygons -
GeometryCollectionis made of valid geometries
Verification 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_validorfix_invaliditymethod 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_reasonmethod ? (because some other checks could fail because of the first invalidity reason) -
Implement a rule that states that a
Polygonis 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
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.