diff --git a/python/cfr/two_step_routing/testdata/small/expected_global_request.json b/python/cfr/two_step_routing/testdata/small/expected_global_request.json index 55b0ec70..216010a8 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_global_request.json +++ b/python/cfr/two_step_routing/testdata/small/expected_global_request.json @@ -34,7 +34,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "1072s", "tags": [ @@ -64,7 +65,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "863s", "tags": [ @@ -91,7 +93,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "863s", "tags": [ @@ -119,7 +122,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "600s", "tags": [ @@ -151,7 +155,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "449s", "tags": [ @@ -182,7 +187,8 @@ "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "449s", "tags": [ diff --git a/python/cfr/two_step_routing/testdata/small/expected_integrated_global_request.json b/python/cfr/two_step_routing/testdata/small/expected_integrated_global_request.json index d99268d3..b981f3fa 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_integrated_global_request.json +++ b/python/cfr/two_step_routing/testdata/small/expected_integrated_global_request.json @@ -65,7 +65,8 @@ "arrivalWaypoint": { "location": { "latLng": { "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "1176s", "tags": ["P001"], @@ -86,7 +87,8 @@ "arrivalWaypoint": { "location": { "latLng": { "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "449s", "tags": ["P002", "parking: P002"] @@ -102,7 +104,8 @@ "arrivalWaypoint": { "location": { "latLng": { "latitude": 48.86482, "longitude": 2.34932 } - } + }, + "sideOfRoad": true }, "duration": "750s", "tags": ["P002", "parking: P002"] diff --git a/python/cfr/two_step_routing/testdata/small/expected_integrated_local_request.json b/python/cfr/two_step_routing/testdata/small/expected_integrated_local_request.json index 986c2a61..0240e82f 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_integrated_local_request.json +++ b/python/cfr/two_step_routing/testdata/small/expected_integrated_local_request.json @@ -151,7 +151,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1 + "travelMode": 2 }, { "costPerHour": 300, diff --git a/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request.json b/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request.json index 01b6c211..7729aaa5 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request.json +++ b/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request.json @@ -301,7 +301,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1 + "travelMode": 2 }, { "costPerHour": 300, diff --git a/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request_with_reload_costs.json b/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request_with_reload_costs.json index ade16fd9..b00b10dc 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request_with_reload_costs.json +++ b/python/cfr/two_step_routing/testdata/small/expected_local_refinement_request_with_reload_costs.json @@ -303,7 +303,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1 + "travelMode": 2 }, { "costPerHour": 300, diff --git a/python/cfr/two_step_routing/testdata/small/expected_local_request.json b/python/cfr/two_step_routing/testdata/small/expected_local_request.json index eedef530..9c7ee729 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_local_request.json +++ b/python/cfr/two_step_routing/testdata/small/expected_local_request.json @@ -210,7 +210,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "fixedCost": 10000, "costPerHour": 300, "costPerKilometer": 60, @@ -248,7 +248,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "fixedCost": 10000, "costPerHour": 300, "costPerKilometer": 60, @@ -286,7 +286,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "fixedCost": 10000, "costPerHour": 300, "costPerKilometer": 60, diff --git a/python/cfr/two_step_routing/testdata/small/expected_local_request_group_by_parking.json b/python/cfr/two_step_routing/testdata/small/expected_local_request_group_by_parking.json index 835685bd..c6b3f7fa 100644 --- a/python/cfr/two_step_routing/testdata/small/expected_local_request_group_by_parking.json +++ b/python/cfr/two_step_routing/testdata/small/expected_local_request_group_by_parking.json @@ -178,7 +178,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "startTags": ["P001"], "endTags": ["P001"] }, @@ -200,7 +200,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "startTags": ["P001"], "endTags": ["P001"] }, @@ -222,7 +222,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "startTags": ["P001"], "endTags": ["P001"] }, @@ -244,7 +244,7 @@ } }, "travelDurationMultiple": 1.1, - "travelMode": 1, + "travelMode": 2, "startTags": ["P001"], "endTags": ["P001"] }, diff --git a/python/cfr/two_step_routing/testdata/small/parking.json b/python/cfr/two_step_routing/testdata/small/parking.json index 64673fe2..6c264cb0 100644 --- a/python/cfr/two_step_routing/testdata/small/parking.json +++ b/python/cfr/two_step_routing/testdata/small/parking.json @@ -1,14 +1,25 @@ { "parking_locations": [ { - "coordinates": { "latitude": 48.86482, "longitude": 2.34932 }, + "waypoint": { + "location": { + "latLng": { "latitude": 48.86482, "longitude": 2.34932 } + }, + "sideOfRoad": true + }, "tag": "P001", + "travel_mode": 2, "travel_duration_multiple": 1.1, "delivery_load_limits": { "ore": 2 }, "max_round_duration": "1800s" }, { - "coordinates": { "latitude": 48.86482, "longitude": 2.34932 }, + "waypoint": { + "location": { + "latLng": { "latitude": 48.86482, "longitude": 2.34932 } + }, + "sideOfRoad": true + }, "tag": "P002", "travel_mode": 2, "delivery_load_limits": { "ore": 2 }, diff --git a/python/cfr/two_step_routing/two_step_routing.py b/python/cfr/two_step_routing/two_step_routing.py index 20bf6a9d..84189116 100644 --- a/python/cfr/two_step_routing/two_step_routing.py +++ b/python/cfr/two_step_routing/two_step_routing.py @@ -49,6 +49,7 @@ import copy import dataclasses import enum +import functools import math import re from typing import Any, TypeAlias, TypeVar, cast @@ -209,6 +210,26 @@ def __post_init__(self, coordinates: cfr_json.LatLng | None): self, "waypoint", {"location": {"latLng": coordinates}} ) + @functools.cached_property + def waypoint_for_local_model(self) -> cfr_json.Waypoint: + """Returns a waypoint for the parking to be used in local models. + + Local models typically use with travel modes other than DRIVE, which is not + compatible with sideOfRoad. To allow using `sideOfRoad` in parking location + waypoints (so that it is used in the global model), we need to remove it + from the waypoint when it is used in a local model. + + Returns: + The waypoint of the parking location. When the travel mode of the parking + location is not DRIVE, `sideOfRoad` is removed from the waypoint. The + returned object is cached and must not be mutated. + """ + if self.travel_mode == 1: + return self.waypoint + waypoint = copy.deepcopy(self.waypoint) + waypoint.pop("sideOfRoad", None) + return waypoint + def load_parking_from_json( parking_json: Any, @@ -682,7 +703,7 @@ def get_non_existent_tag(base: str) -> str: for consecutive_visit_sequence in consecutive_visit_sequences: parking = self._parking_locations[consecutive_visit_sequence.parking_tag] - parking_waypoint = parking.waypoint + parking_waypoint = parking.waypoint_for_local_model refinement_vehicle_index = len(refinement_vehicles) refinement_vehicle_label = ( @@ -1028,7 +1049,7 @@ def add_parking_location_shipment( shipment: cfr_json.Shipment = { "label": f"{parking.tag} {arrival_or_departure}", "deliveries": [{ - "arrivalWaypoint": parking.waypoint, + "arrivalWaypoint": parking.waypoint_for_local_model, "duration": "0s", }], # TODO(ondrasej): Vehicle costs and allowed vehicle indices. @@ -1261,8 +1282,8 @@ def _make_local_model_vehicle( vehicle: cfr_json.Vehicle = { "label": label, # Start and end waypoints. - "endWaypoint": parking.waypoint, - "startWaypoint": parking.waypoint, + "endWaypoint": parking.waypoint_for_local_model, + "startWaypoint": parking.waypoint_for_local_model, # Limits and travel speed. "travelDurationMultiple": parking.travel_duration_multiple, "travelMode": parking.travel_mode,