mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-14 21:28:11 +02:00
Move a bunch of commonly used vector and projection math functions into geo
- geoVecAdd - geoVecSubtract - geoVecScale - geoZoomToScale - geoScaleToZoom
This commit is contained in:
+44
-44
@@ -11,22 +11,21 @@ import _without from 'lodash-es/without';
|
||||
import { osmNode } from '../osm';
|
||||
|
||||
import {
|
||||
geoChooseEdge,
|
||||
geoAngle,
|
||||
geoChooseEdge,
|
||||
geoInterp,
|
||||
geoPathIntersections,
|
||||
geoPathLength,
|
||||
geoSphericalDistance
|
||||
geoSphericalDistance,
|
||||
geoVecAdd,
|
||||
geoVecSubtract
|
||||
} from '../geo';
|
||||
|
||||
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
|
||||
export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
var delta = tryDelta;
|
||||
|
||||
function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
|
||||
function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
|
||||
var _delta = tryDelta;
|
||||
|
||||
function setupCache(graph) {
|
||||
function canMove(nodeId) {
|
||||
@@ -118,11 +117,11 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
|
||||
|
||||
// Place a vertex where the moved vertex used to be, to preserve way shape..
|
||||
function replaceMovedVertex(nodeId, wayId, graph, delta) {
|
||||
var way = graph.entity(wayId),
|
||||
moved = graph.entity(nodeId),
|
||||
movedIndex = way.nodes.indexOf(nodeId),
|
||||
len, prevIndex, nextIndex;
|
||||
function replaceMovedVertex(nodeId, wayId, graph, _delta) {
|
||||
var way = graph.entity(wayId);
|
||||
var moved = graph.entity(nodeId);
|
||||
var movedIndex = way.nodes.indexOf(nodeId);
|
||||
var len, prevIndex, nextIndex;
|
||||
|
||||
if (way.isClosed()) {
|
||||
len = way.nodes.length - 1;
|
||||
@@ -134,14 +133,14 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
nextIndex = movedIndex + 1;
|
||||
}
|
||||
|
||||
var prev = graph.hasEntity(way.nodes[prevIndex]),
|
||||
next = graph.hasEntity(way.nodes[nextIndex]);
|
||||
var prev = graph.hasEntity(way.nodes[prevIndex]);
|
||||
var next = graph.hasEntity(way.nodes[nextIndex]);
|
||||
|
||||
// Don't add orig vertex at endpoint..
|
||||
if (!prev || !next) return graph;
|
||||
|
||||
var key = wayId + '_' + nodeId,
|
||||
orig = cache.replacedVertex[key];
|
||||
var key = wayId + '_' + nodeId;
|
||||
var orig = cache.replacedVertex[key];
|
||||
if (!orig) {
|
||||
orig = osmNode();
|
||||
cache.replacedVertex[key] = orig;
|
||||
@@ -149,9 +148,9 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
}
|
||||
|
||||
var start, end;
|
||||
if (delta) {
|
||||
if (_delta) {
|
||||
start = projection(cache.startLoc[nodeId]);
|
||||
end = projection.invert(vecAdd(start, delta));
|
||||
end = projection.invert(geoVecAdd(start, _delta));
|
||||
} else {
|
||||
end = cache.startLoc[nodeId];
|
||||
}
|
||||
@@ -184,24 +183,24 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
|
||||
// Reorder nodes around intersections that have moved..
|
||||
function unZorroIntersection(intersection, graph) {
|
||||
var vertex = graph.entity(intersection.nodeId),
|
||||
way1 = graph.entity(intersection.movedId),
|
||||
way2 = graph.entity(intersection.unmovedId),
|
||||
isEP1 = intersection.movedIsEP,
|
||||
isEP2 = intersection.unmovedIsEP;
|
||||
var vertex = graph.entity(intersection.nodeId);
|
||||
var way1 = graph.entity(intersection.movedId);
|
||||
var way2 = graph.entity(intersection.unmovedId);
|
||||
var isEP1 = intersection.movedIsEP;
|
||||
var isEP2 = intersection.unmovedIsEP;
|
||||
|
||||
// don't move the vertex if it is the endpoint of both ways.
|
||||
if (isEP1 && isEP2) return graph;
|
||||
|
||||
var nodes1 = _without(graph.childNodes(way1), vertex),
|
||||
nodes2 = _without(graph.childNodes(way2), vertex);
|
||||
var nodes1 = _without(graph.childNodes(way1), vertex);
|
||||
var nodes2 = _without(graph.childNodes(way2), vertex);
|
||||
|
||||
if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
|
||||
if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
|
||||
|
||||
var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection),
|
||||
edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection),
|
||||
loc;
|
||||
var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
|
||||
var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
|
||||
var loc;
|
||||
|
||||
// snap vertex to nearest edge (or some point between them)..
|
||||
if (!isEP1 && !isEP2) {
|
||||
@@ -236,7 +235,7 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
|
||||
function cleanupIntersections(graph) {
|
||||
_each(cache.intersection, function(obj) {
|
||||
graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
|
||||
graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
|
||||
graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
|
||||
graph = unZorroIntersection(obj, graph);
|
||||
});
|
||||
@@ -245,7 +244,7 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
}
|
||||
|
||||
|
||||
// check if moving way endpoint can cross an unmoved way, if so limit delta..
|
||||
// check if moving way endpoint can cross an unmoved way, if so limit _delta..
|
||||
function limitDelta(graph) {
|
||||
_each(cache.intersection, function(obj) {
|
||||
// Don't limit movement if this is vertex joins 2 endpoints..
|
||||
@@ -253,27 +252,28 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
// Don't limit movement if this vertex is not an endpoint anyway..
|
||||
if (!obj.movedIsEP) return;
|
||||
|
||||
var node = graph.entity(obj.nodeId),
|
||||
start = projection(node.loc),
|
||||
end = vecAdd(start, delta),
|
||||
movedNodes = graph.childNodes(graph.entity(obj.movedId)),
|
||||
movedPath = _map(_map(movedNodes, 'loc'),
|
||||
function(loc) { return vecAdd(projection(loc), delta); }),
|
||||
unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
|
||||
unmovedPath = _map(_map(unmovedNodes, 'loc'), projection),
|
||||
hits = geoPathIntersections(movedPath, unmovedPath);
|
||||
var node = graph.entity(obj.nodeId);
|
||||
var start = projection(node.loc);
|
||||
var end = geoVecAdd(start, _delta);
|
||||
var movedNodes = graph.childNodes(graph.entity(obj.movedId));
|
||||
var movedPath = _map(_map(movedNodes, 'loc'), function(loc) {
|
||||
return geoVecAdd(projection(loc), _delta);
|
||||
});
|
||||
var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
|
||||
var unmovedPath = _map(_map(unmovedNodes, 'loc'), projection);
|
||||
var hits = geoPathIntersections(movedPath, unmovedPath);
|
||||
|
||||
for (var i = 0; i < hits.length; i++) {
|
||||
if (_isEqual(hits[i], end)) continue;
|
||||
var edge = geoChooseEdge(unmovedNodes, end, projection);
|
||||
delta = vecSub(projection(edge.loc), start);
|
||||
_delta = geoVecSubtract(projection(edge.loc), start);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var action = function(graph) {
|
||||
if (delta[0] === 0 && delta[1] === 0) return graph;
|
||||
if (_delta[0] === 0 && _delta[1] === 0) return graph;
|
||||
|
||||
setupCache(graph);
|
||||
|
||||
@@ -282,9 +282,9 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
}
|
||||
|
||||
_each(cache.nodes, function(id) {
|
||||
var node = graph.entity(id),
|
||||
start = projection(node.loc),
|
||||
end = vecAdd(start, delta);
|
||||
var node = graph.entity(id);
|
||||
var start = projection(node.loc);
|
||||
var end = geoVecAdd(start, _delta);
|
||||
graph = graph.replace(node.move(projection.invert(end)));
|
||||
});
|
||||
|
||||
@@ -297,7 +297,7 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
|
||||
|
||||
|
||||
action.delta = function() {
|
||||
return delta;
|
||||
return _delta;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+103
-77
@@ -2,94 +2,130 @@ import _every from 'lodash-es/every';
|
||||
import _some from 'lodash-es/some';
|
||||
|
||||
|
||||
export function geoRoundCoords(c) {
|
||||
return [Math.floor(c[0]), Math.floor(c[1])];
|
||||
// constants
|
||||
var TAU = 2 * Math.PI;
|
||||
var EQUATORIAL_RADIUS = 6356752.314245179;
|
||||
var POLAR_RADIUS = 6378137.0;
|
||||
|
||||
|
||||
// vector addition
|
||||
export function geoVecAdd(a, b) {
|
||||
return [ a[0] + b[0], a[1] + b[1] ];
|
||||
}
|
||||
|
||||
// vector subtraction
|
||||
export function geoVecSubtract(a, b) {
|
||||
return [ a[0] - b[0], a[1] - b[1] ];
|
||||
}
|
||||
|
||||
// vector multiplication
|
||||
export function geoVecScale(a, b) {
|
||||
return [ a[0] * b, a[1] * b ];
|
||||
}
|
||||
|
||||
// vector rounding (was: geoRoundCoordinates)
|
||||
export function geoVecFloor(a) {
|
||||
return [ Math.floor(a[0]), Math.floor(a[1]) ];
|
||||
}
|
||||
|
||||
// linear interpolation
|
||||
export function geoInterp(p1, p2, t) {
|
||||
return [p1[0] + (p2[0] - p1[0]) * t,
|
||||
p1[1] + (p2[1] - p1[1]) * t];
|
||||
return [
|
||||
p1[0] + (p2[0] - p1[0]) * t,
|
||||
p1[1] + (p2[1] - p1[1]) * t
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
|
||||
// dot product
|
||||
export function geoDot(a, b, origin) {
|
||||
origin = origin || [0, 0];
|
||||
return (a[0] - origin[0]) * (b[0] - origin[0]) +
|
||||
(a[1] - origin[1]) * (b[1] - origin[1]);
|
||||
}
|
||||
|
||||
|
||||
// 2D cross product of OA and OB vectors, returns magnitude of Z vector
|
||||
// Returns a positive value, if OAB makes a counter-clockwise turn,
|
||||
// negative for clockwise turn, and zero if the points are collinear.
|
||||
export function geoCross(o, a, b) {
|
||||
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
||||
export function geoCross(a, b, origin) {
|
||||
origin = origin || [0, 0];
|
||||
return (a[0] - origin[0]) * (b[1] - origin[1]) -
|
||||
(a[1] - origin[1]) * (b[0] - origin[0]);
|
||||
}
|
||||
|
||||
|
||||
// http://jsperf.com/id-dist-optimization
|
||||
export function geoEuclideanDistance(a, b) {
|
||||
var x = a[0] - b[0], y = a[1] - b[1];
|
||||
var x = a[0] - b[0];
|
||||
var y = a[1] - b[1];
|
||||
return Math.sqrt((x * x) + (y * y));
|
||||
}
|
||||
|
||||
|
||||
// using WGS84 polar radius (6356752.314245179 m)
|
||||
// const = 2 * PI * r / 360
|
||||
export function geoLatToMeters(dLat) {
|
||||
return dLat * 110946.257617;
|
||||
return dLat * (TAU * POLAR_RADIUS / 360);
|
||||
}
|
||||
|
||||
|
||||
// using WGS84 equatorial radius (6378137.0 m)
|
||||
// const = 2 * PI * r / 360
|
||||
export function geoLonToMeters(dLon, atLat) {
|
||||
return Math.abs(atLat) >= 90 ? 0 :
|
||||
dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
|
||||
dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
|
||||
}
|
||||
|
||||
|
||||
// using WGS84 polar radius (6356752.314245179 m)
|
||||
// const = 2 * PI * r / 360
|
||||
export function geoMetersToLat(m) {
|
||||
return m / 110946.257617;
|
||||
return m / (TAU * POLAR_RADIUS / 360);
|
||||
}
|
||||
|
||||
|
||||
// using WGS84 equatorial radius (6378137.0 m)
|
||||
// const = 2 * PI * r / 360
|
||||
export function geoMetersToLon(m, atLat) {
|
||||
return Math.abs(atLat) >= 90 ? 0 :
|
||||
m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
|
||||
m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
|
||||
}
|
||||
|
||||
|
||||
export function geoOffsetToMeters(offset) {
|
||||
var equatRadius = 6356752.314245179,
|
||||
polarRadius = 6378137.0,
|
||||
tileSize = 256;
|
||||
|
||||
export function geoOffsetToMeters(offset, tileSize) {
|
||||
tileSize = tileSize || 256;
|
||||
return [
|
||||
offset[0] * 2 * Math.PI * equatRadius / tileSize,
|
||||
-offset[1] * 2 * Math.PI * polarRadius / tileSize
|
||||
offset[0] * TAU * EQUATORIAL_RADIUS / tileSize,
|
||||
-offset[1] * TAU * POLAR_RADIUS / tileSize
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
export function geoMetersToOffset(meters) {
|
||||
var equatRadius = 6356752.314245179,
|
||||
polarRadius = 6378137.0,
|
||||
tileSize = 256;
|
||||
|
||||
export function geoMetersToOffset(meters, tileSize) {
|
||||
tileSize = tileSize || 256;
|
||||
return [
|
||||
meters[0] * tileSize / (2 * Math.PI * equatRadius),
|
||||
-meters[1] * tileSize / (2 * Math.PI * polarRadius)
|
||||
meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS),
|
||||
-meters[1] * tileSize / (TAU * POLAR_RADIUS)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Equirectangular approximation of spherical distances on Earth
|
||||
export function geoSphericalDistance(a, b) {
|
||||
var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
|
||||
y = geoLatToMeters(a[1] - b[1]);
|
||||
var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
|
||||
var y = geoLatToMeters(a[1] - b[1]);
|
||||
return Math.sqrt((x * x) + (y * y));
|
||||
}
|
||||
|
||||
|
||||
// zoom to scale
|
||||
export function geoZoomToScale(z, tileSize) {
|
||||
tileSize = tileSize || 256;
|
||||
return tileSize * Math.pow(2, z) / TAU;
|
||||
}
|
||||
|
||||
|
||||
// scale to zoom
|
||||
export function geoScaleToZoom(k, tileSize) {
|
||||
tileSize = tileSize || 256;
|
||||
var log2ts = Math.log(tileSize) * Math.LOG2E;
|
||||
return Math.log(k * TAU) / Math.LN2 - log2ts;
|
||||
}
|
||||
|
||||
|
||||
export function geoEdgeEqual(a, b) {
|
||||
return (a[0] === b[0] && a[1] === b[1]) ||
|
||||
(a[0] === b[1] && a[1] === b[0]);
|
||||
@@ -122,23 +158,18 @@ export function geoRotate(points, angle, around) {
|
||||
// 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.
|
||||
export function geoChooseEdge(nodes, point, projection) {
|
||||
var dist = geoEuclideanDistance,
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
min = Infinity,
|
||||
idx, loc;
|
||||
|
||||
function dot(p, q) {
|
||||
return p[0] * q[0] + p[1] * q[1];
|
||||
}
|
||||
var dist = geoEuclideanDistance;
|
||||
var points = nodes.map(function(n) { return projection(n.loc); });
|
||||
var min = Infinity;
|
||||
var idx;
|
||||
var loc;
|
||||
|
||||
for (var i = 0; i < points.length - 1; i++) {
|
||||
var o = points[i],
|
||||
s = [points[i + 1][0] - o[0],
|
||||
points[i + 1][1] - o[1]],
|
||||
v = [point[0] - o[0],
|
||||
point[1] - o[1]],
|
||||
proj = dot(v, s) / dot(s, s),
|
||||
p;
|
||||
var o = points[i];
|
||||
var s = geoVecSubtract(points[i + 1], o);
|
||||
var v = geoVecSubtract(point, o);
|
||||
var proj = geoDot(v, s) / geoDot(s, s);
|
||||
var p;
|
||||
|
||||
if (proj < 0) {
|
||||
p = o;
|
||||
@@ -169,25 +200,18 @@ export function geoChooseEdge(nodes, point, projection) {
|
||||
// This uses the vector cross product approach described below:
|
||||
// http://stackoverflow.com/a/565282/786339
|
||||
export function geoLineIntersection(a, b) {
|
||||
function subtractPoints(point1, point2) {
|
||||
return [point1[0] - point2[0], point1[1] - point2[1]];
|
||||
}
|
||||
function crossProduct(point1, point2) {
|
||||
return point1[0] * point2[1] - point1[1] * point2[0];
|
||||
}
|
||||
|
||||
var p = [a[0][0], a[0][1]],
|
||||
p2 = [a[1][0], a[1][1]],
|
||||
q = [b[0][0], b[0][1]],
|
||||
q2 = [b[1][0], b[1][1]],
|
||||
r = subtractPoints(p2, p),
|
||||
s = subtractPoints(q2, q),
|
||||
uNumerator = crossProduct(subtractPoints(q, p), r),
|
||||
denominator = crossProduct(r, s);
|
||||
var p = [a[0][0], a[0][1]];
|
||||
var p2 = [a[1][0], a[1][1]];
|
||||
var q = [b[0][0], b[0][1]];
|
||||
var q2 = [b[1][0], b[1][1]];
|
||||
var r = geoVecSubtract(p2, p);
|
||||
var s = geoVecSubtract(q2, q);
|
||||
var uNumerator = geoCross(geoVecSubtract(q, p), r);
|
||||
var denominator = geoCross(r, s);
|
||||
|
||||
if (uNumerator && denominator) {
|
||||
var u = uNumerator / denominator,
|
||||
t = crossProduct(subtractPoints(q, p), s) / denominator;
|
||||
var u = uNumerator / denominator;
|
||||
var t = geoCross(geoVecSubtract(q, p), s) / denominator;
|
||||
|
||||
if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
|
||||
return geoInterp(p, p2, t);
|
||||
@@ -202,10 +226,12 @@ export function geoPathIntersections(path1, path2) {
|
||||
var intersections = [];
|
||||
for (var i = 0; i < path1.length - 1; i++) {
|
||||
for (var j = 0; j < path2.length - 1; j++) {
|
||||
var a = [ path1[i], path1[i+1] ],
|
||||
b = [ path2[j], path2[j+1] ],
|
||||
hit = geoLineIntersection(a, b);
|
||||
if (hit) intersections.push(hit);
|
||||
var a = [ path1[i], path1[i+1] ];
|
||||
var b = [ path2[j], path2[j+1] ];
|
||||
var hit = geoLineIntersection(a, b);
|
||||
if (hit) {
|
||||
intersections.push(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersections;
|
||||
@@ -222,9 +248,9 @@ export function geoPathIntersections(path1, path2) {
|
||||
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
||||
//
|
||||
export function geoPointInPolygon(point, polygon) {
|
||||
var x = point[0],
|
||||
y = point[1],
|
||||
inside = false;
|
||||
var x = point[0];
|
||||
var y = point[1];
|
||||
var inside = false;
|
||||
|
||||
for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
var xi = polygon[i][0], yi = polygon[i][1];
|
||||
@@ -250,8 +276,8 @@ export function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
|
||||
function testSegments(outer, inner) {
|
||||
for (var i = 0; i < outer.length - 1; i++) {
|
||||
for (var j = 0; j < inner.length - 1; j++) {
|
||||
var a = [ outer[i], outer[i+1] ],
|
||||
b = [ inner[j], inner[j+1] ];
|
||||
var a = [ outer[i], outer[i +1 ] ];
|
||||
var b = [ inner[j], inner[j + 1] ];
|
||||
if (geoLineIntersection(a, b)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
export { geoAngle } from './geo.js';
|
||||
export { geoChooseEdge } from './geo.js';
|
||||
export { geoCross } from './geo.js';
|
||||
export { geoDot } from './geo.js';
|
||||
export { geoEdgeEqual } from './geo.js';
|
||||
export { geoEuclideanDistance } from './geo.js';
|
||||
export { geoExtent } from './extent.js';
|
||||
export { geoInterp } from './geo.js';
|
||||
export { geoRawMercator } from './raw_mercator.js';
|
||||
export { geoRoundCoords } from './geo.js';
|
||||
export { geoRotate } from './geo.js';
|
||||
export { geoLatToMeters } from './geo.js';
|
||||
export { geoLineIntersection } from './geo.js';
|
||||
@@ -20,5 +20,11 @@ export { geoPathLength } from './geo.js';
|
||||
export { geoPointInPolygon } from './geo.js';
|
||||
export { geoPolygonContainsPolygon } from './geo.js';
|
||||
export { geoPolygonIntersectsPolygon } from './geo.js';
|
||||
export { geoScaleToZoom } from './geo.js';
|
||||
export { geoSphericalDistance } from './geo.js';
|
||||
export { geoVecAdd } from './geo.js';
|
||||
export { geoVecFloor } from './geo.js';
|
||||
export { geoVecSubtract } from './geo.js';
|
||||
export { geoVecScale } from './geo.js';
|
||||
export { geoZoomToScale } from './geo.js';
|
||||
export { geoViewportEdge } from './geo.js';
|
||||
|
||||
@@ -23,7 +23,12 @@ import {
|
||||
modeSelect
|
||||
} from './index';
|
||||
|
||||
import { geoChooseEdge, geoViewportEdge } from '../geo';
|
||||
import {
|
||||
geoChooseEdge,
|
||||
geoVecSubtract,
|
||||
geoViewportEdge
|
||||
} from '../geo';
|
||||
|
||||
import { osmNode } from '../osm';
|
||||
import { utilEntitySelector } from '../util';
|
||||
import { uiFlash } from '../ui';
|
||||
@@ -46,11 +51,6 @@ export function modeDragNode(context) {
|
||||
var _lastLoc;
|
||||
|
||||
|
||||
function vecSub(a, b) {
|
||||
return [a[0] - b[0], a[1] - b[1]];
|
||||
}
|
||||
|
||||
|
||||
function startNudge(entity, nudge) {
|
||||
if (_nudgeInterval) window.clearInterval(_nudgeInterval);
|
||||
_nudgeInterval = window.setInterval(function() {
|
||||
@@ -135,7 +135,7 @@ export function modeDragNode(context) {
|
||||
nudge = nudge || [0, 0];
|
||||
|
||||
var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc);
|
||||
var currMouse = vecSub(currPoint, nudge);
|
||||
var currMouse = geoVecSubtract(currPoint, nudge);
|
||||
var loc = context.projection.invert(currMouse);
|
||||
|
||||
if (!_nudgeInterval) {
|
||||
|
||||
+8
-7
@@ -133,15 +133,16 @@ _extend(osmWay.prototype, {
|
||||
isConvex: function(resolver) {
|
||||
if (!this.isClosed() || this.isDegenerate()) return null;
|
||||
|
||||
var nodes = _uniq(resolver.childNodes(this)),
|
||||
coords = _map(nodes, 'loc'),
|
||||
curr = 0, prev = 0;
|
||||
var nodes = _uniq(resolver.childNodes(this));
|
||||
var coords = _map(nodes, 'loc');
|
||||
var curr = 0;
|
||||
var prev = 0;
|
||||
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var o = coords[(i+1) % coords.length],
|
||||
a = coords[i],
|
||||
b = coords[(i+2) % coords.length],
|
||||
res = geoCross(o, a, b);
|
||||
var o = coords[(i+1) % coords.length];
|
||||
var a = coords[i];
|
||||
var b = coords[(i+2) % coords.length];
|
||||
var res = geoCross(a, b, o);
|
||||
|
||||
curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
|
||||
if (curr === 0) {
|
||||
|
||||
@@ -2,28 +2,29 @@ import { select as d3_select } from 'd3-selection';
|
||||
import { t } from '../util/locale';
|
||||
|
||||
import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile';
|
||||
import { geoEuclideanDistance } from '../geo';
|
||||
import { geoEuclideanDistance, geoScaleToZoom } from '../geo';
|
||||
import { utilPrefixCSSProperty } from '../util';
|
||||
|
||||
|
||||
export function rendererTileLayer(context) {
|
||||
var tileSize = 256,
|
||||
geotile = d3_geoTile(),
|
||||
projection,
|
||||
cache = {},
|
||||
tileOrigin,
|
||||
z,
|
||||
transformProp = utilPrefixCSSProperty('Transform'),
|
||||
source;
|
||||
var tileSize = 256;
|
||||
var transformProp = utilPrefixCSSProperty('Transform');
|
||||
var geotile = d3_geoTile();
|
||||
|
||||
var _projection;
|
||||
var _cache = {};
|
||||
var _tileOrigin;
|
||||
var _zoom;
|
||||
var _source;
|
||||
|
||||
|
||||
// blacklist overlay tiles around Null Island..
|
||||
function nearNullIsland(x, y, z) {
|
||||
if (z >= 7) {
|
||||
var center = Math.pow(2, z - 1),
|
||||
width = Math.pow(2, z - 6),
|
||||
min = center - (width / 2),
|
||||
max = center + (width / 2) - 1;
|
||||
var center = Math.pow(2, z - 1);
|
||||
var width = Math.pow(2, z - 6);
|
||||
var min = center - (width / 2);
|
||||
var max = center + (width / 2) - 1;
|
||||
return x >= min && x <= max && y >= min && y <= max;
|
||||
}
|
||||
return false;
|
||||
@@ -31,8 +32,8 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
var epsilon = 0.002;
|
||||
return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
|
||||
var EPSILON = 0.002;
|
||||
return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + EPSILON;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +50,7 @@ export function rendererTileLayer(context) {
|
||||
function lookUp(d) {
|
||||
for (var up = -1; up > -d[2]; up--) {
|
||||
var tile = atZoom(d, up);
|
||||
if (cache[source.url(tile)] !== false) {
|
||||
if (_cache[_source.url(tile)] !== false) {
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +58,8 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
function uniqueBy(a, n) {
|
||||
var o = [], seen = {};
|
||||
var o = [];
|
||||
var seen = {};
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (seen[a[i][n]] === undefined) {
|
||||
o.push(a[i]);
|
||||
@@ -69,37 +71,37 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
function addSource(d) {
|
||||
d.push(source.url(d));
|
||||
d.push(_source.url(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// Update tiles based on current state of `projection`.
|
||||
function background(selection) {
|
||||
z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
|
||||
_zoom = geoScaleToZoom(_projection.scale(), tileSize);
|
||||
|
||||
var pixelOffset;
|
||||
if (source) {
|
||||
if (_source) {
|
||||
pixelOffset = [
|
||||
source.offset()[0] * Math.pow(2, z),
|
||||
source.offset()[1] * Math.pow(2, z)
|
||||
_source.offset()[0] * Math.pow(2, _zoom),
|
||||
_source.offset()[1] * Math.pow(2, _zoom)
|
||||
];
|
||||
} else {
|
||||
pixelOffset = [0, 0];
|
||||
}
|
||||
|
||||
var translate = [
|
||||
projection.translate()[0] + pixelOffset[0],
|
||||
projection.translate()[1] + pixelOffset[1]
|
||||
_projection.translate()[0] + pixelOffset[0],
|
||||
_projection.translate()[1] + pixelOffset[1]
|
||||
];
|
||||
|
||||
geotile
|
||||
.scale(projection.scale() * 2 * Math.PI)
|
||||
.scale(_projection.scale() * 2 * Math.PI)
|
||||
.translate(translate);
|
||||
|
||||
tileOrigin = [
|
||||
projection.scale() * Math.PI - translate[0],
|
||||
projection.scale() * Math.PI - translate[1]
|
||||
_tileOrigin = [
|
||||
_projection.scale() * Math.PI - translate[0],
|
||||
_projection.scale() * Math.PI - translate[1]
|
||||
];
|
||||
|
||||
render(selection);
|
||||
@@ -107,36 +109,36 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
// Derive the tiles onscreen, remove those offscreen and position them.
|
||||
// Important that this part not depend on `projection` because it's
|
||||
// Important that this part not depend on `_projection` because it's
|
||||
// rentered when tiles load/error (see #644).
|
||||
function render(selection) {
|
||||
if (!source) return;
|
||||
if (!_source) return;
|
||||
var requests = [];
|
||||
var showDebug = context.getDebug('tile') && !source.overlay;
|
||||
var showDebug = context.getDebug('tile') && !_source.overlay;
|
||||
|
||||
if (source.validZoom(z)) {
|
||||
if (_source.validZoom(_zoom)) {
|
||||
geotile().forEach(function(d) {
|
||||
addSource(d);
|
||||
if (d[3] === '') return;
|
||||
if (typeof d[3] !== 'string') return; // Workaround for #2295
|
||||
requests.push(d);
|
||||
if (cache[d[3]] === false && lookUp(d)) {
|
||||
if (_cache[d[3]] === false && lookUp(d)) {
|
||||
requests.push(addSource(lookUp(d)));
|
||||
}
|
||||
});
|
||||
|
||||
requests = uniqueBy(requests, 3).filter(function(r) {
|
||||
if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
|
||||
if (!!_source.overlay && nearNullIsland(r[0], r[1], r[2])) {
|
||||
return false;
|
||||
}
|
||||
// don't re-request tiles which have failed in the past
|
||||
return cache[r[3]] !== false;
|
||||
return _cache[r[3]] !== false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function load(d) {
|
||||
cache[d[3]] = true;
|
||||
_cache[d[3]] = true;
|
||||
d3_select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
@@ -145,7 +147,7 @@ export function rendererTileLayer(context) {
|
||||
}
|
||||
|
||||
function error(d) {
|
||||
cache[d[3]] = false;
|
||||
_cache[d[3]] = false;
|
||||
d3_select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
@@ -154,19 +156,19 @@ export function rendererTileLayer(context) {
|
||||
}
|
||||
|
||||
function imageTransform(d) {
|
||||
var _ts = tileSize * Math.pow(2, z - d[2]);
|
||||
var scale = tileSizeAtZoom(d, z);
|
||||
var ts = tileSize * Math.pow(2, _zoom - d[2]);
|
||||
var scale = tileSizeAtZoom(d, _zoom);
|
||||
return 'translate(' +
|
||||
((d[0] * _ts) - tileOrigin[0]) + 'px,' +
|
||||
((d[1] * _ts) - tileOrigin[1]) + 'px) ' +
|
||||
((d[0] * ts) - _tileOrigin[0]) + 'px,' +
|
||||
((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
|
||||
'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
function tileCenter(d) {
|
||||
var _ts = tileSize * Math.pow(2, z - d[2]);
|
||||
var ts = tileSize * Math.pow(2, _zoom - d[2]);
|
||||
return [
|
||||
((d[0] * _ts) - tileOrigin[0] + (_ts / 2)),
|
||||
((d[1] * _ts) - tileOrigin[1] + (_ts / 2))
|
||||
((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
|
||||
((d[1] * ts) - _tileOrigin[1] + (ts / 2))
|
||||
];
|
||||
}
|
||||
|
||||
@@ -178,10 +180,10 @@ export function rendererTileLayer(context) {
|
||||
|
||||
// Pick a representative tile near the center of the viewport
|
||||
// (This is useful for sampling the imagery vintage)
|
||||
var dims = geotile.size(),
|
||||
mapCenter = [dims[0] / 2, dims[1] / 2],
|
||||
minDist = Math.max(dims[0], dims[1]),
|
||||
nearCenter;
|
||||
var dims = geotile.size();
|
||||
var mapCenter = [dims[0] / 2, dims[1] / 2];
|
||||
var minDist = Math.max(dims[0], dims[1]);
|
||||
var nearCenter;
|
||||
|
||||
requests.forEach(function(d) {
|
||||
var c = tileCenter(d);
|
||||
@@ -255,8 +257,8 @@ export function rendererTileLayer(context) {
|
||||
.selectAll('.tile-label-debug-vintage')
|
||||
.each(function(d) {
|
||||
var span = d3_select(this);
|
||||
var center = context.projection.invert(tileCenter(d));
|
||||
source.getMetadata(center, d, function(err, result) {
|
||||
var center = context._projection.invert(tileCenter(d));
|
||||
_source.getMetadata(center, d, function(err, result) {
|
||||
span.text((result && result.vintage && result.vintage.range) ||
|
||||
t('info_panels.background.vintage') + ': ' + t('info_panels.background.unknown')
|
||||
);
|
||||
@@ -268,8 +270,8 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
background.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
if (!arguments.length) return _projection;
|
||||
_projection = _;
|
||||
return background;
|
||||
};
|
||||
|
||||
@@ -282,10 +284,10 @@ export function rendererTileLayer(context) {
|
||||
|
||||
|
||||
background.source = function(_) {
|
||||
if (!arguments.length) return source;
|
||||
source = _;
|
||||
cache = {};
|
||||
geotile.scaleExtent(source.scaleExtent);
|
||||
if (!arguments.length) return _source;
|
||||
_source = _;
|
||||
_cache = {};
|
||||
geotile.scaleExtent(_source.scaleExtent);
|
||||
return background;
|
||||
};
|
||||
|
||||
|
||||
+14
-14
@@ -500,8 +500,8 @@ export default {
|
||||
}
|
||||
|
||||
// update blacklists
|
||||
var elements = xml.getElementsByTagName('blacklist'),
|
||||
regexes = [];
|
||||
var elements = xml.getElementsByTagName('blacklist');
|
||||
var regexes = [];
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var regex = elements[i].getAttribute('regex'); // needs unencode?
|
||||
if (regex) {
|
||||
@@ -516,8 +516,8 @@ export default {
|
||||
if (rateLimitError) {
|
||||
callback(rateLimitError, 'rateLimited');
|
||||
} else {
|
||||
var apiStatus = xml.getElementsByTagName('status'),
|
||||
val = apiStatus[0].getAttribute('api');
|
||||
var apiStatus = xml.getElementsByTagName('status');
|
||||
var val = apiStatus[0].getAttribute('api');
|
||||
|
||||
callback(undefined, val);
|
||||
}
|
||||
@@ -544,14 +544,14 @@ export default {
|
||||
loadTiles: function(projection, dimensions, callback) {
|
||||
if (off) return;
|
||||
|
||||
var that = this,
|
||||
s = projection.scale() * 2 * Math.PI,
|
||||
z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
|
||||
ts = 256 * Math.pow(2, z - tileZoom),
|
||||
origin = [
|
||||
s / 2 - projection.translate()[0],
|
||||
s / 2 - projection.translate()[1]
|
||||
];
|
||||
var that = this;
|
||||
var s = projection.scale() * 2 * Math.PI;
|
||||
var z = Math.max(Math.log(s) / Math.log(2) - 8, 0);
|
||||
var ts = 256 * Math.pow(2, z - tileZoom);
|
||||
var origin = [
|
||||
s / 2 - projection.translate()[0],
|
||||
s / 2 - projection.translate()[1]
|
||||
];
|
||||
|
||||
var tiles = d3_geoTile()
|
||||
.scaleExtent([tileZoom, tileZoom])
|
||||
@@ -559,8 +559,8 @@ export default {
|
||||
.size(dimensions)
|
||||
.translate(projection.translate())()
|
||||
.map(function(tile) {
|
||||
var x = tile[0] * ts - origin[0],
|
||||
y = tile[1] * ts - origin[1];
|
||||
var x = tile[0] * ts - origin[0];
|
||||
var y = tile[1] * ts - origin[1];
|
||||
|
||||
return {
|
||||
id: tile.toString(),
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
geoEuclideanDistance,
|
||||
geoInterp,
|
||||
geoPolygonIntersectsPolygon,
|
||||
geoPathLength
|
||||
geoPathLength,
|
||||
geoScaleToZoom
|
||||
} from '../geo';
|
||||
|
||||
import { osmEntity } from '../osm';
|
||||
@@ -27,9 +28,6 @@ import {
|
||||
} from '../util';
|
||||
|
||||
|
||||
var TAU = 2 * Math.PI;
|
||||
function ktoz(k) { return Math.log(k * TAU) / Math.LN2 - 8; }
|
||||
|
||||
|
||||
export function svgLabels(projection, context) {
|
||||
var path = d3_geoPath(projection);
|
||||
@@ -261,7 +259,7 @@ export function svgLabels(projection, context) {
|
||||
|
||||
function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
|
||||
var labelable = [];
|
||||
var renderNodeAs = {};
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { dataFeatureIcons } from '../../data';
|
||||
import { geoScaleToZoom } from '../geo';
|
||||
import { osmEntity } from '../osm';
|
||||
import { svgPointTransform, svgTagClasses } from './index';
|
||||
|
||||
|
||||
var TAU = 2 * Math.PI;
|
||||
function ktoz(k) { return Math.log(k * TAU) / Math.LN2 - 8; }
|
||||
|
||||
|
||||
export function svgPoints(projection, context) {
|
||||
|
||||
function markerPath(selection, klass) {
|
||||
@@ -55,7 +52,7 @@ export function svgPoints(projection, context) {
|
||||
|
||||
function drawPoints(selection, graph, entities, filter) {
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
|
||||
// points with a direction will render as vertices at higher zooms
|
||||
function renderAsPoint(entity) {
|
||||
|
||||
@@ -4,14 +4,11 @@ import _values from 'lodash-es/values';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { dataFeatureIcons } from '../../data';
|
||||
import { geoScaleToZoom } from '../geo';
|
||||
import { osmEntity } from '../osm';
|
||||
import { svgPointTransform } from './index';
|
||||
|
||||
|
||||
var TAU = 2 * Math.PI;
|
||||
function ktoz(k) { return Math.log(k * TAU) / Math.LN2 - 8; }
|
||||
|
||||
|
||||
export function svgVertices(projection, context) {
|
||||
var radiuses = {
|
||||
// z16-, z17, z18+, w/icon
|
||||
@@ -46,7 +43,7 @@ export function svgVertices(projection, context) {
|
||||
var icons = {};
|
||||
var directions = {};
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
|
||||
|
||||
|
||||
@@ -258,7 +255,7 @@ export function svgVertices(projection, context) {
|
||||
|
||||
function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
var mode = context.mode();
|
||||
var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
|
||||
|
||||
@@ -318,7 +315,7 @@ export function svgVertices(projection, context) {
|
||||
// partial redraw - only update the selected items..
|
||||
drawVertices.drawSelected = function(selection, graph, target, extent) {
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
|
||||
_prevSelected = _currSelected || {};
|
||||
_currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
|
||||
@@ -334,7 +331,7 @@ export function svgVertices(projection, context) {
|
||||
if (target === _currHoverTarget) return; // continue only if something changed
|
||||
|
||||
var wireframe = context.surface().classed('fill-wireframe');
|
||||
var zoom = ktoz(projection.scale());
|
||||
var zoom = geoScaleToZoom(projection.scale());
|
||||
|
||||
_prevHover = _currHover || {};
|
||||
_currHoverTarget = target;
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
select as d3_select
|
||||
} from 'd3-selection';
|
||||
|
||||
import { geoRoundCoords } from '../geo';
|
||||
import { geoVecFloor } from '../geo';
|
||||
import { textDirection } from '../util/locale';
|
||||
import { uiTooltipHtml } from './tooltipHtml';
|
||||
|
||||
@@ -81,7 +81,7 @@ export function uiEditMenu(context, operations) {
|
||||
.attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
|
||||
.classed('disabled', function (d) { return d.disabled(); })
|
||||
.attr('transform', function (d, i) {
|
||||
return 'translate(' + geoRoundCoords([
|
||||
return 'translate(' + geoVecFloor([
|
||||
0,
|
||||
m + i * buttonHeight
|
||||
]).join(',') + ')';
|
||||
|
||||
@@ -26,7 +26,8 @@ import {
|
||||
|
||||
import {
|
||||
geoExtent,
|
||||
geoRawMercator
|
||||
geoRawMercator,
|
||||
geoZoomToScale
|
||||
} from '../../geo';
|
||||
|
||||
import {
|
||||
@@ -84,7 +85,7 @@ export function uiFieldRestrictions(field, context) {
|
||||
var z = 24;
|
||||
|
||||
projection
|
||||
.scale(256 * Math.pow(2, z) / (2 * Math.PI));
|
||||
.scale(geoZoomToScale(z));
|
||||
|
||||
var s = projection(vertex.loc);
|
||||
|
||||
|
||||
+59
-60
@@ -13,45 +13,44 @@ import {
|
||||
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
|
||||
|
||||
import { t } from '../util/locale';
|
||||
import { svgDebug, svgGpx } from '../svg';
|
||||
import { geoRawMercator } from '../geo';
|
||||
import {
|
||||
geoRawMercator,
|
||||
geoScaleToZoom,
|
||||
geoVecSubtract,
|
||||
geoVecScale,
|
||||
geoZoomToScale,
|
||||
} from '../geo';
|
||||
|
||||
import { rendererTileLayer } from '../renderer';
|
||||
import { svgDebug, svgGpx } from '../svg';
|
||||
import { utilSetTransform } from '../util';
|
||||
import { utilGetDimensions } from '../util/dimensions';
|
||||
|
||||
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
function ktoz(k) { return Math.log(k * TAU) / Math.LN2 - 8; }
|
||||
function vecSub(a, b) { return [ a[0] - b[0], a[1] - b[1] ]; }
|
||||
function vecScale(a, b) { return [ a[0] * b, a[1] * b ]; }
|
||||
|
||||
|
||||
export function uiMapInMap(context) {
|
||||
|
||||
|
||||
function map_in_map(selection) {
|
||||
var backgroundLayer = rendererTileLayer(context),
|
||||
overlayLayers = {},
|
||||
projection = geoRawMercator(),
|
||||
gpxLayer = svgGpx(projection, context).showLabels(false),
|
||||
debugLayer = svgDebug(projection, context),
|
||||
zoom = d3_zoom()
|
||||
.scaleExtent([ztok(0.5), ztok(24)])
|
||||
.on('start', zoomStarted)
|
||||
.on('zoom', zoomed)
|
||||
.on('end', zoomEnded),
|
||||
isTransformed = false,
|
||||
isHidden = true,
|
||||
skipEvents = false,
|
||||
gesture = null,
|
||||
zDiff = 6, // by default, minimap renders at (main zoom - 6)
|
||||
wrap = d3_select(null),
|
||||
tiles = d3_select(null),
|
||||
viewport = d3_select(null),
|
||||
tStart, // transform at start of gesture
|
||||
tCurr, // transform at most recent event
|
||||
timeoutId;
|
||||
var backgroundLayer = rendererTileLayer(context);
|
||||
var overlayLayers = {};
|
||||
var projection = geoRawMercator();
|
||||
var gpxLayer = svgGpx(projection, context).showLabels(false);
|
||||
var debugLayer = svgDebug(projection, context);
|
||||
var zoom = d3_zoom()
|
||||
.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
|
||||
.on('start', zoomStarted)
|
||||
.on('zoom', zoomed)
|
||||
.on('end', zoomEnded);
|
||||
var isTransformed = false;
|
||||
var isHidden = true;
|
||||
var skipEvents = false;
|
||||
var gesture = null;
|
||||
var zDiff = 6; // by default, minimap renders at (main zoom - 6)
|
||||
var wrap = d3_select(null);
|
||||
var tiles = d3_select(null);
|
||||
var viewport = d3_select(null);
|
||||
var tStart; // transform at start of gesture
|
||||
var tCurr; // transform at most recent event
|
||||
var timeoutId;
|
||||
|
||||
|
||||
function zoomStarted() {
|
||||
@@ -64,11 +63,11 @@ export function uiMapInMap(context) {
|
||||
function zoomed() {
|
||||
if (skipEvents) return;
|
||||
|
||||
var x = d3_event.transform.x,
|
||||
y = d3_event.transform.y,
|
||||
k = d3_event.transform.k,
|
||||
isZooming = (k !== tStart.k),
|
||||
isPanning = (x !== tStart.x || y !== tStart.y);
|
||||
var x = d3_event.transform.x;
|
||||
var y = d3_event.transform.y;
|
||||
var k = d3_event.transform.k;
|
||||
var isZooming = (k !== tStart.k);
|
||||
var isPanning = (x !== tStart.x || y !== tStart.y);
|
||||
|
||||
if (!isZooming && !isPanning) {
|
||||
return; // no change
|
||||
@@ -79,12 +78,12 @@ export function uiMapInMap(context) {
|
||||
gesture = isZooming ? 'zoom' : 'pan';
|
||||
}
|
||||
|
||||
var tMini = projection.transform(),
|
||||
tX, tY, scale;
|
||||
var tMini = projection.transform();
|
||||
var tX, tY, scale;
|
||||
|
||||
if (gesture === 'zoom') {
|
||||
var dMini = utilGetDimensions(wrap),
|
||||
cMini = vecScale(dMini, 0.5);
|
||||
var dMini = utilGetDimensions(wrap);
|
||||
var cMini = geoVecScale(dMini, 0.5);
|
||||
scale = k / tMini.k;
|
||||
tX = (cMini[0] / scale - cMini[0]) * scale;
|
||||
tY = (cMini[1] / scale - cMini[1]) * scale;
|
||||
@@ -100,8 +99,8 @@ export function uiMapInMap(context) {
|
||||
isTransformed = true;
|
||||
tCurr = d3_zoomIdentity.translate(x, y).scale(k);
|
||||
|
||||
var zMain = ktoz(context.projection.scale()),
|
||||
zMini = ktoz(k);
|
||||
var zMain = geoScaleToZoom(context.projection.scale());
|
||||
var zMini = geoScaleToZoom(k);
|
||||
|
||||
zDiff = zMain - zMini;
|
||||
|
||||
@@ -115,29 +114,29 @@ export function uiMapInMap(context) {
|
||||
|
||||
updateProjection();
|
||||
gesture = null;
|
||||
var dMini = utilGetDimensions(wrap),
|
||||
cMini = vecScale(dMini, 0.5);
|
||||
var dMini = utilGetDimensions(wrap);
|
||||
var cMini = geoVecScale(dMini, 0.5);
|
||||
context.map().center(projection.invert(cMini)); // recenter main map..
|
||||
}
|
||||
|
||||
|
||||
function updateProjection() {
|
||||
var loc = context.map().center(),
|
||||
dMini = utilGetDimensions(wrap),
|
||||
cMini = vecScale(dMini, 0.5),
|
||||
tMain = context.projection.transform(),
|
||||
zMain = ktoz(tMain.k),
|
||||
zMini = Math.max(zMain - zDiff, 0.5),
|
||||
kMini = ztok(zMini);
|
||||
var loc = context.map().center();
|
||||
var dMini = utilGetDimensions(wrap);
|
||||
var cMini = geoVecScale(dMini, 0.5);
|
||||
var tMain = context.projection.transform();
|
||||
var zMain = geoScaleToZoom(tMain.k);
|
||||
var zMini = Math.max(zMain - zDiff, 0.5);
|
||||
var kMini = geoZoomToScale(zMini);
|
||||
|
||||
projection
|
||||
.translate([tMain.x, tMain.y])
|
||||
.scale(kMini);
|
||||
|
||||
var point = projection(loc),
|
||||
mouse = (gesture === 'pan') ? vecSub([tCurr.x, tCurr.y], [tStart.x, tStart.y]) : [0, 0],
|
||||
xMini = cMini[0] - point[0] + tMain.x + mouse[0],
|
||||
yMini = cMini[1] - point[1] + tMain.y + mouse[1];
|
||||
var point = projection(loc);
|
||||
var mouse = (gesture === 'pan') ? geoVecSubtract([tCurr.x, tCurr.y], [tStart.x, tStart.y]) : [0, 0];
|
||||
var xMini = cMini[0] - point[0] + tMain.x + mouse[0];
|
||||
var yMini = cMini[1] - point[1] + tMain.y + mouse[1];
|
||||
|
||||
projection
|
||||
.translate([xMini, yMini])
|
||||
@@ -152,7 +151,7 @@ export function uiMapInMap(context) {
|
||||
}
|
||||
|
||||
zoom
|
||||
.scaleExtent([ztok(0.5), ztok(zMain - 3)]);
|
||||
.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
|
||||
|
||||
skipEvents = true;
|
||||
wrap.call(zoom.transform, tCurr);
|
||||
@@ -166,8 +165,8 @@ export function uiMapInMap(context) {
|
||||
|
||||
updateProjection();
|
||||
|
||||
var dMini = utilGetDimensions(wrap),
|
||||
zMini = ktoz(projection.scale());
|
||||
var dMini = utilGetDimensions(wrap);
|
||||
var zMini = geoScaleToZoom(projection.scale());
|
||||
|
||||
// setup tile container
|
||||
tiles = wrap
|
||||
@@ -249,8 +248,8 @@ export function uiMapInMap(context) {
|
||||
|
||||
// redraw viewport bounding box
|
||||
if (gesture !== 'pan') {
|
||||
var getPath = d3_geoPath(projection),
|
||||
bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
|
||||
var getPath = d3_geoPath(projection);
|
||||
var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
|
||||
|
||||
viewport = wrap.selectAll('.map-in-map-viewport')
|
||||
.data([0]);
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
select as d3_select
|
||||
} from 'd3-selection';
|
||||
|
||||
import { geoRoundCoords } from '../geo';
|
||||
import { geoVecFloor } from '../geo';
|
||||
import { uiTooltipHtml } from './tooltipHtml';
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export function uiRadialMenu(context, operations) {
|
||||
.attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
|
||||
.classed('disabled', function(d) { return d.disabled(); })
|
||||
.attr('transform', function(d, i) {
|
||||
return 'translate(' + geoRoundCoords([
|
||||
return 'translate(' + geoVecFloor([
|
||||
r * Math.sin(a0 + i * a),
|
||||
r * Math.cos(a0 + i * a)]).join(',') + ')';
|
||||
});
|
||||
|
||||
+162
-125
@@ -1,60 +1,96 @@
|
||||
describe('iD.geo', function() {
|
||||
describe('geoRoundCoords', function() {
|
||||
it('rounds coordinates', function() {
|
||||
expect(iD.geoRoundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.geoRoundCoords([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.geoRoundCoords([0, 1.1])).to.eql([0, 1]);
|
||||
|
||||
describe('geoVecAdd', function() {
|
||||
it('adds vectors', function() {
|
||||
expect(iD.geoVecAdd([1, 2], [3, 4])).to.eql([4, 6]);
|
||||
expect(iD.geoVecAdd([1, 2], [0, 0])).to.eql([1, 2]);
|
||||
expect(iD.geoVecAdd([1, 2], [-3, -4])).to.eql([-2, -2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecSubtract', function() {
|
||||
it('subtracts vectors', function() {
|
||||
expect(iD.geoVecSubtract([1, 2], [3, 4])).to.eql([-2, -2]);
|
||||
expect(iD.geoVecSubtract([1, 2], [0, 0])).to.eql([1, 2]);
|
||||
expect(iD.geoVecSubtract([1, 2], [-3, -4])).to.eql([4, 6]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecScale', function() {
|
||||
it('multiplies vectors', function() {
|
||||
expect(iD.geoVecScale([1, 2], 0)).to.eql([0, 0]);
|
||||
expect(iD.geoVecScale([1, 2], 1)).to.eql([1, 2]);
|
||||
expect(iD.geoVecScale([1, 2], 2)).to.eql([2, 4]);
|
||||
expect(iD.geoVecScale([1, 2], 0.5)).to.eql([0.5, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecFloor (was: geoRoundCoordinates)', function() {
|
||||
it('rounds vectors', function() {
|
||||
expect(iD.geoVecFloor([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.geoVecFloor([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.geoVecFloor([0, 1.1])).to.eql([0, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoInterp', function() {
|
||||
it('interpolates halfway', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
var a = [0, 0];
|
||||
var b = [10, 10];
|
||||
expect(iD.geoInterp(a, b, 0.5)).to.eql([5, 5]);
|
||||
});
|
||||
it('interpolates to one side', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
var a = [0, 0];
|
||||
var b = [10, 10];
|
||||
expect(iD.geoInterp(a, b, 0)).to.eql([0, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoDot', function() {
|
||||
it('dot product of right angle is zero', function() {
|
||||
var a = [1, 0];
|
||||
var b = [0, 1];
|
||||
expect(iD.geoDot(a, b)).to.eql(0);
|
||||
});
|
||||
it('dot product of same vector multiplies', function() {
|
||||
var a = [2, 0];
|
||||
var b = [2, 0];
|
||||
expect(iD.geoDot(a, b)).to.eql(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoCross', function() {
|
||||
it('cross product of right hand turn is positive', function() {
|
||||
var o = [0, 0],
|
||||
a = [2, 0],
|
||||
b = [0, 2];
|
||||
expect(iD.geoCross(o, a, b)).to.eql(4);
|
||||
it('2D cross product of right hand turn is positive', function() {
|
||||
var a = [2, 0];
|
||||
var b = [0, 2];
|
||||
expect(iD.geoCross(a, b)).to.eql(4);
|
||||
});
|
||||
it('cross product of left hand turn is negative', function() {
|
||||
var o = [0, 0],
|
||||
a = [2, 0],
|
||||
b = [0, -2];
|
||||
expect(iD.geoCross(o, a, b)).to.eql(-4);
|
||||
it('2D cross product of left hand turn is negative', function() {
|
||||
var a = [2, 0];
|
||||
var b = [0, -2];
|
||||
expect(iD.geoCross(a, b)).to.eql(-4);
|
||||
});
|
||||
it('cross product of colinear points is zero', function() {
|
||||
var o = [0, 0],
|
||||
a = [-2, 0],
|
||||
b = [2, 0];
|
||||
expect(iD.geoCross(o, a, b)).to.equal(0);
|
||||
it('2D cross product of colinear points is zero', function() {
|
||||
var a = [-2, 0];
|
||||
var b = [2, 0];
|
||||
expect(iD.geoCross(a, b)).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoEuclideanDistance', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
var a = [0, 0];
|
||||
var b = [0, 0];
|
||||
expect(iD.geoEuclideanDistance(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 10 unit line is 10', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 0];
|
||||
var a = [0, 0];
|
||||
var b = [10, 0];
|
||||
expect(iD.geoEuclideanDistance(a, b)).to.eql(10);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
var a = [0, 0];
|
||||
var b = [4, 3];
|
||||
expect(iD.geoEuclideanDistance(a, b)).to.eql(5);
|
||||
});
|
||||
});
|
||||
@@ -64,10 +100,10 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoLatToMeters(0)).to.eql(0);
|
||||
});
|
||||
it('1 degree latitude is approx 111 km', function() {
|
||||
expect(iD.geoLatToMeters(1)).to.be.within(110E3, 112E3);
|
||||
expect(iD.geoLatToMeters(1)).to.be.closeTo(111319, 10);
|
||||
});
|
||||
it('-1 degree latitude is approx -111 km', function() {
|
||||
expect(iD.geoLatToMeters(-1)).to.be.within(-112E3, -110E3);
|
||||
expect(iD.geoLatToMeters(-1)).to.be.closeTo(-111319, 10);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,21 +112,21 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoLonToMeters(0, 0)).to.eql(0);
|
||||
});
|
||||
it('distance of 1 degree longitude varies with latitude', function() {
|
||||
expect(iD.geoLonToMeters(1, 0)).to.be.within(110E3, 112E3);
|
||||
expect(iD.geoLonToMeters(1, 15)).to.be.within(107E3, 108E3);
|
||||
expect(iD.geoLonToMeters(1, 30)).to.be.within(96E3, 97E3);
|
||||
expect(iD.geoLonToMeters(1, 45)).to.be.within(78E3, 79E3);
|
||||
expect(iD.geoLonToMeters(1, 60)).to.be.within(55E3, 56E3);
|
||||
expect(iD.geoLonToMeters(1, 75)).to.be.within(28E3, 29E3);
|
||||
expect(iD.geoLonToMeters(1, 0)).to.be.closeTo(110946, 10);
|
||||
expect(iD.geoLonToMeters(1, 15)).to.be.closeTo(107165, 10);
|
||||
expect(iD.geoLonToMeters(1, 30)).to.be.closeTo(96082, 10);
|
||||
expect(iD.geoLonToMeters(1, 45)).to.be.closeTo(78450, 10);
|
||||
expect(iD.geoLonToMeters(1, 60)).to.be.closeTo(55473, 10);
|
||||
expect(iD.geoLonToMeters(1, 75)).to.be.closeTo(28715, 10);
|
||||
expect(iD.geoLonToMeters(1, 90)).to.eql(0);
|
||||
});
|
||||
it('distance of -1 degree longitude varies with latitude', function() {
|
||||
expect(iD.geoLonToMeters(-1, 0)).to.be.within(-112E3, -110E3);
|
||||
expect(iD.geoLonToMeters(-1, -15)).to.be.within(-108E3, -107E3);
|
||||
expect(iD.geoLonToMeters(-1, -30)).to.be.within(-97E3, -96E3);
|
||||
expect(iD.geoLonToMeters(-1, -45)).to.be.within(-79E3, -78E3);
|
||||
expect(iD.geoLonToMeters(-1, -60)).to.be.within(-56E3, -55E3);
|
||||
expect(iD.geoLonToMeters(-1, -75)).to.be.within(-29E3, -28E3);
|
||||
expect(iD.geoLonToMeters(-1, -0)).to.be.closeTo(-110946, 10);
|
||||
expect(iD.geoLonToMeters(-1, -15)).to.be.closeTo(-107165, 10);
|
||||
expect(iD.geoLonToMeters(-1, -30)).to.be.closeTo(-96082, 10);
|
||||
expect(iD.geoLonToMeters(-1, -45)).to.be.closeTo(-78450, 10);
|
||||
expect(iD.geoLonToMeters(-1, -60)).to.be.closeTo(-55473, 10);
|
||||
expect(iD.geoLonToMeters(-1, -75)).to.be.closeTo(-28715, 10);
|
||||
expect(iD.geoLonToMeters(-1, -90)).to.eql(0);
|
||||
});
|
||||
});
|
||||
@@ -100,10 +136,10 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoMetersToLat(0)).to.eql(0);
|
||||
});
|
||||
it('111 km is approx 1 degree latitude', function() {
|
||||
expect(iD.geoMetersToLat(111E3)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLat(111319)).to.be.closeTo(1, 0.0001);
|
||||
});
|
||||
it('-111 km is approx -1 degree latitude', function() {
|
||||
expect(iD.geoMetersToLat(-111E3)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLat(-111319)).to.be.closeTo(-1, 0.0001);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,22 +148,22 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoMetersToLon(0, 0)).to.eql(0);
|
||||
});
|
||||
it('distance of 1 degree longitude varies with latitude', function() {
|
||||
expect(iD.geoMetersToLon(111320, 0)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(107551, 15)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(96486, 30)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(78847, 45)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(55800, 60)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(28902, 75)).to.be.within(0.995, 1.005);
|
||||
expect(iD.geoMetersToLon(110946, 0)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(107165, 15)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(96082, 30)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(78450, 45)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(55473, 60)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(28715, 75)).to.be.closeTo(1, 1e-4);
|
||||
expect(iD.geoMetersToLon(1, 90)).to.eql(0);
|
||||
});
|
||||
it('distance of -1 degree longitude varies with latitude', function() {
|
||||
expect(iD.geoMetersToLon(-111320, 0)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-107551, 15)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-96486, 30)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-78847, 45)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-55800, 60)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-28902, 75)).to.be.within(-1.005, -0.995);
|
||||
expect(iD.geoMetersToLon(-1, 90)).to.eql(0);
|
||||
expect(iD.geoMetersToLon(-110946, -0)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-107165, -15)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-96082, -30)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-78450, -45)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-55473, -60)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-28715, -75)).to.be.closeTo(-1, 1e-4);
|
||||
expect(iD.geoMetersToLon(-1, -90)).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -159,43 +195,61 @@ describe('iD.geo', function() {
|
||||
|
||||
describe('geoSphericalDistance', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
var a = [0, 0];
|
||||
var b = [0, 0];
|
||||
expect(iD.geoSphericalDistance(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.geoSphericalDistance(a, b)).to.be.within(110E3, 112E3);
|
||||
var a = [0, 0];
|
||||
var b = [1, 0];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.closeTo(110946, 10);
|
||||
});
|
||||
it('a pythagorean triangle is (nearly) right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.within(555E3, 556E3);
|
||||
var a = [0, 0];
|
||||
var b = [4, 3];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.closeTo(555282, 10);
|
||||
});
|
||||
it('east-west distances at high latitude are shorter', function() {
|
||||
var a = [0, 60],
|
||||
b = [1, 60];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.within(55E3, 56E3);
|
||||
var a = [0, 60];
|
||||
var b = [1, 60];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.closeTo(55473, 10);
|
||||
});
|
||||
it('north-south distances at high latitude are not shorter', function() {
|
||||
var a = [0, 60],
|
||||
b = [0, 61];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.within(110E3, 112E3);
|
||||
var a = [0, 60];
|
||||
var b = [0, 61];
|
||||
expect(iD.geoSphericalDistance(a, b)).to.be.closeTo(111319, 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoZoomToScale', function() {
|
||||
it('converts from zoom to projection scale (tileSize = 256)', function() {
|
||||
expect(iD.geoZoomToScale(17)).to.be.closeTo(5340353.715440872, 1e-6);
|
||||
});
|
||||
it('converts from zoom to projection scale (tileSize = 512)', function() {
|
||||
expect(iD.geoZoomToScale(17, 512)).to.be.closeTo(10680707.430881744, 1e-6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoScaleToZoom', function() {
|
||||
it('converts from projection scale to zoom (tileSize = 256)', function() {
|
||||
expect(iD.geoScaleToZoom(5340353.715440872)).to.be.closeTo(17, 1e-6);
|
||||
});
|
||||
it('converts from projection scale to zoom (tileSize = 512)', function() {
|
||||
expect(iD.geoScaleToZoom(10680707.430881744, 512)).to.be.closeTo(17, 1e-6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoEdgeEqual', function() {
|
||||
it('returns false for inequal edges', function() {
|
||||
expect(iD.geoEdgeEqual(['a','b'], ['a','c'])).to.be.false;
|
||||
expect(iD.geoEdgeEqual(['a', 'b'], ['a', 'c'])).to.be.false;
|
||||
});
|
||||
|
||||
it('returns true for equal edges along same direction', function() {
|
||||
expect(iD.geoEdgeEqual(['a','b'], ['a','b'])).to.be.true;
|
||||
expect(iD.geoEdgeEqual(['a', 'b'], ['a', 'b'])).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true for equal edges along opposite direction', function() {
|
||||
expect(iD.geoEdgeEqual(['a','b'], ['b','a'])).to.be.true;
|
||||
expect(iD.geoEdgeEqual(['a', 'b'], ['b', 'a'])).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -211,10 +265,10 @@ describe('iD.geo', function() {
|
||||
|
||||
describe('geoRotate', function() {
|
||||
it('rotates points around [0, 0]', function() {
|
||||
var points = [[5, 0], [5, 1]],
|
||||
angle = Math.PI,
|
||||
around = [0, 0],
|
||||
result = iD.geoRotate(points, angle, around);
|
||||
var points = [[5, 0], [5, 1]];
|
||||
var angle = Math.PI;
|
||||
var around = [0, 0];
|
||||
var result = iD.geoRotate(points, angle, around);
|
||||
expect(result[0][0]).to.be.closeTo(-5, 1e-6);
|
||||
expect(result[0][1]).to.be.closeTo(0, 1e-6);
|
||||
expect(result[1][0]).to.be.closeTo(-5, 1e-6);
|
||||
@@ -222,10 +276,10 @@ describe('iD.geo', function() {
|
||||
});
|
||||
|
||||
it('rotates points around [3, 0]', function() {
|
||||
var points = [[5, 0], [5, 1]],
|
||||
angle = Math.PI,
|
||||
around = [3, 0],
|
||||
result = iD.geoRotate(points, angle, around);
|
||||
var points = [[5, 0], [5, 1]];
|
||||
var angle = Math.PI;
|
||||
var around = [3, 0];
|
||||
var result = iD.geoRotate(points, angle, around);
|
||||
expect(result[0][0]).to.be.closeTo(1, 1e-6);
|
||||
expect(result[0][1]).to.be.closeTo(0, 1e-6);
|
||||
expect(result[1][0]).to.be.closeTo(1, 1e-6);
|
||||
@@ -246,7 +300,7 @@ describe('iD.geo', function() {
|
||||
});
|
||||
|
||||
it('returns undefined properties for a degenerate way (single node)', function() {
|
||||
expect(iD.geoChooseEdge([iD.Node({loc: [0, 0]})], [0, 0], projection)).to.eql({
|
||||
expect(iD.geoChooseEdge([iD.osmNode({loc: [0, 0]})], [0, 0], projection)).to.eql({
|
||||
index: undefined,
|
||||
distance: Infinity,
|
||||
loc: undefined
|
||||
@@ -259,14 +313,10 @@ describe('iD.geo', function() {
|
||||
// c
|
||||
//
|
||||
// * = [2, 0]
|
||||
var a = [0, 0],
|
||||
b = [5, 0],
|
||||
c = [2, 1],
|
||||
nodes = [
|
||||
iD.Node({loc: a}),
|
||||
iD.Node({loc: b})
|
||||
];
|
||||
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [2, 1];
|
||||
var nodes = [ iD.osmNode({loc: a}), iD.osmNode({loc: b}) ];
|
||||
var choice = iD.geoChooseEdge(nodes, c, projection);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(1);
|
||||
@@ -274,14 +324,10 @@ describe('iD.geo', function() {
|
||||
});
|
||||
|
||||
it('returns the starting vertex when the orthogonal projection is < 0', function() {
|
||||
var a = [0, 0],
|
||||
b = [5, 0],
|
||||
c = [-3, 4],
|
||||
nodes = [
|
||||
iD.Node({loc: a}),
|
||||
iD.Node({loc: b})
|
||||
];
|
||||
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [-3, 4];
|
||||
var nodes = [ iD.osmNode({loc: a}), iD.osmNode({loc: b}) ];
|
||||
var choice = iD.geoChooseEdge(nodes, c, projection);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(5);
|
||||
@@ -289,14 +335,10 @@ describe('iD.geo', function() {
|
||||
});
|
||||
|
||||
it('returns the ending vertex when the orthogonal projection is > 1', function() {
|
||||
var a = [0, 0],
|
||||
b = [5, 0],
|
||||
c = [8, 4],
|
||||
nodes = [
|
||||
iD.Node({loc: a}),
|
||||
iD.Node({loc: b})
|
||||
];
|
||||
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [8, 4];
|
||||
var nodes = [ iD.osmNode({loc: a}), iD.osmNode({loc: b}) ];
|
||||
var choice = iD.geoChooseEdge(nodes, c, projection);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(5);
|
||||
@@ -306,28 +348,28 @@ describe('iD.geo', function() {
|
||||
|
||||
describe('geoLineIntersection', function() {
|
||||
it('returns null if lines are colinear with overlap', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[-5, 0], [5, 0]];
|
||||
var a = [[0, 0], [10, 0]];
|
||||
var b = [[-5, 0], [5, 0]];
|
||||
expect(iD.geoLineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns null if lines are colinear but disjoint', function() {
|
||||
var a = [[5, 0], [10, 0]],
|
||||
b = [[-10, 0], [-5, 0]];
|
||||
var a = [[5, 0], [10, 0]];
|
||||
var b = [[-10, 0], [-5, 0]];
|
||||
expect(iD.geoLineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns null if lines are parallel', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[0, 5], [10, 5]];
|
||||
var a = [[0, 0], [10, 0]];
|
||||
var b = [[0, 5], [10, 5]];
|
||||
expect(iD.geoLineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns the intersection point between 2 lines', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[5, 10], [5, -10]];
|
||||
var a = [[0, 0], [10, 0]];
|
||||
var b = [[5, 10], [5, -10]];
|
||||
expect(iD.geoLineIntersection(a, b)).to.eql([5, 0]);
|
||||
});
|
||||
it('returns null if lines are not parallel but not intersecting', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[-5, 10], [-5, -10]];
|
||||
var a = [[0, 0], [10, 0]];
|
||||
var b = [[-5, 10], [-5, -10]];
|
||||
expect(iD.geoLineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
});
|
||||
@@ -339,12 +381,7 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoPointInPolygon(point, poly)).to.be.true;
|
||||
});
|
||||
it('says a point outside of a polygon is outside', function() {
|
||||
var poly = [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
[1, 0],
|
||||
[0, 0]];
|
||||
var poly = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
|
||||
var point = [0.5, 1.5];
|
||||
expect(iD.geoPointInPolygon(point, poly)).to.be.false;
|
||||
});
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
describe('iD.svgAreas', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context, surface;
|
||||
var all = function() { return true; };
|
||||
var none = function() { return false; };
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
describe('iD.svgLayers', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context, container;
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
describe('iD.svgLines', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context, surface;
|
||||
var all = function() { return true; };
|
||||
var none = function() { return false; };
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
describe('iD.svgMidpoints', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context, surface;
|
||||
var _selectedIDs = [];
|
||||
var filter = function() { return true; };
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
describe('iD.svgPoints', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context, surface;
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
describe('iD.svgVertices', function () {
|
||||
var TAU = 2 * Math.PI;
|
||||
function ztok(z) { return 256 * Math.pow(2, z) / TAU; }
|
||||
|
||||
var context;
|
||||
var surface;
|
||||
var projection = d3.geoProjection(function(x, y) { return [x, -y]; })
|
||||
.translate([0, 0])
|
||||
.scale(ztok(17))
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [Infinity, Infinity]]);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user