Skip to content

Commit

Permalink
add penality bike parking
Browse files Browse the repository at this point in the history
  • Loading branch information
princefr committed Nov 13, 2024
1 parent 5a2c592 commit a936795
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 1 deletion.
4 changes: 4 additions & 0 deletions source/jormungandr/jormungandr/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ def additional_parameters(self):
additional_time_after_first_section_taxi = _make_property_getter('additional_time_after_first_section_taxi')
additional_time_before_last_section_taxi = _make_property_getter('additional_time_before_last_section_taxi')

additional_time_after_first_section_bike = _make_property_getter('additional_time_after_first_section_bike')
additional_time_before_last_section_bike = _make_property_getter('additional_time_before_last_section_bike')


max_walking_direct_path_duration = _make_property_getter('max_walking_direct_path_duration')
max_bike_direct_path_duration = _make_property_getter('max_bike_direct_path_duration')
max_bss_direct_path_duration = _make_property_getter('max_bss_direct_path_duration')
Expand Down
5 changes: 5 additions & 0 deletions source/jormungandr/jormungandr/interfaces/v1/Journeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,11 @@ def _set_specific_params(mod):
if args.get('additional_time_before_last_section_taxi') is None:
args['additional_time_before_last_section_taxi'] = mod.additional_time_before_last_section_taxi

if args.get('additional_time_after_first_section_bike') is None:
args['additional_time_after_first_section_bike'] = mod.additional_time_after_first_section_bike
if args.get('additional_time_before_last_section_bike') is None:
args['additional_time_before_last_section_bike'] = mod.additional_time_before_last_section_bike

if args.get('_stop_points_nearby_duration') is None:
args['_stop_points_nearby_duration'] = mod.stop_points_nearby_duration

Expand Down
24 changes: 23 additions & 1 deletion source/jormungandr/jormungandr/interfaces/v1/journey_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# www.navitia.io

from __future__ import absolute_import, print_function, unicode_literals, division
from jormungandr import i_manager, fallback_modes, partner_services, app
from jormungandr import i_manager, fallback_modes, partner_services, app, park_modes
from jormungandr.interfaces.v1.ResourceUri import ResourceUri
from datetime import datetime
from jormungandr.resources_utils import ResourceUtc
Expand Down Expand Up @@ -239,6 +239,14 @@ def __init__(self, output_type_serializer):
action="append",
help='Same as first_section_mode but for the last section.',
)

parser_get.add_argument(
"park_mode[]",
type=OptionValue(park_modes.all_park_modes),
dest="origin_mode",
action="append",
help='Force the park mode if the first section is by bike\n'
)
# for retrocompatibility purpose, we duplicate (without []):
parser_get.add_argument(
"first_section_mode",
Expand Down Expand Up @@ -506,6 +514,20 @@ def __init__(self, output_type_serializer):
type=int,
help="the additional time added to the taxi section, right before riding the taxi but after hopping off the public transit",
)


parser_get.add_argument(
"additional_time_after_first_section_bike",
type=int,
help="the additional time added to the bike section, right after riding the bike but before hopping on the public transit",
)

parser_get.add_argument(
"additional_time_before_last_section_bike",
type=int,
help="the additional time added to the bike section, right before riding the bike but after hopping off the public transit",
)

