Skip to content

Commit

Permalink
Merge pull request #151 from Deltares/feature/104-as-a-cli-user-i-wan…
Browse files Browse the repository at this point in the history
…t-a-clean-graph-from-osm-download

get_clean_graph_from_osm_download function is made without the cleaning functionalities
  • Loading branch information
Carsopre authored Jul 26, 2023
2 parents d3100e0 + 72582fd commit 4747a37
Show file tree
Hide file tree
Showing 11 changed files with 673 additions and 54 deletions.
62 changes: 14 additions & 48 deletions ra2ce/graph/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""


import logging
import os
from typing import Any, List, Tuple

import geopandas as gpd
import networkx as nx
import osmnx
import pandas as pd
import pyproj
from shapely.geometry import MultiLineString

import ra2ce.graph.networks_utils as nut
from ra2ce.graph.osm_network_wrapper.osm_network_wrapper import OsmNetworkWrapper
from ra2ce.graph.segmentation import Segmentation
from ra2ce.io.readers import GraphPickleReader
from ra2ce.io.writers import JsonExporter
Expand Down Expand Up @@ -202,11 +201,15 @@ def network_shp(
# Exporting complex graph because the shapefile should be kept the same as much as possible.
return graph_complex, edges_complex

def _export_linking_tables(self, linking_tables: List[Any]) -> None:
def _export_linking_tables(self, linking_tables: list[Any]) -> None:
_exporter = JsonExporter()
_output_dir = self.config["static"] / "output_graph"
_exporter.export(_output_dir / "simple_to_complex.json", linking_tables[0])
_exporter.export(_output_dir / "complex_to_simple.json", linking_tables[1])
_exporter.export(
_output_dir.joinpath("simple_to_complex.json"), linking_tables[0]
)
_exporter.export(
_output_dir.joinpath("complex_to_simple.json"), linking_tables[1]
)

def network_trails_import(
self, crs: int = 4326
Expand Down Expand Up @@ -288,52 +291,15 @@ def network_trails_import(

return graph_complex, edges_complex

def network_osm_download(self) -> Tuple[nx.classes.graph.Graph, gpd.GeoDataFrame]:
"""Creates a network from a polygon by downloading via the OSM API in the extent of the polygon.
def network_osm_download(self) -> tuple[nx.classes.graph.Graph, gpd.GeoDataFrame]:
"""
Creates a network from a polygon by downloading via the OSM API in the extent of the polygon.
Returns:
graph_simple (NetworkX graph): Simplified graph (for use in the indirect analyses).
complex_edges (GeoDataFrame): Complex graph (for use in the direct analyses).
tuple[nx.classes.graph.Graph, gpd.GeoDataFrame]: Tuple of Simplified graph (for use in the indirect analyses) and Complex graph (for use in the direct analyses).
"""
poly_dict = nut.read_geojson(
self.config["network"]["polygon"][0]
) # It can only read in one geojson
poly = nut.geojson_to_shp(poly_dict)

if not self.config["network"]["road_types"]:
# The user specified only the network type.
graph_complex = osmnx.graph_from_polygon(
polygon=poly,
network_type=self.config["network"]["network_type"],
simplify=False,
retain_all=True,
)
elif not self.config["network"]["network_type"]:
# The user specified only the road types.
cf = '["highway"~"{}"]'.format(
self.config["network"]["road_types"].replace(",", "|")
)
graph_complex = osmnx.graph_from_polygon(
polygon=poly, custom_filter=cf, simplify=False, retain_all=True
)
else:
# The user specified the network type and road types.
cf = '["highway"~"{}"]'.format(
self.config["network"]["road_types"].replace(",", "|")
)
graph_complex = osmnx.graph_from_polygon(
polygon=poly,
network_type=self.config["network"]["network_type"],
custom_filter=cf,
simplify=False,
retain_all=True,
)

logging.info(
"graph downloaded from OSM with {:,} nodes and {:,} edges".format(
len(list(graph_complex.nodes())), len(list(graph_complex.edges()))
)
)
osm_network = OsmNetworkWrapper(self.config, "")
graph_complex = osm_network.get_clean_graph_from_osm()

# Create 'graph_simple'
graph_simple, graph_complex, link_tables = nut.create_simplified_graph(
Expand Down
7 changes: 3 additions & 4 deletions ra2ce/graph/networks_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""


import itertools
import logging
import os
Expand Down Expand Up @@ -1045,7 +1044,7 @@ def graph_check_create_unique_ids(
):

i = 0
for u, v, k in graph.edges(keys=True):
for u, v, k in graph.edges(data=True):
graph[u][v][k][new_id_name] = i
i += 1
logging.info(
Expand Down Expand Up @@ -1080,10 +1079,10 @@ def add_missing_geoms_graph(graph: nx.Graph, geom_name: str = "geometry") -> nx.
graph.nodes[nd][geom_name] = Point(graph.nodes[nd]["x"], graph.nodes[nd]["y"])

edges_without_geom = [
e for e in graph.edges.data(keys=True) if geom_name not in e[-1]
e for e in graph.edges.data(data=True) if geom_name not in e[-1]
]
for ed in edges_without_geom:
graph[ed[0]][ed[1]][ed[2]][geom_name] = LineString(
graph[ed[0]][ed[1]][0][geom_name] = LineString(
[graph.nodes[ed[0]][geom_name], graph.nodes[ed[1]][geom_name]]
)

Expand Down
Empty file.
79 changes: 79 additions & 0 deletions ra2ce/graph/osm_network_wrapper/extremities_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from dataclasses import dataclass

from networkx import MultiDiGraph


@dataclass
class ExtremitiesData:
from_id: int = None
to_id: int = None
from_to_id: tuple = None
to_from_id: tuple = None
from_to_coor: tuple = None
to_from_coor: tuple = None

@staticmethod
def get_extremities_data_for_sub_graph(from_node_id: int, to_node_id: int, sub_graph: MultiDiGraph,
graph: MultiDiGraph, shared_elements: set):
"""Both extremities should be in the unique_graph still makes an edge between similar node to u (the node
with u coordinates and different id, included in the unique_graph) and v Here, sub_graph is the unique_graph
and graph is complex_graph Shared elements are shared btw sub_graph and graph, which are elements to include
when dropping duplicates"""
if shared_elements is None or not isinstance(shared_elements, set):
raise ValueError("unique_elements should be a set")
if from_node_id in sub_graph.nodes() and to_node_id in sub_graph.nodes():
return ExtremitiesData.arrange_extremities_data(
from_node_id=from_node_id, to_node_id=to_node_id, graph=sub_graph
)
elif (graph.nodes[from_node_id]['x'], graph.nodes[from_node_id]['y']) in shared_elements and \
to_node_id in sub_graph.nodes():

from_node_id_prime = ExtremitiesData.find_node_id_by_coor(
sub_graph, graph.nodes[from_node_id]['x'], graph.nodes[from_node_id]['y']
)
if from_node_id_prime == to_node_id:
return ExtremitiesData()
else:
return ExtremitiesData.arrange_extremities_data(from_node_id=from_node_id_prime, to_node_id=to_node_id,
graph=sub_graph)

elif from_node_id in sub_graph.nodes() and \
(graph.nodes[to_node_id]['x'], graph.nodes[to_node_id]['y']) in shared_elements:

to_node_id_prime = ExtremitiesData.find_node_id_by_coor(
sub_graph, graph.nodes[to_node_id]['x'], graph.nodes[to_node_id]['y']
)
if from_node_id == to_node_id_prime:
return ExtremitiesData()
else:
return ExtremitiesData.arrange_extremities_data(from_node_id=from_node_id, to_node_id=to_node_id_prime,
graph=sub_graph)
else:
return ExtremitiesData()

@staticmethod
def arrange_extremities_data(from_node_id: int, to_node_id: int, graph: MultiDiGraph):
return ExtremitiesData(
from_id=from_node_id,
to_id=to_node_id,
from_to_id=(from_node_id, to_node_id),
to_from_id=(to_node_id, from_node_id),
from_to_coor=(
(graph.nodes[from_node_id]['x'], graph.nodes[to_node_id]['x']),
(graph.nodes[from_node_id]['y'], graph.nodes[to_node_id]['y'])
),
to_from_coor=(
(graph.nodes[to_node_id]['x'], graph.nodes[from_node_id]['x']),
(graph.nodes[to_node_id]['y'], graph.nodes[from_node_id]['y'])
)
)

@staticmethod
def find_node_id_by_coor(graph: MultiDiGraph, target_x: float, target_y: float):
"""
finds the node in unique graph with the same coor
"""
for node, data in graph.nodes(data=True):
if 'x' in data and 'y' in data and data['x'] == target_x and data['y'] == target_y:
return node
return None
Loading

0 comments on commit 4747a37

Please sign in to comment.