mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 13:38:26 +02:00
Extract and fix extent/intersection calculations
Extents are now [[min x, min y], [max x, max y]].
This commit is contained in:
@@ -29,6 +29,8 @@ all: \
|
||||
js/id/oauth.js \
|
||||
js/id/services/*.js \
|
||||
js/id/util.js \
|
||||
js/id/geo.js \
|
||||
js/id/geo/*.js \
|
||||
js/id/actions.js \
|
||||
js/id/actions/*.js \
|
||||
js/id/behavior.js \
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
<script src='js/id/oauth.js'></script>
|
||||
<script src='js/id/services/taginfo.js'></script>
|
||||
|
||||
<script src="js/id/geo.js"></script>
|
||||
<script src="js/id/geo/extent.js"></script>
|
||||
|
||||
<script src='js/id/renderer/background.js'></script>
|
||||
<script src='js/id/renderer/background_source.js'></script>
|
||||
<script src='js/id/renderer/map.js'></script>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
iD.geo = {};
|
||||
@@ -0,0 +1,37 @@
|
||||
iD.geo.Extent = function (min, max) {
|
||||
if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
|
||||
if (min instanceof iD.geo.Extent) {
|
||||
return min;
|
||||
} else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
|
||||
this[0] = min[0];
|
||||
this[1] = min[1];
|
||||
} else {
|
||||
this[0] = min || [ Infinity, Infinity];
|
||||
this[1] = max || min || [-Infinity, -Infinity];
|
||||
}
|
||||
};
|
||||
|
||||
iD.geo.Extent.prototype = [[], []];
|
||||
|
||||
_.extend(iD.geo.Extent.prototype, {
|
||||
extend: function (obj) {
|
||||
obj = iD.geo.Extent(obj);
|
||||
return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
|
||||
Math.min(obj[0][1], this[0][1])],
|
||||
[Math.max(obj[1][0], this[1][0]),
|
||||
Math.max(obj[1][1], this[1][1])]);
|
||||
},
|
||||
|
||||
center: function () {
|
||||
return [(this[0][0] + this[1][0]) / 2,
|
||||
(this[0][1] + this[1][1]) / 2];
|
||||
},
|
||||
|
||||
intersects: function (obj) {
|
||||
obj = iD.geo.Extent(obj);
|
||||
return obj[0][0] <= this[1][0] &&
|
||||
obj[0][1] <= this[1][1] &&
|
||||
obj[1][0] >= this[0][0] &&
|
||||
obj[1][1] >= this[0][1];
|
||||
}
|
||||
});
|
||||
@@ -74,11 +74,7 @@ iD.Entity.prototype = {
|
||||
},
|
||||
|
||||
intersects: function(extent, resolver) {
|
||||
var _extent = this.extent(resolver);
|
||||
return _extent[0][0] > extent[0][0] &&
|
||||
_extent[1][0] < extent[1][0] &&
|
||||
_extent[0][1] < extent[0][1] &&
|
||||
_extent[1][1] > extent[1][1];
|
||||
return this.extent(resolver).intersects(extent);
|
||||
},
|
||||
|
||||
hasInterestingTags: function() {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ iD.Node = iD.Entity.extend({
|
||||
type: "node",
|
||||
|
||||
extent: function() {
|
||||
return [this.loc, this.loc];
|
||||
return iD.geo.Extent(this.loc);
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
|
||||
+2
-5
@@ -4,14 +4,11 @@ iD.Way = iD.Entity.extend({
|
||||
|
||||
extent: function(resolver) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
var extent = [[-Infinity, Infinity], [Infinity, -Infinity]];
|
||||
var extent = iD.geo.Extent();
|
||||
for (var i = 0, l = this.nodes.length; i < l; i++) {
|
||||
var node = this.nodes[i];
|
||||
if (node.loc === undefined) node = resolver.entity(node);
|
||||
if (node.loc[0] > extent[0][0]) extent[0][0] = node.loc[0];
|
||||
if (node.loc[0] < extent[1][0]) extent[1][0] = node.loc[0];
|
||||
if (node.loc[1] < extent[0][1]) extent[0][1] = node.loc[1];
|
||||
if (node.loc[1] > extent[1][1]) extent[1][1] = node.loc[1];
|
||||
extent = extent.extend(node.loc);
|
||||
}
|
||||
return extent;
|
||||
});
|
||||
|
||||
@@ -59,8 +59,8 @@ iD.modes.Select = function (entity) {
|
||||
map_size = mode.map.size(),
|
||||
entity_extent = entity.extent(mode.history.graph()),
|
||||
left_edge = map_size[0] - inspector_size[0],
|
||||
left = mode.map.projection(entity_extent[1])[0],
|
||||
right = mode.map.projection(entity_extent[0])[0];
|
||||
left = mode.map.projection(entity_extent[0])[0],
|
||||
right = mode.map.projection(entity_extent[1])[0];
|
||||
|
||||
if (left > left_edge &&
|
||||
right > left_edge) mode.map.centerEase(
|
||||
|
||||
+9
-12
@@ -257,26 +257,23 @@ iD.Map = function() {
|
||||
}, 20);
|
||||
};
|
||||
|
||||
map.extent = function(tl, br) {
|
||||
map.extent = function(_) {
|
||||
if (!arguments.length) {
|
||||
return [projection.invert([0, 0]), projection.invert(dimensions)];
|
||||
return iD.geo.Extent(projection.invert([0, dimensions[1]]),
|
||||
projection.invert([dimensions[0], 0]));
|
||||
} else {
|
||||
|
||||
var TL = projection(tl),
|
||||
BR = projection(br);
|
||||
var extent = iD.geo.Extent(_),
|
||||
tl = projection([extent[0][0], extent[1][1]]),
|
||||
br = projection([extent[1][0], extent[0][1]]);
|
||||
|
||||
// Calculate maximum zoom that fits extent
|
||||
var hFactor = (BR[0] - TL[0]) / dimensions[0],
|
||||
vFactor = (BR[1] - TL[1]) / dimensions[1],
|
||||
var hFactor = (br[0] - tl[0]) / dimensions[0],
|
||||
vFactor = (br[1] - tl[1]) / dimensions[1],
|
||||
hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
|
||||
vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
|
||||
newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
|
||||
|
||||
// Calculate center of projected extent
|
||||
var midPoint = [(TL[0] + BR[0]) / 2, (TL[1] + BR[1]) / 2],
|
||||
midLoc = projection.invert(midPoint);
|
||||
|
||||
map.zoom(newZoom).center(midLoc);
|
||||
map.zoom(newZoom).center(extent.center());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ iD.ui.geocoder = function() {
|
||||
.text('No location found for "' + resp.query[0] + '"');
|
||||
}
|
||||
var bounds = resp.results[0][0].bounds;
|
||||
map.extent([bounds[0], bounds[3]], [bounds[2], bounds[1]]);
|
||||
map.extent(iD.geo.Extent([bounds[0], bounds[1]], [bounds[2], bounds[3]]));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -60,8 +60,7 @@ iD.ui.save = function() {
|
||||
modal.remove();
|
||||
})
|
||||
.on('fix', function(d) {
|
||||
var ext = d.entity.extent(map.history().graph());
|
||||
map.extent(ext[0], ext[1]);
|
||||
map.extent(d.entity.extent(map.history().graph()));
|
||||
if (map.zoom() > 19) map.zoom(19);
|
||||
controller.enter(iD.modes.Select(d.entity));
|
||||
modal.remove();
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
<script src='../js/id/oauth.js'></script>
|
||||
<script src='../js/id/services/taginfo.js'></script>
|
||||
|
||||
<script src="../js/id/geo.js"></script>
|
||||
<script src="../js/id/geo/extent.js"></script>
|
||||
|
||||
<script src='../js/id/renderer/background.js'></script>
|
||||
<script src='../js/id/renderer/background_source.js'></script>
|
||||
<script src='../js/id/renderer/map.js'></script>
|
||||
@@ -139,6 +142,8 @@
|
||||
<script src="spec/format/geojson.js"></script>
|
||||
<script src="spec/format/xml.js"></script>
|
||||
|
||||
<script src="spec/geo/extent.js"></script>
|
||||
|
||||
<script src="spec/graph/graph.js"></script>
|
||||
<script src="spec/graph/entity.js"></script>
|
||||
<script src="spec/graph/node.js"></script>
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
<script src="spec/format/geojson.js"></script>
|
||||
<script src="spec/format/xml.js"></script>
|
||||
|
||||
<script src="spec/geo/extent.js"></script>
|
||||
|
||||
<script src="spec/graph/graph.js"></script>
|
||||
<script src="spec/graph/entity.js"></script>
|
||||
<script src="spec/graph/node.js"></script>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
describe("iD.geo.Extent", function () {
|
||||
describe("constructor", function () {
|
||||
it("defaults to infinitely empty extent", function () {
|
||||
expect(iD.geo.Extent()).to.eql([[Infinity, Infinity], [-Infinity, -Infinity]]);
|
||||
});
|
||||
|
||||
it("constructs via a point", function () {
|
||||
var p = [0, 0];
|
||||
expect(iD.geo.Extent(p)).to.eql([p, p]);
|
||||
});
|
||||
|
||||
it("constructs via two points", function () {
|
||||
var min = [0, 0],
|
||||
max = [5, 10];
|
||||
expect(iD.geo.Extent(min, max)).to.eql([min, max]);
|
||||
});
|
||||
|
||||
it("constructs via an extent", function () {
|
||||
var min = [0, 0],
|
||||
max = [5, 10];
|
||||
expect(iD.geo.Extent([min, max])).to.eql([min, max]);
|
||||
});
|
||||
|
||||
it("constructs via an iD.geo.Extent", function () {
|
||||
var min = [0, 0],
|
||||
max = [5, 10],
|
||||
extent = iD.geo.Extent(min, max);
|
||||
expect(iD.geo.Extent(extent)).to.equal(extent);
|
||||
});
|
||||
|
||||
it("has length 2", function () {
|
||||
expect(iD.geo.Extent().length).to.equal(2);
|
||||
});
|
||||
|
||||
it("has min element", function () {
|
||||
var min = [0, 0],
|
||||
max = [5, 10];
|
||||
expect(iD.geo.Extent(min, max)[0]).to.equal(min);
|
||||
});
|
||||
|
||||
it("has max element", function () {
|
||||
var min = [0, 0],
|
||||
max = [5, 10];
|
||||
expect(iD.geo.Extent(min, max)[1]).to.equal(max);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#center", function () {
|
||||
it("returns the center point", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 10]).center()).to.eql([2.5, 5]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#extend", function () {
|
||||
it("does not modify self", function () {
|
||||
var extent = iD.geo.Extent([0, 0], [0, 0]);
|
||||
extent.extend([1, 1]);
|
||||
expect(extent).to.eql([[0, 0], [0, 0]]);
|
||||
});
|
||||
|
||||
it("returns the minimal extent containing self and the given point", function () {
|
||||
expect(iD.geo.Extent().extend([0, 0])).to.eql([[0, 0], [0, 0]]);
|
||||
expect(iD.geo.Extent([0, 0], [0, 0]).extend([5, 10])).to.eql([[0, 0], [5, 10]]);
|
||||
});
|
||||
|
||||
it("returns the minimal extent containing self and the given extent", function () {
|
||||
expect(iD.geo.Extent().extend([[0, 0], [5, 10]])).to.eql([[0, 0], [5, 10]]);
|
||||
expect(iD.geo.Extent([0, 0], [0, 0]).extend([[4, -1], [5, 10]])).to.eql([[0, -1], [5, 10]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#intersects', function () {
|
||||
it("returns true for a point inside self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([2, 2])).to.be.true;
|
||||
});
|
||||
|
||||
it("returns true for a point on the boundary of self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([0, 0])).to.be.true;
|
||||
});
|
||||
|
||||
it("returns false for a point outside self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([6, 6])).to.be.false;
|
||||
});
|
||||
|
||||
it("returns true for an extent contained by self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([[1, 1], [2, 2]])).to.be.true;
|
||||
expect(iD.geo.Extent([1, 1], [2, 2]).intersects([[0, 0], [5, 5]])).to.be.true;
|
||||
});
|
||||
|
||||
it("returns true for an extent intersected by self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([[1, 1], [6, 6]])).to.be.true;
|
||||
expect(iD.geo.Extent([1, 1], [6, 6]).intersects([[0, 0], [5, 5]])).to.be.true;
|
||||
});
|
||||
|
||||
it("returns false for an extent not intersected by self", function () {
|
||||
expect(iD.geo.Extent([0, 0], [5, 5]).intersects([[6, 6], [7, 7]])).to.be.false;
|
||||
expect(iD.geo.Extent([[6, 6], [7, 7]]).intersects([[0, 0], [5, 5]])).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -29,11 +29,11 @@ describe('iD.Node', function () {
|
||||
|
||||
describe("#intersects", function () {
|
||||
it("returns true for a node within the given extent", function () {
|
||||
expect(iD.Node({loc: [0, 0]}).intersects([[-180, 90], [180, -90]])).to.equal(true);
|
||||
expect(iD.Node({loc: [0, 0]}).intersects([[-5, -5], [5, 5]])).to.equal(true);
|
||||
});
|
||||
|
||||
it("returns false for a node outside the given extend", function () {
|
||||
expect(iD.Node({loc: [0, 0]}).intersects([[100, 90], [180, -90]])).to.equal(false);
|
||||
expect(iD.Node({loc: [6, 6]}).intersects([[-5, -5], [5, 5]])).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('iD.Way', function() {
|
||||
node2 = iD.Node({loc: [5, 10]}),
|
||||
way = iD.Way({nodes: [node1.id, node2.id]}),
|
||||
graph = iD.Graph([node1, node2, way]);
|
||||
expect(way.extent(graph)).to.eql([[5, 0], [0, 10]]);
|
||||
expect(way.extent(graph)).to.eql([[0, 0], [5, 10]]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,14 +50,14 @@ describe('iD.Way', function() {
|
||||
var node = iD.Node({loc: [0, 0]}),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
graph = iD.Graph([node, way]);
|
||||
expect(way.intersects([[-180, 90], [180, -90]], graph)).to.equal(true);
|
||||
expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(true);
|
||||
});
|
||||
|
||||
it("returns false for way with no nodes within the given extent", function () {
|
||||
var node = iD.Node({loc: [0, 0]}),
|
||||
var node = iD.Node({loc: [6, 6]}),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
graph = iD.Graph([node, way]);
|
||||
expect(way.intersects([[100, 90], [180, -90]], graph)).to.equal(false);
|
||||
expect(way.intersects([[-5, -5], [5, 5]], graph)).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -54,16 +54,17 @@ describe('Map', function() {
|
||||
|
||||
describe('#extent', function() {
|
||||
it('gets and sets extent', function() {
|
||||
expect(map.size([100, 100])).to.equal(map);
|
||||
expect(map.center([0, 0])).to.equal(map);
|
||||
map.size([100, 100])
|
||||
.center([0, 0]);
|
||||
|
||||
expect(map.extent()[0][0]).to.be.closeTo(-17.5, 0.5);
|
||||
expect(map.extent()[1][0]).to.be.closeTo(17.5, 0.5);
|
||||
expect(map.extent([10, 1], [30, 1]));
|
||||
expect(map.extent([[10, 1], [30, 1]]));
|
||||
expect(map.extent()[0][0]).to.be.closeTo(10, 0.1);
|
||||
expect(map.extent()[1][0]).to.be.closeTo(30, 0.1);
|
||||
expect(map.extent([-1, -20], [1, -40]));
|
||||
expect(map.extent()[0][1]).to.be.closeTo(-20, 0.1);
|
||||
expect(map.extent()[1][1]).to.be.closeTo(-40, 0.1);
|
||||
expect(map.extent([[-1, -40], [1, -20]]));
|
||||
expect(map.extent()[0][1]).to.be.closeTo(-40, 1);
|
||||
expect(map.extent()[1][1]).to.be.closeTo(-20, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user