From 575c98ab282d1d0c6a201170122815fc95e19500 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 21 Aug 2018 01:25:24 -0400 Subject: [PATCH] Accept both file and vector tile url templates from settings screen --- css/20_map.css | 12 +- modules/svg/data.js | 204 ++++++++++++++++++++--------- modules/ui/map_data.js | 15 +-- modules/ui/settings/custom_data.js | 20 ++- test/spec/svg/data.js | 4 +- 5 files changed, 172 insertions(+), 83 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index ce45960a9..79c127555 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -309,28 +309,28 @@ g.turn circle { /* Other Data (gpx, kml, geojson, mvt, pbf) */ -.layer-geojson { +.layer-mapdata { pointer-events: none; } -.layer-geojson path { +.layer-mapdata path { stroke: #ff26d4; stroke-width: 2; fill: none; } -.layer-geojson text.label-halo, -.layer-geojson text.label { +.layer-mapdata text.label-halo, +.layer-mapdata text.label { font-size: 10px; font-weight: bold; dominant-baseline: middle; } -.layer-geojson text.label { +.layer-mapdata text.label { fill: #ff26d4; } -.layer-geojson text.label-halo { +.layer-mapdata text.label-halo { opacity: 0.7; stroke: #000; stroke-width: 5px; diff --git a/modules/svg/data.js b/modules/svg/data.js index 350d5d76a..f773ba9d0 100644 --- a/modules/svg/data.js +++ b/modules/svg/data.js @@ -2,6 +2,7 @@ import _flatten from 'lodash-es/flatten'; import _isEmpty from 'lodash-es/isEmpty'; import _reduce from 'lodash-es/reduce'; import _union from 'lodash-es/union'; +import _throttle from 'lodash-es/throttle'; import { geoBounds as d3_geoBounds } from 'd3-geo'; @@ -20,6 +21,7 @@ import vt from '@mapbox/vector-tile'; import Protobuf from 'pbf'; import { geoExtent, geoPolygonIntersectsPolygon } from '../geo'; +import { services } from '../services'; import { svgPath } from './index'; import { utilDetect } from '../util/detect'; @@ -30,9 +32,13 @@ var _geojson; export function svgData(projection, context, dispatch) { + var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); var _showLabels = true; var detected = utilDetect(); - var layer; + var layer = d3_select(null); + var _vtService; + var _fileList; + var _template; // todo, if template is set, use vectorTile service var _src; @@ -54,7 +60,7 @@ export function svgData(projection, context, dispatch) { d3_event.stopPropagation(); d3_event.preventDefault(); if (!detected.filedrop) return; - drawData.files(d3_event.dataTransfer.files); + drawData.fileList(d3_event.dataTransfer.files); }) .on('dragenter.svgData', over) .on('dragexit.svgData', over) @@ -64,24 +70,71 @@ export function svgData(projection, context, dispatch) { } + function getService() { + if (services.vectorTile && !_vtService) { + _vtService = services.vectorTile; + _vtService.event.on('loadedData', throttledRedraw); + } else if (!services.vectorTile && _vtService) { + _vtService = null; + } + + return _vtService; + } + + + function showLayer() { + layerOn(); + + layer + .style('opacity', 0) + .transition() + .duration(250) + .style('opacity', 1) + .on('end', function () { dispatch.call('change'); }); + } + + + function hideLayer() { + throttledRedraw.cancel(); + + layer + .transition() + .duration(250) + .style('opacity', 0) + .on('end', layerOff); + } + + + function layerOn() { + layer.style('display', 'block'); + } + + + function layerOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + + function drawData(selection) { var getPath = svgPath(projection).geojson; + var hasData = drawData.hasData(); - layer = selection.selectAll('.layer-geojson') - .data(_enabled ? [0] : []); + layer = selection.selectAll('.layer-mapdata') + .data(_enabled && hasData ? [0] : []); layer.exit() .remove(); layer = layer.enter() .append('g') - .attr('class', 'layer-geojson') + .attr('class', 'layer-mapdata') .merge(layer); var paths = layer .selectAll('path') - .data([_geojson]); + .data(hasData ? [_geojson] : []); paths.exit() .remove(); @@ -95,7 +148,7 @@ export function svgData(projection, context, dispatch) { .attr('d', getPath); - var labelData = _showLabels && _geojson.features ? _geojson.features : []; + var labelData = (_showLabels && hasData && _geojson.features) || []; labelData = labelData.filter(getPath); layer @@ -175,26 +228,34 @@ export function svgData(projection, context, dispatch) { } - function parseSaveAndZoom(extension, data, name) { + drawData.setFile = function(extension, data, src) { + var gj; switch (extension) { case '.gpx': - drawData.geojson(toGeoJSON.gpx(toDom(data)), name).fitZoom(); + gj = toGeoJSON.gpx(toDom(data)); break; case '.kml': - drawData.geojson(toGeoJSON.kml(toDom(data)), name).fitZoom(); + gj = toGeoJSON.kml(toDom(data)); break; case '.pbf': - drawData.geojson(vtToGeoJSON(data), name).fitZoom(); + gj = vtToGeoJSON(data); break; case '.mvt': - drawData.geojson(vtToGeoJSON(data), name).fitZoom(); + gj = vtToGeoJSON(data); break; case '.geojson': case '.json': - drawData.geojson(JSON.parse(data), name).fitZoom(); + gj = JSON.parse(data); break; } - } + + if (!gj || _isEmpty(gj) || _isEmpty(gj.features)) return; + _geojson = gj; + _src = src || 'unknown.geojson'; + + dispatch.call('change'); + return this.fitZoom(); + }; drawData.showLabels = function(val) { @@ -207,6 +268,12 @@ export function svgData(projection, context, dispatch) { drawData.enabled = function(val) { if (!arguments.length) return _enabled; _enabled = val; + if (_enabled) { + showLayer(); + } else { + hideLayer(); + } + dispatch.call('change'); return this; }; @@ -217,16 +284,74 @@ export function svgData(projection, context, dispatch) { }; + drawData.template = function(val) { + if (!arguments.length) return _template; + + _template = val; + _fileList = null; + _geojson = null; + _src = 'vector tiles'; + + dispatch.call('change'); + return this; + }; + + drawData.geojson = function(gj, src) { if (!arguments.length) return _geojson; - if (_isEmpty(gj) || _isEmpty(gj.features)) return this; + + _template = null; + _fileList = null; _geojson = gj; _src = src || 'unknown.geojson'; + dispatch.call('change'); return this; }; + drawData.fileList = function(fileList) { + if (!arguments.length) return _fileList; + + _template = null; + _fileList = fileList; + _geojson = null; + _src = null; + + if (!fileList || !fileList.length) return this; + var f = fileList[0]; + var reader = new FileReader(); + var extension = getExtension(f.name); + + if (extension === 'mvt' || extension === 'pbf') { + reader.onload = (function(file) { + return; // todo find x,y,z + var data = []; + var zxy = [0,0,0]; + + var bufferdata = { data: data, zxy: zxy }; + return function (e) { + bufferdata.data = e.target.result; + drawData.setFile(extension, bufferdata, file.name); + }; + })(f); + + reader.readAsArrayBuffer(f); + + } else { + reader.onload = (function(file) { + return function (e) { + drawData.setFile(extension, e.target.result, file.name); + }; + })(f); + + reader.readAsText(f); + } + + return this; + }; + + drawData.url = function(url) { var extension = getExtension(url); if (extension === 'mvt' || extension === 'pbf') { @@ -238,16 +363,13 @@ export function svgData(projection, context, dispatch) { var match = url.match(/(pbf|mvt)/i); var extension = match ? ('.' + match[0].toLowerCase()) : ''; var zxy = url.match(/\/(\d+)\/(\d+)\/(\d+)/); - var bufferdata = { - data : data, - zxy : zxy - }; - parseSaveAndZoom(extension, bufferdata); + var bufferdata = { data : data, zxy : zxy }; + drawData.setFile(extension, bufferdata, url); }); } else { d3_text(url, function(err, data) { if (!err) { - parseSaveAndZoom(extension, data, url); + drawData.setFile(extension, data, url); } }); } @@ -256,46 +378,6 @@ export function svgData(projection, context, dispatch) { }; - drawData.files = function(fileList) { - if (!fileList.length) return this; - var f = fileList[0]; - var reader = new FileReader(); - var extension = getExtension(f.name); - - if (extension === 'mvt' || extension === 'pbf') { - reader.onload = (function(file) { - return; // todo find x,y,z - var data = []; - var zxy = [0,0,0]; - - _src = file.name; - var extension = getExtension(file.name); - var bufferdata = { - data: data, - zxy: zxy - }; - return function (e) { - bufferdata.data = e.target.result; - parseSaveAndZoom(extension, bufferdata); - }; - })(f); - - reader.readAsArrayBuffer(f); - - } else { - reader.onload = (function(file) { - return function (e) { - parseSaveAndZoom(extension, e.target.result, file.name); - }; - })(f); - - reader.readAsText(f); - } - - return this; - }; - - drawData.getSrc = function() { return _src; }; diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index 0f8ff4e9c..8eec067ff 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -293,14 +293,13 @@ export function uiMapData(context) { function customChanged(d) { -console.log('custom was changed'); - // if (d && d.template) { - // _customSource.template(d.template); - // chooseBackground(_customSource); - // } else { - // _customSource.template(''); - // chooseBackground(context.background().findSource('none')); - // } + var dataLayer = layers.layer('data'); + + if (d && d.template) { + dataLayer.template(d.template); + } else if (d && d.fileList) { + dataLayer.fileList(d.fileList); + } } diff --git a/modules/ui/settings/custom_data.js b/modules/ui/settings/custom_data.js index 26c7039c2..ffa454588 100644 --- a/modules/ui/settings/custom_data.js +++ b/modules/ui/settings/custom_data.js @@ -12,11 +12,13 @@ export function uiSettingsCustomData(context) { var dispatch = d3_dispatch('change'); function render(selection) { + var dataLayer = context.layers().layer('data'); var _origSettings = { - file: context.storage('settings-custom-data-file'), + fileList: (dataLayer && dataLayer.fileList()) || null, template: context.storage('settings-custom-data-template') }; var _currSettings = _cloneDeep(_origSettings); + // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; var modal = uiConfirm(selection).okButton(); @@ -39,12 +41,15 @@ export function uiSettingsCustomData(context) { .append('input') .attr('class', 'field-file') .attr('type', 'file') + .property('files', _currSettings.fileList) // works for all except IE11 .on('change', function() { var files = d3_event.target.files; if (files && files.length) { - _currSettings.file = files[0]; + _currSettings.template = ''; + textSection.select('.field-template').property('value', ''); + _currSettings.fileList = files; } else { - _currSettings.file = undefined; + _currSettings.fileList = null; } }); @@ -93,16 +98,19 @@ export function uiSettingsCustomData(context) { function clickCancel() { textSection.select('.field-template').property('value', _origSettings.template); context.storage('settings-custom-data-template', _origSettings.template); - context.storage('settings-custom-data-file', _origSettings.file); this.blur(); modal.close(); } // accept the current template function clickSave() { - _currSettings.template = textSection.select('.field-template').property('value'); + _currSettings.template = textSection.select('.field-template').property('value').trim(); + + // one or the other but not both + if (_currSettings.template) { _currSettings.fileList = null; } + if (_currSettings.fileList) { _currSettings.template = ''; } + context.storage('settings-custom-data-template', _currSettings.template); - context.storage('settings-custom-data-file', _currSettings.file); this.blur(); modal.close(); dispatch.call('change', this, _currSettings); diff --git a/test/spec/svg/data.js b/test/spec/svg/data.js index fa92be0d5..8068fc18b 100644 --- a/test/spec/svg/data.js +++ b/test/spec/svg/data.js @@ -41,11 +41,11 @@ describe('iD.svgData', function () { }); - it('creates layer-geojson', function () { + it('creates layer-mapdata', function () { var render = iD.svgData(projection, context, dispatch); surface.call(render); - var layers = surface.selectAll('g.layer-geojson').nodes(); + var layers = surface.selectAll('g.layer-mapdata').nodes(); expect(layers.length).to.eql(1); });