mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-16 13:59:27 +02:00
344 lines
9.3 KiB
JavaScript
344 lines
9.3 KiB
JavaScript
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 { geoBounds as d3_geoBounds } from 'd3-geo';
|
|
|
|
import {
|
|
request as d3_request,
|
|
text as d3_text
|
|
} from 'd3-request';
|
|
|
|
import {
|
|
event as d3_event,
|
|
select as d3_select
|
|
} from 'd3-selection';
|
|
|
|
import toGeoJSON from '@mapbox/togeojson';
|
|
import vt from '@mapbox/vector-tile';
|
|
import Protobuf from 'pbf';
|
|
|
|
import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
|
|
import { svgPath } from './index';
|
|
import { utilDetect } from '../util/detect';
|
|
|
|
|
|
var _initialized = false;
|
|
var _enabled = false;
|
|
var _geojson;
|
|
|
|
|
|
export function svgData(projection, context, dispatch) {
|
|
var _showLabels = true;
|
|
var detected = utilDetect();
|
|
var layer;
|
|
var _src;
|
|
|
|
|
|
function init() {
|
|
if (_initialized) return; // run once
|
|
|
|
_geojson = {};
|
|
_enabled = true;
|
|
|
|
function over() {
|
|
d3_event.stopPropagation();
|
|
d3_event.preventDefault();
|
|
d3_event.dataTransfer.dropEffect = 'copy';
|
|
}
|
|
|
|
d3_select('body')
|
|
.attr('dropzone', 'copy')
|
|
.on('drop.svgData', function() {
|
|
d3_event.stopPropagation();
|
|
d3_event.preventDefault();
|
|
if (!detected.filedrop) return;
|
|
drawData.files(d3_event.dataTransfer.files);
|
|
})
|
|
.on('dragenter.svgData', over)
|
|
.on('dragexit.svgData', over)
|
|
.on('dragover.svgData', over);
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
|
|
function drawData(selection) {
|
|
var getPath = svgPath(projection).geojson;
|
|
|
|
layer = selection.selectAll('.layer-geojson')
|
|
.data(_enabled ? [0] : []);
|
|
|
|
layer.exit()
|
|
.remove();
|
|
|
|
layer = layer.enter()
|
|
.append('g')
|
|
.attr('class', 'layer-geojson')
|
|
.merge(layer);
|
|
|
|
|
|
var paths = layer
|
|
.selectAll('path')
|
|
.data([_geojson]);
|
|
|
|
paths.exit()
|
|
.remove();
|
|
|
|
paths = paths.enter()
|
|
.append('path')
|
|
.attr('class', 'pathdata')
|
|
.merge(paths);
|
|
|
|
paths
|
|
.attr('d', getPath);
|
|
|
|
|
|
var labelData = _showLabels && _geojson.features ? _geojson.features : [];
|
|
labelData = labelData.filter(getPath);
|
|
|
|
layer
|
|
.call(drawLabels, 'label-halo', labelData)
|
|
.call(drawLabels, 'label', labelData);
|
|
|
|
|
|
function drawLabels(selection, textClass, data) {
|
|
var labels = selection.selectAll('text.' + textClass)
|
|
.data(data);
|
|
|
|
// exit
|
|
labels.exit()
|
|
.remove();
|
|
|
|
// enter/update
|
|
labels = labels.enter()
|
|
.append('text')
|
|
.attr('class', textClass)
|
|
.merge(labels)
|
|
.text(function(d) {
|
|
if (d.properties) {
|
|
return d.properties.desc || d.properties.name;
|
|
}
|
|
return null;
|
|
})
|
|
.attr('x', function(d) {
|
|
var centroid = getPath.centroid(d);
|
|
return centroid[0] + 11;
|
|
})
|
|
.attr('y', function(d) {
|
|
var centroid = getPath.centroid(d);
|
|
return centroid[1];
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
function getExtension(fileName) {
|
|
if (fileName === undefined) {
|
|
return '';
|
|
}
|
|
|
|
var lastDotIndex = fileName.lastIndexOf('.');
|
|
if (lastDotIndex < 0) {
|
|
return '';
|
|
}
|
|
|
|
return fileName.substr(lastDotIndex);
|
|
}
|
|
|
|
|
|
function toDom(textdata) {
|
|
return (new DOMParser()).parseFromString(textdata, 'text/xml');
|
|
}
|
|
|
|
|
|
function vtToGeoJSON(bufferdata) {
|
|
var tile = new vt.VectorTile(new Protobuf(bufferdata.data.response));
|
|
var layers = Object.keys(tile.layers);
|
|
if (!Array.isArray(layers)) { layers = [layers]; }
|
|
|
|
var collection = {type: 'FeatureCollection', features: []};
|
|
|
|
layers.forEach(function (layerID) {
|
|
var layer = tile.layers[layerID];
|
|
if (layer) {
|
|
for (var i = 0; i < layer.length; i++) {
|
|
var feature = layer.feature(i).toGeoJSON(bufferdata.zxy[2], bufferdata.zxy[3], bufferdata.zxy[1]);
|
|
if (layers.length > 1) feature.properties.vt_layer = layerID;
|
|
collection.features.push(feature);
|
|
}
|
|
}
|
|
});
|
|
|
|
return collection;
|
|
}
|
|
|
|
|
|
function parseSaveAndZoom(extension, data, name) {
|
|
switch (extension) {
|
|
case '.gpx':
|
|
drawData.geojson(toGeoJSON.gpx(toDom(data)), name).fitZoom();
|
|
break;
|
|
case '.kml':
|
|
drawData.geojson(toGeoJSON.kml(toDom(data)), name).fitZoom();
|
|
break;
|
|
case '.pbf':
|
|
drawData.geojson(vtToGeoJSON(data), name).fitZoom();
|
|
break;
|
|
case '.mvt':
|
|
drawData.geojson(vtToGeoJSON(data), name).fitZoom();
|
|
break;
|
|
case '.geojson':
|
|
case '.json':
|
|
drawData.geojson(JSON.parse(data), name).fitZoom();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
drawData.showLabels = function(val) {
|
|
if (!arguments.length) return _showLabels;
|
|
_showLabels = val;
|
|
return this;
|
|
};
|
|
|
|
|
|
drawData.enabled = function(val) {
|
|
if (!arguments.length) return _enabled;
|
|
_enabled = val;
|
|
dispatch.call('change');
|
|
return this;
|
|
};
|
|
|
|
|
|
drawData.hasData = function() {
|
|
return (!(_isEmpty(_geojson) || _isEmpty(_geojson.features)));
|
|
};
|
|
|
|
|
|
drawData.geojson = function(gj, src) {
|
|
if (!arguments.length) return _geojson;
|
|
if (_isEmpty(gj) || _isEmpty(gj.features)) return this;
|
|
_geojson = gj;
|
|
_src = src || 'unknown.geojson';
|
|
dispatch.call('change');
|
|
return this;
|
|
};
|
|
|
|
|
|
drawData.url = function(url) {
|
|
var extension = getExtension(url);
|
|
if (extension === 'mvt' || extension === 'pbf') {
|
|
d3_request(url)
|
|
.responseType('arraybuffer')
|
|
.get(function(err, data) {
|
|
if (err || !data) return;
|
|
_src = url;
|
|
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);
|
|
});
|
|
} else {
|
|
d3_text(url, function(err, data) {
|
|
if (!err) {
|
|
parseSaveAndZoom(extension, data, url);
|
|
}
|
|
});
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
drawData.fitZoom = function() {
|
|
if (!this.hasData()) return this;
|
|
|
|
var map = context.map();
|
|
var viewport = map.trimmedExtent().polygon();
|
|
var coords = _reduce(_geojson.features, function(coords, feature) {
|
|
var c = feature.geometry.coordinates;
|
|
|
|
/* eslint-disable no-fallthrough */
|
|
switch (feature.geometry.type) {
|
|
case 'Point':
|
|
c = [c];
|
|
case 'MultiPoint':
|
|
case 'LineString':
|
|
break;
|
|
|
|
case 'MultiPolygon':
|
|
c = _flatten(c);
|
|
case 'Polygon':
|
|
case 'MultiLineString':
|
|
c = _flatten(c);
|
|
break;
|
|
}
|
|
/* eslint-enable no-fallthrough */
|
|
|
|
return _union(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;
|
|
};
|
|
|
|
|
|
init();
|
|
return drawData;
|
|
}
|