diff --git a/API.md b/API.md index 4e4f994fe..de5a9a21c 100644 --- a/API.md +++ b/API.md @@ -45,8 +45,8 @@ of iD (e.g. `https://ideditor-release.netlify.app`), the following parameters ar _Available values:_ `streetside` (Microsoft Bing), `mapillary`, `mapillary-signs`, `mapillary-map-features`, `openstreetcam` * __`photo_dates`__ - The range of capture dates by which to filter street-level photos. Dates are given in YYYY-MM-DD format and separated by `_`. One-sided ranges are supported.
_Example:_ `photo_dates=2019-01-01_2020-12-31`, `photo_dates=2019-01-01_`, `photo_dates=_2020-12-31`
-* __`photo_username`__ - The Mapillary or OpenStreetCam username by which to filter street-level photos.
- _Example:_ `photo_user=quincylvania`
+* __`photo_username`__ - The Mapillary or OpenStreetCam username by which to filter street-level photos. Multiple comma-separated usernames are supported.
+ _Example:_ `photo_user=quincylvania`, `photo_user=quincylvania,chrisbeddow`
* __`photo`__ - The service and ID of the street-level photo to show.
_Example:_ `photo=streetside/718514589`
_Available prefixes:_ `streetside/`, `mapillary/`, `openstreetcam/` diff --git a/modules/renderer/photos.js b/modules/renderer/photos.js index 865797e5e..d8267a048 100644 --- a/modules/renderer/photos.js +++ b/modules/renderer/photos.js @@ -13,7 +13,7 @@ export function rendererPhotos(context) { var _dateFilters = ['fromDate', 'toDate']; var _fromDate; var _toDate; - var _username; + var _usernames; function photos() {} @@ -81,10 +81,21 @@ export function rendererPhotos(context) { }; photos.setUsernameFilter = function(val, updateUrl) { - _username = val; + if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(','); + if (val) { + val = val.map(d => d.trim()).filter(Boolean); + if (!val.length) { + val = null; + } + } + _usernames = val; dispatch.call('change', this); if (updateUrl) { - setUrlFilterValue('photo_username', val); + var hashString; + if (_usernames) { + hashString = _usernames.join(','); + } + setUrlFilterValue('photo_username', hashString); } }; @@ -117,7 +128,7 @@ export function rendererPhotos(context) { }; photos.shouldFilterByUsername = function() { - return showsLayer('mapillary') || showsLayer('openstreetcam'); + return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside'); }; photos.showsPhotoType = function(val) { @@ -153,8 +164,8 @@ export function rendererPhotos(context) { return photos; }; - photos.username = function() { - return _username; + photos.usernames = function() { + return _usernames; }; photos.init = function() { diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index ee44d441f..60e969f5a 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -492,12 +492,12 @@ export default { var showsFlat = context.photos().showsFlat(); var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); var filter = ['all']; if (!showsPano) filter.push(['==', 'pano', false]); if (!showsFlat && showsPano) filter.push(['==', 'pano', true]); - if (username) filter.push(['==', 'username', username]); + if (usernames && usernames.length) filter.push(['==', 'username', usernames[0]]); if (fromDate) { var fromTimestamp = new Date(fromDate).getTime(); filter.push(['>=', 'capturedAt', fromTimestamp]); diff --git a/modules/svg/mapillary_images.js b/modules/svg/mapillary_images.js index 88b148e20..515bdda12 100644 --- a/modules/svg/mapillary_images.js +++ b/modules/svg/mapillary_images.js @@ -116,7 +116,7 @@ export function svgMapillaryImages(projection, context, dispatch) { var showsFlat = context.photos().showsFlat(); var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (!showsPano || !showsFlat) { images = images.filter(function(image) { @@ -136,9 +136,9 @@ export function svgMapillaryImages(projection, context, dispatch) { return new Date(image.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { images = images.filter(function(image) { - return image.captured_by === username; + return usernames.indexOf(image.captured_by) !== -1; }); } return images; @@ -149,7 +149,7 @@ export function svgMapillaryImages(projection, context, dispatch) { var showsFlat = context.photos().showsFlat(); var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (!showsPano || !showsFlat) { sequences = sequences.filter(function(sequence) { @@ -184,9 +184,9 @@ export function svgMapillaryImages(projection, context, dispatch) { return new Date(sequence.properties.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { sequences = sequences.filter(function(sequence) { - return sequence.properties.username === username; + return usernames.indexOf(sequence.properties.username) !== -1; }); } diff --git a/modules/svg/openstreetcam_images.js b/modules/svg/openstreetcam_images.js index aac65acdd..786ac4a79 100644 --- a/modules/svg/openstreetcam_images.js +++ b/modules/svg/openstreetcam_images.js @@ -110,7 +110,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { function filterImages(images) { var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (fromDate) { var fromTimestamp = new Date(fromDate).getTime(); @@ -124,9 +124,9 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { return new Date(item.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { images = images.filter(function(item) { - return item.captured_by === username; + return usernames.indexOf(item.captured_by) !== -1; }); } @@ -136,7 +136,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { function filterSequences(sequences) { var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (fromDate) { var fromTimestamp = new Date(fromDate).getTime(); @@ -150,9 +150,9 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { return new Date(image.properties.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { sequences = sequences.filter(function(image) { - return image.properties.captured_by === username; + return usernames.indexOf(image.properties.captured_by) !== -1; }); } @@ -177,7 +177,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { sequences = filterSequences(sequences); images = filterImages(images); } - + var traces = layer.selectAll('.sequences').selectAll('.sequence') .data(sequences, function(d) { return d.properties.key; }); diff --git a/modules/svg/streetside.js b/modules/svg/streetside.js index e591824b7..ab1bc4a15 100644 --- a/modules/svg/streetside.js +++ b/modules/svg/streetside.js @@ -162,7 +162,7 @@ export function svgStreetside(projection, context, dispatch) { function filterBubbles(bubbles) { var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (fromDate) { var fromTimestamp = new Date(fromDate).getTime(); @@ -176,9 +176,9 @@ export function svgStreetside(projection, context, dispatch) { return new Date(bubble.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { bubbles = bubbles.filter(function(bubble) { - return bubble.captured_by === username; + return usernames.indexOf(bubble.captured_by) !== -1; }); } @@ -188,7 +188,7 @@ export function svgStreetside(projection, context, dispatch) { function filterSequences(sequences) { var fromDate = context.photos().fromDate(); var toDate = context.photos().toDate(); - var username = context.photos().username(); + var usernames = context.photos().usernames(); if (fromDate) { var fromTimestamp = new Date(fromDate).getTime(); @@ -202,9 +202,9 @@ export function svgStreetside(projection, context, dispatch) { return new Date(sequences.properties.captured_at).getTime() <= toTimestamp; }); } - if (username) { + if (usernames) { sequences = sequences.filter(function(sequences) { - return sequences.properties.captured_by === username; + return usernames.indexOf(sequences.properties.captured_by) !== -1; }); } diff --git a/modules/ui/sections/photo_overlays.js b/modules/ui/sections/photo_overlays.js index 9ea1a8739..7d661c5b1 100644 --- a/modules/ui/sections/photo_overlays.js +++ b/modules/ui/sections/photo_overlays.js @@ -237,7 +237,7 @@ export function uiSectionPhotoOverlays(context) { function drawUsernameFilter(selection) { function filterEnabled() { - return context.photos().username(); + return context.photos().usernames(); } var ul = selection .selectAll('.layer-list-username-filter') @@ -280,15 +280,22 @@ export function uiSectionPhotoOverlays(context) { .attr('type', 'text') .attr('class', 'list-item-input') .call(utilNoAuto) - .property('value', context.photos().username()) + .property('value', usernameValue) .on('change', function() { var value = d3_select(this).property('value'); context.photos().setUsernameFilter(value, true); + d3_select(this).property('value', usernameValue); }); li .merge(liEnter) .classed('active', filterEnabled); + + function usernameValue() { + var usernames = context.photos().usernames(); + if (usernames) return usernames.join('; '); + return usernames; + } } function toggleLayer(which) {