Skip to content

Commit

Permalink
Merge pull request #2391 from mbounabi/NAVP-820
Browse files Browse the repository at this point in the history
Jormungandr: Add free_radius parameters
  • Loading branch information
TeXitoi authored Apr 17, 2018
2 parents 802d249 + e290cff commit e988ab6
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 11 deletions.
Binary file added documentation/slate/source/images/free_radius.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions documentation/slate/source/includes/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,8 @@ The [isochrones](#isochrones) service exposes another response structure, which
| nop | direct_path | enum | Specify if direct paths should be suggested.<br>possible values: <ul><li>indifferent</li><li>none</li><li>only</li></ul> | indifferent |
| nop | add_poi_infos[] | boolean | Activate the output of additional infomations about the poi. For example, parking availability(BSS, car parking etc.) in the pois of response. Possible values are `bss_stands`, `car_park` | []
| nop | debug | boolean | Debug mode<br>No journeys are filtered in this mode | False |
| nop | free_radius_from | int | Radius length (in meters) around the coordinates of departure in which the stop points are considered free to go (crowfly=0) | 0 |
| nop | free_radius_to | int | Radius length (in meters) around the coordinates of arrival in which the stop points are considered free to go (crowfly=0) | 0 |
### Precisions on `forbidden_uris[]` and `allowed_id[]`
Expand All @@ -1006,6 +1008,18 @@ Examples:
* A user doesn't like line A metro in hers city. She adds the parameter `forbidden_uris[]=line:A` when calling the API.
* A user would only like to use Buses and Tramways. She adds the parameter `allowed_id[]=physical_mode:Bus&allowed_id[]=physical_mode:Tramway`.
### Precisions on `free_radius_from/free_radius_to`
These parameters find the nearest stop point (within free_radius distance) to the given coordinates.
Then, it allows skipping walking sections between the point of departure/arrival and those nearest stop points.
Example:
![image](free_radius.png)
In this example, the stop points within the circle (SP1, SP2 et SP3) can be reached via a crowfly of 0 second. The other stop points, outside the circle, will be reached by walking.
#### Technically
The journeys can only use allowed vehicle journeys (as present in the `public_transport` or `on_demand_transport` sections).
Expand Down
4 changes: 2 additions & 2 deletions source/jormungandr/jormungandr/interfaces/v1/Journeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ def __init__(self):
dest="add_poi_infos", action="append",
help="Show more information about the poi if it's available, for instance, show "
"BSS/car park availability in the pois(BSS/car park) of response")

self.get_decorators.append(complete_links(self))

if parser_get.parse_args().get("add_poi_infos") or parser_get.parse_args().get("bss_stands"):
Expand All @@ -506,8 +507,7 @@ def get(self, region=None, lon=None, lat=None, uri=None):
possible_regions = compute_possible_region(region, args)
args.update(self.parse_args(region, uri))


#count override min_nb_journey or max_nb_journey
# count override min_nb_journey or max_nb_journey
if 'count' in args and args['count']:
args['min_nb_journeys'] = args['count']
args['max_nb_journeys'] = args['count']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ def __init__(self, output_type_serializer):
default='indifferent',
help="Specify if direct path should be suggested")

parser_get.add_argument("free_radius_from", type=int, default=0,
help="Radius length (in meters) around the coordinates of departure "
"in which the stop points are considered free to go (crowfly=0)")
parser_get.add_argument("free_radius_to", type=int, default=0,
help="Radius length (in meters) around the coordinates of arrival "
"in which the stop points are considered free to go (crowfly=0)")

def parse_args(self, region=None, uri=None):
args = self.parsers['get'].parse_args()

Expand Down
7 changes: 6 additions & 1 deletion source/jormungandr/jormungandr/scenarios/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,12 @@ def parse_journey_request(self, requested_type, request):
for allowed_id in get_or_default(request, "allowed_id[]", []):
req.journeys.allowed_id.append(allowed_id)
if not "type" in request:
request["type"] = "all" #why ?
request["type"] = "all" # why ?

if request["free_radius_from"]:
req.journeys.free_radius_from = request["free_radius_from"]
if request["free_radius_to"]:
req.journeys.free_radius_to = request["free_radius_to"]

#for the default scenario, we filter the walking if we have walking + bss

Expand Down
2 changes: 1 addition & 1 deletion source/jormungandr/jormungandr/scenarios/distributed.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _compute_all(future_manager, request, instance, krakens_call):
streetnetwork_path_type=StreetNetworkPathType.DIRECT)
for mode in requested_dep_modes]

# We'd like to get the know the duration a direct path to do some optimizations in ProximitiesByCrowflyPool and
# We'd like to get the duration of a direct path to do some optimizations in ProximitiesByCrowflyPool and
# FallbackDurationsPool.
# Note :direct_paths_by_mode is a dict of mode vs future of a direct paths, this line is not blocking
direct_paths_by_mode = streetnetwork_path_pool.get_all_direct_paths()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,21 @@ def _do_request(self):
# When max_duration_to_pt is 0, there is no need to compute the fallback to pt, except if place is a
# stop_point or a stop_area
center_isochrone = self._requested_place_obj
free_access = self._places_free_access.wait_and_get()
all_free_access = free_access.crowfly | free_access.odt
proximities_by_crowfly = self._proximities_by_crowfly_pool.wait_and_get(self._mode)

