From 7a2ab8aeaa0a3b1a9d123d6c8aa5b4f8d0899aac Mon Sep 17 00:00:00 2001 From: mukesh Date: Wed, 3 Aug 2022 15:37:09 +0200 Subject: [PATCH] Draft: still in progress --- modules/svg/data.js | 36 +- modules/svg/layers.js | 2 + modules/svg/local_photos.js | 440 +++++++++++++++++++++++ modules/svg/mapillary_images.js | 17 +- modules/ui/sections/data_layers.js | 192 +++++++++- modules/ui/settings/local_photos_data.js | 22 +- package.json | 8 +- 7 files changed, 683 insertions(+), 34 deletions(-) create mode 100644 modules/svg/local_photos.js diff --git a/modules/svg/data.js b/modules/svg/data.js index 40a89f34e..11559cb13 100644 --- a/modules/svg/data.js +++ b/modules/svg/data.js @@ -31,12 +31,14 @@ export function svgData(projection, context, dispatch) { function init() { + console.log('init() called'); if (_initialized) return; // run once _geojson = {}; _enabled = true; function over(d3_event) { + console.log('over() called'); d3_event.stopPropagation(); d3_event.preventDefault(); d3_event.dataTransfer.dropEffect = 'copy'; @@ -59,6 +61,7 @@ export function svgData(projection, context, dispatch) { function getService() { + console.log('getService() called'); if (services.vectorTile && !_vtService) { _vtService = services.vectorTile; _vtService.event.on('loadedData', throttledRedraw); @@ -71,6 +74,7 @@ export function svgData(projection, context, dispatch) { function showLayer() { + console.log('showLayer() called'); layerOn(); layer @@ -83,6 +87,7 @@ export function svgData(projection, context, dispatch) { function hideLayer() { + console.log('hideLayer() called'); throttledRedraw.cancel(); layer @@ -94,11 +99,13 @@ export function svgData(projection, context, dispatch) { function layerOn() { + console.log('layerOn() called'); layer.style('display', 'block'); } function layerOff() { + console.log('layerOff() called'); layer.selectAll('.viewfield-group').remove(); layer.style('display', 'none'); } @@ -106,6 +113,7 @@ export function svgData(projection, context, dispatch) { // ensure that all geojson features in a collection have IDs function ensureIDs(gj) { + console.log('ensureIDs() called'); if (!gj) return null; if (gj.type === 'FeatureCollection') { @@ -121,6 +129,7 @@ export function svgData(projection, context, dispatch) { // ensure that each single Feature object has a unique ID function ensureFeatureID(feature) { + console.log('ensureFeatureID() called'); if (!feature) return; feature.__featurehash__ = utilHashcode(stringify(feature)); return feature; @@ -129,6 +138,7 @@ export function svgData(projection, context, dispatch) { // Prefer an array of Features instead of a FeatureCollection function getFeatures(gj) { + console.log('getFeatures() called'); if (!gj) return []; if (gj.type === 'FeatureCollection') { @@ -140,21 +150,25 @@ export function svgData(projection, context, dispatch) { function featureKey(d) { + console.log('featureKey() called'); return d.__featurehash__; } function isPolygon(d) { + console.log('isPolygon() called'); return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon'; } function clipPathID(d) { + console.log('clipPathID() called'); return 'ideditor-data-' + d.__featurehash__ + '-clippath'; } function featureClasses(d) { + console.log('featureClasses() called'); return [ 'data' + d.__featurehash__, d.geometry.type, @@ -165,6 +179,7 @@ export function svgData(projection, context, dispatch) { function drawData(selection) { + console.log('drawData() called'); var vtService = getService(); var getPath = svgPath(projection).geojson; var getAreaPath = svgPath(projection, null, true).geojson; @@ -269,6 +284,7 @@ export function svgData(projection, context, dispatch) { function drawLabels(selection, textClass, data) { + console.log('drawLabels() called'); var labelPath = d3_geoPath(projection); var labelData = data.filter(function(d) { return _showLabels && d.properties && (d.properties.desc || d.properties.name); @@ -302,6 +318,7 @@ export function svgData(projection, context, dispatch) { function getExtension(fileName) { + console.log('getExtension() called'); if (!fileName) return; var re = /\.(gpx|kml|(geo)?json|png)$/i; @@ -311,11 +328,13 @@ export function svgData(projection, context, dispatch) { function xmlToDom(textdata) { + console.log('xmlToDom() called'); return (new DOMParser()).parseFromString(textdata, 'text/xml'); } function stringifyGeojsonProperties(feature) { + console.log('stringifyGeojsonProperties() called'); const properties = feature.properties; for (const key in properties) { const property = properties[key]; @@ -331,6 +350,7 @@ export function svgData(projection, context, dispatch) { drawData.setFile = function(extension, data) { + console.log('drawData.setFile called'); _template = null; _fileList = null; _geojson = null; @@ -353,11 +373,11 @@ export function svgData(projection, context, dispatch) { stringifyGeojsonProperties(gj); } break; - case '.png': + // case '.png': // xx = JSON.parse(data); - console.log('Hello world!'); - console.log(data); - break; + // console.log('Hello world!'); + // console.log(data); + // break; } gj = gj || {}; @@ -373,6 +393,7 @@ export function svgData(projection, context, dispatch) { drawData.showLabels = function(val) { + console.log('drawData.showLabels called'); if (!arguments.length) return _showLabels; _showLabels = val; @@ -381,6 +402,7 @@ export function svgData(projection, context, dispatch) { drawData.enabled = function(val) { + console.log('drawData.enabled called'); if (!arguments.length) return _enabled; _enabled = val; @@ -396,12 +418,14 @@ export function svgData(projection, context, dispatch) { drawData.hasData = function() { + console.log('drawData.hasData called'); var gj = _geojson || {}; return !!(_template || Object.keys(gj).length); }; drawData.template = function(val, src) { + console.log('drawData.template called'); if (!arguments.length) return _template; // test source against OSM imagery blocklists.. @@ -440,6 +464,7 @@ export function svgData(projection, context, dispatch) { drawData.geojson = function(gj, src) { + console.log('drawData.geojson called'); if (!arguments.length) return _geojson; _template = null; @@ -459,6 +484,7 @@ export function svgData(projection, context, dispatch) { drawData.fileList = function(fileList) { + console.log('drawData.fileList called'); if (!arguments.length) return _fileList; _template = null; @@ -483,6 +509,7 @@ export function svgData(projection, context, dispatch) { drawData.url = function(url, defaultExtension) { + console.log('drawData.url called'); _template = null; _fileList = null; _geojson = null; @@ -515,6 +542,7 @@ export function svgData(projection, context, dispatch) { drawData.fitZoom = function() { + console.log('drawData.fitZoom called'); var features = getFeatures(_geojson); if (!features.length) return; diff --git a/modules/svg/layers.js b/modules/svg/layers.js index 169805c4c..2aada8e59 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -2,6 +2,7 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { select as d3_select } from 'd3-selection'; import { svgData } from './data'; +import { svgLocalPhotos} from './local_photos'; import { svgDebug } from './debug'; import { svgGeolocate } from './geolocate'; import { svgKeepRight } from './keepRight'; @@ -27,6 +28,7 @@ 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) }, diff --git a/modules/svg/local_photos.js b/modules/svg/local_photos.js new file mode 100644 index 000000000..c2f8d0a86 --- /dev/null +++ b/modules/svg/local_photos.js @@ -0,0 +1,440 @@ +import _throttle from 'lodash-es/throttle'; + +import { utilDetect } from '../util/detect'; +import { select as d3_select } from 'd3-selection'; +import { svgPath, svgPointTransform } from './helpers'; +// import { services } from '../services'; +// Modern Node.js can import CommonJS +import exifr from 'exifr'; // => exifr/dist/full.umd.cjs + +// new +var _initialized = false; +var _enabled = false; + +export function svgLocalPhotos(projection, context, dispatch) { + // required + const throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); + const minZoom = 12; + const minMarkerZoom = 16; + const minViewfieldZoom = 18; + var detected = utilDetect(); + let layer = d3_select(null); + // maybe required + // let _mapillary; + var _fileList; + + // instead of svgLocalPhotos.something, using global variable at top + // function init() { + // if (svgMapillaryImages.initialized) return; // run once + // svgMapillaryImages.enabled = false; + // svgMapillaryImages.initialized = true; + // } + + // new + function init() { + console.log('inti() called'); + if (_initialized) return; // run once + + _enabled = true; + + function over(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + d3_event.dataTransfer.dropEffect = 'copy'; + } + + context.container() + .attr('dropzone', 'copy') + // .on('drop.svgData', function(d3_event) { + .on('drop.svgLocalPhotos', function(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + if (!detected.filedrop) return; + drawPhotos.fileList(d3_event.dataTransfer.files); + }) + // .on('dragenter.svgData', over) + // .on('dragexit.svgData', over) + // .on('dragover.svgData', over); + .on('dragenter.svgLocalPhotos', over) + .on('dragexit.svgLocalPhotos', over) + .on('dragover.svgLocalPhotos', over); + + _initialized = true; + } + + function showLayer() { + console.log('showLayer() called'); + // if images are not available return + // const service = getService(); + // if (!service) return; + + // same as layerOn() in data.js + editOn(); + + layer + .style('opacity', 0) + .transition() + .duration(250) + .style('opacity', 1) + .on('end', function () { dispatch.call('change'); }); + } + + + function hideLayer() { + console.log('hideLayer() called'); + throttledRedraw.cancel(); + + layer + .transition() + .duration(250) + .style('opacity', 0) + .on('end', editOff); + } + + // same as layerOn() in data.js + function editOn() { + console.log('editOn() called'); + layer.style('display', 'block'); + } + + + // same as layerOff() in data.js + function editOff() { + console.log('editOff() called'); + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + + // opens the image at bottom left + function click(d3_event, image) { + console.log('click() called'); + // const service = getService(); + // if (!service) return; + + // service + // .ensureViewerLoaded(context) + // .then(function() { + // service + // .selectImage(context, image.id) + // .showViewer(context); + // }); + + context.map().centerEase(image.loc); + } + + + function mouseover(d3_event, image) { + console.log('mouseover() called'); + // const service = getService(); + + // if (service) service.setStyles(context, image); + } + + + function mouseout() { + console.log('mouseout() called'); + // const service = getService(); + // if (service) service.setStyles(context, null); + } + + // if you want to put any image with geo coordinates + // this is coordinates transformation + // converting gps coordinates on screen + function transform(d) { + console.log('transform() called'); + let t = svgPointTransform(projection)(d); + if (d.ca) { + t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; + } + return t; + } + + // a sequence is a list of images + // no need to filter sequence + // function filterSequences(sequences) {...} + + // puts the images on the map + function update() { + console.log('update() called'); + const z = ~~context.map().zoom(); + const showMarkers = (z >= minMarkerZoom); + const showViewfields = (z >= minViewfieldZoom); + + // const service = getService(); + // let sequences = (service ? service.sequences(projection) : []); + // let images = (service && showMarkers ? service.images(projection) : []); + + // images = filterImages(images); + // sequences = filterSequences(sequences, service); + + // service.filterViewer(context); + + let traces = layer.selectAll('.sequences').selectAll('.sequence'); + // .data(sequences, function(d) { return d.properties.id; }); + + // exit + traces.exit() + .remove(); + + // enter/update + traces = traces.enter() + .append('path') + .attr('class', 'sequence') + .merge(traces) + .attr('d', svgPath(projection).geojson); + + + const groups = layer.selectAll('.markers').selectAll('.viewfield-group'); + // .data(images, function(d) { return d.id; }); + + // exit + groups.exit() + .remove(); + + // enter + const groupsEnter = groups.enter() + .append('g') + .attr('class', 'viewfield-group') + .on('mouseenter', mouseover) + .on('mouseleave', mouseout) + .on('click', click); + + groupsEnter + .append('g') + .attr('class', 'viewfield-scale'); + + // update + const markers = groups + .merge(groupsEnter) + .sort(function(a, b) { + return b.loc[1] - a.loc[1]; // sort Y + }) + .attr('transform', transform) + .select('.viewfield-scale'); + + + markers.selectAll('circle') + .data([0]) + .enter() + .append('circle') + .attr('dx', '0') + .attr('dy', '0') + .attr('r', '6'); + + const viewfields = markers.selectAll('.viewfield') + .data(showViewfields ? [0] : []); + + viewfields.exit() + .remove(); + + viewfields.enter() // viewfields may or may not be drawn... + .insert('path', 'circle') // but if they are, draw below the circles + .attr('class', 'viewfield') + .classed('pano', function() { return this.parentNode.__data__.is_pano; }) + .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') + .attr('d', viewfieldPath); + + function viewfieldPath() { + console.log('viewfieldPath() called'); + if (this.parentNode.__data__.is_pano) { + return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; + } else { + return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; + } + } + } + + + // draws the actual images + // create your onw css for this + function drawPhotos(selection) { + console.log('drawPhotos fn called'); + // const enabled = svgMapillaryImages.enabled; + const enabled = _enabled; + // const service = getService(); + + // layer = selection.selectAll('.local-photos') + // layer = selection.selectAll('.layer-local-photos'); + // layer = selection.selectAll('.data'); + // .data(service ? [0] : []); + // .data([0]); + + // layer.exit() + // .remove(); + + // const layerEnter = layer.enter() + // .append('g') + // // .attr('class', 'layer-local-photos') + // .attr('class', 'layer-mapillary') + // .style('display', enabled ? 'block' : 'none'); + + // layerEnter + // .append('g') + // .attr('class', 'sequences'); + + // layerEnter + // .append('g') + // .attr('class', 'markers'); + + // layer = layerEnter + // .merge(layer); + + // if (enabled) { + // if (~~context.map().zoom() >= minZoom) { + // editOn(); + // update(); + // // service.loadImages(projection); + // } else { + // editOff(); + // } + // } + } + + + // drawImages.enabled = function(_) { + // if (!arguments.length) return svgMapillaryImages.enabled; + // svgMapillaryImages.enabled = _; + // if (svgMapillaryImages.enabled) { + // showLayer(); + // context.photos().on('change.mapillary_images', update); + // } else { + // hideLayer(); + // context.photos().on('change.mapillary_images', null); + // } + // dispatch.call('change'); + // return this; + // }; + + // new + // use this since using global value + // slightly modified for photos + drawPhotos.enabled = function(val) { + console.log('drawPhotos.enabled called'); + if (!arguments.length) return _enabled; + + _enabled = val; + if (_enabled) { + showLayer(); + // context.photos().on('change.mapillary_images', update); + context.photos().on('change.local_photos', update); + } else { + hideLayer(); + // context.photos().on('change.mapillary_images', null); + context.photos().on('change.local_photos', null); + } + + dispatch.call('change'); + return this; + }; + + function extract_exif(image) { + var reader = new FileReader(); + + reader.onload = function () { + exifr.parse(image) + .then(output => console.log('Image parsed', output)); + }; + + reader.readAsText(image); + } + + // Step 2 + // this is where the exif parsing library comes into play + // get all info from the image + // drawPhotos.setFile = function(extension, data) { + // drawPhotos.setFile = function(fileList) { + drawPhotos.setFile = function(file) { + console.log('drawPhotos.setFile called'); + _fileList = null; + + // fileList.forEach(image => + // extract_exif(image) + // .then(console.log('All images parsed successfully')) + // .catch(err => console.log(err)) + // ); + + extract_exif(file); + + dispatch.call('change'); + return this; + }; + + // Step 1: entry point + drawPhotos.fileList = function(fileList) { + console.log('drawPhotos.fileList called'); + console.log('Step 2: fileList read in local_photos.js', fileList); + // if (!arguments.length) return _fileList; + + _fileList = fileList; + + if (!fileList || !fileList.length) return this; + // its just fetching one entry + // fetch all for local photos + // probablay a promise is required + var f = fileList[0]; + drawPhotos.setFile(f); + + // var reader = new FileReader(); + // reader.onload = (function() { + // return function(e) { + // // Step 2 + // drawPhotos.setFile(extension, e.target.result); + // }; + // })(f); + + return this; + }; + + // new + // when all photos are uploaded, zoom to see them all + drawPhotos.fitZoom = function() { + console.log('drawPhotos.fitZoom called'); + // var features = getFeatures(_geojson); + // if (!features.length) return; + + var map = context.map(); + var viewport = map.trimmedExtent().polygon(); + // features is not defined + // var coords = features.reduce(function(coords) { + // var geom = feature.geometry; + // if (!geom) return coords; + + // var c = geom.coordinates; + + // /* eslint-disable no-fallthrough */ + // switch (geom.type) { + // case 'Point': + // c = [c]; + // case 'MultiPoint': + // case 'LineString': + // break; + + // case 'MultiPolygon': + // // c = utilArrayFlatten(c); + // case 'Polygon': + // case 'MultiLineString': + // // c = utilArrayFlatten(c); + // break; + // } + // /* eslint-enable no-fallthrough */ + + // return utilArrayUnion(coords, c); + // }, []); + + // if (!geoPolygonIntersectsPolygon(viewport, coords, true)) { + // var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords })); + // map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); + // } + + return this; + }; + + drawPhotos.supported = function() { + console.log('drawPhotos.supported called'); + // return !!getService(); + }; + + + init(); + return drawPhotos; +} diff --git a/modules/svg/mapillary_images.js b/modules/svg/mapillary_images.js index 7e9d9ea08..a7f9d61a7 100644 --- a/modules/svg/mapillary_images.js +++ b/modules/svg/mapillary_images.js @@ -15,6 +15,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function init() { + console.log('init() called'); if (svgMapillaryImages.initialized) return; // run once svgMapillaryImages.enabled = false; svgMapillaryImages.initialized = true; @@ -22,6 +23,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function getService() { + console.log('getService() called'); if (services.mapillary && !_mapillary) { _mapillary = services.mapillary; _mapillary.event.on('loadedImages', throttledRedraw); @@ -34,6 +36,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function showLayer() { + console.log('showLayer() called'); const service = getService(); if (!service) return; @@ -49,6 +52,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function hideLayer() { + console.log('hideLayer() called'); throttledRedraw.cancel(); layer @@ -60,17 +64,20 @@ export function svgMapillaryImages(projection, context, dispatch) { function editOn() { + console.log('editOn() called'); layer.style('display', 'block'); } function editOff() { + console.log('editOff() called'); layer.selectAll('.viewfield-group').remove(); layer.style('display', 'none'); } function click(d3_event, image) { + console.log('click() called'); const service = getService(); if (!service) return; @@ -87,6 +94,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function mouseover(d3_event, image) { + console.log('mouseover() called'); const service = getService(); if (service) service.setStyles(context, image); @@ -94,12 +102,14 @@ export function svgMapillaryImages(projection, context, dispatch) { function mouseout() { + console.log('mouseout() called'); const service = getService(); if (service) service.setStyles(context, null); } function transform(d) { + console.log('transform() called'); let t = svgPointTransform(projection)(d); if (d.ca) { t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; @@ -109,6 +119,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function filterImages(images) { + console.log('filterImages() called'); const showsPano = context.photos().showsPanoramic(); const showsFlat = context.photos().showsFlat(); const fromDate = context.photos().fromDate(); @@ -135,6 +146,7 @@ export function svgMapillaryImages(projection, context, dispatch) { } function filterSequences(sequences) { + console.log('filterSequences() called'); const showsPano = context.photos().showsPanoramic(); const showsFlat = context.photos().showsFlat(); const fromDate = context.photos().fromDate(); @@ -164,7 +176,7 @@ export function svgMapillaryImages(projection, context, dispatch) { } function update() { - + console.log('update() called'); const z = ~~context.map().zoom(); const showMarkers = (z >= minMarkerZoom); const showViewfields = (z >= minViewfieldZoom); @@ -254,6 +266,7 @@ export function svgMapillaryImages(projection, context, dispatch) { function drawImages(selection) { + console.log('drawImages() called'); const enabled = svgMapillaryImages.enabled; const service = getService(); @@ -292,6 +305,7 @@ export function svgMapillaryImages(projection, context, dispatch) { drawImages.enabled = function(_) { + console.log('drawImages.enabled called'); if (!arguments.length) return svgMapillaryImages.enabled; svgMapillaryImages.enabled = _; if (svgMapillaryImages.enabled) { @@ -307,6 +321,7 @@ export function svgMapillaryImages(projection, context, dispatch) { drawImages.supported = function() { + console.log('drawImages.supported called'); return !!getService(); }; diff --git a/modules/ui/sections/data_layers.js b/modules/ui/sections/data_layers.js index 211004731..16aab4a4f 100644 --- a/modules/ui/sections/data_layers.js +++ b/modules/ui/sections/data_layers.js @@ -22,6 +22,7 @@ export function uiSectionDataLayers(context) { var settingsLocalPhotosData = uiSettingsLocalPhotosData(context) .on('change', localPhotosChanged); + // refers to `modules/svg/layers.js` -> function drawLayers(selection) {...} var layers = context.layers(); var section = uiSection('data-layers', context) @@ -39,6 +40,7 @@ export function uiSectionDataLayers(context) { .call(drawOsmItems) .call(drawQAItems) .call(drawCustomDataItems) + // .call(drawLocalPhotos) .call(drawVectorItems) // Beta - Detroit mapping challenge .call(drawPanelItems); } @@ -292,6 +294,178 @@ export function uiSectionDataLayers(context) { } } + // original function + // function drawCustomDataItems(selection) { + // var dataLayer = layers.layer('data'); + // var hasData = dataLayer && dataLayer.hasData(); + // var showsData = hasData && dataLayer.enabled(); + + // var ul = selection + // .selectAll('.layer-list-data') + // .data(dataLayer ? [0] : []); + + // // Exit + // ul.exit() + // .remove(); + + // // Enter + // var ulEnter = ul.enter() + // .append('ul') + // .attr('class', 'layer-list layer-list-data'); + + // var liEnter = ulEnter + // .append('li') + // .attr('class', 'list-item-data'); + + // var labelEnter = liEnter + // .append('label') + // .call(uiTooltip() + // .title(() => t.append('map_data.layers.custom.tooltip')) + // .placement('top') + // ); + + // labelEnter + // .append('input') + // .attr('type', 'checkbox') + // .on('change', function() { toggleLayer('data'); }); + + // labelEnter + // .append('span') + // .call(t.append('map_data.layers.custom.title')); + + // liEnter + // .append('button') + // .attr('class', 'open-data-options') + // .call(uiTooltip() + // .title(() => t.append('settings.custom_data.tooltip')) + // .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') + // ) + // .on('click', function(d3_event) { + // d3_event.preventDefault(); + // editCustom(); + // }) + // .call(svgIcon('#iD-icon-more')); + + // liEnter + // .append('button') + // .attr('class', 'zoom-to-data') + // .call(uiTooltip() + // .title(() => t.append('map_data.layers.custom.zoom')) + // .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') + // ) + // .on('click', function(d3_event) { + // if (d3_select(this).classed('disabled')) return; + + // d3_event.preventDefault(); + // d3_event.stopPropagation(); + // dataLayer.fitZoom(); + // }) + // .call(svgIcon('#iD-icon-framed-dot', 'monochrome')); + + // // Update + // ul = ul + // .merge(ulEnter); + + // ul.selectAll('.list-item-data') + // .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); + // } + + // added/testing: new function for local photos + function drawLocalPhotos(selection) { + var dataLayer = layers.layer('local-photos'); + console.log(dataLayer, 'dataLayer'); + // var hasData = dataLayer && dataLayer.hasData(); + // var showsData = hasData && dataLayer.enabled(); + + // var ul = selection + // .selectAll('.layer-list-local-photos') + // .data(dataLayer ? [0] : []); + + var ul = selection + .selectAll('.layer-list-local-photos') + .data(dataLayer ? [0] : []); + console.log(ul, 'ul inside drawLocalPhotos'); + + // Exit + ul.exit() + .remove(); + + // Enter + var ulEnter = ul.enter() + .append('ul') + .attr('class', 'layer-list layer-list-data'); + console.log(ulEnter, 'ulEnter inside drawLocalPhotos'); + var localPhotosEnter = ulEnter + .append('li') + .attr('class', 'list-item-local-photos'); + + var localPhotosLabelEnter = localPhotosEnter + .append('label'); + // TODO: Add tooltip + + localPhotosLabelEnter + .append('input') + .attr('type', 'checkbox') + .on('change', function() { toggleLayer('local-photos'); }); + + localPhotosLabelEnter + .append('span') + .text('Local Photos'); + + localPhotosEnter + .append('button') + .attr('class', 'open-data-options') + .call(uiTooltip() + .title(t.html('settings.custom_data.tooltip')) + .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') + ) + .on('click', function(d3_event) { + d3_event.preventDefault(); + editLocalPhotos(); + }) + .call(svgIcon('#iD-icon-more')); + + localPhotosEnter + .append('button') + .attr('class', 'zoom-to-data') + .call(uiTooltip() + .title(t.html('map_data.layers.custom.zoom')) + .placement((localizer.textDirection() === 'rtl') ? 'right' : 'left') + ) + .on('click', function(d3_event) { + if (d3_select(this).classed('disabled')) return; + + d3_event.preventDefault(); + d3_event.stopPropagation(); + dataLayer.fitZoom(); + }) + .call(svgIcon('#iD-icon-framed-dot', 'monochrome')); + + // Update + ul = ul + .merge(ulEnter); + + // ul.selectAll('.list-item-data') + // .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); + } + + // current fn function drawCustomDataItems(selection) { var dataLayer = layers.layer('data'); var hasData = dataLayer && dataLayer.hasData(); @@ -428,12 +602,9 @@ export function uiSectionDataLayers(context) { .call(settingsCustomData); } - function editLocalPhotos() { - context.container() - .call(settingsLocalPhotosData); - } - function customChanged(d) { + console.log('customChanged called'); + console.log(layers); var dataLayer = layers.layer('data'); if (d && d.url) { @@ -443,11 +614,18 @@ export function uiSectionDataLayers(context) { } } + function editLocalPhotos() { + context.container() + .call(settingsLocalPhotosData); + } + function localPhotosChanged(d) { - var dataLayer = layers.layer('data'); + console.log('localPhotosChanged called'); + var localPhotosLayer = layers.layer('local-photos'); if (d && d.fileList) { - dataLayer.fileList(d.fileList); + console.log('Step 1: fileList set', d.fileList); + localPhotosLayer.fileList(d.fileList); } } diff --git a/modules/ui/settings/local_photos_data.js b/modules/ui/settings/local_photos_data.js index 11b87945a..c6278dcdc 100644 --- a/modules/ui/settings/local_photos_data.js +++ b/modules/ui/settings/local_photos_data.js @@ -10,7 +10,7 @@ export function uiSettingsLocalPhotosData (context) { var dispatch = d3_dispatch('change'); function render(selection) { - var dataLayer = context.layers().layer('data'); + var dataLayer = context.layers().layer('local-photos'); // keep separate copies of original and current settings var _origSettings = { @@ -38,7 +38,7 @@ export function uiSettingsLocalPhotosData (context) { //TODO: Add translation textSection .append('pre') - .text('Choose local photos. Supported types are: .jpg, .jpeg, .png'); + .text('Choose local photos'); // .attr('class', 'instructions-file') // .call(t.append('settings.custom_data.file.instructions')); @@ -46,11 +46,11 @@ export function uiSettingsLocalPhotosData (context) { .append('input') .attr('class', 'field-file') .attr('type', 'file') + .attr('multiple', 'multiple') // .attr('accept', '.gpx,.kml,.geojson,.json,application/gpx+xml,application/vnd.google-earth.kml+xml,application/geo+json,application/json') .property('files', _currSettings.fileList) .on('change', function(d3_event) { var files = d3_event.target.files; - console.log(files); if (files && files.length) { // _currSettings.url = ''; // textSection.select('.field-url').property('value', ''); @@ -60,22 +60,6 @@ export function uiSettingsLocalPhotosData (context) { } }); - // textSection - // .append('h4') - // .call(t.append('settings.custom_data.or')); - - // textSection - // .append('pre') - // .attr('class', 'instructions-url') - // .call(t.append('settings.custom_data.url.instructions')); - - // textSection - // .append('textarea') - // .attr('class', 'field-url') - // .attr('placeholder', t('settings.custom_data.url.placeholder')) - // .call(utilNoAuto) - // .property('value', _currSettings.url); - // insert a cancel button var buttonSection = modal.select('.modal-section.buttons'); diff --git a/package.json b/package.json index 6dce58c1f..1e0719e3e 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,10 @@ "alif-toolkit": "^1.2.9", "core-js-bundle": "^3.19.0", "diacritics": "1.3.0", + "exifr": "^7.1.3", "fast-deep-equal": "~3.1.1", "fast-json-stable-stringify": "2.1.0", + "fs": "^0.0.1-security", "lodash-es": "~4.17.15", "marked": "~4.0.12", "node-diff3": "~3.1.0", @@ -77,9 +79,9 @@ "autoprefixer": "^10.0.1", "btoa": "^1.2.1", "chai": "^4.3.4", + "chalk": "^4.1.2", "cldr-core": "37.0.0", "cldr-localenames-full": "37.0.0", - "chalk": "^4.1.2", "concat-files": "^0.1.1", "d3": "~7.4.4", "editor-layer-index": "github:osmlab/editor-layer-index#gh-pages", @@ -97,8 +99,8 @@ "karma-coverage": "2.1.1", "karma-mocha": "^2.0.1", "karma-remap-istanbul": "^0.6.0", - "mapillary-js": "4.1.0", "mapillary_sprite_source": "^1.8.0", + "mapillary-js": "4.1.0", "minimist": "^1.2.3", "mocha": "^9.2.0", "name-suggestion-index": "~6.0", @@ -122,4 +124,4 @@ "browserslist": [ "> 0.2%, last 6 major versions, Firefox ESR, maintained node versions" ] -} \ No newline at end of file +}