Skip to content

Commit

Permalink
Chore/563 tests run for too long leading to timeouts (#565)
Browse files Browse the repository at this point in the history
## Issue addressed
Solves #563 

## Code of conduct
- [x] I HAVE NOT added sensitive or compromised (test) data to the
repository.
- [x] I HAVE NOT added vulnerabilities to the repository.
- [x] I HAVE discussed my solution with (other) members of the RA2CE
team.

## What has been done?
- Moved logic in protocol as static method in the `Segmentation` class.
Not ideal, but better than before.
- Modified test `network.ini` files so that their segmentation length
property is `100` (meters) rather than `0.0001` which makes it way
longer to compute.

### Checklist
- [x] Code is formatted using our custom `black` and `isort`
definitions.
- [x] Tests are either added or updated.
- [x] Branch is up to date with `master`.
- [x] Updated documentation if needed.
  • Loading branch information
Carsopre authored Aug 30, 2024
2 parents 814b4f9 + d921d74 commit 6bd6ce7
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 51 deletions.
43 changes: 1 addition & 42 deletions ra2ce/network/network_wrappers/network_wrapper_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,14 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import math
from typing import Optional, Protocol, Union, runtime_checkable
from typing import Protocol, runtime_checkable

from geopandas import GeoDataFrame
from networkx import MultiGraph

from ra2ce.network.segmentation import Segmentation


@runtime_checkable
class NetworkWrapperProtocol(Protocol):
def segment_graph(
self,
edges: GeoDataFrame,
export_link_table: bool,
link_tables: Optional[tuple] = None,
) -> Union[GeoDataFrame, tuple[GeoDataFrame, tuple]]:
"""
Segments a complex graph based on the given segmentation length.
Args:
- segmentation_length (Optional[float]): The length to segment the graph edges. If None, no segmentation is applied.
- edges_complex (gpd.GeoDataFrame): The GeoDataFrame containing the complex graph edges.
- crs (str): The coordinate reference system to apply if the CRS is missing after segmentation.
Returns:
- gpd.GeoDataFrame: The segmented edges_complex GeoDataFrame.
"""
if not math.isnan(self.segmentation_length):
segmentation = Segmentation(edges, self.segmentation_length)
segmented_edges = segmentation.apply_segmentation()
if segmented_edges.crs is None: # The CRS might have disappeared.
segmented_edges.crs = self.crs # set the right CRS

if export_link_table:
updated_link_tables = segmentation.generate_link_tables()
segmented_edges.drop(columns=["rfid_c"], inplace=True)
segmented_edges.rename(columns={"splt_id": "rfid_c"}, inplace=True)
return segmented_edges, updated_link_tables
return segmented_edges
elif export_link_table and link_tables:
return edges, link_tables
elif export_link_table and not link_tables:
logging.warning("empty link_tables is passed")
return edges, tuple()
else:
return edges

def get_network(self) -> tuple[MultiGraph, GeoDataFrame]:
"""
Gets a network built within this wrapper instance. No arguments are accepted, the `__init__` method is meant to assign all required attributes for a wrapper.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
is_endnode_check,
modify_graph,
)
from ra2ce.network.segmentation import Segmentation


class OsmNetworkWrapper(NetworkWrapperProtocol):
Expand Down Expand Up @@ -160,8 +161,12 @@ def get_network(self) -> tuple[MultiGraph, GeoDataFrame]:
logging.info("Finished converting the graph to a geodataframe")

# Segment the complex graph
edges_complex, link_tables = self.segment_graph(
edges_complex, export_link_table=True, link_tables=link_tables
edges_complex, link_tables = Segmentation.segment_graph(
self.segmentation_length,
self.crs,
edges_complex,
export_link_table=True,
link_tables=link_tables,
)

# Save the link tables linking complex and simple IDs
Expand Down
5 changes: 4 additions & 1 deletion ra2ce/network/network_wrappers/shp_network_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from ra2ce.network.network_wrappers.network_wrapper_protocol import (
NetworkWrapperProtocol,
)
from ra2ce.network.segmentation import Segmentation


class ShpNetworkWrapper(NetworkWrapperProtocol):
Expand Down Expand Up @@ -188,6 +189,8 @@ def get_network(

graph_complex, edges_complex = self._get_complex_graph_and_edges(edges, id_name)

edges_complex = self.segment_graph(edges_complex, export_link_table=False)
edges_complex = Segmentation.segment_graph(
self.segmentation_length, self.crs, edges_complex, export_link_table=False
)

return graph_complex, edges_complex
5 changes: 4 additions & 1 deletion ra2ce/network/network_wrappers/trails_network_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
NetworkWrapperProtocol,
)
from ra2ce.network.networks_utils import graph_from_gdf
from ra2ce.network.segmentation import Segmentation


class TrailsNetworkWrapper(NetworkWrapperProtocol):
Expand Down Expand Up @@ -90,7 +91,9 @@ def get_network(self) -> tuple[MultiGraph, GeoDataFrame]:
)

# Segment the complex graph
edges_complex = self.segment_graph(edges, export_link_table=False)
edges_complex = Segmentation.segment_graph(
self.segmentation_length, self.crs, edges, export_link_table=False
)

graph_complex = graph_simple # NOTE THAT DIFFERENCE
# BETWEEN SIMPLE AND COMPLEX DOES NOT EXIST WHEN IMPORTING WITH TRAILS
Expand Down
9 changes: 7 additions & 2 deletions ra2ce/network/network_wrappers/vector_network_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from ra2ce.network.network_wrappers.network_wrapper_protocol import (
NetworkWrapperProtocol,
)
from ra2ce.network.segmentation import Segmentation


class VectorNetworkWrapper(NetworkWrapperProtocol):
Expand Down Expand Up @@ -129,8 +130,12 @@ def get_network(
logging.info("Finished converting the graph to a geodataframe")

# Segment the complex graph
edges_complex, link_tables = self.segment_graph(
edges_complex, export_link_table=True, link_tables=link_tables
edges_complex, link_tables = Segmentation.segment_graph(
self.segmentation_length,
self.crs,
edges_complex,
export_link_table=True,
link_tables=link_tables,
)

# Save the link tables linking complex and simple IDs
Expand Down
42 changes: 41 additions & 1 deletion ra2ce/network/segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
"""

import logging
import math
from decimal import Decimal
from typing import Union
from typing import Optional, Union

import geopandas as gpd
from geopy import distance
Expand All @@ -46,6 +47,45 @@ class Segmentation: # Todo: more naturally, this would be METHOD of the network
"""

@staticmethod
def segment_graph(
segmentation_length: float,
crs: float,
edges: gpd.GeoDataFrame,
export_link_table: bool,
link_tables: Optional[tuple] = None,
) -> Union[gpd.GeoDataFrame, tuple[gpd.GeoDataFrame, tuple]]:
"""
Segments a complex graph based on the given segmentation length.
Args:
- segmentation_length (Optional[float]): The length to segment the graph edges. If None, no segmentation is applied.
- edges_complex (gpd.GeoDataFrame): The GeoDataFrame containing the complex graph edges.
- crs (str): The coordinate reference system to apply if the CRS is missing after segmentation.
Returns:
- gpd.GeoDataFrame: The segmented edges_complex GeoDataFrame.
"""
if not math.isnan(segmentation_length):
segmentation = Segmentation(edges, segmentation_length)
segmented_edges = segmentation.apply_segmentation()
if segmented_edges.crs is None: # The CRS might have disappeared.
segmented_edges.crs = crs # set the right CRS

if export_link_table:
updated_link_tables = segmentation.generate_link_tables()
segmented_edges.drop(columns=["rfid_c"], inplace=True)
segmented_edges.rename(columns={"splt_id": "rfid_c"}, inplace=True)
return segmented_edges, updated_link_tables
return segmented_edges
elif export_link_table and link_tables:
return edges, link_tables
elif export_link_table and not link_tables:
logging.warning("empty link_tables is passed")
return edges, tuple()
else:
return edges

def __init__(
self,
edges_input: gpd.GeoDataFrame,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_data/1_network_shape/network.ini
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ hazard_crs = None
[cleanup]
snapping_threshold = None
pruning_threshold = None
segmentation_length = 0.0001
segmentation_length = 100
merge_lines = False
cut_at_intersections = True
2 changes: 1 addition & 1 deletion tests/test_data/2_network_shape/network.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ hazard_crs = None
[cleanup]
snapping_threshold = None
pruning_threshold = None
segmentation_length = 0.0001
segmentation_length = 100
merge_lines = True
cut_at_intersections = True

0 comments on commit 6bd6ce7

Please sign in to comment.