parser_get.add_argument(
"_pt_planner",
type=OptionValue(['kraken', 'loki']),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class ParametersSerializer(serpy.Serializer):
max_extra_second_pass = Field(schema_type=int)
additional_time_after_first_section_taxi = Field(schema_type=int)
additional_time_before_last_section_taxi = Field(schema_type=int)
additional_time_after_first_section_bike = Field(schema_type=int)
additional_time_before_last_section_bike = Field(schema_type=int)
max_walking_direct_path_duration = Field(schema_type=int)
max_bike_direct_path_duration = Field(schema_type=int)
max_bss_direct_path_duration = Field(schema_type=int)
Expand Down
75 changes: 75 additions & 0 deletions source/jormungandr/jormungandr/park_modes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright (c) 2001-2024, Hove and/or its affiliates. All rights reserved.
#
# This file is part of Navitia,
# the software to build cool stuff with public transport.
#
# Hope you'll enjoy and contribute to this project,
# powered by Hove (www.hove.com).
# Help us simplify mobility and open public transport:
# a non ending quest to the responsive locomotion way of traveling!
#
# LICENCE: This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Stay tuned using
# twitter @navitia
# channel `#navitia` on riot https://riot.im/app/#/room/#navitia:matrix.org
# https://groups.google.com/d/forum/navitia
# www.navitia.io
#
#


from enum import Enum
from navitiacommon import response_pb2



class ParkModes(Enum):
none = response_pb2.None
without_park = response_pb2.WithoutPark
with_park = response_pb2.WithPark


@classmethod
def modes_str(cls):

return {e.name for e in cls}


@classmethod
def modes_enum(cls):
return set(cls)


@classmethod
def get_allowed_combinations_enums(cls):
def _combi(first_sections_modes, last_section_modes):
from itertools import product

# cartesian product between two iterables
return set(product(first_sections_modes, last_section_modes))

return _combi(cls.modes_enum(), cls.modes_enum())


@classmethod
def get_allowed_combinations_str(cls):
# python 2/3 portability
import six
allowed_combinations_enum = cls.get_allowed_combinations_enums()
# transform all enum to str
return set(six.moves.map(lambda modes: (modes[0].name, modes[1].name), allowed_combinations_enum))


all_park_modes = ParkModes.modes_str()
10 changes: 10 additions & 0 deletions source/jormungandr/jormungandr/scenarios/distributed.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,16 @@ def graphical_isochrones(self, request, instance):
'additional_time_before_last_section_taxi'
] = instance.additional_time_after_first_section_taxi

if request.get('additional_time_after_first_section_bike') is None:
request[
'additional_time_after_first_section_bike'
] = instance.additional_time_after_first_section_bike

if request.get('additional_time_before_last_section_bike') is None:
request[
'additional_time_before_last_section_bike'
] = instance.additional_time_before_last_section_bike

krakens_call = set({(request["origin_mode"][0], request["destination_mode"][0], "indifferent")})
pt_object_origin = None
pt_object_destination = None
Expand Down
1 change: 1 addition & 0 deletions source/jormungandr/jormungandr/street_network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
from jormungandr.street_network.car_with_park import CarWithPark
from jormungandr.street_network.handimap import Handimap
from jormungandr.street_network.andyamo import Andyamo
from jormungandr.street_network.bike import Bike
198 changes: 198 additions & 0 deletions source/jormungandr/jormungandr/street_network/bike.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Copyright (c) 2001-2024, Hove and/or its affiliates. All rights reserved.
#
# This file is part of Navitia,
# the software to build cool stuff with public transport.
#
# Hope you'll enjoy and contribute to this project,
# powered by Hove (www.hove.com).
# Help us simplify mobility and open public transport:
# a non ending quest to the responsive locomotion way of traveling!
#
# LICENCE: This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Stay tuned using
# twitter @navitia
# channel `#navitia` on riot https://riot.im/app/#/room/#navitia:matrix.org
# https://groups.google.com/d/forum/navitia
# www.navitia.io
from __future__ import absolute_import, print_function, unicode_literals, division

import six
import logging
import copy
from jormungandr.street_network.street_network import AbstractStreetNetworkService, StreetNetworkPathType
from jormungandr import utils, park_modes as pm
from jormungandr.utils import SectionSorter
from functools import cmp_to_key


from navitiacommon import response_pb2


class Bike(AbstractStreetNetworkService):
def __init__(self, instance, service_url, modes=None, id=None, timeout=10, api_key=None, **kwargs):
self.instance = instance
self.modes = modes or [pm.ParkModes.with_park]
assert list(self.modes) == [pm.ParkModes.with_park], (
'Class: ' + str(self.__class__) + ' can only be used with ParkModes.with_park mode. '
)
self.sn_system_id = id or 'bike'
config = kwargs.get('street_network', {})
if 'service_url' not in config['args']:
config['args'].update({'service_url': None})
if 'instance' not in config['args']:
config['args'].update({'instance': instance})

config['args'].update({'modes': self.modes})
self.street_network = utils.create_object(config)

def status(self):
return {
'id': six.text_type(self.sn_system_id),
'class': self.__class__.__name__,
'modes': self.modes,
'backend_class': self.street_network.__class__.__name__,
}



def _direct_path(self, instance, mode, pt_object_origin, pt_object_destination, fallback_extremity, request, direct_path_type, request_id):
copy_request = copy.deepcopy(request)
response = self.street_network.direct_path(
instance, mode, pt_object_origin, pt_object_destination, fallback_extremity, copy_request, direct_path_type, request_id
)

if not response:
return response


def _add_additional_section_in_fallback(
self, response, pt_object_origin, pt_object_destination, request, direct_path_type
):
logger = logging.getLogger(__name__)
logger.info("Creating additional section for direct path")

