diff --git a/deployment/ansible/group_vars/all b/deployment/ansible/group_vars/all
index be0b7725f..82d64e30b 100644
--- a/deployment/ansible/group_vars/all
+++ b/deployment/ansible/group_vars/all
@@ -3,15 +3,12 @@ app_username: "vagrant"
packer_version: "0.7.5"
-nodejs_version: 6.11.0
+nodejs_version: 6.11.4
virtualenv_version: 15.1.0
otp_router: "default"
-otp_version: "1.2.0"
-otp_jar_sha1: "a7f659a63a54e894457bab6fc162fb0f47586057"
-
# used by nginx and gunicorn to set timeouts; OTP defaults to 30s
otp_session_timeout_s: 30
diff --git a/deployment/ansible/roles.yml b/deployment/ansible/roles.yml
index 0635a1680..450e41fb1 100644
--- a/deployment/ansible/roles.yml
+++ b/deployment/ansible/roles.yml
@@ -5,7 +5,7 @@
version: 0.2.6
- src: azavea.opentripplanner
- version: 1.0.6
+ version: 1.0.7
- src: azavea.nginx
version: 0.2.2
diff --git a/deployment/ansible/roles/cac-tripplanner.app/defaults/main.yml b/deployment/ansible/roles/cac-tripplanner.app/defaults/main.yml
index fd71bf799..08a202b8e 100644
--- a/deployment/ansible/roles/cac-tripplanner.app/defaults/main.yml
+++ b/deployment/ansible/roles/cac-tripplanner.app/defaults/main.yml
@@ -24,10 +24,10 @@ cac_python_dependencies:
- { name: 'boto', version: '2.48.0' }
- { name: 'django', version: '1.8.18' }
- { name: 'django-ckeditor', version: '5.3.0' }
- - { name: 'django-extensions', version: '1.9.0' }
- - { name: 'django-storages-redux', version: '1.3.3' }
+ - { name: 'django-extensions', version: '1.9.1' }
+ - { name: 'django-storages', version: '1.6.5' }
- { name: 'gunicorn', version: '19.7.1' }
- - { name: 'pillow', version: '4.2.1' }
+ - { name: 'pillow', version: '4.3.0' }
- { name: 'psycopg2', version: '2.7.3' }
- { name: 'pytz', version: '2017.2' }
- { name: 'pyyaml', version: '3.12' }
diff --git a/python/cac_tripplanner/cac_tripplanner/tests.py b/python/cac_tripplanner/cac_tripplanner/tests.py
index d93475562..6378e9b81 100644
--- a/python/cac_tripplanner/cac_tripplanner/tests.py
+++ b/python/cac_tripplanner/cac_tripplanner/tests.py
@@ -59,7 +59,7 @@ def test_isochrone_partitioning(self):
# use current date for query
dt = datetime.now()
day_str = str(dt.date())
- isochrone_start = '/map/reachable?fromPlace=39.954688%2C-75.204677&mode%5B%5D=WALK%2DTRANSIT&time=7%3A30am&cutoffSec=5000&maxWalkDistance=5000'
+ isochrone_start = '/map/reachable?fromPlace=39.954688%2C-75.204677&mode%5B%5D=WALK%2DTRANSIT&time=7%3A30am&cutoffSec=3600&maxWalkDistance=5000'
isochrone_url = ('{start}&date={day_str}').format(start=isochrone_start, day_str=day_str)
response = self.client.get(isochrone_url)
json_response = json.loads(response.content)
@@ -80,7 +80,7 @@ def test_empty_isochrone(self):
dt = datetime.now()
day_str = str(dt.date())
- isochrone_start = '/map/reachable?fromPlace=79.954688%2D-45.204677&mode%5B%5D=WALK%2DTRANSIT&time=7%3A30am&cutoffSec=5000&maxWalkDistance=5000'
+ isochrone_start = '/map/reachable?fromPlace=79.954688%2D-45.204677&mode%5B%5D=WALK%2DTRANSIT&time=7%3A30am&cutoffSec=2000&maxWalkDistance=5000'
isochrone_url = ('{start}&date={day_str}').format(start=isochrone_start, day_str=day_str)
response = self.client.get(isochrone_url)
@@ -89,3 +89,16 @@ def test_empty_isochrone(self):
self.assertEqual(0, len(matched))
isochrone = json_response['isochrone']
self.assertEqual({}, isochrone)
+
+ def test_isochrone_outside_range(self):
+ """Return error if cutoffSec parameter is outside allowed range"""
+
+ # use current date for query
+ dt = datetime.now()
+ day_str = str(dt.date())
+
+ isochrone_start = '/map/reachable?fromPlace=79.954688%2D-45.204677&mode%5B%5D=WALK%2DTRANSIT&time=7%3A30am&cutoffSec=9000&maxWalkDistance=5000'
+ isochrone_url = ('{start}&date={day_str}').format(start=isochrone_start, day_str=day_str)
+
+ response = self.client.get(isochrone_url)
+ self.assertEqual(400, response.status_code)
diff --git a/python/cac_tripplanner/destinations/views.py b/python/cac_tripplanner/destinations/views.py
index 70de5dcee..0c7e26519 100644
--- a/python/cac_tripplanner/destinations/views.py
+++ b/python/cac_tripplanner/destinations/views.py
@@ -132,9 +132,33 @@ def image_to_url(dest_dict, field_name):
return image.url if image else ''
+def set_destination_properties(destination):
+ """Helper for adding and converting properties in serializing destinations as JSON
+
+ :param destination: Destination model object
+ :returns: Dictionary representation of object, with added properties
+ """
+ obj = model_to_dict(destination)
+ obj['address'] = obj['name']
+ obj['image'] = image_to_url(obj, 'image')
+ obj['wide_image'] = image_to_url(obj, 'wide_image')
+ obj['point'] = json.loads(obj['point'].json)
+ # convert to format like properties on ESRI geocoder results
+ x = obj['point']['coordinates'][0]
+ y = obj['point']['coordinates'][1]
+ obj['extent'] = {'xmax': x, 'xmin': x, 'ymax': y, 'ymin': y}
+ obj['location'] = {'x': x, 'y': y}
+ obj['attributes'] = {
+ 'City': obj['city'],
+ 'Postal': obj['zip'],
+ 'Region': obj['state'],
+ 'StAddr': obj['address']
+ }
+ return obj
+
+
class FindReachableDestinations(View):
"""Class based view for fetching isochrone and finding destinations of interest within it"""
- # TODO: make decisions on acceptable ranges of values that this endpoint will support
otp_router = 'default'
isochrone_url = settings.ISOCHRONE_URL
@@ -166,6 +190,12 @@ def get(self, request, *args, **kwargs):
Return both the isochrone GeoJSON and the list of matched destinations."""
params = request.GET.copy() # make mutable
+ # allow a max travelshed size of 60 minutes in a query
+ cutoff_sec = int(params.get('cutoffSec', -1))
+ if not cutoff_sec or cutoff_sec < 0 or cutoff_sec > 3600:
+ return HttpResponse(status=400,
+ reason='cutoffSec must be greater than 0 and less than 360')
+
json_poly = self.isochrone(params)
# Have a FeatureCollection of MultiPolygons
@@ -174,18 +204,14 @@ def get(self, request, *args, **kwargs):
for poly in json_poly['features']:
geom_str = json.dumps(poly['geometry'])
geom = GEOSGeometry(geom_str)
- matched_objects = (Destination.objects.filter(published=True)
+ matched_objects = (Destination.objects.filter(published=True, point__within=geom)
.distance(geom)
.order_by('distance'))
else:
matched_objects = []
# make locations JSON serializable
- matched_objects = [model_to_dict(x) for x in matched_objects]
- for obj in matched_objects:
- obj['point'] = json.loads(obj['point'].json)
- obj['image'] = image_to_url(obj, 'image')
- obj['wide_image'] = image_to_url(obj, 'wide_image')
+ matched_objects = [set_destination_properties(x) for x in matched_objects]
response = {'matched': matched_objects, 'isochrone': json_poly}
return HttpResponse(json.dumps(response), 'application/json')
@@ -239,31 +265,7 @@ def get(self, request, *args, **kwargs):
return HttpResponse(error, 'application/json')
results = results[:limit_int]
- data = [model_to_dict(x) for x in results]
- for obj in data:
- obj['address'] = obj['name']
- obj['point'] = json.loads(obj['point'].json)
- obj['image'] = image_to_url(obj, 'image')
- obj['wide_image'] = image_to_url(obj, 'wide_image')
- # convert to format like properties on ESRI geocoder results
- extent = {
- 'xmax': obj['point']['coordinates'][0],
- 'xmin': obj['point']['coordinates'][0],
- 'ymax': obj['point']['coordinates'][1],
- 'ymin': obj['point']['coordinates'][1]
- }
- obj['extent'] = extent
- obj['attributes'] = {
- 'City': obj['city'],
- 'Postal': obj['zip'],
- 'Region': obj['state'],
- 'StAddr': obj['address']
- }
-
- obj['location'] = {
- 'x': obj['point']['coordinates'][0],
- 'y': obj['point']['coordinates'][1]
- }
+ data = [set_destination_properties(x) for x in results]
response = {'destinations': data}
return HttpResponse(json.dumps(response), 'application/json')
diff --git a/python/cac_tripplanner/templates/base.html b/python/cac_tripplanner/templates/base.html
index 24a68169c..b694fb97c 100644
--- a/python/cac_tripplanner/templates/base.html
+++ b/python/cac_tripplanner/templates/base.html
@@ -81,7 +81,7 @@
{% block jsimports %}
-
+
diff --git a/python/cac_tripplanner/templates/service-worker.js b/python/cac_tripplanner/templates/service-worker.js
index 9b2337496..e27f74932 100644
--- a/python/cac_tripplanner/templates/service-worker.js
+++ b/python/cac_tripplanner/templates/service-worker.js
@@ -1,7 +1,7 @@
// Service Worker to support functioning as a PWA
// https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
-var CACHE_NAME = 'cac_tripplanner_v2';
+var CACHE_NAME = 'cac_tripplanner_v3';
var cacheFiles = {{ cache_files | safe }};
diff --git a/scripts/lint.sh b/scripts/lint.sh
index 9bd70c8d3..fd2f60129 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -1,16 +1,18 @@
#!/bin/bash
function mark_unstable {
- java -jar jenkins-cli.jar -s $JENKINS_URI set-build-result unstable
+ java -jar jenkins-cli.jar -s "$JENKINS_URI set-build-result unstable"
}
set -x
trap 'mark_unstable' ERR
# Python linting
-vagrant ssh app -c "flake8 /opt/app/python --exclude=migrations"
-# run twice to get console output and to write to file
-vagrant ssh app -c "flake8 /opt/app/python --exclude=migrations --output-file=/opt/app/python/violations.txt"
+# first remove contents of violations file, as it will not get overwritten if there are no warnings
+vagrant ssh app -c "touch /opt/app/python/violations.txt"
+# get console output and to write to file
+vagrant ssh app -c "flake8 /opt/app/python --exclude=migrations \
+ --output-file=/opt/app/python/violations.txt --exit-zero --tee"
# Run JS linting
vagrant ssh app -c "cd /opt/app/src && npm run gulp-lint"
diff --git a/src/app/scripts/cac/control/cac-control-directions.js b/src/app/scripts/cac/control/cac-control-directions.js
index 42abbfca6..a4cef905d 100644
--- a/src/app/scripts/cac/control/cac-control-directions.js
+++ b/src/app/scripts/cac/control/cac-control-directions.js
@@ -12,10 +12,13 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
var defaults = {
selectors: {
+ directions: '.directions-results',
hiddenClass: 'hidden',
itineraryBlock: '.route-summary',
+ places: '.places',
selectedItineraryClass: 'selected',
- spinner: '.directions-results > .sk-spinner'
+ spinner: '.directions-results > .sk-spinner',
+ visible: ':visible'
}
};
var options = {};
@@ -29,6 +32,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
var mapControl = null;
var itineraryControl = null;
+ var exploreControl = null;
var tabControl = null;
var urlRouter = null;
var directionsFormControl = null;
@@ -39,6 +43,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
options = $.extend({}, defaults, params);
mapControl = options.mapControl;
tabControl = options.tabControl;
+ exploreControl = options.exploreControl;
itineraryControl = mapControl.itineraryControl;
urlRouter = options.urlRouter;
directionsFormControl = options.directionsFormControl;
@@ -96,6 +101,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
* Throttled to cut down on requests.
*/
var planTrip = _.throttle(function() { // jshint ignore:line
+ showPlaces(false);
if (!(directions.origin && directions.destination)) {
directionsFormControl.setError('origin');
directionsFormControl.setError('destination');
@@ -172,6 +178,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
setFromUserPreferences();
} else {
clearDirections();
+ showPlaces(true);
}
}
@@ -188,11 +195,23 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
}
function showSpinner() {
+ showPlaces(false);
itineraryListControl.hide();
directionsListControl.hide();
$(options.selectors.spinner).removeClass(options.selectors.hiddenClass);
}
+ // helper to call plan trip if a destination is set, or show places list if no destination
+ function planTripOrShowPlaces() {
+ if (directions.destination) {
+ showPlaces(false);
+ planTrip();
+ } else {
+ showPlaces(true);
+ exploreControl.getNearbyPlaces();
+ }
+ }
+
/**
* Get parameters to pass to OpenTripPlanner, based on current settings
*
@@ -234,6 +253,8 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
}
function onDirectionsBackClicked() {
+ // show directions list again
+ showPlaces(false);
// show the other itineraries again
itineraryListControl.showItineraries(true);
currentItinerary.highlight(true);
@@ -246,6 +267,9 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
* Handles click events to select a given itinerary
*/
function onItineraryClicked(event, itinerary) {
+ // hide both the directions list and the places list
+ $(options.selectors.directions).hide();
+ $(options.selectors.places).hide();
if (itinerary) {
// hide all other itineraries
itineraryListControl.showItineraries(false);
@@ -301,13 +325,14 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
*/
function queryWithWaypoints(event, points) {
UserPreferences.setPreference('waypoints', points.waypoints);
+ showPlaces(false);
planTrip();
}
// trigger re-query when trip options update
function setOptions() {
if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
- planTrip();
+ planTripOrShowPlaces();
}
}
@@ -326,7 +351,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
}
// update the directions for the reverse trip
- planTrip();
+ planTripOrShowPlaces();
}
function onTypeaheadCleared(event, key) {
@@ -335,6 +360,8 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
mapControl.clearDirectionsMarker(key);
+ showPlaces(true);
+ exploreControl.getNearbyPlaces();
}
}
@@ -345,7 +372,7 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
}
setDirections(key, [result.location.y, result.location.x]);
if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
- planTrip();
+ planTripOrShowPlaces();
}
}
@@ -381,6 +408,17 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
}
}
+ // toggles between showing directions tab content or places list (explore mode content)
+ function showPlaces(doShowPlaces) {
+ if (doShowPlaces) {
+ $(options.selectors.directions).hide();
+ $(options.selectors.places).show();
+ } else {
+ $(options.selectors.directions).show();
+ $(options.selectors.places).hide();
+ }
+ }
+
// Updates the URL to match the currently-selected options
function updateUrl() {
urlRouter.updateUrl(urlRouter.buildDirectionsUrlFromPrefs());
@@ -402,8 +440,12 @@ CAC.Control.Directions = (function (_, $, moment, Control, Geocoder, Routing, Te
directions.destination = [destination.location.y, destination.location.x ];
}
- if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS) && (origin || destination)) {
- planTrip();
+ if (tabControl.isTabShowing(tabControl.TABS.DIRECTIONS)) {
+ // get nearby places if no destination has been set yet
+ planTripOrShowPlaces();
+ } else {
+ // explore tab visible
+ showPlaces(true);
}
}
diff --git a/src/app/scripts/cac/control/cac-control-explore.js b/src/app/scripts/cac/control/cac-control-explore.js
index d92bd4c94..251637f60 100644
--- a/src/app/scripts/cac/control/cac-control-explore.js
+++ b/src/app/scripts/cac/control/cac-control-explore.js
@@ -37,6 +37,8 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
var exploreLatLng = null;
var fetchingIsochrone = false;
+ var allDestinations = []; // cache full list of destinations
+
function ExploreControl(params) {
options = $.extend({}, defaults, params);
mapControl = options.mapControl;
@@ -57,6 +59,8 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
directionsFormControl.events.on(directionsFormControl.eventNames.geocodeError,
onGeocodeError);
+ showSpinner();
+
if (tabControl.isTabShowing(tabControl.TABS.EXPLORE)) {
setFromUserPreferences();
clickedExplore();
@@ -71,7 +75,7 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
showPlacesContent();
}
- var debouncedFetchIsochrone = _.debounce(fetchIsochrone, ISOCHRONE_DEBOUNCE_MILLIS);
+ var fetchIsochrone = _.debounce(_fetchIsochrone, ISOCHRONE_DEBOUNCE_MILLIS);
var getNearbyPlaces = _.debounce(_getNearbyPlaces, ISOCHRONE_DEBOUNCE_MILLIS);
@@ -86,11 +90,12 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
return ExploreControl;
- // When the explore tab is activated, do the thing. If some other tab is activated, clear the
- // isochrone and destination markers.
+ // When the explore tab is activated, load destinations and isochrone, if origin set.
+ // If some other tab is activated, clear the isochrone and destination markers.
function onTabShown(event, tabId) {
+ // always show spinner on tab change, to avoid stale destinations list flashing
+ showSpinner();
if (tabId === tabControl.TABS.EXPLORE) {
- showSpinner();
UserPreferences.setPreference('method', 'explore');
setFromUserPreferences();
$(options.selectors.isochroneSliderContainer).removeClass(options.selectors.hiddenClass);
@@ -162,17 +167,17 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
mapControl.isochroneControl.clearIsochrone();
if (exploreLatLng) {
- debouncedFetchIsochrone();
+ fetchIsochrone();
+ } else {
+ getNearbyPlaces();
}
-
- getNearbyPlaces();
}
/**
* Load options and compose OTP params, fetch travelshed from OpenTripPlanner,
* then populate side bar with featured locations found within the travelshed.
*/
- function fetchIsochrone() {
+ function _fetchIsochrone() {
showSpinner();
// do not hide spinner until isochrone fetch resolves
@@ -200,9 +205,9 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
mapControl.isochroneControl.fetchIsochrone(exploreLatLng, date, exploreMinutes, otpOptions,
true).then(
- function () {
+ function (data) {
fetchingIsochrone = false;
- showPlacesContent();
+ listIsochronePlaces(data);
}, function (error) {
console.error(error);
fetchingIsochrone = false;
@@ -258,9 +263,10 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
function onTypeaheadCleared(event, key) {
if (key === 'origin') {
exploreLatLng = null;
- // selectedPlaceId = null;
$(options.selectors.alert).remove();
mapControl.isochroneControl.clearIsochrone();
+ // get all places in sidebar when no origin set
+ getNearbyPlaces();
}
}
@@ -327,14 +333,86 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
}
}
- function _getNearbyPlaces() {
+ /**
+ * Helper to build and show templated place cards
+ *
+ * @param destinations {Array} Detination objects to load into template cards
+ * @Param exploreMinutes {String} String representation of integer number of travel minutes
+ the travelshed encompasses; -1 if not in travelshed mode
+ */
+ function displayPlaces(destinations, exploreMinutes) {
+ exploreMinutes = exploreMinutes || '-1';
+ var isTransit = UserPreferences.getPreference('mode').indexOf('TRANSIT') > -1;
+ var isMax = (exploreMinutes === $(options.selectors.isochroneSlider).prop('max'));
+
+ // alternate text string to display if there are no destinations found
+ var text = null;
+ if (!destinations || !destinations.length) {
+ if (exploreMinutes === '-1') {
+ // if not in travel mode, should fetch all destinations; should always have some
+ console.error('No destinations in the app!');
+ text = 'No featured destinations found. Please check back later';
+ } else if (!isTransit && !isMax) {
+ text = 'No featured destinations within ' + exploreMinutes +
+ ' minutes. Try including transit or allowing for more time.';
+ } else if (!isTransit && isMax) {
+ text = 'No featured destinations within ' + exploreMinutes +
+ ' minutes. Try including transit, or removing the travel time limit ' +
+ '(click \"within\" above).';
+ } else if (isTransit && !isMax) {
+ text = 'No featured destinations within ' + exploreMinutes +
+ ' minutes. Try allowing for more time.';
+ } else {
+ text = 'No featured destinations within ' + exploreMinutes +
+ ' minutes. Try removing the travel time limit (click \"within\" above).';
+ }
+ }
+
+ var newPlaces = HomeTemplates.destinations(destinations, text);
+ $(options.selectors.placesContent).html(newPlaces);
+
+ // also draw all destinations on explore map (not just those in the isochrone)
+ if (tabControl.isTabShowing(tabControl.TABS.EXPLORE) && mapControl.isLoaded()) {
+ if (allDestinations.length > 0) {
+ mapControl.isochroneControl.drawDestinations(allDestinations, destinations);
+ } else {
+ // if destinations not cached already, go fetch them
+ getAllPlaces().then(function(fullDestinationsList) {
+ allDestinations = fullDestinationsList;
+ mapControl.isochroneControl.drawDestinations(allDestinations, destinations);
+ }).fail(function(error) {
+ console.error('error fetching destinations to map:');
+ console.error(error);
+ allDestinations = [];
+ });
+ }
+ }
+
+ showPlacesContent();
+
+ // now places list has been updated, go fetch the travel time
+ // from the new origin to each place
+ getTimesToPlaces();
+ }
+
+ // Given desintations from the FindReachableDestinations app endpoint,
+ // display the returned list of places within the travelshed in the sidebar cards.
+ function listIsochronePlaces(destinations) {
showSpinner();
var $placeCards = $(options.selectors.placeCard);
// hide existing times to places now showing (if any)
$placeCards.addClass(options.selectors.noOriginClass);
+ displayPlaces(destinations, $(options.selectors.isochroneSlider).val());
+ }
+ /**
+ * Query Django app for all destinations. If origin set, will order by distance.
+ *
+ * @return {promise} Promise which resolves to list of destinations
+ */
+ function getAllPlaces() {
+ var dfd = $.Deferred();
var searchUrl = '/api/destinations/search';
-
var params = {
url: searchUrl,
type: 'GET'
@@ -355,28 +433,37 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
if (!data || !data.destinations) {
console.error('no places found');
console.error(data);
- showPlacesContent();
- return;
- }
-
- var newPlaces = HomeTemplates.destinations(data.destinations);
- $(options.selectors.placesContent).html(newPlaces);
-
- // also draw on explore map
- if (tabControl.isTabShowing(tabControl.TABS.EXPLORE) && mapControl.isLoaded()) {
- mapControl.isochroneControl.drawDestinations(data.destinations);
+ dfd.resolve([]);
+ } else {
+ dfd.resolve(data.destinations);
}
+ }).fail(function(error) {
+ console.error('error fetching destinations:');
+ console.error(error);
+ dfd.reject();
+ });
+ return dfd.promise();
+ }
- showPlacesContent();
+ function _getNearbyPlaces() {
+ showSpinner();
+ var $placeCards = $(options.selectors.placeCard);
+ // hide existing times to places now showing (if any)
+ $placeCards.addClass(options.selectors.noOriginClass);
- // now places list has been updated, go fetch the travel time
- // from the new origin to each place
- getTimesToPlaces();
+ // use cached results
+ if (allDestinations.length > 0) {
+ displayPlaces(allDestinations, '-1');
+ return;
+ }
+ getAllPlaces().then(function(destinations) {
+ allDestinations = destinations;
+ displayPlaces(destinations, '-1');
}).fail(function(error) {
console.error('error fetching destinations:');
console.error(error);
-
+ allDestinations = [];
showPlacesContent();
});
}
@@ -421,6 +508,9 @@ CAC.Control.Explore = (function (_, $, Geocoder, MapTemplates, HomeTemplates, Ro
.text(originLabel);
$card.removeClass(options.selectors.noOriginClass);
}
+ }).fail(function(error) {
+ console.error('error finding travel time to a place');
+ console.error(error);
});
});
}
diff --git a/src/app/scripts/cac/home/cac-home-templates.js b/src/app/scripts/cac/home/cac-home-templates.js
index 54e23efd6..0a663787f 100644
--- a/src/app/scripts/cac/home/cac-home-templates.js
+++ b/src/app/scripts/cac/home/cac-home-templates.js
@@ -11,10 +11,12 @@ CAC.Home.Templates = (function (Handlebars) {
* Take list of destination objects and return templated HTML snippet for sidebar.
*
* @param useDestinations {Array} Collection of JSON destinations from /api/destinations/search
+ * @param alternateMessage {String} Text to display if there are no destinations
* @return html {String} Snippets for boxes to display on home page for each destination
*/
- function destinations(useDestinations) {
+ function destinations(useDestinations, alternateMessage) {
var source = [
+ '{{#unless alternateMessage}}',
'