mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 09:12:52 +00:00
Add iD.geo.sphericalDistance
iD.geo.euclideanDistance should only be used for calculations of projected coordinates or display (pixel) coordinates. iD.geo.sphericalDistance calculates approximate geographical distances, accounting for distortions at higher latitudes. This can be used for determining the nearest node (operations.Delete, actions.Circularize) or relative length comparisons (actions.Split).
This commit is contained in:
@@ -8,7 +8,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) {
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
|
||||
centroid = d3.geom.polygon(points).centroid(),
|
||||
radius = d3.median(points, function(p) { return iD.geo.dist(centroid, p); }),
|
||||
radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
|
||||
sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
|
||||
ids;
|
||||
|
||||
@@ -44,7 +44,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) {
|
||||
}
|
||||
|
||||
// position this key node
|
||||
distance = iD.geo.dist(centroid, keyPoints[i]);
|
||||
distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
|
||||
keyPoints[i] = [
|
||||
centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
|
||||
centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
|
||||
|
||||
@@ -84,7 +84,7 @@ iD.actions.Orthogonalize = function(wayId, projection) {
|
||||
p = subtractPoints(a, b),
|
||||
q = subtractPoints(c, b);
|
||||
|
||||
var scale = 2*Math.min(iD.geo.dist(p, [0, 0]), iD.geo.dist(q, [0, 0]));
|
||||
var scale = 2*Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ iD.actions.Split = function(nodeId, newWayIds) {
|
||||
return iD.util.wrap(index,nodes.length);
|
||||
}
|
||||
function _dist(nA, nB) {
|
||||
return iD.geo.dist(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
}
|
||||
|
||||
// calculate lengths
|
||||
|
||||
@@ -34,8 +34,8 @@ iD.behavior.Draw = function(context) {
|
||||
|
||||
d3.select(window).on('mouseup.draw', function() {
|
||||
element.on('mousemove.draw', mousemove);
|
||||
if (iD.geo.dist(pos, point()) < closeTolerance ||
|
||||
(iD.geo.dist(pos, point()) < tolerance &&
|
||||
if (iD.geo.euclideanDistance(pos, point()) < closeTolerance ||
|
||||
(iD.geo.euclideanDistance(pos, point()) < tolerance &&
|
||||
(+new Date() - time) < 500)) {
|
||||
|
||||
// Prevent a quick second click
|
||||
|
||||
10
js/id/geo.js
10
js/id/geo.js
@@ -10,17 +10,23 @@ iD.geo.interp = function(p1, p2, t) {
|
||||
};
|
||||
|
||||
// http://jsperf.com/id-dist-optimization
|
||||
iD.geo.dist = function(a, b) {
|
||||
iD.geo.euclideanDistance = function(a, b) {
|
||||
var x = a[0] - b[0], y = a[1] - b[1];
|
||||
return Math.sqrt((x * x) + (y * y));
|
||||
};
|
||||
// Equirectangular approximation of spherical distances on Earth
|
||||
iD.geo.sphericalDistance = function(a, b) {
|
||||
var x = Math.cos(a[1]*Math.PI/180) * (a[0] - b[0]),
|
||||
y = a[1] - b[1];
|
||||
return 6.3710E6 * Math.sqrt((x * x) + (y * y)) * Math.PI/180;
|
||||
};
|
||||
|
||||
// Choose the edge with the minimal distance from `point` to its orthogonal
|
||||
// projection onto that edge, if such a projection exists, or the distance to
|
||||
// the closest vertex on that edge. Returns an object with the `index` of the
|
||||
// chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
|
||||
iD.geo.chooseEdge = function(nodes, point, projection) {
|
||||
var dist = iD.geo.dist,
|
||||
var dist = iD.geo.euclideanDistance,
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
min = Infinity,
|
||||
idx, loc;
|
||||
|
||||
@@ -27,8 +27,8 @@ iD.operations.Delete = function(selectedIDs, context) {
|
||||
} else if (i === nodes.length - 1) {
|
||||
i--;
|
||||
} else {
|
||||
var a = iD.geo.dist(entity.loc, context.entity(nodes[i - 1]).loc),
|
||||
b = iD.geo.dist(entity.loc, context.entity(nodes[i + 1]).loc);
|
||||
var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
|
||||
b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
|
||||
i = a < b ? i - 1 : i + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ iD.svg = {
|
||||
b = [x, y];
|
||||
|
||||
if (a) {
|
||||
var span = iD.geo.dist(a, b) - offset;
|
||||
var span = iD.geo.euclideanDistance(a, b) - offset;
|
||||
|
||||
if (span >= 0) {
|
||||
var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
|
||||
|
||||
@@ -20,7 +20,7 @@ iD.svg.Midpoints = function(projection, context) {
|
||||
// If neither of the nodes changed, no need to redraw midpoint
|
||||
if (!midpoints[id] && (filter(a) || filter(b))) {
|
||||
var loc = iD.geo.interp(a.loc, b.loc, 0.5);
|
||||
if (extent.intersects(loc) && iD.geo.dist(projection(a.loc), projection(b.loc)) > 40) {
|
||||
if (extent.intersects(loc) && iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
|
||||
midpoints[id] = {
|
||||
type: 'midpoint',
|
||||
id: id,
|
||||
|
||||
@@ -48,7 +48,7 @@ describe("iD.actions.Circularize", function () {
|
||||
|
||||
graph = iD.actions.Circularize('-', projection)(graph);
|
||||
|
||||
expect(iD.geo.dist(graph.entity('d').loc, [2, -2])).to.be.lt(0.5);
|
||||
expect(iD.geo.euclideanDistance(graph.entity('d').loc, [2, -2])).to.be.lt(0.5);
|
||||
});
|
||||
|
||||
function angle(point1, point2, center) {
|
||||
@@ -56,10 +56,10 @@ describe("iD.actions.Circularize", function () {
|
||||
vector2 = [point2[0] - center[0], point2[1] - center[1]],
|
||||
distance;
|
||||
|
||||
distance = iD.geo.dist(vector1, [0, 0]);
|
||||
distance = iD.geo.euclideanDistance(vector1, [0, 0]);
|
||||
vector1 = [vector1[0] / distance, vector1[1] / distance];
|
||||
|
||||
distance = iD.geo.dist(vector2, [0, 0]);
|
||||
distance = iD.geo.euclideanDistance(vector2, [0, 0]);
|
||||
vector2 = [vector2[0] / distance, vector2[1] / distance];
|
||||
|
||||
return 180 / Math.PI * Math.acos(vector1[0] * vector2[0] + vector1[1] * vector2[1]);
|
||||
|
||||
@@ -97,12 +97,12 @@ describe("iD.actions.Orthogonalize", function () {
|
||||
'd': iD.Node({id: 'd', loc: tests[i][3]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
}),
|
||||
initialWidth = iD.geo.dist(graph.entity('a').loc, graph.entity('b').loc),
|
||||
initialWidth = iD.geo.sphericalDistance(graph.entity('a').loc, graph.entity('b').loc),
|
||||
finalWidth;
|
||||
|
||||
graph = iD.actions.Orthogonalize('-', projection)(graph);
|
||||
|
||||
finalWidth = iD.geo.dist(graph.entity('a').loc, graph.entity('b').loc);
|
||||
finalWidth = iD.geo.sphericalDistance(graph.entity('a').loc, graph.entity('b').loc);
|
||||
expect(finalWidth / initialWidth).within(0.90, 1.10);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,21 +18,49 @@ describe('iD.geo', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.dist', function() {
|
||||
describe('.euclideanDistance', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(0);
|
||||
expect(iD.geo.euclideanDistance(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 10 unit line is 10', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(10);
|
||||
expect(iD.geo.euclideanDistance(a, b)).to.eql(10);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.geo.dist(a, b)).to.eql(5);
|
||||
expect(iD.geo.euclideanDistance(a, b)).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.sphericalDistance', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
expect(iD.geo.sphericalDistance(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 1 degree line at the equator is aproximately 111 km', function() {
|
||||
var a = [0, 0],
|
||||
b = [1, 0];
|
||||
expect(iD.geo.sphericalDistance(a, b)).to.be.within(100E3,120E3);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.geo.sphericalDistance(a, b)).to.be.within(500E3,600E3);
|
||||
});
|
||||
it('east-west distances at high latitude are shorter', function() {
|
||||
var a = [0, 60],
|
||||
b = [1, 60];
|
||||
expect(iD.geo.sphericalDistance(a, b)).to.be.within(50E3,60E3);
|
||||
});
|
||||
it('north-south distances at high latitude are not shorter', function() {
|
||||
var a = [0, 60],
|
||||
b = [0, 61];
|
||||
expect(iD.geo.sphericalDistance(a, b)).to.be.within(100E3,120E3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user