Skip to content

Commit

Permalink
Route tours with waypoints
Browse files Browse the repository at this point in the history
Plan trip for tours with destinations as waypoints.

Connects #1121.
  • Loading branch information
flibbertigibbet committed Nov 25, 2019
1 parent 17092f3 commit 44434fe
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 29 deletions.
108 changes: 99 additions & 9 deletions src/app/scripts/cac/control/cac-control-directions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* View control for the directions form
*
*/
CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferences) {
CAC.Control.Directions = (function (_, $, moment, Control, Places, Routing, UserPreferences) {

'use strict';

Expand All @@ -17,6 +17,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
places: '.places',
selectedItineraryClass: 'selected',
spinner: '.directions-results > .sk-spinner',
tourDestinationBlock: '.tour-destination-summary',
visible: ':visible'
}
};
Expand All @@ -40,6 +41,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
var directionsFormControl = null;
var directionsListControl = null;
var itineraryListControl = null;
var tourListControl = null;

function DirectionsControl(params) {
options = $.extend({}, defaults, params);
Expand Down Expand Up @@ -89,6 +91,8 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
function(e, lon, lat) {
mapControl.displayPoint(lon, lat);
});

tourListControl = new Control.TourList();
}

DirectionsControl.prototype = {
Expand Down Expand Up @@ -137,6 +141,11 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
planTripRequest.reject(Error(OUTDATED_REQUEST_ERROR));
}

console.log('about to plan trip with destination:');
console.log(directions.destination);
console.log('otp options:');
console.log(otpOptions);

planTripRequest = Routing.planTrip(directions.origin,
directions.destination,
date,
Expand All @@ -163,7 +172,9 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
// Only one itinerary is returned if there are waypoints, so this
// lets the user to continue to add or modify waypoints without
// having to select it in the list.
if (itineraries.length === 1 && !UserPreferences.getPreference('arriveBy')) {
if (itineraries.length === 1 && !UserPreferences.getPreference('arriveBy') &&
!UserPreferences.getPreference('tourMode')) {

itineraryControl.draggableItinerary(currentItinerary);
}

Expand Down Expand Up @@ -208,7 +219,9 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
}

function clearItineraries() {
UserPreferences.setPreference('waypoints', undefined);
if (!UserPreferences.getPreference('tourMode')) {
UserPreferences.setPreference('waypoints', undefined);
}
itineraryControl.clearItineraries();
itineraryListControl.hide();
directionsListControl.hide();
Expand Down Expand Up @@ -243,7 +256,8 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen

var otpOptions = {
arriveBy: arriveBy,
maxWalkDistance: UserPreferences.getPreference('maxWalk')
maxWalkDistance: UserPreferences.getPreference('maxWalk'),
tourMode: UserPreferences.getPreference('tourMode')
};

// add intermediatePlaces if user edited route
Expand Down Expand Up @@ -297,7 +311,9 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
itinerary.highlight(true);

// TODO: alert user that cannot use waypoints with arriveBy
if (!UserPreferences.getPreference('arriveBy')) {
if (!UserPreferences.getPreference('arriveBy') &&
!UserPreferences.getPreference('tourMode')) {

itineraryControl.draggableItinerary(itinerary);
}

Expand Down Expand Up @@ -357,6 +373,9 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
}

function reverseOriginDestination(event, newOrigin, newDestination) {
if (newOrigin && newOrigin.id.indexOf('tour') > -1) {
console.warn('cannot have a tour as origin!');
}
// set on this object and validate
if (newOrigin && newOrigin.location) {
setDirections('origin', [newOrigin.location.y, newOrigin.location.x]);
Expand Down Expand Up @@ -388,14 +407,85 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
}
}

/**
* Helper to finish `onTypeaheadSelected` event
* @param key {String} Either 'origin' or 'destination'
* @param destinations {Array} List of places, the last of which is the final destination.
* Can be a single destination, but cannot be empty.
*/
function onTypeaheadSelectDone(key, destinations) {
var places = Object.assign([], destinations);
var end = places.pop();

setDirections(key, [end.location.y, end.location.x]);

// Tour mode is not applicable to origin
if (key === 'destination') {
var waypoints = _.map(places, function(d) {
return [d.location.y, d.location.x];
});

// Set waypoints after `setDirections`, which clears waypoints
if (waypoints && waypoints.length) {
UserPreferences.setPreference('waypoints', waypoints);
updateUrl();
}
}

if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
planTripOrShowPlaces();
}
}

function onTypeaheadSelected(event, key, result) {
if (!result) {
setDirections(key, null);
return;
}
setDirections(key, [result.location.y, result.location.x]);
if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
planTripOrShowPlaces();


// Tour mode does not apply to origin
if (key === 'origin') {
onTypeaheadSelectDone(key, [result]);
return;
}
// If the typeahead result is a tour, go into tour mode
// and route between multiple destinations instead of to a single one.
var isTourMode = !!(result.id && result.id.indexOf('tour') > -1);
UserPreferences.setPreference('tourMode', isTourMode);

if (!isTourMode) {
// Send the single destination.
onTypeaheadSelectDone(key, [result]);
return;
}

if (result.destinations) {
// Result came from Typeahead, and so already has full destinations
// results from API search endpoint.
onTypeaheadSelectDone(key, result.destinations);
} else {
// If destinations are not set on the result, it didn't come from
// an autocomplete query, so go search for the tour now in a blocking query.
Places.queryPlaces(null, result.address).then(function(data) {
// In case multiple tours have the same name, find the one
// with the ID of our tour.
var tourId = parseInt(result.id.split('_')[1]);
var tour = _.find(data.tours, function(tour) {
return tourId === tour.id;
});
if (tour) {
onTypeaheadSelectDone(key, tour.destinations);
} else {
console.error('Failed to find destinations for tour ' + result.address);
onTypeaheadSelectDone(key, [result]);
}
}).fail(function(error) {
console.error('Error querying for tour destinations:');
console.error(error);
onTypeaheadSelectDone(key, [result]);
});
}
}

Expand Down Expand Up @@ -507,4 +597,4 @@ CAC.Control.Directions = (function (_, $, moment, Control, Routing, UserPreferen
}
}

})(_, jQuery, moment, CAC.Control, CAC.Routing.Plans, CAC.User.Preferences);
})(_, jQuery, moment, CAC.Control, CAC.Places.Places, CAC.Routing.Plans, CAC.User.Preferences);
8 changes: 4 additions & 4 deletions src/app/scripts/cac/control/cac-control-explore.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
return;
}

