Skip to content

Commit

Permalink
Merge pull request #172 from NREL/ndr/memory-efficiency
Browse files Browse the repository at this point in the history
New IGraphMap
  • Loading branch information
nreinicke committed May 8, 2023
2 parents cf2a285 + b1755fa commit 00532b4
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 19 deletions.
Empty file.
421 changes: 421 additions & 0 deletions mappymatch/maps/igraph/igraph_map.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mappymatch/maps/map_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def shortest_path(
self,
origin: Coordinate,
destination: Coordinate,
weight: Union[str, Callable] = DEFAULT_TIME_WEIGHT,
weight: Optional[Union[str, Callable]] = None,
) -> List[Road]:
"""
Computes the shortest path on the road network
Expand Down
48 changes: 30 additions & 18 deletions mappymatch/maps/nx/nx_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,20 @@ def __init__(self, graph: nx.MultiDiGraph):

self._build_rtree()

def _has_road_id(self, road_id: RoadId) -> bool:
return self.g.has_edge(*road_id)

def _build_road(
self,
origin_id: Union[str, int],
destination_id: Union[str, int],
key: Union[str, int],
edge_data: Dict[str, Any],
road_id: RoadId,
) -> Road:
"""
Build a road from an origin, destination and networkx edge data
Build a road from a road id, pulling the edge data from the graph
Be sure to check if the road id (_has_road_id) is in the graph before calling this method
"""
edge_data = self.g.get_edge_data(*road_id)

metadata = edge_data.get(self._metadata_key)

if metadata is None:
Expand All @@ -94,8 +98,6 @@ def _build_road(
metadata[self._dist_weight] = edge_data.get(self._dist_weight)
metadata[self._time_weight] = edge_data.get(self._time_weight)

road_id = RoadId(origin_id, destination_id, key)

road = Road(
road_id,
edge_data[self._geom_key],
Expand All @@ -105,21 +107,21 @@ def _build_road(
return road

def _build_rtree(self):
road_lookup = {}

idx = rt.index.Index()
for i, gtuple in enumerate(self.g.edges(data=True, keys=True)):
u, v, k, d = gtuple
road = self._build_road(u, v, k, d)
road_id = RoadId(u, v, k)
# road = self._build_road(u, v, k, d)
geom = d[self._geom_key]
box = geom.bounds

idx.insert(i, box, obj=road.road_id)
idx.insert(i, box, obj=road_id)

road_lookup[road.road_id] = road
# road_lookup[road.road_id] = road

self.rtree = idx
self._road_lookup: Dict[RoadId, Road] = road_lookup
# self._road_lookup: Dict[RoadId, Road] = road_lookup

def __str__(self):
output_lines = [
Expand Down Expand Up @@ -150,7 +152,10 @@ def road_by_id(self, road_id: RoadId) -> Optional[Road]:
Returns:
The road with the given id, or None if it does not exist
"""
return self._road_lookup.get(road_id)
if self._has_road_id(road_id):
return self._build_road(road_id)
else:
return None

def set_road_attributes(self, attributes: Dict[RoadId, Dict[str, Any]]):
"""
Expand All @@ -167,7 +172,11 @@ def set_road_attributes(self, attributes: Dict[RoadId, Dict[str, Any]]):

@property
def roads(self) -> List[Road]:
return list(self._road_lookup.values())
roads = [
self._build_road(RoadId(u, v, k))
for u, v, k in self.g.edges(keys=True)
]
return roads

@classmethod
def from_file(cls, file: Union[str, Path]) -> NxMap:
Expand Down Expand Up @@ -305,20 +314,20 @@ def nearest_road(self, coord: Coordinate, buffer: float = 10.0) -> Road:
nearest_id = nearest_candidates[0]
else:
distances = [
self._road_lookup[i].geom.distance(coord.geom)
self._build_road(i).geom.distance(coord.geom)
for i in nearest_candidates
]
nearest_id = nearest_candidates[np.argmin(distances)]

road = self._road_lookup[nearest_id]
road = self._build_road(nearest_id)

return road

def shortest_path(
self,
origin: Coordinate,
destination: Coordinate,
weight: Union[str, Callable] = DEFAULT_TIME_WEIGHT,
weight: Optional[Union[str, Callable]] = None,
) -> List[Road]:
"""
Computes the shortest path between an origin and a destination
Expand All @@ -340,6 +349,9 @@ def shortest_path(
f"crs of destination {destination.crs} must match crs of map {self.crs}"
)

