diff --git a/data/core.yaml b/data/core.yaml index 6320908e5..f2e911a8c 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1442,6 +1442,13 @@ en: tooltip: "Street-level photos from Mapilio" street_side: minzoom_tooltip: "Zoom in to see street-side photos" + local_photos: + tooltip: Add georeferenced photos from local files + tooltip_edit: Edit georeferenced photos + header: Georeferenced Photos + file: + instructions: "Choose georeferenced photos to be displayed. Supported types are:\n .jpg with exif location data" + label: "Browse files" note: note: Note title: Edit note diff --git a/modules/svg/layers.js b/modules/svg/layers.js index b61af08ad..b213334d4 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -30,7 +30,6 @@ export function svgLayers(projection, context) { { id: 'osm', layer: svgOsm(projection, context, dispatch) }, { id: 'notes', layer: svgNotes(projection, context, dispatch) }, { id: 'data', layer: svgData(projection, context, dispatch) }, - { id: 'local-photos', layer: svgLocalPhotos(projection, context, dispatch) }, { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch) }, { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch) }, { id: 'osmose', layer: svgOsmose(projection, context, dispatch) }, @@ -42,6 +41,7 @@ export function svgLayers(projection, context) { { id: 'kartaview', layer: svgKartaviewImages(projection, context, dispatch) }, { id: 'mapilio', layer: svgMapilioImages(projection, context, dispatch) }, { id: 'vegbilder', layer: svgVegbilder(projection, context, dispatch) }, + { id: 'local-photos', layer: svgLocalPhotos(projection, context, dispatch) }, { id: 'debug', layer: svgDebug(projection, context, dispatch) }, { id: 'geolocate', layer: svgGeolocate(projection, context, dispatch) }, { id: 'touch', layer: svgTouch(projection, context, dispatch) }, diff --git a/modules/svg/local_photos.js b/modules/svg/local_photos.js index b99efaab7..974da61a4 100644 --- a/modules/svg/local_photos.js +++ b/modules/svg/local_photos.js @@ -6,6 +6,7 @@ import { geoExtent } from '../geo'; import { isArray, isNumber } from 'lodash-es'; var _initialized = false; +var _enabled = false; export function svgLocalPhotos(projection, context, dispatch) { var detected = utilDetect(); @@ -16,6 +17,8 @@ export function svgLocalPhotos(projection, context, dispatch) { function init() { if (_initialized) return; // run once + _enabled = true; + function over(d3_event) { d3_event.stopPropagation(); d3_event.preventDefault(); @@ -227,6 +230,48 @@ export function svgLocalPhotos(projection, context, dispatch) { map.centerZoom(extent.center(), Math.min(18, map.trimmedExtentZoom(extent))); }; + function showLayer() { + layer.style('display', 'block'); + + layer + .style('opacity', 0) + .transition() + .duration(250) + .style('opacity', 1) + .on('end', function () { dispatch.call('change'); }); + } + + + function hideLayer() { + layer + .transition() + .duration(250) + .style('opacity', 0) + .on('end', () => { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + }); + } + + drawPhotos.enabled = function(val) { + if (!arguments.length) return _enabled; + + _enabled = val; + if (_enabled) { + showLayer(); + } else { + hideLayer(); + } + + dispatch.call('change'); + return this; + }; + + drawPhotos.hasData = function() { + return isArray(_imageList) && _imageList.length > 0; + }; + + init(); return drawPhotos; } diff --git a/modules/ui/sections/photo_overlays.js b/modules/ui/sections/photo_overlays.js index 99f8abc96..3680f98be 100644 --- a/modules/ui/sections/photo_overlays.js +++ b/modules/ui/sections/photo_overlays.js @@ -340,11 +340,13 @@ export function uiSectionPhotoOverlays(context) { } function drawLocalPhotos(selection) { - var dataLayer = layers.layer('local-photos'); + var photoLayer = layers.layer('local-photos'); + var hasData = photoLayer && photoLayer.hasData(); + var showsData = hasData && photoLayer.enabled(); var ul = selection .selectAll('.layer-list-local-photos') - .data(dataLayer ? [0] : []); + .data(photoLayer ? [0] : []); // Exit ul.exit() @@ -360,24 +362,22 @@ export function uiSectionPhotoOverlays(context) { .attr('class', 'list-item-local-photos'); var localPhotosLabelEnter = localPhotosEnter - .append('label'); - // TODO: Add tooltip - - // TODO - // localPhotosLabelEnter - // .append('input') - // .attr('type', 'checkbox') - // .on('change', function() { toggleLayer('local-photos'); }); + .append('label') + .call(uiTooltip().title(() => t.append('local_photos.tooltip'))); localPhotosLabelEnter - .append('span') - .text('Local Photos'); + .append('input') + .attr('type', 'checkbox') + .on('change', function() { toggleLayer('local-photos'); }); + + localPhotosLabelEnter + .call(t.append('local_photos.header')); localPhotosEnter .append('button') .attr('class', 'open-data-options') .call(uiTooltip() - .title(t.html('settings.custom_data.tooltip')) + .title(() => t.append('local_photos.tooltip_edit')) .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') ) .on('click', function(d3_event) { @@ -390,7 +390,7 @@ export function uiSectionPhotoOverlays(context) { .append('button') .attr('class', 'zoom-to-data') .call(uiTooltip() - .title(t.html('map_data.layers.custom.zoom')) + .title(() => t.append('map_data.layers.custom.zoom')) .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') ) .on('click', function(d3_event) { @@ -399,7 +399,7 @@ export function uiSectionPhotoOverlays(context) { d3_event.preventDefault(); d3_event.stopPropagation(); //TODO - dataLayer.fitZoom(); + photoLayer.fitZoom(); }) .call(svgIcon('#iD-icon-framed-dot', 'monochrome')); @@ -407,6 +407,16 @@ export function uiSectionPhotoOverlays(context) { ul = ul .merge(ulEnter); + ul.selectAll('.list-item-local-photos') + .classed('active', showsData) + .selectAll('label') + .classed('deemphasize', !hasData) + .selectAll('input') + .property('disabled', !hasData) + .property('checked', showsData); + + ul.selectAll('button.zoom-to-data') + .classed('disabled', !hasData); } function editLocalPhotos() { diff --git a/modules/ui/settings/local_photos.js b/modules/ui/settings/local_photos.js index 423cb5558..2cf542135 100644 --- a/modules/ui/settings/local_photos.js +++ b/modules/ui/settings/local_photos.js @@ -1,6 +1,5 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { prefs } from '../../core/preferences'; import { t } from '../../core/localizer'; import { uiConfirm } from '../confirm'; import { utilRebind } from '../../util'; @@ -12,14 +11,8 @@ export function uiSettingsLocalPhotos(context) { function render(selection) { var dataLayer = context.layers().layer('local-photos'); - // keep separate copies of original and current settings - var _origSettings = { - fileList: (dataLayer && dataLayer.fileList()) || null, - url: prefs('settings-custom-data-url') - }; var _currSettings = { - fileList: (dataLayer && dataLayer.fileList()) || null, - url: prefs('settings-custom-data-url') + fileList: (dataLayer && dataLayer.fileList()) || null }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; @@ -30,17 +23,15 @@ export function uiSettingsLocalPhotos(context) { modal.select('.modal-section.header') .append('h3') - .call(t.append('settings.custom_data.header')); + .call(t.append('local_photos.header')); var textSection = modal.select('.modal-section.message-text'); - //TODO: Add translation textSection .append('pre') - .text('Choose local photos'); - // .attr('class', 'instructions-file') - // .call(t.append('settings.custom_data.file.instructions')); + .attr('class', 'instructions-local-photos') + .call(t.append('local_photos.file.instructions')); textSection .append('input') @@ -52,8 +43,6 @@ export function uiSettingsLocalPhotos(context) { .on('change', function(d3_event) { var files = d3_event.target.files; if (files && files.length) { - // _currSettings.url = ''; - // textSection.select('.field-url').property('value', ''); _currSettings.fileList = files; } else { _currSettings.fileList = null; @@ -83,23 +72,12 @@ export function uiSettingsLocalPhotos(context) { } - // restore the original url function clickCancel() { - textSection.select('.field-url').property('value', _origSettings.url); - prefs('settings-custom-data-url', _origSettings.url); this.blur(); modal.close(); } - // accept the current url function clickSave() { - // _currSettings.url = textSection.select('.field-url').property('value').trim(); - - // one or the other but not both - // if (_currSettings.url) { _currSettings.fileList = null; } - if (_currSettings.fileList) { _currSettings.url = ''; } - - // prefs('settings-custom-data-url', _currSettings.url); this.blur(); modal.close(); dispatch.call('change', this, _currSettings);