mirror of
https://github.com/FoggedLens/iD.git
synced 2026-06-03 13:38:04 +02:00
Partition viewport by tiles, not by pixels
(closes #4297) The previous approach split the viewport up by pixels, but each time the view moved, the pixels would change, so it was not a stable selection of the streetview data, and the markers would fight for position as the user moved around. This approach uses utilTiler to partition the view into stable tiles.
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
/* global Mapillary:false */
|
||||
import _find from 'lodash-es/find';
|
||||
import _flatten from 'lodash-es/flatten';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
import _map from 'lodash-es/map';
|
||||
import _some from 'lodash-es/some';
|
||||
import _union from 'lodash-es/union';
|
||||
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { request as d3_request } from 'd3-request';
|
||||
import {
|
||||
@@ -213,57 +210,29 @@ function parsePagination(links) {
|
||||
}
|
||||
|
||||
|
||||
// partition viewport into `psize` x `psize` regions
|
||||
function partitionViewport(psize, projection) {
|
||||
var dimensions = projection.clipExtent()[1];
|
||||
psize = psize || 16;
|
||||
var cols = d3_range(0, dimensions[0], psize);
|
||||
var rows = d3_range(0, dimensions[1], psize);
|
||||
var partitions = [];
|
||||
// partition viewport into higher zoom tiles
|
||||
function partitionViewport(projection) {
|
||||
var z = geoScaleToZoom(projection.scale());
|
||||
var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5
|
||||
var tiler = utilTiler().zoomExtent([z2, z2]);
|
||||
|
||||
rows.forEach(function(y) {
|
||||
cols.forEach(function(x) {
|
||||
var min = [x, y + psize];
|
||||
var max = [x + psize, y];
|
||||
partitions.push(
|
||||
geoExtent(projection.invert(min), projection.invert(max)));
|
||||
});
|
||||
});
|
||||
|
||||
return partitions;
|
||||
return tiler.getTiles(projection)
|
||||
.map(function(tile) { return tile.extent; });
|
||||
}
|
||||
|
||||
|
||||
// no more than `limit` results per partition.
|
||||
function searchLimited(psize, limit, projection, rtree) {
|
||||
limit = limit || 3;
|
||||
function searchLimited(limit, projection, rtree) {
|
||||
limit = limit || 5;
|
||||
|
||||
var partitions = partitionViewport(psize, projection);
|
||||
var results;
|
||||
return partitionViewport(projection)
|
||||
.reduce(function(result, extent) {
|
||||
var found = rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
|
||||
// console.time('previous');
|
||||
results = _flatten(_map(partitions, function(extent) {
|
||||
return rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
}));
|
||||
// console.timeEnd('previous');
|
||||
|
||||
// console.time('new');
|
||||
// results = partitions.reduce(function(result, extent) {
|
||||
// var found = rtree.search(extent.bbox())
|
||||
// .map(function(d) { return d.data; })
|
||||
// .sort(function(a, b) {
|
||||
// return a.loc[1] - b.loc[1];
|
||||
// // return a.key.localeCompare(b.key);
|
||||
// })
|
||||
// .slice(0, limit);
|
||||
|
||||
// return (found.length ? result.concat(found) : result);
|
||||
// }, []);
|
||||
// console.timeEnd('new');
|
||||
|
||||
return results;
|
||||
return (found.length ? result.concat(found) : result);
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,14 +278,14 @@ export default {
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, _mlyCache.images.rtree);
|
||||
var limit = 5;
|
||||
return searchLimited(limit, projection, _mlyCache.images.rtree);
|
||||
},
|
||||
|
||||
|
||||
signs: function(projection) {
|
||||
var psize = 32, limit = 3;
|
||||
return searchLimited(psize, limit, projection, _mlyCache.map_features.rtree);
|
||||
var limit = 5;
|
||||
return searchLimited(limit, projection, _mlyCache.map_features.rtree);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import _find from 'lodash-es/find';
|
||||
import _flatten from 'lodash-es/flatten';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
import _map from 'lodash-es/map';
|
||||
import _union from 'lodash-es/union';
|
||||
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { request as d3_request } from 'd3-request';
|
||||
|
||||
@@ -164,40 +161,29 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
}
|
||||
|
||||
|
||||
// partition viewport into `psize` x `psize` regions
|
||||
function partitionViewport(psize, projection) {
|
||||
var dimensions = projection.clipExtent()[1];
|
||||
psize = psize || 16;
|
||||
var cols = d3_range(0, dimensions[0], psize);
|
||||
var rows = d3_range(0, dimensions[1], psize);
|
||||
var partitions = [];
|
||||
// partition viewport into higher zoom tiles
|
||||
function partitionViewport(projection) {
|
||||
var z = geoScaleToZoom(projection.scale());
|
||||
var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5
|
||||
var tiler = utilTiler().zoomExtent([z2, z2]);
|
||||
|
||||
rows.forEach(function(y) {
|
||||
cols.forEach(function(x) {
|
||||
var min = [x, y + psize];
|
||||
var max = [x + psize, y];
|
||||
partitions.push(
|
||||
geoExtent(projection.invert(min), projection.invert(max)));
|
||||
});
|
||||
});
|
||||
|
||||
return partitions;
|
||||
return tiler.getTiles(projection)
|
||||
.map(function(tile) { return tile.extent; });
|
||||
}
|
||||
|
||||
|
||||
// no more than `limit` results per partition.
|
||||
function searchLimited(psize, limit, projection, rtree) {
|
||||
limit = limit || 3;
|
||||
function searchLimited(limit, projection, rtree) {
|
||||
limit = limit || 5;
|
||||
|
||||
var partitions = partitionViewport(psize, projection);
|
||||
var results;
|
||||
return partitionViewport(projection)
|
||||
.reduce(function(result, extent) {
|
||||
var found = rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
|
||||
results = _flatten(_map(partitions, function(extent) {
|
||||
return rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
}));
|
||||
return results;
|
||||
return (found.length ? result.concat(found) : result);
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -237,8 +223,8 @@ export default {
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, _oscCache.images.rtree);
|
||||
var limit = 5;
|
||||
return searchLimited(limit, projection, _oscCache.images.rtree);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
import _find from 'lodash-es/find';
|
||||
import _flatten from 'lodash-es/flatten';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
import _map from 'lodash-es/map';
|
||||
import _union from 'lodash-es/union';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { timer as d3_timer } from 'd3-timer';
|
||||
|
||||
import {
|
||||
@@ -25,6 +22,7 @@ import {
|
||||
geoMetersToLon,
|
||||
geoPointInPolygon,
|
||||
geoRotate,
|
||||
geoScaleToZoom,
|
||||
geoVecLength
|
||||
} from '../geo';
|
||||
|
||||
@@ -233,45 +231,30 @@ function getBubbles(url, tile, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* partitionViewport() partition viewport into `psize` x `psize` regions.
|
||||
*/
|
||||
function partitionViewport(psize, projection) {
|
||||
var dimensions = projection.clipExtent()[1];
|
||||
psize = psize || 16;
|
||||
|
||||
var cols = d3_range(0, dimensions[0], psize);
|
||||
var rows = d3_range(0, dimensions[1], psize);
|
||||
var partitions = [];
|
||||
// partition viewport into higher zoom tiles
|
||||
function partitionViewport(projection) {
|
||||
var z = geoScaleToZoom(projection.scale());
|
||||
var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5
|
||||
var tiler = utilTiler().zoomExtent([z2, z2]);
|
||||
|
||||
rows.forEach(function (y) {
|
||||
cols.forEach(function (x) {
|
||||
var min = [x, y + psize];
|
||||
var max = [x + psize, y];
|
||||
partitions.push(geoExtent(projection.invert(min), projection.invert(max)));
|
||||
});
|
||||
});
|
||||
|
||||
return partitions;
|
||||
return tiler.getTiles(projection)
|
||||
.map(function(tile) { return tile.extent; });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* searchLimited().
|
||||
*/
|
||||
function searchLimited(psize, limit, projection, rtree) {
|
||||
limit = limit || 3;
|
||||
// no more than `limit` results per partition.
|
||||
function searchLimited(limit, projection, rtree) {
|
||||
limit = limit || 5;
|
||||
|
||||
var partitions = partitionViewport(psize, projection);
|
||||
var results;
|
||||
return partitionViewport(projection)
|
||||
.reduce(function(result, extent) {
|
||||
var found = rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
|
||||
results = _flatten(_map(partitions, function (extent) {
|
||||
return rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function (d) { return d.data; });
|
||||
}));
|
||||
|
||||
return results;
|
||||
return (found.length ? result.concat(found) : result);
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -485,8 +468,8 @@ export default {
|
||||
* bubbles()
|
||||
*/
|
||||
bubbles: function (projection) {
|
||||
var psize = 32, limit = 3;
|
||||
return searchLimited(psize, limit, projection, _ssCache.bubbles.rtree);
|
||||
var limit = 5;
|
||||
return searchLimited(limit, projection, _ssCache.bubbles.rtree);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -217,8 +217,8 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function drawImages(selection) {
|
||||
var enabled = svgMapillaryImages.enabled,
|
||||
service = getService();
|
||||
var enabled = svgMapillaryImages.enabled;
|
||||
var service = getService();
|
||||
|
||||
layer = selection.selectAll('.layer-mapillary-images')
|
||||
.data(service ? [0] : []);
|
||||
|
||||
@@ -248,18 +248,19 @@ describe('iD.serviceMapillary', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('limits results no more than 3 stacked images in one spot', function() {
|
||||
it('limits results no more than 5 stacked images in one spot', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90 } }
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], ca: 90 } }
|
||||
];
|
||||
|
||||
mapillary.cache().images.rtree.load(features);
|
||||
var res = mapillary.images(context.projection);
|
||||
expect(res).to.have.length.of.at.most(3);
|
||||
expect(res).to.have.length.of.at.most(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -284,7 +285,7 @@ describe('iD.serviceMapillary', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('limits results no more than 3 stacked signs in one spot', function() {
|
||||
it('limits results no more than 5 stacked signs in one spot', function() {
|
||||
var detections = [{
|
||||
detection_key: '78vqha63gs1upg15s823qckcmn',
|
||||
image_key: 'bwYs-uXLDvm_meo_EC5Nzw'
|
||||
@@ -294,12 +295,13 @@ describe('iD.serviceMapillary', function() {
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], detections: detections } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], detections: detections } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } }
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], detections: detections } }
|
||||
];
|
||||
|
||||
mapillary.cache().map_features.rtree.load(features);
|
||||
var res = mapillary.signs(context.projection);
|
||||
expect(res).to.have.length.of.at.most(3);
|
||||
expect(res).to.have.length.of.at.most(5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -244,18 +244,19 @@ describe('iD.serviceOpenstreetcam', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('limits results no more than 3 stacked images in one spot', function() {
|
||||
it('limits results no more than 5 stacked images in one spot', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 2 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 3 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 4 } }
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 4 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 5 } }
|
||||
];
|
||||
|
||||
openstreetcam.cache().images.rtree.load(features);
|
||||
var res = openstreetcam.images(context.projection);
|
||||
expect(res).to.have.length.of.at.most(3);
|
||||
expect(res).to.have.length.of.at.most(5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -132,18 +132,19 @@ describe('iD.serviceStreetside', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('limits results no more than 3 stacked bubbles in one spot', function() {
|
||||
it('limits results no more than 5 stacked bubbles in one spot', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 1, loc: [10, 0], ca: 90, pr: undefined, ne: 2, pano: true, sequence_id: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 2, loc: [10, 0], ca: 90, pr: 1, ne: 3, pano: true, sequence_id: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 3, loc: [10, 0], ca: 90, pr: 2, ne: 4, pano: true, sequence_id: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 4, loc: [10, 0], ca: 90, pr: 3, ne: 5, pano: true, sequence_id: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 5, loc: [10, 0], ca: 90, pr: 4, ne: undefined, pano: true, sequence_id: 1 } }
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 5, loc: [10, 0], ca: 90, pr: 4, ne: 6, pano: true, sequence_id: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 6, loc: [10, 0], ca: 90, pr: 5, ne: undefined, pano: true, sequence_id: 1 } }
|
||||
];
|
||||
|
||||
streetside.cache().bubbles.rtree.load(features);
|
||||
var res = streetside.bubbles(context.projection);
|
||||
expect(res).to.have.length.of.at.most(3);
|
||||
expect(res).to.have.length.of.at.most(5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user