mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Merge matching polygons across tile boundaries
This commit is contained in:
+4
-1
@@ -27,7 +27,10 @@ module.exports = function buildSrc() {
|
||||
input: './modules/id.js',
|
||||
plugins: [
|
||||
includePaths( {
|
||||
paths: ['node_modules/d3/node_modules'] // npm2 or windows
|
||||
paths: ['node_modules/d3/node_modules'], // npm2 or windows
|
||||
include: {
|
||||
'martinez-polygon-clipping': 'node_modules/martinez-polygon-clipping/dist/martinez.umd.js'
|
||||
}
|
||||
}),
|
||||
nodeResolve({
|
||||
module: true,
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import _find from 'lodash-es/find';
|
||||
import _isEqual from 'lodash-es/isEqual';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { request as d3_request } from 'd3-request';
|
||||
|
||||
import bboxClip from '@turf/bbox-clip';
|
||||
import turf_bboxClip from '@turf/bbox-clip';
|
||||
import stringify from 'fast-json-stable-stringify';
|
||||
import martinez from 'martinez-polygon-clipping';
|
||||
|
||||
import Protobuf from 'pbf';
|
||||
import vt from '@mapbox/vector-tile';
|
||||
|
||||
import { utilHashcode, utilRebind, utilTiler } from '../util';
|
||||
|
||||
|
||||
var tiler = utilTiler().tileSize(512);
|
||||
var tiler = utilTiler().tileSize(512).margin(1);
|
||||
var dispatch = d3_dispatch('loadedData');
|
||||
var _vtCache;
|
||||
|
||||
@@ -21,7 +25,7 @@ function abortRequest(i) {
|
||||
}
|
||||
|
||||
|
||||
function vtToGeoJSON(data, tile) {
|
||||
function vtToGeoJSON(data, tile, mergeCache) {
|
||||
var vectorTile = new vt.VectorTile(new Protobuf(data.response));
|
||||
var layers = Object.keys(vectorTile.layers);
|
||||
if (!Array.isArray(layers)) { layers = [layers]; }
|
||||
@@ -32,16 +36,58 @@ function vtToGeoJSON(data, tile) {
|
||||
if (layer) {
|
||||
for (var i = 0; i < layer.length; i++) {
|
||||
var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
|
||||
var geometry = feature.geometry;
|
||||
if (layers.length > 1) {
|
||||
feature.properties.vt_layer = layerID;
|
||||
}
|
||||
// clip to tile bounds
|
||||
feature = bboxClip(feature, tile.extent.rectangle());
|
||||
|
||||
// Treat all Polygons as MultiPolygons
|
||||
if (geometry.type === 'Polygon') {
|
||||
geometry.type = 'MultiPolygon';
|
||||
geometry.coordinates = [geometry.coordinates];
|
||||
}
|
||||
|
||||
// Clip to tile bounds
|
||||
if (geometry.type === 'MultiPolygon') {
|
||||
var isClipped = false;
|
||||
var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
|
||||
if (!_isEqual(feature.geometry, featureClip.geometry)) {
|
||||
// feature = featureClip;
|
||||
isClipped = true;
|
||||
}
|
||||
if (!feature.geometry.coordinates.length) continue; // not actually on this tile
|
||||
if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
|
||||
}
|
||||
|
||||
// force some unique id generation
|
||||
feature.__featurehash__ = utilHashcode(JSON.stringify(feature));
|
||||
feature.__propertyhash__ = utilHashcode(JSON.stringify(feature.properties || {}));
|
||||
var featurehash = utilHashcode(stringify(feature));
|
||||
var propertyhash = utilHashcode(stringify(feature.properties || {}));
|
||||
feature.__featurehash__ = featurehash;
|
||||
feature.__propertyhash__ = propertyhash;
|
||||
features.push(feature);
|
||||
|
||||
// Clipped Polygons at same zoom with identical properties can get merged
|
||||
if (isClipped && geometry.type === 'MultiPolygon') {
|
||||
var merged = mergeCache[propertyhash];
|
||||
if (merged && merged.length) {
|
||||
var other = merged[0];
|
||||
var coords = martinez.union(
|
||||
feature.geometry.coordinates, other.geometry.coordinates
|
||||
);
|
||||
|
||||
if (!coords || !coords.length) {
|
||||
continue; // something failed in martinez union
|
||||
}
|
||||
|
||||
merged.push(feature);
|
||||
for (var j = 0; j < merged.length; j++) { // all these features get...
|
||||
merged[j].geometry.coordinates = coords; // same coords
|
||||
merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
|
||||
}
|
||||
} else {
|
||||
mergeCache[propertyhash] = [feature];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -64,19 +110,19 @@ function loadTile(source, tile) {
|
||||
return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
|
||||
});
|
||||
|
||||
|
||||
source.inflight[tile.id] = d3_request(url)
|
||||
.responseType('arraybuffer')
|
||||
.get(function(err, data) {
|
||||
source.loaded[tile.id] = {};
|
||||
source.loaded[tile.id] = [];
|
||||
delete source.inflight[tile.id];
|
||||
if (err || !data) return;
|
||||
|
||||
source.loaded[tile.id] = {
|
||||
data: data,
|
||||
features: vtToGeoJSON(data, tile)
|
||||
};
|
||||
var z = tile.xyz[2];
|
||||
if (!source.canMerge[z]) {
|
||||
source.canMerge[z] = {}; // initialize mergeCache
|
||||
}
|
||||
|
||||
source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
|
||||
dispatch.call('loadedData');
|
||||
});
|
||||
}
|
||||
@@ -106,7 +152,7 @@ export default {
|
||||
|
||||
|
||||
addSource: function(sourceID, template) {
|
||||
_vtCache[sourceID] = { template: template, inflight: {}, loaded: {} };
|
||||
_vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
|
||||
return _vtCache[sourceID];
|
||||
},
|
||||
|
||||
@@ -120,13 +166,14 @@ export default {
|
||||
var results = [];
|
||||
|
||||
for (var i = 0; i < tiles.length; i++) {
|
||||
var loaded = source.loaded[tiles[i].id];
|
||||
if (!loaded || !loaded.features) continue;
|
||||
var features = source.loaded[tiles[i].id];
|
||||
if (!features || !features.length) continue;
|
||||
|
||||
for (var j = 0; j < loaded.features.length; j++) {
|
||||
var feature = loaded.features[j];
|
||||
if (seen[feature.__featurehash__]) continue;
|
||||
seen[feature.__featurehash__] = true;
|
||||
for (var j = 0; j < features.length; j++) {
|
||||
var feature = features[j];
|
||||
var hash = feature.__featurehash__;
|
||||
if (seen[hash]) continue;
|
||||
seen[hash] = true;
|
||||
results.push(feature);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@ import {
|
||||
select as d3_select
|
||||
} from 'd3-selection';
|
||||
|
||||
import stringify from 'fast-json-stable-stringify';
|
||||
import toGeoJSON from '@mapbox/togeojson';
|
||||
|
||||
import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
|
||||
@@ -133,7 +134,7 @@ export function svgData(projection, context, dispatch) {
|
||||
// ensure that each single Feature object has a unique ID
|
||||
function ensureFeatureID(feature) {
|
||||
if (!feature) return;
|
||||
feature.__featurehash__ = utilHashcode(JSON.stringify(feature));
|
||||
feature.__featurehash__ = utilHashcode(stringify(feature));
|
||||
return feature;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,10 @@
|
||||
"@mapbox/vector-tile": "^1.3.1",
|
||||
"@turf/bbox-clip": "^6.0.0",
|
||||
"diacritics": "1.3.0",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"lodash-es": "4.17.10",
|
||||
"marked": "0.5.0",
|
||||
"martinez-polygon-clipping": "0.5.0",
|
||||
"node-diff3": "1.0.0",
|
||||
"osm-auth": "1.0.2",
|
||||
"pannellum": "2.4.1",
|
||||
|
||||
Reference in New Issue
Block a user