# Path: roadmaptools/printer.py
# def print_info(info, file=sys.stdout, end="\n"):
#     print("[{0}]: {1}".format(datetime.now().strftime('%H:%M:%S'), info), file=file, end=end)
# 
# Path: roadmaptools/road_structures.py
# class LinestringEdge:
# 
# 	def __init__(self, geojson_linestring: geojson.geometry.LineString,
# 				 coordinate_convertor: Callable[[float, float], Tuple[float, float]], node_from: Node, node_to: Node):
# 		self.geojson_linestring = geojson_linestring
# 		self.linestring = roadmaptools.geojson_shp.geojson_linestring_to_shp_linestring(geojson_linestring,
# 																							coordinate_convertor)
# 		self.node_from = node_from
# 		self.node_to = node_to
# 		self.id = get_edge_id(node_from.id, node_to.id)
# 
# 	def __eq__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id == o.id
# 		return False
# 
# 	def __ne__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id != o.id
# 		return False
# 
# 	def __hash__(self) -> int:
# 		return hash(self.id)
# 
# class Node:
# 	def __init__(self, x: float, y: float, id: int):
# 		self.id = id
# 		self.x = x
# 		self.y = y
# 		self._point = None
# 
# 	def get_point(self) -> Point:
# 		if not self._point:
# 			self._point = Point(self.x, self.y)
# 
# 		return self._point
# 
# 	def __eq__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id == o.id
# 		return False
# 
# 	def __ne__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id != o.id
# 		return False
# 
# 	def __hash__(self) -> int:
# 		return hash(self.id)
# 
# Path: roadmaptools/utm.py
# class CoordinateConvertor:
#     projection = default_projection
# 
#     @staticmethod
#     def convert(latitude: float, longitude: float) -> Tuple[float, float]:
#         return wgs84_to_utm(latitude, longitude, CoordinateConvertor.projection)


import os.path
import networkx.algorithms.shortest_paths
import roadmaptools.utm
import roadmaptools.geometry
import roadmaptools.shp
import roadmaptools.inout
import roadmaptools.plotting
from typing import Dict, Union, Optional, List, Callable, Tuple
from networkx import DiGraph
from shapely.geometry import Point
from scipy.spatial.kdtree import KDTree
from geojson import FeatureCollection
from tqdm import tqdm
from roadmaptools.printer import print_info
from roadmaptools.road_structures import LinestringEdge, Node
from roadmaptools.utm import CoordinateConvertor
class RoadGraph:
    def __init__(self, use_cache: bool = True, cache_filepath: str = "",
                 node_creator: Callable[[float, float, int], Node] = _create_node):
        self.use_cache = use_cache
        self.cache_filepath = cache_filepath
        self.node_creator = node_creator
        self.graph: DiGraph = None
        self.kdtree: KDTree = None
        self.projection = None
        self.node_map: Dict[int, Node] = {}

    def load_from_geojson(self, geojson_filepath: str):
        if self.use_cache and os.path.exists(self.cache_filepath):
            self.graph = networkx.read_gpickle(self.cache_filepath)
            self.projection = roadmaptools.utm.TransposedUTM.from_zone(self.graph.graph["zone_number"],
                                                                       self.graph.graph["zone_letter"])
            CoordinateConvertor.projection = self.projection

            print_info("Projection loaded from the cache: {}{}".format(
                self.projection.origin_zone_number, self.projection.origin_zone_letter))
        else:
            geojson = roadmaptools.inout.load_geojson(geojson_filepath)

            # projection determination
            for item in geojson['features']:
                if item["geometry"]["type"] == "LineString":
                    first_coord = geojson['features'][0]['geometry']['coordinates'][0]
                    break
                    
            self.projection = roadmaptools.utm.TransposedUTM.from_gps(first_coord[1], first_coord[0])

