109 lines
3.1 KiB
Python
109 lines
3.1 KiB
Python
|
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)
|