for journey in response.journeys:
# Depending of the fallback type(beginning/ending fallback), the additional section's
# place can be the origin/destination of the pt_journey
#
if direct_path_type == StreetNetworkPathType.BEGINNING_FALLBACK:
self._add_additional_section_after_first_section_bike(
journey, pt_object_destination, request["additional_time_after_last_section_bike"]
)
else:
self._add_additional_section_before_last_section_bike(
journey, pt_object_origin, request["additional_time_after_last_section_bike"]
)


def _add_additional_section_after_first_section_bike(self, journey, pt_object, additional_time):
logging.getLogger(__name__).info("Creating additional section_after_first_section_bike")

additional_section = self._create_additional_section(
pt_object, additional_time, journey.sections[-1].end_date_time, "section_1"
)

# We have to complete the destination of the first section ourselves
# Because Jormun does not do it afterwards
journey.sections[-1].destination.CopyFrom(pt_object)

self._update_journey(journey, additional_section)

def _add_additional_section_before_last_section_bike(self, journey, pt_object, additional_time):
logging.getLogger(__name__).info("Creating additional section_after_first_section_bike")

additional_section = self._create_additional_section(
pt_object, additional_time, journey.sections[-1].end_date_time, "section_1"
)

# We have to complete the destination of the first section ourselves
# Because Jormun does not do it afterwards
journey.sections[-1].destination.CopyFrom(pt_object)

for s in journey.sections:
s.end_date_time += additional_time
s.begin_date_time += additional_time

self._update_journey(journey, additional_section)

def _create_additional_section(self, pt_object, additional_time, begin_date_time, section_id):
additional_section = response_pb2.Section()

additional_section.id = section_id
additional_section.origin.CopyFrom(pt_object)
additional_section.destination.CopyFrom(pt_object)
additional_section.duration = additional_time
additional_section.type = response_pb2.WAITING
additional_section.begin_date_time = begin_date_time
additional_section.end_date_time = additional_section.begin_date_time + additional_section.duration

return additional_section

def _update_journey(self, journey, additional_section):
journey.duration += additional_section.duration
journey.durations.total += additional_section.duration
journey.arrival_date_time += additional_section.duration

journey.sections.extend([additional_section])
journey.sections.sort(key=cmp_to_key(SectionSorter()))

journey.nb_sections += 1

def _get_street_network_routing_matrix(
self, instance, origins, destinations, street_network_mode, max_duration, request, request_id, **kwargs
):

copy_request = copy.deepcopy(request)
response = self.street_network.get_street_network_routing_matrix(
instance,
origins,
destinations,
street_network_mode,
max_duration,
copy_request,
request_id,
**kwargs
)

if response and len(response.rows):
self._add_additional_time_in_routing_matrix(
response.rows[0].routing_response, origins, destinations, copy_request
)

return response


def _add_additional_time_in_routing_matrix(self, response, origins, destinations, request):
addtional_matrix_time = (
request["additional_time_after_first_section_bike"]
if len(origins) == 1
else request["additional_time_before_last_section_bike"]
)

for r in response:
r.duration += addtional_matrix_time

def make_path_key(self, mode, orig_uri, dest_uri, streetnetwork_path_type, period_extremity):
"""
:param orig_uri, dest_uri, mode: matters obviously
:param streetnetwork_path_type: whether it's a fallback at
the beginning, the end of journey or a direct path without PT also matters especially for car (to know if we
park before or after)
:param period_extremity: is a PeriodExtremity (a datetime and its meaning on the
fallback period)
Nota: period_extremity is not taken into consideration so far because we assume that a
direct path from A to B remains the same even the departure time are different (no realtime)
"""
return self.street_network.make_path_key(mode, orig_uri, dest_uri, streetnetwork_path_type, None)
6 changes: 6 additions & 0 deletions source/navitiacommon/navitiacommon/default_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@
# Additionnal time in second before the taxi section when used as last section mode
additional_time_before_last_section_taxi = 5 * 60

# Additionnal time in second after the bike section when used as first section mode
additional_time_after_first_section_bike = 5 * 60

# Additionnal time in second before the bike section when used as last section mode
additional_time_before_last_section_bike = 5 * 60

max_walking_direct_path_duration = 24 * 60 * 60

max_bike_direct_path_duration = 24 * 60 * 60
Expand Down
Loading

0 comments on commit a936795

Please sign in to comment.