if weight is None:
weight = self._time_weight

origin_road = self.nearest_road(origin)
dest_road = self.nearest_road(destination)

Expand Down Expand Up @@ -382,7 +394,7 @@ def shortest_path(

road_id = RoadId(road_start_node, road_end_node, road_key)

road = self._road_lookup[road_id]
road = self._build_road(road_id)

path.append(road)

Expand Down
3 changes: 3 additions & 0 deletions mappymatch/maps/nx/readers/osm_readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def compress(g) -> nx.MultiDiGraph:
"speed_kph",
"osmid",
"street_count",
"junction",
"bridge",
"tunnel",
"y",
"x",
]
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies = [
"matplotlib",
"osmnx",
"networkx<3",
"igraph",
"folium",
"requests",
"polyline",
Expand Down
13 changes: 13 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# pip-compile requirements-dev.in
#

-e file:.
# via
# -c requirements-tests.txt
Expand Down Expand Up @@ -109,6 +110,10 @@ idna==3.3
# -c requirements-tests.txt
# -c requirements.txt
# requests
igraph==0.10.4
# via
# -c requirements.txt
# mappymatch
importlib-metadata==4.12.0
# via
# keyring
Expand Down Expand Up @@ -224,6 +229,10 @@ pluggy==1.0.0
# via
# -c requirements-tests.txt
# pytest
polyline==2.0.0
# via
# -c requirements.txt
# mappymatch
pre-commit==2.19.0
# via mappymatch
py==1.11.0
Expand Down Expand Up @@ -310,6 +319,10 @@ six==1.16.0
# virtualenv
tabulate==0.8.10
# via interrogate
texttable==1.6.7
# via
# -c requirements.txt
# igraph
toml==0.10.2
# via
# interrogate
Expand Down
13 changes: 13 additions & 0 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# pip-compile requirements-tests.in
#

-e file:.
# via -r requirements-tests.in
attrs==21.4.0
Expand Down Expand Up @@ -65,6 +66,10 @@ idna==3.3
# via
# -c requirements.txt
# requests
igraph==0.10.4
# via
# -c requirements.txt
# mappymatch
iniconfig==1.1.1
# via pytest
jinja2==3.1.2
Expand Down Expand Up @@ -134,6 +139,10 @@ platformdirs==2.5.2
# via black
pluggy==1.0.0
# via pytest
polyline==2.0.0
# via
# -c requirements.txt
# mappymatch
py==1.11.0
# via pytest
pyparsing==3.0.9
Expand Down Expand Up @@ -182,6 +191,10 @@ six==1.16.0
# -c requirements.txt
# fiona
# python-dateutil
texttable==1.6.7
# via
# -c requirements.txt
# igraph
tomli==2.0.1
# via
# black
Expand Down
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ geopandas==0.11.0
# osmnx
idna==3.3
# via requests
igraph==0.10.4
# via mappymatch (setup.py)
jinja2==3.1.2
# via
# branca
Expand Down Expand Up @@ -74,6 +76,8 @@ pandas==1.4.3
# osmnx
pillow==9.2.0
# via matplotlib
polyline==2.0.0
# via mappymatch (setup.py)
pyparsing==3.0.9
# via
# matplotlib
Expand Down Expand Up @@ -107,6 +111,8 @@ six==1.16.0
# via
# fiona
# python-dateutil
texttable==1.6.7
# via igraph
urllib3==1.26.9
# via requests

Expand Down

0 comments on commit 00532b4

Please sign in to comment.