Skip to content

Commit

Permalink
Filter and highlight tours and events on map
Browse files Browse the repository at this point in the history
Filter multi-destination events and tours by isochrone on the map page.
Highlight all destinations on card hover for events and tours.

Connects azavea#1137.
  • Loading branch information
flibbertigibbet committed Nov 6, 2019
1 parent df10769 commit 0b1f26b
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 39 deletions.
6 changes: 3 additions & 3 deletions src/app/scripts/cac/control/cac-control-explore.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
if (tabControl.isTabShowing(tabControl.TABS.EXPLORE) && mapControl.isLoaded()) {
// allDestinations has been loaded by now
mapControl.isochroneControl.drawDestinations(filterPlacesCategory(allDestinations),
destinations);
isochroneDestinationIds);
}
showPlacesContent();
}
Expand Down Expand Up @@ -455,8 +455,8 @@ CAC.Control.Explore = (function (_, $, MapTemplates, HomeTemplates, Places, Rout
// Include events or tours with any matching destinations.
return filterPlacesCategory(_.filter(places, function(place) {
const destinationIds = place.destinations ?
_.flatMap(place.destinations, 'id') : [place.placeID];
return _.intersection(isochroneDestinationIds, destinationIds);
_.flatMap(place.destinations, 'id') : [place.id];
return _.intersection(isochroneDestinationIds, destinationIds).length;
}));
}

Expand Down
20 changes: 18 additions & 2 deletions src/app/scripts/cac/home/cac-home-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ 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
Handlebars.registerHelper('cardId', function(isEvent, isTour, id) {
var prefix = isEvent ? 'event' : (isTour ? 'tour' : 'place');
return prefix + '_' + id;
});

// helper to get the IDs for all associated destinations,
// for tours or events, or for this destination
Handlebars.registerHelper('placeIds', function(destinations, id) {
var placeIds = (destinations && destinations.length) ?
_.map(destinations, 'id') : [id];
return JSON.stringify(placeIds);
});

// date/time formatting helpers for events
Handlebars.registerHelper('eventDate', function(dateTime) {
var dt = moment(dateTime); // get ISO string
Expand Down Expand Up @@ -117,7 +131,8 @@ CAC.Home.Templates = (function (Handlebars, moment) {
'{{#each destinations}}',
'<li class="place-card {{#unless this.formattedDistance}}no-origin{{/unless}} ',
'{{#if this.is_event}}event-card{{/if}} {{#if this.is_tour}}tour-card{{/if}}" ',
'data-destination-id="{{ this.id }}_{{this.placeID}}" ',
'data-destination-id="{{ cardId this.is_event this.is_tour this.id }}" ',
'data-destination-places="{{ placeIds this.destinations this.id }}" ',
'data-destination-x="{{ this.location.x }}" ',
'data-destination-y="{{ this.location.y }}">',
'<div class="place-card-photo-container">',
Expand Down Expand Up @@ -166,7 +181,8 @@ CAC.Home.Templates = (function (Handlebars, moment) {
'<div class="place-card-actions">',
'{{#if this.placeID}}',
'<a class="place-card-action place-action-go" ',
'data-destination-id="{{ this.id }}_{{this.placeID}}" ',
'data-destination-id="{{ cardId this.is_event this.is_tour this.id }}" ',
'data-destination-places="{{ placeIds this.destinations this.id }}" ',
'href="#">Directions</a>',
'{{/if}}',
'<a class="place-card-action place-action-details" href=',
Expand Down
74 changes: 45 additions & 29 deletions src/app/scripts/cac/map/cac-map-isochrone.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
var isochroneLayer = null;
var destinationMarkers = {};
var destinationsLayer = null;
var lastHighlightedMarker = null;
var lastHighlightedMarkers = null;
var options = null;
var destinationIcon = L.AwesomeMarkers.icon({
icon: 'default',
Expand Down Expand Up @@ -64,7 +64,7 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
IsochroneControl.prototype.fetchIsochrone = fetchIsochrone;
IsochroneControl.prototype.drawDestinations = drawDestinations;
IsochroneControl.prototype.clearDestinations = clearDestinations;
IsochroneControl.prototype.highlightDestination = highlightDestination;
IsochroneControl.prototype.highlightDestinations = highlightDestinations;

return IsochroneControl;

Expand Down Expand Up @@ -214,30 +214,33 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
* Draw an array of geojson destination points onto the map
*
* @param {Array} all All destinations to draw
* @param {Array} matched Destinations witin the travelshed; styles differ for the markers
* @param {Array} IDs of matched Destinations witin the travelshed
*/
function drawDestinations(all, matched) {
// put destination details onto point geojson object's properties
// build map of unconverted destination objects
var destinations = {};
clearDestinations();

var locationGeoJSON = _.chain(all).reject(function(place) {
// Do not attempt to map events without a location (destination)
return !place.placeID;
}).map(function(destination) {
// index by combination of ID and place ID to prevent conflict between
// event and destination IDs
destinations[destination.id + '_' + destination.placeID] = destination;
var locationGeoJSON = _.chain(all).map(function(place) {
var destinations = [place];
// Pull in any unpublished destinations on events or tours
if (place.destinations && place.destinations.length) {
destinations = place.destinations;
}
return destinations;
}).flatten().uniqBy('id').map(function(destination) {
// set matched property to true if place is within isochrone
destination.matched = _.findIndex(matched, function(match) {
return match === destination.id;
}) > -1;
destinations[destination.id] = destination;
var point = _.property('point')(destination);
point.properties = _.omit(destination, 'point');

// set matched property to true if destination is within isochrone
point.properties.matched = _.findIndex(matched, function(match) {
return match.placeID && (match.placeID === destination.placeID);
}) > -1;
point.properties.matched = !!destination.matched
return point;
}).value();

destinationMarkers = {};
destinationsLayer = cartodb.L.geoJson(locationGeoJSON, {
pointToLayer: function (geojson, latLng) {
Expand All @@ -250,18 +253,19 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
'href="{{geojson.properties.website_url}}" ',
'target="_blank">Visit website</a>',
'<a class="destination-directions-link" ',
'id="{{geojson.properties.id}}_{{geojson.properties.placeID}}"',
'id="{{geojson.properties.id}}"',
'>Get Directions</a></p>'
].join('');
var template = Handlebars.compile(popupTemplate);
var popupContent = template({geojson: geojson});
var markerId = geojson.properties.id + '_' + geojson.properties.placeID;
var markerId = geojson.properties.id;

// use a different icon for places outside of the travel than those within it
var useIcon = geojson.properties.matched ? destinationIcon:
destinationOutsideTravelshedIcon;
var marker = new cartodb.L.marker(latLng, {icon: useIcon})
.bindPopup(popupContent, {className: options.selectors.poiPopupClassName});
marker.matched = geojson.properties.matched;
destinationMarkers[markerId] = {
marker: marker,
destination: destinations[markerId]
Expand All @@ -281,25 +285,37 @@ CAC.Map.IsochroneControl = (function ($, Handlebars, cartodb, L, turf, _) {
}).addTo(map);
}

function highlightDestination(destinationId, opts) {
function highlightDestinations(destinationIds, opts) {
var defaults = {
panTo: false
};

var highlightOpts = $.extend({}, defaults, opts);
if (!destinationId || !destinationMarkers[destinationId]) {
// revert to original marker if set
if (lastHighlightedMarker) {
lastHighlightedMarker.setIcon(destinationIcon);
}

// If passed no destinations to highlight, un-highlight instead
if ((!destinationIds || !destinationIds.length) && lastHighlightedMarkers) {
_.each(lastHighlightedMarkers, function(marker) {
var icon = marker.matched ? destinationIcon:
destinationOutsideTravelshedIcon;
marker.setIcon(icon);
});
lastHighlightedMarkers = null;
return;
}
// Update icon for passed destination
var marker = destinationMarkers[destinationId].marker;
marker.setIcon(highlightIcon);
if (highlightOpts.panTo) {
map.panTo(marker.getLatLng());
// Update icons for passed destinations
lastHighlightedMarkers = [];
_.each(destinationIds, function(placeId) {
var marker = destinationMarkers[placeId];
if (marker) {
marker = marker.marker;
marker.setIcon(highlightIcon);
lastHighlightedMarkers.push(marker);
}
});
// pan to the first destination of those passed, if panning option set
if (highlightOpts.panTo && lastHighlightedMarkers.length) {
map.panTo(lastHighlightedMarkers[0].getLatLng());
}
lastHighlightedMarker = marker;
}

function clearDestinations() {
Expand Down
12 changes: 7 additions & 5 deletions src/app/scripts/cac/pages/cac-pages-home.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,23 +387,25 @@ CAC.Pages.Home = (function ($, FilterOptions, ModeOptions, MapControl, TripOpti
if (!mapControl || !mapControl.isochroneControl) {
return;
}
var placeId = $(event.target).closest(options.selectors.placeCard).data('destination-id');
mapControl.isochroneControl.highlightDestination(placeId, { panTo: true });
var placeCard = $(event.target).closest(options.selectors.placeCard);
var placeIds = placeCard.data('destination-places');
mapControl.isochroneControl.highlightDestinations([placeId], { panTo: true });
}

function onPlaceHovered(event) {
if (!mapControl || !mapControl.isochroneControl) {
return;
}
var placeId = $(event.target).closest(options.selectors.placeCard).data('destination-id');
mapControl.isochroneControl.highlightDestination(placeId);
var placeCard = $(event.target).closest(options.selectors.placeCard);
var placeIds = placeCard.data('destination-places');
mapControl.isochroneControl.highlightDestinations(placeIds);
}

function onPlaceBlurred() {
if (!mapControl || !mapControl.isochroneControl) {
return;
}
mapControl.isochroneControl.highlightDestination(null);
mapControl.isochroneControl.highlightDestinations(null);
}

/**
Expand Down

0 comments on commit 0b1f26b

Please sign in to comment.