#validity #geo-types

geo-validity-check

提供一个 'Valid' trait 以检查 geo-types 几何形状的有效性并报告无效原因

1 个不稳定版本

0.1.0 2023年4月11日

#144 in 地理空间

MIT/Apache

73KB
1.5K SLoC

geo-validity-check

提供一个 Valid trait 来检查 rust geo-types 几何形状是否有效。

Valid trait 的签名如下

trait Valid {
    fn is_valid(&self) -> bool;
    fn explain_invalidity(&self) -> Option<ProblemReport>;
}

无效原因的结果以 ProblemReport 结构体(它包含一个 Vec(Problem, ProblemPosition),两个枚举分别表示问题的类型和问题在测试几何形状中的位置 - 拥有这种机器可读信息可能有助于尝试修复几何形状)。此 ProblemReport 结果还可以格式化为字符串,因为它实现了 Display trait。

实现检查

  • CoordPoint 使用有限数字
  • MultiPoint 由有效点组成
  • RectLineTriangle 由有效坐标组成
  • Triangle 不能为空或退化(即所有点都不同且不共线)
  • Line 的长度不为0(即两个点不相同)
  • LineString 由有效点组成
  • LineString 不能为空
  • LineString 至少有两个不同的点
  • MultiLineString 由有效线字符串组成
  • Polygon 环由有效点组成
  • Polygon 环至少有4个点(包括闭合点)
  • Polygon 内部环包含在外部环中(但可以在一个点上接触它)
  • Polygon 内部环互不交叉(但可以在一个点上接触)
  • MultiPolygon 组件互不交叉(但可以在一个点上接触)
  • MultiPolygon 由有效多边形组成
  • GeometryCollection 由有效几何形状组成

与 GEOS 进行验证(任何根据 GEOS 无效的几何形状都应在本 crate 中无效 - 反之不一定为真,因为我们进行了额外的检查)。

示例

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"

待办事项 / 灵感

  • 改进无效性原因的描述(例如,"内部环0与外部环相交"可以改为"内部环0在点(1.5, 1.5)与外部环相交"

  • 添加一个make_validfix_invalidity方法来尝试修复几何形状(例如,通过移除无效的点?)

  • invalidity_reason方法中返回找到的第一个无效性原因(而不是所有原因)?(因为其他检查可能因为第一个无效性原因而失败)

  • 实现一条规则,该规则规定如果多边形内部是单纯连通的(即,环必须不会以分割多边形成多个部分的方式接触),则Polygon是有效的?

许可证

以下任一许可证下许可:

任选其一。

依赖关系

~3.5MB
~65K SLoC