free_access = self._places_free_access.wait_and_get()

free_radius_distance = None
if self._direct_path_type == StreetNetworkPathType.BEGINNING_FALLBACK:
free_radius_distance = self._request.free_radius_from
elif self._direct_path_type == StreetNetworkPathType.ENDING_FALLBACK:
free_radius_distance = self._request.free_radius_to

if free_radius_distance is not None:
free_access.free_radius.update(p.uri for p in proximities_by_crowfly if p.distance < free_radius_distance)

all_free_access = free_access.crowfly | free_access.odt | free_access.free_radius

# if a place is freely accessible, there is no need to compute it's access duration in isochrone
places_isochrone = [p for p in proximities_by_crowfly if p.uri not in all_free_access]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,9 @@ def compute_fallback(from_obj,
continue

places_free_access = orig_places_free_access.wait_and_get()
orig_all_free_access = places_free_access.odt | places_free_access.crowfly
orig_all_free_access = places_free_access.odt | places_free_access.crowfly | places_free_access.free_radius
places_free_access = dest_places_free_access.wait_and_get()
dest_all_free_access = places_free_access.odt | places_free_access.crowfly
dest_all_free_access = places_free_access.odt | places_free_access.crowfly | places_free_access.free_radius

for journey in pt_journeys.journeys:
# from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from collections import namedtuple
import logging

PlaceFreeAccessResult = namedtuple('PlaceFreeAccessResult', ['crowfly', 'odt'])
PlaceFreeAccessResult = namedtuple('PlaceFreeAccessResult', ['crowfly', 'odt', 'free_radius'])


class PlacesFreeAccess:
Expand Down Expand Up @@ -79,7 +79,10 @@ def _do_request(self):

logger.debug("finish places with free access from %s", self._requested_place_obj.uri)

return PlaceFreeAccessResult(crowfly, odt)
# free_radius is empty until the proximities by crowfly are available
free_radius = set()

return PlaceFreeAccessResult(crowfly, odt, free_radius)

def _async_request(self):
self._value = self._future_manager.create_future(self._do_request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def wait_and_get(self):
class StreetNetworkPathPool:
"""
A direct path pool is a set of pure street network journeys which are computed by the given street network service.
According to its usage, a StreetNetworkPath can be direct, begnning_fallback and ending_fallback
According to its usage, a StreetNetworkPath can be direct, beginning_fallback and ending_fallback
"""
def __init__(self, future_manager, instance):
self._future_manager = future_manager
Expand Down
5 changes: 5 additions & 0 deletions source/jormungandr/jormungandr/scenarios/new_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ def create_pb_request(requested_type, request, dep_mode, arr_mode):

req.journeys.bike_in_pt = (dep_mode == 'bike') and (arr_mode == 'bike')

if request["free_radius_from"]:
req.journeys.free_radius_from = request["free_radius_from"]
if request["free_radius_to"]:
req.journeys.free_radius_to = request["free_radius_to"]

return req


Expand Down
59 changes: 59 additions & 0 deletions source/jormungandr/tests/journey_common_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,65 @@ def test_journey_from_non_valid_object_type(self):
assert response['error']['id'] == u'unknown_object'
assert response['error']['message'] == u'The entry point: vehicle_journey:SNC is not valid'

def test_free_radius_from(self):
# The coordinates of departure and the stop point are separated by 20m
# Query journeys with free_radius = 0
r = self.query('/v1/coverage/main_routing_test/journeys?from=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&to=stopA&datetime=20120614080000')
assert(r['journeys'][0]['sections'][0]['type'] == 'street_network')
assert(r['journeys'][0]['sections'][0]['duration'] != 0)

# Query journeys with free_radius = 19
r = self.query('/v1/coverage/main_routing_test/journeys?from=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&to=stopA&datetime=20120614080000&free_radius_from=19')
assert(r['journeys'][0]['sections'][0]['type'] == 'street_network')
assert(r['journeys'][0]['sections'][0]['duration'] != 0)

# Query journeys with free_radius = 20
r = self.query('/v1/coverage/main_routing_test/journeys?from=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&to=stopA&datetime=20120614080000&free_radius_from=20')
assert(r['journeys'][0]['sections'][0]['type'] == 'crow_fly')
assert(r['journeys'][0]['sections'][0]['duration'] == 0)

# The time of departure of PT is 08:01:00 and it takes 17s to walk to the station
# If the requested departure time is 08:00:50, the PT journey shouldn't be displayed
r = self.query('/v1/coverage/main_routing_test/journeys?from=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&to=stopA&datetime=20120614T080050&datetime_represents=departure&')
assert(r['journeys'][0]['sections'][0]['type'] == 'street_network')

# With the free_radius, the PT journey is displayed thanks to the 'free' crow_fly
r = self.query('/v1/coverage/main_routing_test/journeys?from=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&to=stopA&datetime=20120614T080100&free_radius_from=20&datetime_represents=departure&')
assert(len(r['journeys'][0]['sections']) > 1)
assert(r['journeys'][0]['sections'][0]['type'] == 'crow_fly')
assert(r['journeys'][0]['sections'][0]['duration'] == 0)
assert(r['journeys'][0]['sections'][1]['type'] == 'public_transport')

def test_free_radius_to(self):
# The coordinates of arrival and the stop point are separated by 20m
# Query journeys with free_radius = 0
r = self.query('/v1/coverage/main_routing_test/journeys?from=stopA&to=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&datetime=20120614080000&')
assert(r['journeys'][0]['sections'][-1]['type'] == 'street_network')
assert(r['journeys'][0]['sections'][-1]['duration'] != 0)

# Query journeys with free_radius = 19
r = self.query('/v1/coverage/main_routing_test/journeys?from=stopA&to=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&datetime=20120614080000&free_radius_from=19')
assert(r['journeys'][0]['sections'][-1]['type'] == 'street_network')
assert(r['journeys'][0]['sections'][-1]['duration'] != 0)

# Query journeys with free_radius = 20
r = self.query('/v1/coverage/main_routing_test/journeys?from=stopA&to=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&datetime=20120614080000&free_radius_to=20&')
assert(r['journeys'][0]['sections'][-1]['type'] == 'crow_fly')
assert(r['journeys'][0]['sections'][-1]['duration'] == 0)

# The estimated time of arrival without free_radius is 08:01:19
# If the requested arrival time is before 08:01:19, the PT journey shouldn't be displayed
r = self.query('/v1/coverage/main_routing_test/journeys?from=stopA&to=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&datetime=20120614T080118&free_radius_to=19&datetime_represents=arrival&')
assert(r['journeys'][0]['sections'][0]['type'] == 'street_network')

# With the free_radius, the PT journey is displayed thanks to the 'free' crow_fly
r = self.query('/v1/coverage/main_routing_test/journeys?from=stopA&to=coord%3A8.98311981954709e-05%3A8.98311981954709e-05&datetime=20120614T080118&free_radius_to=20&_override_scenario=experimental&datetime_represents=arrival&')
assert(len(r['journeys'][0]['sections']) > 1)
assert(r['journeys'][0]['sections'][-1]['type'] == 'crow_fly')
assert(r['journeys'][0]['sections'][-1]['duration'] == 0)
assert(r['journeys'][0]['sections'][-2]['type'] == 'public_transport')


@dataset({"main_stif_test": {}})
class AddErrorFieldInJormun(object):
def test_add_error_field(self):
Expand Down
12 changes: 12 additions & 0 deletions source/jormungandr/tests/routing_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ def test_min_nb_journeys_with_night_bus_filter(self):
"""
pass

def test_free_radius_from(self):
"""
TO DO : Enable when NAVP-819 is available
"""
pass

def test_free_radius_to(self):
"""
TO DO : Enable when NAVP-819 is available
"""
pass


@config()
class TestJourneysNoRegionDefault(JourneysNoRegion, AbstractTestFixture):
Expand Down
12 changes: 12 additions & 0 deletions source/jormungandr/tests/routing_tests_destineo.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,15 @@ def test_journeys_destineo_with_bss(self):
assert response['journeys'][1]['type'] == 'non_pt_bss'
assert response['journeys'][2]['type'] == 'non_pt_walk'
assert response['journeys'][3]['type'] == 'non_pt_bike'

def test_free_radius_from(self):
"""
This feature is not supported for this scenario
"""
pass

def test_free_radius_to(self):
"""
This feature is not supported for this scenario
"""
pass
23 changes: 23 additions & 0 deletions source/jormungandr/tests/routing_tests_new_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,17 @@ def exec_and_check(query):
.format(first='ridesharing', last='walking')
exec_and_check(query)

def test_free_radius_from(self):
"""
TO DO : Enable when NAVP-819 is available
"""
pass

def test_free_radius_to(self):
"""
TO DO : Enable when NAVP-819 is available
"""
pass

@config({"scenario": "new_default",
'instance_config': {
Expand Down Expand Up @@ -367,6 +378,18 @@ def exec_and_check(query):
.format(first='ridesharing', last='walking')
exec_and_check(query)

def test_free_radius_from(self):
"""
This feature is not supported for this scenario
"""
pass

def test_free_radius_to(self):
"""
This feature is not supported for this scenario
"""
pass


@config({"scenario": "new_default"})
class TestNewDefaultJourneysWithPtref(JourneysWithPtref, NewDefaultScenarioAbstractTestFixture):
Expand Down

0 comments on commit e988ab6

Please sign in to comment.