uphill-rivers/processing/river.py

109 lines
3.1 KiB
Python
Raw Normal View History

2024-05-06 14:00:17 +00:00
import statistics
import overpy
import utils
import time
CorrectSlope = bool
SlopeConfidence = float
class RiverTooShort(Exception):
pass
class River:
POOR_CONFIDENCE = 0.1
MEDIUM_CONFIDENCE = 0.5
HIGH_CONFIDENCE = 0.9
id: int
way: overpy.Way
is_correct_slope: bool
correct_slope_confidence: float
start_ele: float
end_ele: float
def __init__(self, wayid, way):
self.wayid = wayid
self.way = way
@property
def name(self):
return self.way.tags.get('name', None)
def osm_link(self):
return f'OSM LINK: https://www.openstreetmap.org/way/{self.wayid}'
def __repr__(self):
return f"River {self.name} {self.wayid}"
@property
def length(self) -> float:
return utils.distance(self.way.nodes[0], self.way.nodes[-1])
def to_dict(self):
return {
'name': self.name,
'wayid': self.wayid,
'nodes': [(float(pt.lat), float(pt.lon)) for pt in self.way.nodes],
'length': self.length,
'start_ele': utils.get_elevation(self.way.nodes[0].lat, self.way.nodes[0].lon),
'end_ele': utils.get_elevation(self.way.nodes[1].lat, self.way.nodes[1].lon),
'is_correct_slope': self.is_correct_slope,
'correct_slope_confidence': self.correct_slope_confidence,
'centroid': [float(self.centroid[0]), float(self.centroid[1])],
'timestap': int(time.time()),
}
@property
def centroid(self) -> tuple[float, float]:
lats = [pt.lat for pt in self.way.nodes]
lons = [pt.lon for pt in self.way.nodes]
return (statistics.mean(lats), statistics.mean(lons))
def check_slope(self):
self.is_correct_slope, self.correct_slope_confidence = self._calculate_slope()
def _calculate_slope(self) -> tuple[CorrectSlope, SlopeConfidence]:
start_pts = self.way.nodes[0:5]
end_pts = self.way.nodes[-5:]
start_eles = [utils.get_elevation(pt.lat, pt.lon) for pt in start_pts]
end_eles = [utils.get_elevation(pt.lat, pt.lon) for pt in end_pts]
start_median = statistics.median(start_eles)
self.start_ele = start_median
end_median = statistics.median(end_eles)
self.end_ele = end_median
length = self.length
if length < 1:
raise RiverTooShort()
slope = (end_median - start_median) / length
absele = abs(end_median - start_median)
if absele < 10:
# 10 meters difference between start and end... too risky
return (start_median >= end_median, self.POOR_CONFIDENCE)
if absele > 300:
# 300 meters difference. High confidence
return (start_median > end_median, self.HIGH_CONFIDENCE)
if slope < -0.02:
return (True, self.MEDIUM_CONFIDENCE)
if slope < 0:
return (True, self.POOR_CONFIDENCE)
if slope > 0.03:
return (False, self.MEDIUM_CONFIDENCE)
if slope >= 0:
return (False, self.POOR_CONFIDENCE)
# we should never reach this point
1/0
return (False, self.POOR_CONFIDENCE)