Places.getAllPlaces(exploreLatLng).then(function(data) {
Places.queryPlaces(exploreLatLng).then(function(data) {
setDestinationsEventsTours(data);
displayPlaces(filterPlaces(allDestinations, filter));
}).fail(function(error) {
Expand All @@ -454,7 +454,7 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
// Filter by both isochrone and category.
// Include events or tours with any matching destinations.
return filterPlacesCategory(_.filter(places, function(place) {
const destinationIds = place.destinations ?
var destinationIds = place.destinations ?
_.flatMap(place.destinations, 'id') : [place.id];
return _.intersection(isochroneDestinationIds, destinationIds).length;
}));
Expand Down Expand Up @@ -503,7 +503,7 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
return;
}

Places.getAllPlaces(exploreLatLng).then(function(data) {
Places.queryPlaces(exploreLatLng).then(function(data) {
setDestinationsEventsTours(data);
displayPlaces(filterPlaces(allDestinations, filter));
}).fail(function(error) {
Expand All @@ -519,7 +519,7 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
*
* Sets both destinations and events. If given empty data, will unset local cache.
*
* @param data {Object} response from getAllPlaces
* @param data {Object} response from queryPlaces
*/
function setDestinationsEventsTours(data) {
if (!data) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/scripts/cac/control/cac-control-tour-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CAC.Control.TourList = (function (_, $, MapTemplates) {
alert: '.alert',
container: '.directions-list',
hiddenClass: 'hidden',
destinationList: '.routes-list',
destinationList: '.tour-destinations-list',
destinationItem: '.route-summary'
}
};
Expand Down
6 changes: 5 additions & 1 deletion src/app/scripts/cac/home/cac-home-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ CAC.Home.Templates = (function (Handlebars, moment) {
Handlebars.registerPartial('filterDropdown', filterDropdownTemplate);
Handlebars.registerHelper('filterPartial', filterPartial);

// helper to get the identifier for a home page card for a place, event, or tour
/** Helper to get the identifier for a home page card for a place, event, or tour.
*
* These must match the prefixes used by the `get_directions_id` Django template
* helper and by the typeahead results.
*/
Handlebars.registerHelper('cardId', function(isEvent, isTour, id) {
var prefix = isEvent ? 'event' : (isTour ? 'tour' : 'place');
return prefix + '_' + id;
Expand Down
2 changes: 1 addition & 1 deletion src/app/scripts/cac/map/cac-map-isochrone.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
destinations[destination.id] = destination;
var point = _.property('point')(destination);
point.properties = _.omit(destination, 'point');
point.properties.matched = !!destination.matched
point.properties.matched = !!destination.matched;
return point;
}).value();

Expand Down
4 changes: 2 additions & 2 deletions src/app/scripts/cac/map/cac-map-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ CAC.Map.Templates = (function (Handlebars, moment, Utils) {
// Template for tour destinations
function tourDestinationList(destinations) {
var source = [
'<h1>Tour Destinations: TODO</h1><div class="routes-list">',
'<h1>Tour Destinations: TODO</h1><div class="tour-destinations-list">',
'{{#each destinations}}',
'<div class="route-summary" data-itinerary="{{this.id}}">',
'<div class="tour-destination-summary" data-itinerary="{{this.id}}">',
'<div class="route-name">{{this.name}}</div>',
'</div>',
'{{/each}}',
Expand Down
19 changes: 12 additions & 7 deletions src/app/scripts/cac/places/cac-places.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@ CAC.Places.Places = (function(_, $, UserPreferences, Utils) {
'use strict';

var module = {
getAllPlaces: getAllPlaces,
queryPlaces: queryPlaces,
getOtpOptions: getOtpOptions,
getDistancesToPlaces: getDistancesToPlaces
};

return module;

/**
* Query Django app for all destinations. If origin set, will order by distance.
* Query Django app for all places, events, and tours.
* If origin set, will order by distance.
*
* @param exploreLatLng {Array} Coordinates of origin point to query with; may be unset
* @param searchText {String} Substring of name of place/event/tour to query for; may be unset
* @return {promise} Promise which resolves to list of destinations
*/
function getAllPlaces(exploreLatLng) {
function queryPlaces(exploreLatLng, searchText) {
var dfd = $.Deferred();
var searchUrl = '/api/destinations/search';
var params = {
url: searchUrl,
type: 'GET'
};

if (!exploreLatLng) {
// if origin is not set, re-fetch all by querying with a blank text search
params.data = {text: ''};
} else {
if (exploreLatLng) {
// use origin
params.data = {
lat: exploreLatLng[0],
lon: exploreLatLng[1]
};
} else if (searchText) {
params.data = {text: searchText};
} else {
// neither origin nor search text set
// re-fetch all by querying with a blank text search
params.data = {text: ''};
}

$.ajax(params).done(function(data) {
Expand Down
4 changes: 2 additions & 2 deletions src/app/scripts/cac/search/cac-typeahead.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,19 @@ CAC.Search.Typeahead = (function (_, $, Geocoder, SearchParams, Utils) {
url: '/api/destinations/search?text=%QUERY',
filter: function (response) {
if (response) {
console.log('typeahead response:');
console.log(response);
var destinations = response.destinations;
var events = _.filter(response.events, function(event) {
return event.destinations.length > 0;
});
var tours = response.tours;

if (destinations.length || events.length || tours.length) {
var all = destinations.concat(events).concat(tours);
// Prefix the IDs to ensure uniqueness.
// These prefixes must match those used by the
// `get_directions_id` Django template helper and by
// the `cardId` Handlebars template helper.
// FIXME: Factor out to utils to share with Handlebars
_.each(all, function(obj) {
var prefix = 'place';
if (obj.is_event) {
Expand Down
1 change: 1 addition & 0 deletions src/app/scripts/cac/urlrouting/cac-urlrouting.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ CAC.UrlRouting.UrlRouter = (function (_, $, UserPreferences, Utils, route) {

var DIRECTIONS_ENCODE = SHARED_ENCODE.concat(['destination',
'destinationText',
'tourMode',
'waypoints']);

var events = $({});
Expand Down
1 change: 1 addition & 0 deletions src/app/scripts/cac/user/cac-user-preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ CAC.User.Preferences = (function(Storages, _) {
mode: 'TRANSIT,WALK',
originText: '',
destinationText: '',
tourMode: false,
waypoints: [],
wheelchair: false
};
Expand Down
4 changes: 2 additions & 2 deletions src/app/styles/components/_directions-results.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
z-index: 1000;
box-shadow: 0 0 16px rgba(128, 128, 128, 0.7);

.routes-list {
.routes-list, .tour-destinations-list {
height: auto;

&:empty {
Expand All @@ -43,7 +43,7 @@
}
}

.route-summary {
.route-summary, .tour-destination-summary {
display: flex;
position: relative;
flex-flow: column nowrap;
Expand Down

0 comments on commit 44434fe

Please sign in to comment.