diff --git a/css/60_photos.css b/css/60_photos.css index c644eada5..76612928b 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -341,6 +341,19 @@ label.panoramax-hd { margin: 0 5px; } +.slider-wrap { + display: inline-block; +} + +.year-datalist { + display: flex; + justify-content: space-between; +} + +.list-option-date-slider{ + direction: rtl +} + /* Streetside Viewer (pannellum) */ .ms-wrapper .photo-attribution .image-link { @@ -621,11 +634,3 @@ label.streetside-hires { cursor: pointer; } -.slider-wrap { - display: inline-block; -} - -.year-datalist { - display: flex; - justify-content: space-between; -} \ No newline at end of file diff --git a/modules/renderer/photos.js b/modules/renderer/photos.js index 3b0646b4b..0fdae6a4b 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 _maxPhotoAge; + var _maxPhotoDate; var _maxPhotoYear; var _usernames; @@ -49,7 +49,7 @@ export function rendererPhotos(context) { }; photos.maxPhotoAge = function() { - return _maxPhotoAge; + return _maxPhotoDate; }; photos.maxPhotoYear = function() { @@ -90,33 +90,32 @@ export function rendererPhotos(context) { } }; - photos.setMaxPhotoAge = function(maxPhotoAge){ - if(maxPhotoAge != -1){ + photos.setFromDateFilter = function(date){ + if (date !== -1){ var fromDate = new Date(); - fromDate.setDate(fromDate.getDate() - maxPhotoAge); + fromDate.setDate(fromDate.getDate() - date); var dd = String(fromDate.getDate()).padStart(2, '0'); var mm = String(fromDate.getMonth() + 1).padStart(2, '0'); var yyyy = fromDate.getFullYear(); fromDate = mm + '/' + dd + '/' + yyyy; - photos.setDateFilter('fromDate', fromDate) - _maxPhotoAge = maxPhotoAge; - } - else - photos.setDateFilter('fromDate', null) - } - - photos.setMaxPhotoYear = function(maxPhotoYear){ - if(maxPhotoYear != null){ - var fromDate = new Date(); - fromDate = '01/01/' + maxPhotoYear; photos.setDateFilter('fromDate', fromDate); - _maxPhotoYear = maxPhotoYear; - } - else{ + _maxPhotoDate = date; + } else { photos.setDateFilter('fromDate', null); } - } + }; + + photos.setFromYearFilter = function(year){ + if (year !== null){ + var fromDate = new Date(); + fromDate = '01/01/' + year; + photos.setDateFilter('fromDate', fromDate); + _maxPhotoYear = year; + } else { + photos.setDateFilter('fromDate', null); + } + }; photos.setUsernameFilter = function(val, updateUrl) { @@ -163,11 +162,11 @@ export function rendererPhotos(context) { photos.shouldFilterByMaxAge = function(){ return false; - } + }; photos.shouldFilterDateBySlider = function(){ return showsLayer('panoramax'); - } + }; photos.shouldFilterByPhotoType = function() { return showsLayer('mapillary') || diff --git a/modules/services/panoramax.js b/modules/services/panoramax.js index b68b47507..ca99f8d2f 100644 --- a/modules/services/panoramax.js +++ b/modules/services/panoramax.js @@ -4,22 +4,21 @@ import Protobuf from 'pbf'; import RBush from 'rbush'; import { VectorTile } from '@mapbox/vector-tile'; -import { utilRebind, utilTiler, utilQsString, utilStringQs, utilSetTransform, utilUniqueDomId} from '../util'; +import { utilRebind, utilTiler, utilQsString, utilStringQs, utilUniqueDomId} from '../util'; import { geoExtent, geoScaleToZoom } from '../geo'; import { t, localizer } from '../core/localizer'; import pannellumPhotoFrame from './pannellum_photo'; import planePhotoFrame from './plane_photo'; const apiUrl = 'https://panoramax.openstreetmap.fr/'; -const tileUrl = apiUrl + 'api/map/{z}/{x}/{y}.pbf'; +const tileUrl = apiUrl + 'api/map/{z}/{x}/{y}.mvt'; const imageBlobUrl = apiUrl + 'api/pictures/{pictureID}/{definition}.jpg'; const imageDataUrl = apiUrl + 'api/collections/{collectionId}/items/{itemId}'; -const userIdUrl = apiUrl + 'api/users/search?q={username}' -const usernameURL = apiUrl + 'api/users/{userId}' +const userIdUrl = apiUrl + 'api/users/search?q={username}'; +const usernameURL = apiUrl + 'api/users/{userId}'; -const highDefinition = "hd"; -const standardDefinition = "sd"; -const thumbnailDefinition = "thumb"; +const highDefinition = 'hd'; +const standardDefinition = 'sd'; const pictureLayer = 'pictures'; const sequenceLayer = 'sequences'; @@ -31,7 +30,6 @@ const dispatch = d3_dispatch('loadedImages', 'loadedLines', 'viewerChanged'); let _cache; let _loadViewerPromise; -let _pannellumViewer; let _definition = standardDefinition; let _isHD = false; @@ -153,23 +151,23 @@ function loadTileDataToCache(data, tile) { capture_time: feature.properties.ts, id: feature.properties.id, account_id: feature.properties.account_id, - sequence_id: feature.properties.sequences.split("\"")[1], + sequence_id: feature.properties.sequences.split('\"')[1], heading: feature.properties.heading, - image_path: "", + image_path: '', resolution: feature.properties.resolution, - isPano: feature.properties.type == "equirectangular", + isPano: feature.properties.type === 'equirectangular', model: feature.properties.model, }; cache.forImageId[d.id] = d; features.push({ minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d }); - - if(_oldestDate){ - if(d.capture_time < _oldestDate) + + if (_oldestDate){ + if (d.capture_time < _oldestDate){ _oldestDate = d.capture_time; - } - else{ + } + } else { _oldestDate = d.capture_time; } } @@ -189,16 +187,15 @@ function loadTileDataToCache(data, tile) { } else { cache.lineString[feature.properties.id] = [feature]; } - if(_oldestDate){ - if(feature.properties.date < _oldestDate) + if (_oldestDate){ + if (feature.properties.date < _oldestDate){ _oldestDate = feature.properties.date; - } - else{ + } + } else { _oldestDate = feature.properties.date; } } } - } // Quick access to image @@ -211,7 +208,7 @@ function getImageURL(image_id, definition){ async function getImageData(collection_id, image_id){ const requestUrl = imageDataUrl.replace('{collectionId}', collection_id) - .replace('{itemId}', image_id) + .replace('{itemId}', image_id); const response = await fetch(requestUrl, { method: 'GET' }); if (!response.ok) { @@ -266,7 +263,6 @@ export default { return _cache.images.forImageId[imageKey]; }, - // Load images in the visible area loadImages: function(projection) { loadTiles('images', tileUrl, imageMinZoom, projection); @@ -279,7 +275,7 @@ export default { getUserId: async function(username){ const requestUrl = userIdUrl.replace('{username}', username); - + const response = await fetch(requestUrl, { method: 'GET' }); if (!response.ok) { throw new Error(response.status + ' ' + response.statusText); @@ -289,8 +285,9 @@ export default { }, getOldestDate: function(){ - if(_oldestDate) + if (_oldestDate){ return _oldestDate.substr(0, 4); + } }, // Get visible sequences @@ -302,8 +299,7 @@ export default { const sequenceIds = {}; let lineStrings = []; - - if(zoom >= imageMinZoom){ + if (zoom >= imageMinZoom){ _cache.images.rtree.search(bbox).forEach(function(d) { if (d.data.sequence_id) { sequenceIds[d.data.sequence_id] = true; @@ -316,11 +312,11 @@ export default { }); return lineStrings; } - if(zoom >= lineMinZoom){ + if (zoom >= lineMinZoom){ Object.keys(_cache.sequences.lineString).forEach(function(sequenceId) { lineStrings = lineStrings.concat(_cache.sequences.lineString[sequenceId]); }); - } + } return lineStrings; }, @@ -395,8 +391,7 @@ export default { let viewer = context.container() .select('.photoviewer'); - if (!viewer.empty()) - viewer.datum(d); + if (!viewer.empty()) viewer.datum(d); this.setStyles(context, null); @@ -404,7 +399,7 @@ export default { let wrap = context.container() .select('.photoviewer .panoramax-wrapper'); - + let attribution = wrap.selectAll('.photo-attribution').text(''); let line1 = attribution @@ -449,7 +444,7 @@ export default { attribution .append('a') .attr('class', 'report-photo') - .attr('href', "mailto:signalement.ign@panoramax.fr") + .attr('href', 'mailto:signalement.ign@panoramax.fr') .call(t.append('panoramax.report')); attribution @@ -464,35 +459,37 @@ export default { .text('panoramax.fr'); getImageData(d.sequence_id, d.id).then(function(data){ - _currentScene = { currentImage: null, nextImage: null, prevImage: null }; - _currentScene.currentImage = data["assets"][_definition]; - const nextIndex = data.links.findIndex(x => x.rel == "next"); - const prevIndex = data.links.findIndex(x => x.rel == "prev"); - if (nextIndex != -1) + _currentScene.currentImage = data.assets[_definition]; + const nextIndex = data.links.findIndex(x => x.rel === 'next'); + const prevIndex = data.links.findIndex(x => x.rel === 'prev'); + + if (nextIndex !== -1){ _currentScene.nextImage = data.links[nextIndex]; - if (prevIndex != -1) + } + if (prevIndex !== -1){ _currentScene.prevImage = data.links[prevIndex]; + } d.image_path = _currentScene.currentImage.href; wrap .selectAll('button.back') - .classed('hide', _currentScene.prevImage == null); + .classed('hide', _currentScene.prevImage === null); wrap .selectAll('button.forward') - .classed('hide', _currentScene.nextImage == null); - + .classed('hide', _currentScene.nextImage === null); + _currentFrame = d.isPano ? _pannellumFrame : _planeFrame; _currentFrame .selectPhoto(d, true) .showPhotoFrame(wrap); - + }); function localeDateString(s) { @@ -502,7 +499,7 @@ export default { if (isNaN(d.getTime())) return null; return d.toLocaleDateString(localizer.localeCode(), options); } - + if (d.account_id) { let line2 = attribution .append('div') @@ -541,7 +538,6 @@ export default { .selectAll('.panoramax-wrapper') .data([0]); - //TODO maybe all of this should be in panoramax_images? let wrapEnter = wrap.enter() .append('div') .attr('class', 'photo-wrapper panoramax-wrapper') @@ -581,18 +577,16 @@ export default { _planeFrame.event.on('viewerChanged', () => dispatch.call('viewerChanged')); }); - //TODO: maybe this should be here (export?) function step(stepBy) { return function () { if (!_currentScene.currentImage) return; let nextId; - if(stepBy === 1) - nextId = _currentScene.nextImage.id; + if (stepBy === 1) nextId = _currentScene.nextImage.id; else nextId = _currentScene.prevImage.id; if (!nextId) return; - + const nextImage = _cache.images.forImageId[nextId]; context.map().centerEase(nextImage.loc); diff --git a/modules/services/plane_photo.js b/modules/services/plane_photo.js index 938bf8750..4261ed085 100644 --- a/modules/services/plane_photo.js +++ b/modules/services/plane_photo.js @@ -85,7 +85,7 @@ export default { return this; }, - selectPhoto: function (data, keepOrientation) { + selectPhoto: function (data) { dispatch.call('viewerChanged'); loadImage(_photo, ''); diff --git a/modules/svg/panoramax_images.js b/modules/svg/panoramax_images.js index 490bdae14..7c311c007 100644 --- a/modules/svg/panoramax_images.js +++ b/modules/svg/panoramax_images.js @@ -10,11 +10,12 @@ export function svgPanoramaxImages(projection, context, dispatch) { const imageMinZoom = 15; const lineMinZoom = 10; const viewFieldZoomLevel = 18; + const maxOldestYear = 2010; let layer = d3_select(null); let _panoramax; let _viewerYaw = 0; let _selectedSequence; - + function init() { if (svgPanoramaxImages.initialized) return; svgPanoramaxImages.enabled = false; @@ -65,8 +66,8 @@ export function svgPanoramaxImages(projection, context, dispatch) { let id = await service.getUserId(username); images = images.filter(function(image) { - return id == image.account_id; - }); + return id === image.account_id; + }); } return images; @@ -83,7 +84,7 @@ export function svgPanoramaxImages(projection, context, dispatch) { if (!showsPano || !showsFlat) { sequences = sequences.filter(function(sequence) { - if (sequence.properties.type === "equirectangular") return showsPano; + if (sequence.properties.type === 'equirectangular') return showsPano; return showsFlat; }); } @@ -101,7 +102,7 @@ export function svgPanoramaxImages(projection, context, dispatch) { let id = await service.getUserId(username); sequences = sequences.filter(function(sequence) { - return id == sequence.properties.account_id; + return id === sequence.properties.account_id; }); } @@ -185,12 +186,11 @@ export function svgPanoramaxImages(projection, context, dispatch) { if (service) service.setStyles(context, null); } - function updateSlider(oldestDate){ - let maxOldestYear = 2010; + function updateYearSlider(oldestDate){ let currYear = new Date(); let slider = d3_select('.list-option-date-slider'); - let sliderWrap = slider.select(function() { return this.parentNode; }) + let sliderWrap = slider.select(function() { return this.parentNode; }); sliderWrap.selectAll('datalist').remove(); @@ -198,28 +198,27 @@ export function svgPanoramaxImages(projection, context, dispatch) { .attr('id', 'dateValues') .attr('class', 'year-datalist'); - if(oldestDate > maxOldestYear){ + if (oldestDate > maxOldestYear){ slider.attr('min', oldestDate); datalist .append('option') .attr('value', oldestDate) - .attr('label', oldestDate) - } - else{ + .attr('label', oldestDate); + } else { slider.attr('min', maxOldestYear); datalist .append('option') .attr('value', maxOldestYear) - .attr('label', maxOldestYear) - } + .attr('label', maxOldestYear); + }; datalist .append('option') .attr('value', currYear.getFullYear()) - .attr('label', currYear.getFullYear()) - } + .attr('label', currYear.getFullYear()); + }; async function update() { const zoom = ~~context.map().zoom(); @@ -229,8 +228,8 @@ export function svgPanoramaxImages(projection, context, dispatch) { let sequences = (service ? service.sequences(projection, zoom) : []); let images = (service ? service.images(projection) : []); let oldestDate = service.getOldestDate(); - - updateSlider(oldestDate); + + updateYearSlider(oldestDate); images = await filterImages(images); sequences = await filterSequences(sequences, service); @@ -326,7 +325,7 @@ export function svgPanoramaxImages(projection, context, dispatch) { function drawImages(selection) { - + const enabled = svgPanoramaxImages.enabled; const service = getService(); @@ -355,17 +354,15 @@ export function svgPanoramaxImages(projection, context, dispatch) { if (enabled) { let zoom = ~~context.map().zoom(); if (service){ - if(zoom >= imageMinZoom) { + if (zoom >= imageMinZoom) { editOn(); update(); service.loadImages(projection); - } - else if(zoom >= lineMinZoom) { + } else if (zoom >= lineMinZoom) { editOn(); update(); service.loadLines(projection); - } - else { + } else { editOff(); } } else { diff --git a/modules/ui/sections/photo_overlays.js b/modules/ui/sections/photo_overlays.js index 531490226..ee892015b 100644 --- a/modules/ui/sections/photo_overlays.js +++ b/modules/ui/sections/photo_overlays.js @@ -303,8 +303,8 @@ export function uiSectionPhotoOverlays(context) { .append('select') .attr('type', 'text') .attr('class', 'list-option') - .call(utilNoAuto) - + .call(utilNoAuto); + var select = labelEnter.selectAll('.list-option'); select @@ -330,7 +330,7 @@ export function uiSectionPhotoOverlays(context) { select .on('change', function() { var value = d3_select(this).property('value'); - context.photos().setMaxPhotoAge(parseInt(value)); + context.photos().setMaxPhotoAge(parseInt(value, 10)); }); li @@ -345,7 +345,7 @@ export function uiSectionPhotoOverlays(context) { } var currYear = new Date(); - currYear = currYear.getFullYear(); + currYear = parseInt(currYear.getFullYear(), 10); var ul = selection .selectAll('.layer-list-date-slider') @@ -385,36 +385,36 @@ export function uiSectionPhotoOverlays(context) { let sliderWrap = labelEnter .append('div') - .attr('class','slider-wrap') + .attr('class','slider-wrap'); let output = sliderWrap .append('output') - .html(currYear) .attr('class','year-selected'); sliderWrap .append('input') .attr('type', 'range') .attr('max', currYear) - .attr('min', currYear) .attr('list', 'dateValues') .attr('class', 'list-option-date-slider') .call(utilNoAuto) .on('change', function() { - var value = d3_select(this).property('value'); - context.photos().setMaxPhotoYear(parseInt(value)); - output.html(value); + let value = parseInt(d3_select(this).property('value'), 10); + let minYear = parseInt(d3_select(this).property('min'), 10); + value = minYear + (currYear - value); + context.photos().setFromYearFilter(value); + output.html(value + ' - ' + currYear); }); let datalist = sliderWrap .append('datalist') .attr('class', 'year-datalist') - .attr('id', 'dateValues') - + .attr('id', 'dateValues'); + datalist .append('option') .attr('value', currYear) - .attr('label', currYear) + .attr('label', currYear); li .merge(liEnter)