///|
let topology_epsilon : Double = 1.0e-12
///|
priv enum RingLocation {
Outside
Inside
Boundary
} derive(Eq)
///|
fn topology_abs(value : Double) -> Double {
if value < 0.0 {
-value
} else {
value
}
}
///|
fn topology_equal_xy(left : Coordinate, right : Coordinate) -> Bool {
topology_abs(left.x - right.x) <= topology_epsilon &&
topology_abs(left.y - right.y) <= topology_epsilon
}
///|
fn topology_cross(
start : Coordinate,
first : Coordinate,
second : Coordinate,
) -> Double {
(first.x - start.x) * (second.y - start.y) -
(first.y - start.y) * (second.x - start.x)
}
///|
fn topology_between(value : Double, low : Double, high : Double) -> Bool {
value >= (if low < high { low } else { high }) - topology_epsilon &&
value <= (if low > high { low } else { high }) + topology_epsilon
}
///|
fn topology_point_on_segment(
point : Coordinate,
start : Coordinate,
end : Coordinate,
) -> Bool {
if topology_abs(topology_cross(start, end, point)) > topology_epsilon {
return false
}
topology_between(point.x, start.x, end.x) &&
topology_between(point.y, start.y, end.y)
}
///|
fn topology_segments_intersect(
a : Coordinate,
b : Coordinate,
c : Coordinate,
d : Coordinate,
) -> Bool {
let o1 = topology_cross(a, b, c)
let o2 = topology_cross(a, b, d)
let o3 = topology_cross(c, d, a)
let o4 = topology_cross(c, d, b)
if (
(o1 > topology_epsilon && o2 < -topology_epsilon) ||
(o1 < -topology_epsilon && o2 > topology_epsilon)
) &&
(
(o3 > topology_epsilon && o4 < -topology_epsilon) ||
(o3 < -topology_epsilon && o4 > topology_epsilon)
) {
return true
}
(topology_abs(o1) <= topology_epsilon && topology_point_on_segment(c, a, b)) ||
(topology_abs(o2) <= topology_epsilon && topology_point_on_segment(d, a, b)) ||
(topology_abs(o3) <= topology_epsilon && topology_point_on_segment(a, c, d)) ||
(topology_abs(o4) <= topology_epsilon && topology_point_on_segment(b, c, d))
}
///|
fn topology_ring_is_simple(
ring : Array[Coordinate],
ring_index : Int32,
path : String,
) -> Result[Unit, GeoError] {
let edges = ring.length() - 1
for index in 0.. Result[Unit, GeoError] {
if line.length() == 0 {
return Ok(())
}
let edges = line.length() - 1
for index in 0.. RingLocation {
let mut inside = false
let end = ring.length() - 1
for index in 0.. point.y) != (end.y > point.y)
if crosses {
let intersection_x = start.x +
(point.y - start.y) * (end.x - start.x) / (end.y - start.y)
if point.x < intersection_x {
inside = !inside
}
}
}
if inside {
Inside
} else {
Outside
}
}
///|
fn topology_rings_intersect(
left : Array[Coordinate],
right : Array[Coordinate],
) -> Bool {
let left_end = left.length() - 1
let right_end = right.length() - 1
for left_index in 0.. RingLocation {
if polygon.length() == 0 {
return Outside
}
match topology_point_in_ring(point, polygon[0]) {
Outside => Outside
Boundary => Boundary
Inside => {
for index in 1.. return Outside
Boundary => return Boundary
Outside => ()
}
}
Inside
}
}
}
///|
fn validate_polygon_topology(
rings : Array[Array[Coordinate]],
path : String,
) -> Result[Unit, GeoError] {
if rings.length() == 0 {
return Ok(())
}
match topology_ring_is_simple(rings[0], 0, path) {
Ok(_) => ()
Err(err) => return Err(err)
}
for index in 1.. ()
Err(err) => return Err(err)
}
if topology_rings_intersect(rings[0], hole) {
return Err(InvalidBoundary(path + " hole intersects shell", index))
}
match topology_point_in_ring(hole[0], rings[0]) {
Inside => ()
_ => return Err(InvalidBoundary(path + " hole is outside shell", index))
}
for other in 1.. Result[Unit, GeoError] {
for index, polygon in polygons {
let polygon_path = path + ".polygons[" + index.to_string() + "]"
match validate_polygon_topology(polygon, polygon_path) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
for left in 0.. 0 && right_polygon.length() > 0 {
if topology_point_in_polygon(left_polygon[0][0], right_polygon) ==
Inside ||
topology_point_in_polygon(right_polygon[0][0], left_polygon) == Inside {
return Err(
InvalidGeometry(
path +
".polygons[" +
left.to_string() +
"] overlaps polygons[" +
right.to_string() +
"]",
),
)
}
}
}
}
Ok(())
}
///|
fn validate_geometry_topology_at(
geometry : Geometry,
path : String,
) -> Result[Unit, GeoError] {
match geometry {
Point(_) => Ok(())
EmptyPoint => Ok(())
LineString(line) => topology_line_is_simple(line, path)
Polygon(rings) => validate_polygon_topology(rings, path)
MultiPoint(_) => Ok(())
MultiLineString(lines) => {
for index, line in lines {
let line_path = path + ".lines[" + index.to_string() + "]"
match topology_line_is_simple(line, line_path) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
Ok(())
}
MultiPolygon(polygons) => validate_multi_polygon_topology(polygons, path)
GeometryCollection(geometries) => {
for index, child in geometries {
let child_path = path + ".geometries[" + index.to_string() + "]"
match validate_geometry_topology_at(child, child_path) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
Ok(())
}
}
}
///|
pub fn validate_geometry_topology(
geometry : Geometry,
) -> Result[Unit, GeoError] {
match validate_geometry(geometry) {
Ok(_) => ()
Err(err) => return Err(err)
}
validate_geometry_topology_at(geometry, "geometry")
}