Source code for openlr_dereferencer.decoding.tools

"Some tooling functions for path and offset handling"

from math import degrees
from typing import List
from logging import debug
from shapely.geometry import LineString, Point
from shapely.ops import substring
from openlr import Coordinates, LocationReferencePoint
from .routes import Route, PointOnLine
from ..maps import Line
from ..maps.wgs84 import interpolate, bearing, line_string_length


[docs]def remove_offsets(path: Route, p_off: float, n_off: float) -> Route: """Remove start+end offsets, measured in meters, from a route and return the result""" debug(f"Will consider positive offset = {p_off} m and negative offset {n_off} m.") lines = path.lines debug(f"This routes consists of {lines} and is {path.length()} m long.") # Remove positive offset debug(f"first line's offset is {path.absolute_start_offset}") remaining_poff = p_off + path.absolute_start_offset while remaining_poff >= lines[0].length: debug(f"Remaining positive offset {remaining_poff} is greater than " f"the first line. Removing it.") remaining_poff -= lines.pop(0).length if not lines: raise LRDecodeError("Offset is bigger than line location path") # Remove negative offset remaining_noff = n_off + path.absolute_end_offset while remaining_noff >= lines[-1].length: debug(f"Remaining negative offset {remaining_noff} is greater than " f"the last line. Removing it.") remaining_noff -= lines.pop().length if not lines: raise LRDecodeError("Offset is bigger than line location path") start_line = lines.pop(0) if lines: end_line = lines.pop() else: end_line = start_line return Route( PointOnLine.from_abs_offset(start_line, remaining_poff), lines, PointOnLine.from_abs_offset(end_line, end_line.length - remaining_noff) )
[docs]class LRDecodeError(Exception): "An error that happens through decoding location references"
[docs]def coords(lrp: LocationReferencePoint) -> Coordinates: "Return the coordinates of an LRP" return Coordinates(lrp.lon, lrp.lat)
[docs]def project(line: Line, coord: Coordinates) -> PointOnLine: """Computes the nearest point to `coord` on the line Returns: The point on `line` where this nearest point resides""" fraction = line.geometry.project(Point(coord.lon, coord.lat), normalized=True) to_projection_point = substring(line.geometry, 0.0, fraction, normalized=True) meters_to_projection_point = line_string_length(to_projection_point) length_fraction = meters_to_projection_point / line.length return PointOnLine(line, length_fraction)
[docs]def linestring_coords(line: LineString) -> List[Coordinates]: "Returns the edges of the line geometry as Coordinate list" return [Coordinates(*point) for point in line.coords]
[docs]def compute_bearing( lrp: LocationReferencePoint, candidate: PointOnLine, is_last_lrp: bool, bear_dist: float ) -> float: "Returns the bearing angle of a partial line in degrees in the range 0.0 .. 360.0" line1, line2 = candidate.split() if is_last_lrp: if line1 is None: return 0.0 coordinates = linestring_coords(line1) coordinates.reverse() relative_offset = 1.0 - candidate.relative_offset else: if line2 is None: return 0.0 coordinates = linestring_coords(line2) relative_offset = candidate.relative_offset absolute_offset = candidate.line.length * relative_offset bearing_point = interpolate(coordinates, absolute_offset + bear_dist) bear = bearing(coordinates[0], bearing_point) return degrees(bear) % 360