# Path: roadmaptools/printer.py
# def print_info(info, file=sys.stdout, end="\n"):
#     print("[{0}]: {1}".format(datetime.now().strftime('%H:%M:%S'), info), file=file, end=end)
# 
# Path: roadmaptools/road_structures.py
# class LinestringEdge:
# 
# 	def __init__(self, geojson_linestring: geojson.geometry.LineString,
# 				 coordinate_convertor: Callable[[float, float], Tuple[float, float]], node_from: Node, node_to: Node):
# 		self.geojson_linestring = geojson_linestring
# 		self.linestring = roadmaptools.geojson_shp.geojson_linestring_to_shp_linestring(geojson_linestring,
# 																							coordinate_convertor)
# 		self.node_from = node_from
# 		self.node_to = node_to
# 		self.id = get_edge_id(node_from.id, node_to.id)
# 
# 	def __eq__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id == o.id
# 		return False
# 
# 	def __ne__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id != o.id
# 		return False
# 
# 	def __hash__(self) -> int:
# 		return hash(self.id)
# 
# class Node:
# 	def __init__(self, x: float, y: float, id: int):
# 		self.id = id
# 		self.x = x
# 		self.y = y
# 		self._point = None
# 
# 	def get_point(self) -> Point:
# 		if not self._point:
# 			self._point = Point(self.x, self.y)
# 
# 		return self._point
# 
# 	def __eq__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id == o.id
# 		return False
# 
# 	def __ne__(self, o: object) -> bool:
# 		if isinstance(o, self.__class__):
# 			return self.id != o.id
# 		return False
# 
# 	def __hash__(self) -> int:
# 		return hash(self.id)
# 
# Path: roadmaptools/utm.py
# class CoordinateConvertor:
#     projection = default_projection
# 
#     @staticmethod
#     def convert(latitude: float, longitude: float) -> Tuple[float, float]:
#         return wgs84_to_utm(latitude, longitude, CoordinateConvertor.projection)


# Path: roadmaptools/graph.py
import os.path
import networkx.algorithms.shortest_paths
import roadmaptools.utm
import roadmaptools.geometry
import roadmaptools.shp
import roadmaptools.inout
import roadmaptools.plotting
from typing import Dict, Union, Optional, List, Callable, Tuple
from networkx import DiGraph
from shapely.geometry import Point
from scipy.spatial.kdtree import KDTree
from geojson import FeatureCollection
from tqdm import tqdm
from roadmaptools.printer import print_info
from roadmaptools.road_structures import LinestringEdge, Node
from roadmaptools.utm import CoordinateConvertor

class RoadGraph:
    def __init__(self, use_cache: bool = True, cache_filepath: str = "",
                 node_creator: Callable[[float, float, int], Node] = _create_node):
        self.use_cache = use_cache
        self.cache_filepath = cache_filepath
        self.node_creator = node_creator
        self.graph: DiGraph = None
        self.kdtree: KDTree = None
        self.projection = None
        self.node_map: Dict[int, Node] = {}

    def load_from_geojson(self, geojson_filepath: str):
        if self.use_cache and os.path.exists(self.cache_filepath):
            self.graph = networkx.read_gpickle(self.cache_filepath)
            self.projection = roadmaptools.utm.TransposedUTM.from_zone(self.graph.graph["zone_number"],
                                                                       self.graph.graph["zone_letter"])
            CoordinateConvertor.projection = self.projection

            print_info("Projection loaded from the cache: {}{}".format(
                self.projection.origin_zone_number, self.projection.origin_zone_letter))
        else:
            geojson = roadmaptools.inout.load_geojson(geojson_filepath)

            # projection determination
            for item in geojson['features']:
                if item["geometry"]["type"] == "LineString":
                    first_coord = geojson['features'][0]['geometry']['coordinates'][0]
                    break
                    
            self.projection = roadmaptools.utm.TransposedUTM.from_gps(first_coord[1], first_coord[0])

            print_info("Projection determined from the first coordinate: {}{}".format(