feat: date and username filtering for photo overlay layers

This commit is contained in:
Nikola Plesa
2020-07-23 13:28:19 +02:00
parent bd9d4bce74
commit 22bc5121a4
10 changed files with 337 additions and 3 deletions

View File

@@ -168,7 +168,8 @@ input[type=search],
input[type=number],
input[type=url],
input[type=tel],
input[type=email] {
input[type=email],
input[type=date] {
background-color: #fff;
color: #333;
border: 1px solid #ccc;
@@ -178,6 +179,11 @@ input[type=email] {
text-overflow: ellipsis;
overflow: hidden;
}
input.list-item-input {
height: 20px;
padding: 0px 4px;
width: 160px;
}
.ideditor[dir='rtl'] textarea,
.ideditor[dir='rtl'] input[type=text],
.ideditor[dir='rtl'] input[type=search],

View File

@@ -740,6 +740,16 @@ en:
panoramic:
title: "Panoramic Photos"
tooltip: "360° photos"
date_filter:
fromDate:
title: "From"
tooltip: "Show photos taken after this date"
toDate:
title: "To"
tooltip: "Show photos taken before this date"
username_filter:
title: "Username"
tooltip: "Show only photos by this user"
feature:
points:
description: Points

14
dist/locales/en.json vendored
View File

@@ -928,6 +928,20 @@
"title": "Panoramic Photos",
"tooltip": "360° photos"
}
},
"date_filter": {
"fromDate": {
"title": "From",
"tooltip": "Show photos taken after this date"
},
"toDate": {
"title": "To",
"tooltip": "Show photos taken before this date"
}
},
"username_filter": {
"title": "Username",
"tooltip": "Show only photos by this user"
}
},
"feature": {

View File

@@ -9,6 +9,10 @@ export function rendererPhotos(context) {
var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
var _allPhotoTypes = ['flat', 'panoramic'];
var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
var _dateFilters = ['fromDate', 'toDate'];
var _fromDate;
var _toDate;
var _username;
function photos() {}
@@ -37,16 +41,43 @@ export function rendererPhotos(context) {
return _allPhotoTypes;
};
photos.dateFilters = function() {
return _dateFilters;
};
photos.dateFilterValue = function(val) {
return val === _dateFilters[0] ? _fromDate : _toDate;
};
photos.setDateFilter = function(type, val) {
if (type === _dateFilters[0]) _fromDate = val;
if (type === _dateFilters[1]) _toDate = val;
dispatch.call('change', this);
};
photos.setUsernameFilter = function(val) {
_username = val;
dispatch.call('change', this);
};
function showsLayer(id) {
var layer = context.layers().layer(id);
return layer && layer.supported() && layer.enabled();
}
photos.shouldFilterByDate = function() {
return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
};
photos.shouldFilterByPhotoType = function() {
return showsLayer('mapillary') ||
(showsLayer('streetside') && showsLayer('openstreetcam'));
};
photos.shouldFilterByUsername = function() {
return showsLayer('mapillary') || showsLayer('openstreetcam');
};
photos.showsPhotoType = function(val) {
if (!photos.shouldFilterByPhotoType()) return true;
@@ -61,6 +92,14 @@ export function rendererPhotos(context) {
return photos.showsPhotoType('panoramic');
};
photos.fromDate = function() {
return _fromDate;
};
photos.toDate = function() {
return _toDate;
};
photos.togglePhotoType = function(val) {
var index = _shownPhotoTypes.indexOf(val);
if (index !== -1) {
@@ -72,6 +111,10 @@ export function rendererPhotos(context) {
return photos;
};
photos.username = function() {
return _username;
};
photos.init = function() {
var hash = utilStringQs(window.location.hash);
if (hash.photo_overlay) {

View File

@@ -46,6 +46,7 @@ var _mlyCache;
var _mlyClicks;
var _mlySelectedImageKey;
var _mlyViewer;
var _mlyViewerFilter = ['all'];
function abortRequest(controller) {
@@ -417,6 +418,34 @@ export default {
});
},
filterViewer: function(context) {
var showsPano = context.photos().showsPanoramic();
var showsFlat = context.photos().showsFlat();
var fromDate = context.photos().fromDate();
var toDate = context.photos().toDate();
var username = context.photos().username();
var filter = ['all'];
if (!showsPano) filter.push(['==', 'pano', false]);
if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
if (username) filter.push(['==', 'username', username]);
if (fromDate) {
var fromTimestamp = new Date(fromDate).getTime();
filter.push(['>=', 'capturedAt', fromTimestamp]);
}
if (toDate) {
var toTimestamp = new Date(toDate).getTime();
filter.push(['>=', 'capturedAt', toTimestamp]);
}
if (_mlyViewer) {
_mlyViewer.setFilter(filter);
}
_mlyViewerFilter = filter;
return filter;
},
showViewer: function(context) {
var wrap = context.container().select('.photoviewer')
@@ -512,6 +541,9 @@ export default {
_mlyViewer.on('bearingchanged', bearingChanged);
_mlyViewer.moveToKey(imageKey)
.catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console
if (_mlyViewerFilter) {
_mlyViewer.setFilter(_mlyViewerFilter);
}
}
// nodeChanged: called after the viewer has changed images and is ready.

View File

@@ -124,18 +124,43 @@ export function svgMapillaryImages(projection, context, dispatch) {
function filterImages(images) {
var showsPano = context.photos().showsPanoramic();
var showsFlat = context.photos().showsFlat();
var fromDate = context.photos().fromDate();
var toDate = context.photos().toDate();
var username = context.photos().username();
if (!showsPano || !showsFlat) {
images = images.filter(function(image) {
if (image.pano) return showsPano;
return showsFlat;
});
}
if (fromDate) {
var fromTimestamp = new Date(fromDate).getTime();
images = images.filter(function(image) {
return new Date(image.captured_at).getTime() >= fromTimestamp;
});
}
if (toDate) {
var toTimestamp = new Date(toDate).getTime();
images = images.filter(function(image) {
return new Date(image.captured_at).getTime() <= toTimestamp;
});
}
if (username) {
images = images.filter(function(image) {
return image.captured_by === username;
});
}
return images;
}
function filterSequences(sequences, service) {
var showsPano = context.photos().showsPanoramic();
var showsFlat = context.photos().showsFlat();
var fromDate = context.photos().fromDate();
var toDate = context.photos().toDate();
var username = context.photos().username();
if (!showsPano || !showsFlat) {
sequences = sequences.filter(function(sequence) {
if (sequence.properties.hasOwnProperty('pano')) {
@@ -157,6 +182,23 @@ export function svgMapillaryImages(projection, context, dispatch) {
}
});
}
if (fromDate) {
var fromTimestamp = new Date(fromDate).getTime();
sequences = sequences.filter(function(sequence) {
return new Date(sequence.captured_at).getTime() >= fromTimestamp;
});
}
if (toDate) {
var toTimestamp = new Date(toDate).getTime();
sequences = sequences.filter(function(sequence) {
return new Date(sequence.captured_at).getTime() <= toTimestamp;
});
}
if (username) {
sequences = sequences.filter(function(sequence) {
return sequence.captured_by === username;
});
}
return sequences;
}
@@ -173,6 +215,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
images = filterImages(images);
sequences = filterSequences(sequences, service);
service.filterViewer(context);
var traces = layer.selectAll('.sequences').selectAll('.sequence')
.data(sequences, function(d) { return d.properties.key; });

View File

@@ -105,6 +105,32 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
context.photos().on('change.openstreetcam_images', update);
function filterImages(images) {
var fromDate = context.photos().fromDate();
var toDate = context.photos().toDate();
var username = context.photos().username();
if (fromDate) {
var fromTimestamp = new Date(fromDate).getTime();
images = images.filter(function(image) {
return new Date(image.captured_at).getTime() >= fromTimestamp;
});
}
if (toDate) {
var toTimestamp = new Date(toDate).getTime();
images = images.filter(function(image) {
return new Date(image.captured_at).getTime() <= toTimestamp;
});
}
if (username) {
images = images.filter(function(image) {
return image.captured_by === username;
});
}
return images;
}
function update() {
var viewer = context.container().select('.photoviewer');
var selected = viewer.empty() ? undefined : viewer.datum();
@@ -121,6 +147,8 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
sequences = (service ? service.sequences(projection) : []);
images = (service && showMarkers ? service.images(projection) : []);
}
images = filterImages(images);
var traces = layer.selectAll('.sequences').selectAll('.sequence')
.data(sequences, function(d) { return d.properties.key; });

View File

@@ -159,6 +159,26 @@ export function svgStreetside(projection, context, dispatch) {
context.photos().on('change.streetside', update);
function filterBubbles(bubbles) {
var fromDate = context.photos().fromDate();
var toDate = context.photos().toDate();
if (fromDate) {
var fromTimestamp = new Date(fromDate).getTime();
bubbles = bubbles.filter(function(bubble) {
return new Date(bubble.captured_at).getTime() >= fromTimestamp;
});
}
if (toDate) {
var toTimestamp = new Date(toDate).getTime();
bubbles = bubbles.filter(function(bubble) {
return new Date(bubble.captured_at).getTime() <= toTimestamp;
});
}
return bubbles;
}
/**
* update().
*/
@@ -178,6 +198,8 @@ export function svgStreetside(projection, context, dispatch) {
bubbles = (service && showMarkers ? service.bubbles(projection) : []);
}
bubbles = filterBubbles(bubbles);
var traces = layer.selectAll('.sequences').selectAll('.sequence')
.data(sequences, function(d) { return d.properties.key; });

View File

@@ -24,7 +24,9 @@ export function uiSectionPhotoOverlays(context) {
.attr('class', 'photo-overlay-container')
.merge(container)
.call(drawPhotoItems)
.call(drawPhotoTypeItems);
.call(drawPhotoTypeItems)
.call(drawDateFilter)
.call(drawUsernameFilter);
}
function drawPhotoItems(selection) {
@@ -92,7 +94,6 @@ export function uiSectionPhotoOverlays(context) {
return t(id.replace(/-/g, '_') + '.title');
});
// Update
li
.merge(liEnter)
@@ -164,6 +165,126 @@ export function uiSectionPhotoOverlays(context) {
.property('checked', typeEnabled);
}
function drawDateFilter(selection) {
var data = context.photos().dateFilters();
function filterEnabled(d) {
return context.photos().dateFilterValue(d);
}
var ul = selection
.selectAll('.layer-list-date-filter')
.data(context.photos().shouldFilterByDate() ? [0] : []);
ul.exit()
.remove();
ul = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-date-filter')
.merge(ul);
var li = ul.selectAll('.list-item-date-filter')
.data(data);
li.exit()
.remove();
var liEnter = li.enter()
.append('li')
.attr('class', 'list-item-date-filter');
var labelEnter = liEnter
.append('label')
.each(function(d) {
d3_select(this)
.call(uiTooltip()
.title(t('photo_overlays.date_filter.' + d + '.tooltip'))
.placement('top')
);
});
labelEnter
.append('span')
.text(function(d) {
return t('photo_overlays.date_filter.' + d + '.title');
});
labelEnter
.append('input')
.attr('type', 'date')
.attr('class', 'list-item-input')
.attr('placeholder', 'dd/mm/yyyy')
.on('change', function(d) {
var value = d3_select(this).property('value');
context.photos().setDateFilter(d, value);
});
li
.merge(liEnter)
.classed('active', filterEnabled)
.selectAll('input')
.property('value', function(d) {
return context.photos().dateFilterValue(d);
});
}
function drawUsernameFilter(selection) {
function filterEnabled() {
return context.photos().username();
}
var ul = selection
.selectAll('.layer-list-username-filter')
.data(context.photos().shouldFilterByUsername() ? [0] : []);
ul.exit()
.remove();
ul = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-username-filter')
.merge(ul);
var li = ul.selectAll('.list-item-username-filter')
.data(['username-filter']);
li.exit()
.remove();
var liEnter = li.enter()
.append('li')
.attr('class', 'list-item-username-filter');
var labelEnter = liEnter
.append('label')
.each(function() {
d3_select(this)
.call(uiTooltip()
.title(t('photo_overlays.username_filter.tooltip'))
.placement('top')
);
});
labelEnter
.append('span')
.text(t('photo_overlays.username_filter.title'));
labelEnter
.append('input')
.attr('type', 'text')
.attr('class', 'list-item-input')
.on('change', function() {
var value = d3_select(this).property('value');
context.photos().setUsernameFilter(value);
});
li
.merge(liEnter)
.classed('active', filterEnabled)
.selectAll('input')
.property('value', context.photos().username());
}
function toggleLayer(which) {
setLayer(which, !showsLayer(which));
}

View File

@@ -368,4 +368,19 @@ describe('iD.serviceMapillary', function() {
});
});
describe('#filterViewer', function() {
it('filters images by username', function() {
context.photos().setUsernameFilter('mapillary');
var filter = mapillary.filterViewer(context);
expect(filter.length).to.be.equal(2);
});
it('filters images by dates', function() {
context.photos().setDateFilter('fromDate', '2020-01-01');
context.photos().setDateFilter('toDate', '2021-01-01');
var filter = mapillary.filterViewer(context);
expect(filter.length).to.be.equal(3);
});
});
});