Updated SMHI API and improved the polygon contains logic
This commit is contained in:
+22
-58
@@ -1,6 +1,7 @@
|
||||
# tools/geometry.py
|
||||
|
||||
from dataclasses import dataclass
|
||||
import math
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -68,8 +69,8 @@ class LineString:
|
||||
return min(line.distance_to_point(point) for line in self.lineList)
|
||||
|
||||
def is_intersecting(self, point: Point) -> bool:
|
||||
"""Checks if a given point lies exactly on the LineString."""
|
||||
return self.distance_to(point) == 0.0
|
||||
"""Checks if a given point lies exactly on the LineString (with tolerance)."""
|
||||
return math.isclose(self.distance_to(point), 0.0, abs_tol=1e-9)
|
||||
|
||||
|
||||
class Polygon:
|
||||
@@ -78,72 +79,35 @@ class Polygon:
|
||||
def __init__(self, points: list[list[float]]) -> None:
|
||||
self.listPoints: list[Point] = [Point(p[0], p[1]) for p in points]
|
||||
|
||||
def on_line(self, l1: Line, p: Point) -> bool:
|
||||
"""Checks if collinear point 'p' lies strictly on the line segment 'l1'."""
|
||||
return min(l1.p1.x, l1.p2.x) <= p.x <= max(l1.p1.x, l1.p2.x) and min(
|
||||
l1.p1.y, l1.p2.y
|
||||
) <= p.y <= max(l1.p1.y, l1.p2.y)
|
||||
|
||||
def direction(self, a: Point, b: Point, c: Point) -> int:
|
||||
"""
|
||||
Finds the orientation of an ordered triplet (a, b, c).
|
||||
Returns:
|
||||
0 : Collinear
|
||||
1 : Clockwise
|
||||
2 : Counterclockwise
|
||||
"""
|
||||
val = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y)
|
||||
if val == 0:
|
||||
return 0
|
||||
return 2 if val < 0 else 1
|
||||
|
||||
def is_intersect(self, l1: Line, l2: Line) -> bool:
|
||||
"""Checks if line segment l1 intersects with line segment l2."""
|
||||
dir1 = self.direction(l1.p1, l1.p2, l2.p1)
|
||||
dir2 = self.direction(l1.p1, l1.p2, l2.p2)
|
||||
dir3 = self.direction(l2.p1, l2.p2, l1.p1)
|
||||
dir4 = self.direction(l2.p1, l2.p2, l1.p2)
|
||||
|
||||
# General case intersection
|
||||
if dir1 != dir2 and dir3 != dir4:
|
||||
return True
|
||||
|
||||
# Special collinear cases
|
||||
if dir1 == 0 and self.on_line(l1, l2.p1):
|
||||
return True
|
||||
if dir2 == 0 and self.on_line(l1, l2.p2):
|
||||
return True
|
||||
if dir3 == 0 and self.on_line(l2, l1.p1):
|
||||
return True
|
||||
if dir4 == 0 and self.on_line(l2, l1.p2):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def contains(self, p: Point) -> bool:
|
||||
"""
|
||||
Determines if a point is strictly inside the Polygon using the Ray-Casting algorithm.
|
||||
Draws a horizontal line to the right of the point and counts edge intersections.
|
||||
Determines if a point is inside the Polygon using the Even-Odd rule.
|
||||
Includes an explicit check for points lying exactly on the boundary edges.
|
||||
"""
|
||||
n = len(self.listPoints)
|
||||
if n < 3:
|
||||
return False
|
||||
|
||||
# Create a horizontal ray starting from the point and going infinitely right
|
||||
exline = Line(p, Point(99999.0, p.y))
|
||||
count = 0
|
||||
|
||||
inside = False
|
||||
j = n - 1
|
||||
for i in range(n):
|
||||
side = Line(self.listPoints[i], self.listPoints[(i + 1) % n])
|
||||
pi = self.listPoints[i]
|
||||
pj = self.listPoints[j]
|
||||
|
||||
if self.is_intersect(side, exline):
|
||||
# If the point is collinear with the side, check if it's strictly on the side
|
||||
if self.direction(side.p1, p, side.p2) == 0:
|
||||
return self.on_line(side, p)
|
||||
count += 1
|
||||
cross_product = (p.y - pi.y) * (pj.x - pi.x) - (p.x - pi.x) * (pj.y - pi.y)
|
||||
if math.isclose(cross_product, 0.0, abs_tol=1e-9):
|
||||
if min(pi.x, pj.x) <= p.x <= max(pi.x, pj.x) and min(
|
||||
pi.y, pj.y
|
||||
) <= p.y <= max(pi.y, pj.y):
|
||||
return True
|
||||
|
||||
# If the number of intersections is odd, the point is inside the polygon
|
||||
return bool(count & 1)
|
||||
if ((pi.y > p.y) != (pj.y > p.y)) and (
|
||||
p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x
|
||||
):
|
||||
inside = not inside
|
||||
j = i
|
||||
|
||||
return inside
|
||||
|
||||
|
||||
class MultiPolygon:
|
||||
|
||||
Reference in New Issue
Block a user