mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 17:23:02 +00:00
290 lines
9.8 KiB
JavaScript
290 lines
9.8 KiB
JavaScript
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||
|
||
import { services } from '../services';
|
||
import { utilRebind } from '../util/rebind';
|
||
import { utilQsString, utilStringQs } from '../util';
|
||
|
||
|
||
export function rendererPhotos(context) {
|
||
var dispatch = d3_dispatch('change');
|
||
var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'kartaview', 'mapilio', 'vegbilder', 'panoramax'];
|
||
var _allPhotoTypes = ['flat', 'panoramic'];
|
||
var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
|
||
var _dateFilters = ['fromDate', 'toDate'];
|
||
var _fromDate;
|
||
var _toDate;
|
||
var _usernames;
|
||
|
||
function photos() {}
|
||
|
||
function updateStorage() {
|
||
var hash = utilStringQs(window.location.hash);
|
||
var enabled = context.layers().all().filter(function(d) {
|
||
return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
|
||
}).map(function(d) {
|
||
return d.id;
|
||
});
|
||
if (enabled.length) {
|
||
hash.photo_overlay = enabled.join(',');
|
||
} else {
|
||
delete hash.photo_overlay;
|
||
}
|
||
window.history.replaceState(null, '', '#' + utilQsString(hash, true));
|
||
}
|
||
|
||
/**
|
||
* @returns The layer ID
|
||
*/
|
||
photos.overlayLayerIDs = function() {
|
||
return _layerIDs;
|
||
};
|
||
|
||
/**
|
||
* @returns All the photo types
|
||
*/
|
||
photos.allPhotoTypes = function() {
|
||
return _allPhotoTypes;
|
||
};
|
||
|
||
/**
|
||
* @returns The date filters value
|
||
*/
|
||
photos.dateFilters = function() {
|
||
return _dateFilters;
|
||
};
|
||
|
||
photos.dateFilterValue = function(val) {
|
||
return val === _dateFilters[0] ? _fromDate : _toDate;
|
||
};
|
||
|
||
/**
|
||
* Sets the date filter (min/max date)
|
||
* @param {*} type Either 'fromDate' or 'toDate'
|
||
* @param {*} val The actual Date
|
||
* @param {boolean} updateUrl Whether the URL should update or not
|
||
*/
|
||
photos.setDateFilter = function(type, val, updateUrl) {
|
||
// validate the date
|
||
var date = val && new Date(val);
|
||
if (date && !isNaN(date)) {
|
||
val = date.toISOString().slice(0, 10);
|
||
} else {
|
||
val = null;
|
||
}
|
||
if (type === _dateFilters[0]) {
|
||
_fromDate = val;
|
||
if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
|
||
_toDate = _fromDate;
|
||
}
|
||
}
|
||
if (type === _dateFilters[1]) {
|
||
_toDate = val;
|
||
if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
|
||
_fromDate = _toDate;
|
||
}
|
||
}
|
||
dispatch.call('change', this);
|
||
if (updateUrl) {
|
||
var rangeString;
|
||
if (_fromDate || _toDate) {
|
||
rangeString = (_fromDate || '') + '_' + (_toDate || '');
|
||
}
|
||
setUrlFilterValue('photo_dates', rangeString);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Sets the username filter
|
||
* @param {string} val The username
|
||
* @param {boolean} updateUrl Whether the URL should update or not
|
||
*/
|
||
photos.setUsernameFilter = function(val, updateUrl) {
|
||
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) {
|
||
var hashString;
|
||
if (_usernames) {
|
||
hashString = _usernames.join(',');
|
||
}
|
||
setUrlFilterValue('photo_username', hashString);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Util function to set the slider date filter
|
||
* @param {*} val Either 'panoramic' or 'flat'
|
||
* @param {boolean} updateUrl Whether the URL should update or not
|
||
*/
|
||
photos.togglePhotoType = function(val, updateUrl) {
|
||
var index = _shownPhotoTypes.indexOf(val);
|
||
if (index !== -1) {
|
||
_shownPhotoTypes.splice(index, 1);
|
||
} else {
|
||
_shownPhotoTypes.push(val);
|
||
}
|
||
|
||
if (updateUrl) {
|
||
var hashString;
|
||
if (_shownPhotoTypes) {
|
||
hashString = _shownPhotoTypes.join(',');
|
||
}
|
||
setUrlFilterValue('photo_type', hashString);
|
||
}
|
||
|
||
dispatch.call('change', this);
|
||
return photos;
|
||
};
|
||
|
||
/**
|
||
* Updates the URL with new values
|
||
* @param {*} val value to save
|
||
* @param {string} property Name of the value
|
||
*/
|
||
function setUrlFilterValue(property, val) {
|
||
const hash = utilStringQs(window.location.hash);
|
||
if (val) {
|
||
if (hash[property] === val) return;
|
||
hash[property] = val;
|
||
} else {
|
||
if (!(property in hash)) return;
|
||
delete hash[property];
|
||
}
|
||
window.history.replaceState(null, '', '#' + utilQsString(hash, true));
|
||
}
|
||
|
||
function showsLayer(id) {
|
||
var layer = context.layers().layer(id);
|
||
return layer && layer.supported() && layer.enabled();
|
||
}
|
||
|
||
/**
|
||
* @returns If the Date Slider filter should be drawn
|
||
*/
|
||
photos.shouldFilterDateBySlider = function(){
|
||
return showsLayer('mapillary') || showsLayer('kartaview') || showsLayer('mapilio')
|
||
|| showsLayer('streetside') || showsLayer('vegbilder') || showsLayer('panoramax');
|
||
};
|
||
|
||
/**
|
||
* @returns If the Photo Type filter should be drawn
|
||
*/
|
||
photos.shouldFilterByPhotoType = function() {
|
||
return showsLayer('mapillary') ||
|
||
(showsLayer('streetside') && showsLayer('kartaview')) || showsLayer('vegbilder') || showsLayer('panoramax');
|
||
};
|
||
|
||
/**
|
||
* @returns If the Username filter should be drawn
|
||
*/
|
||
photos.shouldFilterByUsername = function() {
|
||
return !showsLayer('mapillary') && showsLayer('kartaview') && !showsLayer('streetside') || showsLayer('panoramax');
|
||
};
|
||
|
||
photos.showsPhotoType = function(val) {
|
||
if (!photos.shouldFilterByPhotoType()) return true;
|
||
|
||
return _shownPhotoTypes.indexOf(val) !== -1;
|
||
};
|
||
|
||
photos.showsFlat = function() {
|
||
return photos.showsPhotoType('flat');
|
||
};
|
||
|
||
photos.showsPanoramic = function() {
|
||
return photos.showsPhotoType('panoramic');
|
||
};
|
||
|
||
photos.fromDate = function() {
|
||
return _fromDate;
|
||
};
|
||
|
||
photos.toDate = function() {
|
||
return _toDate;
|
||
};
|
||
|
||
photos.usernames = function() {
|
||
return _usernames;
|
||
};
|
||
|
||
/**
|
||
* Inits the streetlevel layer given the saved values in the URL
|
||
*/
|
||
photos.init = function() {
|
||
var hash = utilStringQs(window.location.hash);
|
||
var parts;
|
||
if (hash.photo_dates) {
|
||
// expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
|
||
parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
|
||
this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
|
||
this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
|
||
}
|
||
if (hash.photo_username) {
|
||
this.setUsernameFilter(hash.photo_username, false);
|
||
}
|
||
if (hash.photo_type) {
|
||
parts = hash.photo_type.replace(/;/g, ',').split(',');
|
||
_allPhotoTypes.forEach(d => {
|
||
if (!parts.includes(d)) this.togglePhotoType(d, false);
|
||
});
|
||
}
|
||
if (hash.photo_overlay) {
|
||
// support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=kartaview;mapillary;streetside`
|
||
var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
|
||
hashOverlayIDs.forEach(function(id) {
|
||
if (id === 'openstreetcam') id = 'kartaview'; // legacy alias
|
||
var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
|
||
if (layer && !layer.enabled()) layer.enabled(true);
|
||
});
|
||
}
|
||
if (hash.photo) {
|
||
// support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
|
||
var photoIds = hash.photo.replace(/;/g, ',').split(',');
|
||
var photoId = photoIds.length && photoIds[0].trim();
|
||
var results = /(.*)\/(.*)/g.exec(photoId);
|
||
if (results && results.length >= 3) {
|
||
var serviceId = results[1];
|
||
if (serviceId === 'openstreetcam') serviceId = 'kartaview'; // legacy alias
|
||
var photoKey = results[2];
|
||
var service = services[serviceId];
|
||
if (service && service.ensureViewerLoaded) {
|
||
|
||
// if we're showing a photo then make sure its layer is enabled too
|
||
var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
|
||
if (layer && !layer.enabled()) layer.enabled(true);
|
||
|
||
var baselineTime = Date.now();
|
||
|
||
service.on('loadedImages.rendererPhotos', function() {
|
||
// don't open the viewer if too much time has elapsed
|
||
if (Date.now() - baselineTime > 45000) {
|
||
service.on('loadedImages.rendererPhotos', null);
|
||
return;
|
||
}
|
||
|
||
if (!service.cachedImage(photoKey)) return;
|
||
|
||
service.on('loadedImages.rendererPhotos', null);
|
||
service.ensureViewerLoaded(context)
|
||
.then(function() {
|
||
service
|
||
.selectImage(context, photoKey)
|
||
.showViewer(context);
|
||
});
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
context.layers().on('change.rendererPhotos', updateStorage);
|
||
};
|
||
|
||
return utilRebind(photos, dispatch, 'on');
|
||
}
|