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)