Source code for gmaps_avoid_swiss.geo_check

import json
from importlib import resources
from typing import Union

from google.maps.routing_v2 import Polyline
from polyline import polyline
from shapely import MultiLineString
from shapely.geometry import shape, Point, Polygon, LineString


[docs]class GeographicChecker: INNSBRUCK = {"lat": 47.2692, "lng": 11.4041} LION = {"lat": 45.7640, "lng": 4.8357} BEAUNE = {"lat": 47.0260, "lng": 4.8400} def __init__(self): """ Initialize the GeographicChecker with a predefined polygon of Switzerland. """ self.swiss_geojson = "switzerland.geojson" # Define the boundary of Switzerland using a geojson. self.swiss_polygon = self._load_switzerland_boundary() self.cities = []
[docs] def is_point_in_swiss(self, lat: float, lng: float) -> bool: """ Check if a geographic point is within Switzerland. :param lat: Latitude of the point. :type lat: float :param lng: Longitude of the point. :type lng: float :return: True if the point is within the polygon of Switzerland, False otherwise. :rtype: bool """ point = Point(lng, lat) return self.swiss_polygon.contains(point)
[docs] def does_route_cross_swiss(self, path: Polyline) -> LineString: """ Determine whether a route, defined by a polyline (encoded string or GeoJSON), crosses Switzerland. :param path: Polyline data which could be an encoded string or a GeoJSON object. :type path: Polyline :return: True if the route crosses through Switzerland, False otherwise. :rtype: bool """ try: points = polyline.decode(path.encoded_polyline, geojson=True) except AttributeError: raise ValueError("Missing or invalid 'encoded_polyline' attribute.") except Exception as e: raise ValueError(f"Error decoding polyline: {str(e)}") route_linestring = LineString(points) return self.swiss_polygon.intersection(route_linestring)
[docs] def sort_cities_by_distance(self, intersection: Union[LineString, MultiLineString]): """ Sort the cities based on their total distance to the start and end points of the intersection with Switzerland. :param intersection: The LineString representing the intersection of the route with Switzerland. :type intersection: LineString """ self.cities.clear() if isinstance(intersection, LineString): start = Point(intersection.coords[0]) # Start of LineString end = Point(intersection.coords[-1]) # End of LineString elif isinstance(intersection, MultiLineString): start = Point(intersection.geoms[0].coords[0]) # First point of the first LineString end = Point(intersection.geoms[-1].coords[-1]) # Last point of the last LineString else: raise TypeError("Unsupported geometry type for intersection") for city in [self.INNSBRUCK, self.LION, self.BEAUNE]: city_point = Point(city['lng'], city['lat']) distance_to_start = start.distance(city_point) distance_from_end = end.distance(city_point) total_distance = distance_to_start + distance_from_end self.cities.append({'city': city, 'dist': total_distance}) # Sort cities by the total distance self.cities.sort(reverse=True, key=lambda x: x['dist'])
def _load_switzerland_boundary(self): """ Load the boundary of Switzerland from a GeoJSON file. :return: A Shapely Polygon object representing the boundary of Switzerland. :rtype: Polygon :raises FileNotFoundError: If the GeoJSON file could not be found. :raises json.JSONDecodeError: If there is an error decoding the GeoJSON. :raises RuntimeError: If there is an unexpected error during the file loading or parsing. """ try: with resources.open_text('gmaps_avoid_swiss.data', self.swiss_geojson) as file: data = json.load(file) feature = data['features'][0] polygon = shape(feature['geometry']) return polygon except FileNotFoundError: raise FileNotFoundError("The GeoJSON file could not be found.") except json.JSONDecodeError: raise except Exception as e: raise RuntimeError(f"Failed to load or parse the GeoJSON file: {e}")