mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-17 22:24:49 +02:00
Merge branch 'master' into add-feature-search-bar
This commit is contained in:
+5
-1
@@ -85,12 +85,16 @@ en:
|
||||
orthogonalize:
|
||||
title: Square
|
||||
description:
|
||||
vertex: Square this corner.
|
||||
line: Square the corners of this line.
|
||||
area: Square the corners of this area.
|
||||
key: S
|
||||
key: Q
|
||||
annotation:
|
||||
vertex: Squared a single corner.
|
||||
line: Squared the corners of a line.
|
||||
area: Squared the corners of an area.
|
||||
end_vertex: This can't be squared because it is an end node.
|
||||
square_enough: This can't be made more square than it already is.
|
||||
not_squarish: This can't be made square because it is not squarish.
|
||||
too_large: This can't be made square because not enough of it is currently visible.
|
||||
connected_to_hidden: This can't be made square because it is connected to a hidden feature.
|
||||
|
||||
+2
-2
@@ -4020,7 +4020,7 @@ en:
|
||||
highway/footway:
|
||||
# highway=footway
|
||||
name: Foot Path
|
||||
# 'terms: hike,hiking,trackway,trail,walk'
|
||||
# 'terms: hike,hiking,promenade,trackway,trail,walk'
|
||||
terms: '<translate with synonyms or related terms for ''Foot Path'', separated by commas>'
|
||||
highway/footway/conveying:
|
||||
# 'highway=footway, conveying=*'
|
||||
@@ -4187,7 +4187,7 @@ en:
|
||||
highway/steps:
|
||||
# highway=steps
|
||||
name: Steps
|
||||
# 'terms: stairs,staircase'
|
||||
# 'terms: stairs,staircase,stairway'
|
||||
terms: '<translate with synonyms or related terms for ''Steps'', separated by commas>'
|
||||
highway/steps/conveying:
|
||||
# 'highway=steps, conveying=*'
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
"highway/crossing/unmarked": {"fields": ["crossing", "kerb", "tactile_paving"], "geometry": ["vertex"], "addTags": {"highway": "crossing", "crossing": "unmarked"}, "removeTags": {"highway": "crossing", "crossing": "unmarked"}, "tags": {"highway": "crossing"}, "reference": {"key": "highway", "value": "crossing"}, "terms": [], "name": "Unmarked Crossing"},
|
||||
"highway/cycleway": {"icon": "maki-bicycle", "fields": ["name", "oneway", "surface", "width", "structure", "access", "incline"], "moreFields": ["wheelchair", "lit", "smoothness", "maxspeed", "covered", "dog"], "geometry": ["line"], "tags": {"highway": "cycleway"}, "terms": ["bike"], "name": "Cycle Path"},
|
||||
"highway/elevator": {"icon": "temaki-elevator", "fields": ["access_simple", "opening_hours", "maxweight", "ref", "wheelchair"], "moreFields": ["maxheight"], "geometry": ["vertex"], "tags": {"highway": "elevator"}, "terms": ["lift"], "name": "Elevator"},
|
||||
"highway/footway": {"icon": "temaki-pedestrian", "fields": ["name", "surface", "width", "structure", "access", "incline"], "moreFields": ["wheelchair", "lit", "smoothness", "covered", "dog"], "geometry": ["line"], "terms": ["hike", "hiking", "trackway", "trail", "walk"], "tags": {"highway": "footway"}, "name": "Foot Path"},
|
||||
"highway/footway": {"icon": "temaki-pedestrian", "fields": ["name", "surface", "width", "structure", "access", "incline"], "moreFields": ["wheelchair", "lit", "smoothness", "covered", "dog"], "geometry": ["line"], "terms": ["hike", "hiking", "promenade", "trackway", "trail", "walk"], "tags": {"highway": "footway"}, "name": "Foot Path"},
|
||||
"highway/footway/zebra-raised": {"icon": "temaki-pedestrian", "fields": ["crossing", "access", "surface", "kerb", "tactile_paving"], "geometry": ["line"], "tags": {"highway": "footway", "footway": "crossing", "crossing": "zebra", "traffic_calming": "table"}, "reference": {"key": "traffic_calming", "value": "table"}, "terms": ["zebra crossing", "marked crossing", "crosswalk", "flat top", "hump", "speed", "slow"], "name": "Marked Crosswalk (Raised)", "searchable": false},
|
||||
"highway/footway/zebra": {"icon": "temaki-pedestrian", "fields": ["crossing", "access", "surface", "kerb", "tactile_paving"], "geometry": ["line"], "tags": {"highway": "footway", "footway": "crossing", "crossing": "zebra"}, "reference": {"key": "footway", "value": "crossing"}, "terms": ["zebra crossing", "marked crossing", "crosswalk"], "name": "Marked Crosswalk", "searchable": false},
|
||||
"highway/footway/conveying": {"icon": "temaki-pedestrian", "fields": ["name", "conveying", "access_simple", "lit", "width", "wheelchair"], "geometry": ["line"], "terms": ["moving sidewalk", "autwalk", "skywalk", "travolator", "travelator", "travellator", "conveyor"], "tags": {"highway": "footway", "conveying": "*"}, "name": "Moving Walkway"},
|
||||
@@ -462,7 +462,7 @@
|
||||
"highway/service/parking_aisle": {"icon": "iD-highway-service", "geometry": ["line"], "tags": {"highway": "service", "service": "parking_aisle"}, "reference": {"key": "service", "value": "parking_aisle"}, "name": "Parking Aisle"},
|
||||
"highway/services": {"icon": "maki-car", "fields": ["{highway/rest_area}"], "moreFields": ["{highway/rest_area}"], "geometry": ["point", "vertex", "area"], "tags": {"highway": "services"}, "terms": ["services", "travel plaza", "service station"], "name": "Service Area"},
|
||||
"highway/speed_camera": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["direction", "ref", "maxspeed"], "tags": {"highway": "speed_camera"}, "terms": [], "name": "Speed Camera"},
|
||||
"highway/steps": {"icon": "iD-highway-steps", "fields": ["surface", "lit", "width", "incline_steps", "handrail", "step_count"], "geometry": ["line"], "tags": {"highway": "steps"}, "terms": ["stairs", "staircase"], "name": "Steps"},
|
||||
"highway/steps": {"icon": "iD-highway-steps", "fields": ["incline_steps", "handrail", "step_count", "surface", "lit", "width"], "moreFields": ["covered", "dog"], "geometry": ["line"], "tags": {"highway": "steps"}, "terms": ["stairs", "staircase", "stairway"], "name": "Steps"},
|
||||
"highway/steps/conveying": {"icon": "maki-entrance", "fields": ["name", "incline_steps", "conveying", "access_simple", "lit", "width", "handrail", "step_count"], "geometry": ["line"], "terms": ["moving staircase", "moving stairway", "people mover"], "tags": {"highway": "steps", "conveying": "*"}, "name": "Escalator"},
|
||||
"highway/stop": {"icon": "temaki-stop", "fields": ["stop", "direction_vertex"], "geometry": ["vertex"], "tags": {"highway": "stop"}, "terms": ["stop", "halt", "sign"], "name": "Stop Sign"},
|
||||
"highway/street_lamp": {"icon": "temaki-bulb", "geometry": ["point", "vertex"], "tags": {"highway": "street_lamp"}, "fields": ["lamp_type", "direction", "ref"], "terms": ["streetlight", "street light", "lamp", "light", "gaslight"], "name": "Street Lamp"},
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"terms": [
|
||||
"hike",
|
||||
"hiking",
|
||||
"promenade",
|
||||
"trackway",
|
||||
"trail",
|
||||
"walk"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
{
|
||||
"icon": "iD-highway-steps",
|
||||
"fields": [
|
||||
"surface",
|
||||
"lit",
|
||||
"width",
|
||||
"incline_steps",
|
||||
"handrail",
|
||||
"step_count"
|
||||
"step_count",
|
||||
"surface",
|
||||
"lit",
|
||||
"width"
|
||||
],
|
||||
"moreFields": [
|
||||
"covered",
|
||||
"dog"
|
||||
],
|
||||
"geometry": [
|
||||
"line"
|
||||
@@ -16,7 +20,8 @@
|
||||
},
|
||||
"terms": [
|
||||
"stairs",
|
||||
"staircase"
|
||||
"staircase",
|
||||
"stairway"
|
||||
],
|
||||
"name": "Steps"
|
||||
}
|
||||
|
||||
Vendored
+7
-3
@@ -113,14 +113,18 @@
|
||||
"orthogonalize": {
|
||||
"title": "Square",
|
||||
"description": {
|
||||
"vertex": "Square this corner.",
|
||||
"line": "Square the corners of this line.",
|
||||
"area": "Square the corners of this area."
|
||||
},
|
||||
"key": "S",
|
||||
"key": "Q",
|
||||
"annotation": {
|
||||
"vertex": "Squared a single corner.",
|
||||
"line": "Squared the corners of a line.",
|
||||
"area": "Squared the corners of an area."
|
||||
},
|
||||
"end_vertex": "This can't be squared because it is an end node.",
|
||||
"square_enough": "This can't be made more square than it already is.",
|
||||
"not_squarish": "This can't be made square because it is not squarish.",
|
||||
"too_large": "This can't be made square because not enough of it is currently visible.",
|
||||
"connected_to_hidden": "This can't be made square because it is connected to a hidden feature."
|
||||
@@ -5600,7 +5604,7 @@
|
||||
},
|
||||
"highway/footway": {
|
||||
"name": "Foot Path",
|
||||
"terms": "hike,hiking,trackway,trail,walk"
|
||||
"terms": "hike,hiking,promenade,trackway,trail,walk"
|
||||
},
|
||||
"highway/footway/zebra-raised": {
|
||||
"name": "Marked Crosswalk (Raised)",
|
||||
@@ -5744,7 +5748,7 @@
|
||||
},
|
||||
"highway/steps": {
|
||||
"name": "Steps",
|
||||
"terms": "stairs,staircase"
|
||||
"terms": "stairs,staircase,stairway"
|
||||
},
|
||||
"highway/steps/conveying": {
|
||||
"name": "Escalator",
|
||||
|
||||
+216
-112
@@ -1,34 +1,65 @@
|
||||
import _clone from 'lodash-es/clone';
|
||||
import _uniq from 'lodash-es/uniq';
|
||||
import _cloneDeep from 'lodash-es/cloneDeep';
|
||||
|
||||
import { actionDeleteNode } from './delete_node';
|
||||
import { geoVecInterp, geoVecLength } from '../geo';
|
||||
import {
|
||||
geoVecAdd,
|
||||
geoVecEqual,
|
||||
geoVecInterp,
|
||||
geoVecLength,
|
||||
geoVecNormalize,
|
||||
geoVecNormalizedDot,
|
||||
geoVecProject,
|
||||
geoVecScale,
|
||||
geoVecSubtract
|
||||
} from '../geo';
|
||||
|
||||
|
||||
/*
|
||||
* Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
|
||||
*/
|
||||
export function actionOrthogonalize(wayId, projection) {
|
||||
var threshold = 12, // degrees within right or straight to alter
|
||||
lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
|
||||
upperThreshold = Math.cos(threshold * Math.PI / 180);
|
||||
export function actionOrthogonalize(wayID, projection, vertexID) {
|
||||
var epsilon = 1e-4;
|
||||
var threshold = 13; // degrees within right or straight to alter
|
||||
|
||||
// We test normalized dot products so we can compare as cos(angle)
|
||||
var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
|
||||
var upperThreshold = Math.cos(threshold * Math.PI / 180);
|
||||
|
||||
|
||||
var action = function(graph, t) {
|
||||
if (t === null || !isFinite(t)) t = 1;
|
||||
t = Math.min(Math.max(+t, 0), 1);
|
||||
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
points = _uniq(nodes).map(function(n) { return projection(n.loc); }),
|
||||
corner = {i: 0, dotp: 1},
|
||||
epsilon = 1e-4,
|
||||
node, loc, score, motions, i, j;
|
||||
var way = graph.entity(wayID);
|
||||
way = way.removeNode(''); // sanity check - remove any consecutive duplicates
|
||||
graph = graph.replace(way);
|
||||
|
||||
var isClosed = way.isClosed();
|
||||
var nodes = _clone(graph.childNodes(way));
|
||||
if (isClosed) nodes.pop();
|
||||
|
||||
if (vertexID !== undefined) {
|
||||
nodes = nodeSubset(nodes, vertexID, isClosed);
|
||||
if (nodes.length !== 3) return graph;
|
||||
}
|
||||
|
||||
// note: all geometry functions here use the unclosed node/point/coord list
|
||||
|
||||
var nodeCount = {};
|
||||
var points = [];
|
||||
var corner = { i: 0, dotp: 1 };
|
||||
var node, point, loc, score, motions, i, j;
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
node = nodes[i];
|
||||
nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
|
||||
points.push({ id: node.id, coord: projection(node.loc) });
|
||||
}
|
||||
|
||||
|
||||
if (points.length === 3) { // move only one vertex for right triangle
|
||||
for (i = 0; i < 1000; i++) {
|
||||
motions = points.map(calcMotion);
|
||||
points[corner.i] = addPoints(points[corner.i], motions[corner.i]);
|
||||
|
||||
points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
|
||||
score = corner.dotp;
|
||||
if (score < epsilon) {
|
||||
break;
|
||||
@@ -36,22 +67,45 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
}
|
||||
|
||||
node = graph.entity(nodes[corner.i].id);
|
||||
loc = projection.invert(points[corner.i]);
|
||||
loc = projection.invert(points[corner.i].coord);
|
||||
graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
|
||||
|
||||
} else {
|
||||
var best,
|
||||
originalPoints = _clone(points);
|
||||
var straights = [];
|
||||
var simplified = [];
|
||||
|
||||
// Remove points from nearly straight sections..
|
||||
// This produces a simplified shape to orthogonalize
|
||||
for (i = 0; i < points.length; i++) {
|
||||
point = points[i];
|
||||
var dotp = 0;
|
||||
if (isClosed || (i > 0 && i < points.length - 1)) {
|
||||
var a = points[(i - 1 + points.length) % points.length];
|
||||
var b = points[(i + 1) % points.length];
|
||||
dotp = Math.abs(normalizedDotProduct(a.coord, b.coord, point.coord));
|
||||
}
|
||||
|
||||
if (dotp > upperThreshold) {
|
||||
straights.push(point);
|
||||
} else {
|
||||
simplified.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
// Orthogonalize the simplified shape
|
||||
var bestPoints = _cloneDeep(simplified);
|
||||
var originalPoints = _cloneDeep(simplified);
|
||||
score = Infinity;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
motions = points.map(calcMotion);
|
||||
motions = simplified.map(calcMotion);
|
||||
|
||||
for (j = 0; j < motions.length; j++) {
|
||||
points[j] = addPoints(points[j],motions[j]);
|
||||
simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
|
||||
}
|
||||
var newScore = squareness(points);
|
||||
var newScore = calcScore(simplified, isClosed);
|
||||
if (newScore < score) {
|
||||
best = _clone(points);
|
||||
bestPoints = _cloneDeep(simplified);
|
||||
score = newScore;
|
||||
}
|
||||
if (score < epsilon) {
|
||||
@@ -59,30 +113,41 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
}
|
||||
}
|
||||
|
||||
points = best;
|
||||
var bestCoords = bestPoints.map(function(p) { return p.coord; });
|
||||
if (isClosed) bestCoords.push(bestCoords[0]);
|
||||
|
||||
for (i = 0; i < points.length; i++) {
|
||||
// only move the points that actually moved
|
||||
if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
|
||||
loc = projection.invert(points[i]);
|
||||
node = graph.entity(nodes[i].id);
|
||||
// move the nodes that should move
|
||||
for (i = 0; i < bestPoints.length; i++) {
|
||||
point = bestPoints[i];
|
||||
if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
|
||||
node = graph.entity(point.id);
|
||||
loc = projection.invert(point.coord);
|
||||
graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty nodes on straight sections
|
||||
for (i = 0; t === 1 && i < points.length; i++) {
|
||||
node = graph.entity(nodes[i].id);
|
||||
// move the nodes along straight segments
|
||||
for (i = 0; i < straights.length; i++) {
|
||||
point = straights[i];
|
||||
if (nodeCount[point.id] > 1) continue; // skip self-intersections
|
||||
|
||||
if (graph.parentWays(node).length > 1 ||
|
||||
graph.parentRelations(node).length ||
|
||||
node.hasInterestingTags()) {
|
||||
continue;
|
||||
}
|
||||
node = graph.entity(point.id);
|
||||
|
||||
var dotp = normalizedDotProduct(i, points);
|
||||
if (dotp < -1 + epsilon) {
|
||||
if (t === 1 &&
|
||||
graph.parentWays(node).length === 1 &&
|
||||
graph.parentRelations(node).length === 0 &&
|
||||
!node.hasInterestingTags()
|
||||
) {
|
||||
// remove uninteresting points..
|
||||
graph = actionDeleteNode(node.id)(graph);
|
||||
|
||||
} else {
|
||||
// move interesting points to the nearest edge..
|
||||
var choice = geoVecProject(point.coord, bestCoords);
|
||||
if (choice) {
|
||||
loc = projection.invert(choice.target);
|
||||
graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,107 +155,146 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
return graph;
|
||||
|
||||
|
||||
function calcMotion(b, i, array) {
|
||||
var a = array[(i - 1 + array.length) % array.length],
|
||||
c = array[(i + 1) % array.length],
|
||||
p = subtractPoints(a, b),
|
||||
q = subtractPoints(c, b),
|
||||
scale, dotp;
|
||||
function calcMotion(point, i, array) {
|
||||
// don't try to move the endpoints of a non-closed way.
|
||||
if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0];
|
||||
// don't try to move a node that appears more than once (self intersection)
|
||||
if (nodeCount[array[i].id] > 1) return [0, 0];
|
||||
|
||||
scale = 2 * Math.min(geoVecLength(p, [0, 0]), geoVecLength(q, [0, 0]));
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
var a = array[(i - 1 + array.length) % array.length].coord;
|
||||
var origin = point.coord;
|
||||
var b = array[(i + 1) % array.length].coord;
|
||||
var p = geoVecSubtract(a, origin);
|
||||
var q = geoVecSubtract(b, origin);
|
||||
|
||||
dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
|
||||
var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
|
||||
p = geoVecNormalize(p);
|
||||
q = geoVecNormalize(q);
|
||||
|
||||
// nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
|
||||
if (array.length > 3) {
|
||||
if (dotp < -0.707106781186547) {
|
||||
dotp += 1.0;
|
||||
}
|
||||
} else if (dotp && Math.abs(dotp) < corner.dotp) {
|
||||
var dotp = (p[0] * q[0] + p[1] * q[1]);
|
||||
var val = Math.abs(dotp);
|
||||
|
||||
if (val < lowerThreshold) { // nearly orthogonal
|
||||
corner.i = i;
|
||||
corner.dotp = Math.abs(dotp);
|
||||
corner.dotp = val;
|
||||
var vec = geoVecNormalize(geoVecAdd(p, q));
|
||||
return geoVecScale(vec, 0.1 * dotp * scale);
|
||||
}
|
||||
|
||||
return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
|
||||
return [0, 0]; // do nothing
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function squareness(points) {
|
||||
return points.reduce(function(sum, val, i, array) {
|
||||
var dotp = normalizedDotProduct(i, array);
|
||||
|
||||
dotp = filterDotProduct(dotp);
|
||||
return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
function normalizedDotProduct(i, points) {
|
||||
var a = points[(i - 1 + points.length) % points.length],
|
||||
b = points[i],
|
||||
c = points[(i + 1) % points.length],
|
||||
p = subtractPoints(a, b),
|
||||
q = subtractPoints(c, b);
|
||||
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
|
||||
return p[0] * q[0] + p[1] * q[1];
|
||||
}
|
||||
|
||||
|
||||
function subtractPoints(a, b) {
|
||||
return [a[0] - b[0], a[1] - b[1]];
|
||||
}
|
||||
|
||||
|
||||
function addPoints(a, b) {
|
||||
return [a[0] + b[0], a[1] + b[1]];
|
||||
}
|
||||
|
||||
|
||||
function normalizePoint(point, scale) {
|
||||
var vector = [0, 0];
|
||||
var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
|
||||
if (length !== 0) {
|
||||
vector[0] = point[0] / length;
|
||||
vector[1] = point[1] / length;
|
||||
function normalizedDotProduct(a, b, origin) {
|
||||
if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
|
||||
return 1; // coincident points, treat as straight and try to remove
|
||||
}
|
||||
|
||||
vector[0] *= scale;
|
||||
vector[1] *= scale;
|
||||
|
||||
return vector;
|
||||
return geoVecNormalizedDot(a, b, origin);
|
||||
}
|
||||
|
||||
|
||||
function filterDotProduct(dotp) {
|
||||
if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
|
||||
return dotp;
|
||||
var val = Math.abs(dotp);
|
||||
if (val < epsilon) {
|
||||
return 0; // already orthogonal
|
||||
} else if (val < lowerThreshold || val > upperThreshold) {
|
||||
return dotp; // can be adjusted
|
||||
} else {
|
||||
return null; // ignore vertex
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function calcScore(points, isClosed) {
|
||||
var score = 0;
|
||||
var first = isClosed ? 0 : 1;
|
||||
var last = isClosed ? points.length : points.length - 1;
|
||||
var coords = points.map(function(p) { return p.coord; });
|
||||
|
||||
for (var i = first; i < last; i++) {
|
||||
var a = coords[(i - 1 + coords.length) % coords.length];
|
||||
var origin = coords[i];
|
||||
var b = coords[(i + 1) % coords.length];
|
||||
|
||||
var dotp = filterDotProduct(normalizedDotProduct(a, b, origin));
|
||||
if (dotp === null) continue; // ignore vertex
|
||||
score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
// similar to calcScore, but returns quickly if there is something to do
|
||||
function canOrthogonalize(coords, isClosed) {
|
||||
var score = null;
|
||||
var first = isClosed ? 0 : 1;
|
||||
var last = isClosed ? coords.length : coords.length - 1;
|
||||
|
||||
for (var i = first; i < last; i++) {
|
||||
var a = coords[(i - 1 + coords.length) % coords.length];
|
||||
var origin = coords[i];
|
||||
var b = coords[(i + 1) % coords.length];
|
||||
|
||||
var dotp = filterDotProduct(normalizedDotProduct(a, b, origin));
|
||||
if (dotp === null) continue; // ignore vertex
|
||||
if (Math.abs(dotp) > 0) return 1; // something to do
|
||||
score = 0; // already square
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
// if we are only orthogonalizing one vertex,
|
||||
// get that vertex and the previous and next
|
||||
function nodeSubset(nodes, vertexID, isClosed) {
|
||||
var first = isClosed ? 0 : 1;
|
||||
var last = isClosed ? nodes.length : nodes.length - 1;
|
||||
|
||||
for (var i = first; i < last; i++) {
|
||||
if (nodes[i].id === vertexID) {
|
||||
return [
|
||||
nodes[(i - 1 + nodes.length) % nodes.length],
|
||||
nodes[i],
|
||||
nodes[(i + 1) % nodes.length]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
action.disabled = function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
points = _uniq(nodes).map(function(n) { return projection(n.loc); });
|
||||
var way = graph.entity(wayID);
|
||||
way = way.removeNode(''); // sanity check - remove any consecutive duplicates
|
||||
graph = graph.replace(way);
|
||||
|
||||
if (squareness(points)) {
|
||||
return false;
|
||||
var isClosed = way.isClosed();
|
||||
var nodes = _clone(graph.childNodes(way));
|
||||
if (isClosed) nodes.pop();
|
||||
|
||||
if (vertexID !== undefined) {
|
||||
nodes = nodeSubset(nodes, vertexID, isClosed);
|
||||
if (nodes.length !== 3) return 'end_vertex';
|
||||
}
|
||||
|
||||
return 'not_squarish';
|
||||
var coords = nodes.map(function(n) { return projection(n.loc); });
|
||||
var score = canOrthogonalize(coords, isClosed);
|
||||
|
||||
if (score === null) {
|
||||
return 'not_squarish';
|
||||
} else if (score === 0) {
|
||||
return 'square_enough';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
action.transitionable = true;
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import _values from 'lodash-es/values';
|
||||
|
||||
|
||||
/*
|
||||
iD.Difference represents the difference between two graphs.
|
||||
iD.coreDifference represents the difference between two graphs.
|
||||
It knows how to calculate the set of entities that were
|
||||
created, modified, or deleted, and also contains the logic
|
||||
for recursively extending a difference to the complete set
|
||||
|
||||
@@ -36,5 +36,8 @@ export { geoVecEqual } from './vector.js';
|
||||
export { geoVecFloor } from './vector.js';
|
||||
export { geoVecInterp } from './vector.js';
|
||||
export { geoVecLength } from './vector.js';
|
||||
export { geoVecNormalize } from './vector.js';
|
||||
export { geoVecNormalizedDot } from './vector.js';
|
||||
export { geoVecProject } from './vector.js';
|
||||
export { geoVecSubtract } from './vector.js';
|
||||
export { geoVecScale } from './vector.js';
|
||||
|
||||
+61
-4
@@ -37,11 +37,21 @@ export function geoVecInterp(a, b, t) {
|
||||
|
||||
// http://jsperf.com/id-dist-optimization
|
||||
export function geoVecLength(a, b) {
|
||||
b = b || [0, 0];
|
||||
var x = a[0] - b[0];
|
||||
var y = a[1] - b[1];
|
||||
return Math.sqrt((x * x) + (y * y));
|
||||
}
|
||||
|
||||
// get a unit vector
|
||||
export function geoVecNormalize(a) {
|
||||
var length = Math.sqrt((a[0] * a[0]) + (a[1] * a[1]));
|
||||
if (length !== 0) {
|
||||
return geoVecScale(a, 1 / length);
|
||||
}
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
// Return the counterclockwise angle in the range (-pi, pi)
|
||||
// between the positive X axis and the line intersecting a and b.
|
||||
export function geoVecAngle(a, b) {
|
||||
@@ -51,8 +61,17 @@ export function geoVecAngle(a, b) {
|
||||
// dot product
|
||||
export function geoVecDot(a, b, origin) {
|
||||
origin = origin || [0, 0];
|
||||
return (a[0] - origin[0]) * (b[0] - origin[0]) +
|
||||
(a[1] - origin[1]) * (b[1] - origin[1]);
|
||||
var p = geoVecSubtract(a, origin);
|
||||
var q = geoVecSubtract(b, origin);
|
||||
return (p[0]) * (q[0]) + (p[1]) * (q[1]);
|
||||
}
|
||||
|
||||
// normalized dot product
|
||||
export function geoVecNormalizedDot(a, b, origin) {
|
||||
origin = origin || [0, 0];
|
||||
var p = geoVecNormalize(geoVecSubtract(a, origin));
|
||||
var q = geoVecNormalize(geoVecSubtract(b, origin));
|
||||
return geoVecDot(p, q);
|
||||
}
|
||||
|
||||
// 2D cross product of OA and OB vectors, returns magnitude of Z vector
|
||||
@@ -60,7 +79,45 @@ export function geoVecDot(a, b, origin) {
|
||||
// negative for clockwise turn, and zero if the points are collinear.
|
||||
export function geoVecCross(a, b, origin) {
|
||||
origin = origin || [0, 0];
|
||||
return (a[0] - origin[0]) * (b[1] - origin[1]) -
|
||||
(a[1] - origin[1]) * (b[0] - origin[0]);
|
||||
var p = geoVecSubtract(a, origin);
|
||||
var q = geoVecSubtract(b, origin);
|
||||
return (p[0]) * (q[1]) - (p[1]) * (q[0]);
|
||||
}
|
||||
|
||||
|
||||
// find closest orthogonal projection of point onto points array
|
||||
export function geoVecProject(a, points) {
|
||||
var min = Infinity;
|
||||
var idx;
|
||||
var target;
|
||||
|
||||
for (var i = 0; i < points.length - 1; i++) {
|
||||
var o = points[i];
|
||||
var s = geoVecSubtract(points[i + 1], o);
|
||||
var v = geoVecSubtract(a, o);
|
||||
var proj = geoVecDot(v, s) / geoVecDot(s, s);
|
||||
var p;
|
||||
|
||||
if (proj < 0) {
|
||||
p = o;
|
||||
} else if (proj > 1) {
|
||||
p = points[i + 1];
|
||||
} else {
|
||||
p = [o[0] + proj * s[0], o[1] + proj * s[1]];
|
||||
}
|
||||
|
||||
var dist = geoVecLength(p, a);
|
||||
if (dist < min) {
|
||||
min = dist;
|
||||
idx = i + 1;
|
||||
target = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx !== undefined) {
|
||||
return { index: idx, distance: min, target: target };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,31 +6,59 @@ import { behaviorOperation } from '../behavior/index';
|
||||
|
||||
|
||||
export function operationOrthogonalize(selectedIDs, context) {
|
||||
var entityId = selectedIDs[0],
|
||||
entity = context.entity(entityId),
|
||||
extent = entity.extent(context.graph()),
|
||||
geometry = context.geometry(entityId),
|
||||
action = actionOrthogonalize(entityId, context.projection);
|
||||
var _entityID;
|
||||
var _entity;
|
||||
var _geometry;
|
||||
var action = chooseAction();
|
||||
|
||||
|
||||
function chooseAction() {
|
||||
if (selectedIDs.length !== 1) return null;
|
||||
|
||||
_entityID = selectedIDs[0];
|
||||
_entity = context.entity(_entityID);
|
||||
_geometry = context.geometry(_entityID);
|
||||
|
||||
// square a line/area
|
||||
if (_entity.type === 'way' && _uniq(_entity.nodes).length > 2 ) {
|
||||
return actionOrthogonalize(_entityID, context.projection);
|
||||
|
||||
// square a single vertex
|
||||
} else if (_geometry === 'vertex') {
|
||||
var graph = context.graph();
|
||||
var parents = graph.parentWays(_entity);
|
||||
if (parents.length === 1) {
|
||||
var way = parents[0];
|
||||
if (way.nodes.indexOf(_entityID) !== -1) {
|
||||
return actionOrthogonalize(way.id, context.projection, _entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var operation = function() {
|
||||
if (!action) return;
|
||||
context.perform(action, operation.annotation());
|
||||
};
|
||||
|
||||
|
||||
operation.available = function() {
|
||||
return selectedIDs.length === 1 &&
|
||||
entity.type === 'way' &&
|
||||
entity.isClosed() &&
|
||||
_uniq(entity.nodes).length > 2;
|
||||
return Boolean(action);
|
||||
};
|
||||
|
||||
|
||||
operation.disabled = function() {
|
||||
if (!action) return '';
|
||||
|
||||
var extent = _entity.extent(context.graph());
|
||||
var reason;
|
||||
if (extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
|
||||
if (_geometry !== 'vertex' && extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
reason = 'too_large';
|
||||
} else if (context.hasHiddenConnections(entityId)) {
|
||||
} else if (context.hasHiddenConnections(_entityID)) {
|
||||
reason = 'connected_to_hidden';
|
||||
}
|
||||
return action.disabled(context.graph()) || reason;
|
||||
@@ -41,12 +69,12 @@ export function operationOrthogonalize(selectedIDs, context) {
|
||||
var disable = operation.disabled();
|
||||
return disable ?
|
||||
t('operations.orthogonalize.' + disable) :
|
||||
t('operations.orthogonalize.description.' + geometry);
|
||||
t('operations.orthogonalize.description.' + _geometry);
|
||||
};
|
||||
|
||||
|
||||
operation.annotation = function() {
|
||||
return t('operations.orthogonalize.annotation.' + geometry);
|
||||
return t('operations.orthogonalize.annotation.' + _geometry);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -195,7 +195,6 @@ _extend(osmWay.prototype, {
|
||||
|
||||
// returns an object with the tag that implies this is an area, if any
|
||||
tagSuggestingArea: function() {
|
||||
|
||||
if (this.tags.area === 'yes') return { area: 'yes' };
|
||||
if (this.tags.area === 'no') return null;
|
||||
|
||||
@@ -230,7 +229,6 @@ _extend(osmWay.prototype, {
|
||||
},
|
||||
|
||||
isArea: function() {
|
||||
|
||||
if (this.tags.area === 'yes')
|
||||
return true;
|
||||
if (!this.isClosed() || this.tags.area === 'no')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { t } from '../../util/locale';
|
||||
import { geoSphericalDistance } from '../../geo';
|
||||
import { geoSphericalDistance, geoVecNormalizedDot } from '../../geo';
|
||||
|
||||
|
||||
export function pointBox(loc, context) {
|
||||
@@ -117,45 +117,20 @@ export function isMostlySquare(points) {
|
||||
var threshold = 15; // degrees within right or straight
|
||||
var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
|
||||
var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
|
||||
var mag;
|
||||
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
mag = Math.abs(normalizedDotProduct(i, points));
|
||||
var a = points[(i - 1 + points.length) % points.length];
|
||||
var origin = points[i];
|
||||
var b = points[(i + 1) % points.length];
|
||||
|
||||
var dotp = geoVecNormalizedDot(a, b, origin);
|
||||
var mag = Math.abs(dotp);
|
||||
if (mag > lowerBound && mag < upperBound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
function normalizedDotProduct(i, points) {
|
||||
var a = points[(i - 1 + points.length) % points.length];
|
||||
var b = points[i];
|
||||
var c = points[(i + 1) % points.length];
|
||||
var p = subtractPoints(a, b);
|
||||
var q = subtractPoints(c, b);
|
||||
|
||||
p = normalizePoint(p);
|
||||
q = normalizePoint(q);
|
||||
|
||||
return p[0] * q[0] + p[1] * q[1];
|
||||
|
||||
|
||||
function subtractPoints(a, b) {
|
||||
return [a[0] - b[0], a[1] - b[1]];
|
||||
}
|
||||
|
||||
function normalizePoint(point) {
|
||||
var vector = [0, 0];
|
||||
var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
|
||||
if (length !== 0) {
|
||||
vector[0] = point[0] / length;
|
||||
vector[1] = point[1] / length;
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@
|
||||
"js-yaml": "^3.9.0",
|
||||
"json-stringify-pretty-compact": "^1.1.0",
|
||||
"jsonschema": "^1.1.0",
|
||||
"mapillary-js": "2.16.0",
|
||||
"mapillary-js": "2.17.0",
|
||||
"mapillary_sprite_source": "^1.7.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mocha": "^6.0.0",
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
describe('iD.actionCopyEntities', function () {
|
||||
it('copies a node', function () {
|
||||
var a = iD.osmNode({id: 'a'}),
|
||||
base = iD.coreGraph([a]),
|
||||
head = iD.actionCopyEntities(['a'], base)(base),
|
||||
diff = iD.Difference(base, head),
|
||||
created = diff.created();
|
||||
var a = iD.osmNode({id: 'a'});
|
||||
var base = iD.coreGraph([a]);
|
||||
var head = iD.actionCopyEntities(['a'], base)(base);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
var created = diff.created();
|
||||
|
||||
expect(head.hasEntity('a')).to.be.ok;
|
||||
expect(created).to.have.length(1);
|
||||
});
|
||||
|
||||
it('copies a way', function () {
|
||||
var a = iD.osmNode({id: 'a'}),
|
||||
b = iD.osmNode({id: 'b'}),
|
||||
w = iD.osmWay({id: 'w', nodes: ['a', 'b']}),
|
||||
base = iD.coreGraph([a, b, w]),
|
||||
action = iD.actionCopyEntities(['w'], base),
|
||||
head = action(base),
|
||||
diff = iD.Difference(base, head),
|
||||
created = diff.created();
|
||||
var a = iD.osmNode({id: 'a'});
|
||||
var b = iD.osmNode({id: 'b'});
|
||||
var w = iD.osmWay({id: 'w', nodes: ['a', 'b']});
|
||||
var base = iD.coreGraph([a, b, w]);
|
||||
var action = iD.actionCopyEntities(['w'], base);
|
||||
var head = action(base);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
var created = diff.created();
|
||||
|
||||
expect(head.hasEntity('w')).to.be.ok;
|
||||
expect(created).to.have.length(3);
|
||||
@@ -26,13 +26,13 @@ describe('iD.actionCopyEntities', function () {
|
||||
|
||||
it('copies multiple nodes', function () {
|
||||
var base = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'})
|
||||
]),
|
||||
action = iD.actionCopyEntities(['a', 'b'], base),
|
||||
head = action(base),
|
||||
diff = iD.Difference(base, head),
|
||||
created = diff.created();
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'})
|
||||
]);
|
||||
var action = iD.actionCopyEntities(['a', 'b'], base);
|
||||
var head = action(base);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
var created = diff.created();
|
||||
|
||||
expect(head.hasEntity('a')).to.be.ok;
|
||||
expect(head.hasEntity('b')).to.be.ok;
|
||||
@@ -41,29 +41,27 @@ describe('iD.actionCopyEntities', function () {
|
||||
|
||||
it('copies multiple ways, keeping the same connections', function () {
|
||||
var base = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmWay({id: 'w1', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: 'w2', nodes: ['b', 'c']})
|
||||
]),
|
||||
action = iD.actionCopyEntities(['w1', 'w2'], base),
|
||||
head = action(base),
|
||||
diff = iD.Difference(base, head),
|
||||
created = diff.created();
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmWay({id: 'w1', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: 'w2', nodes: ['b', 'c']})
|
||||
]);
|
||||
var action = iD.actionCopyEntities(['w1', 'w2'], base);
|
||||
var head = action(base);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
var created = diff.created();
|
||||
|
||||
expect(created).to.have.length(5);
|
||||
expect(action.copies().w1.nodes[1]).to.eql(action.copies().w2.nodes[0]);
|
||||
});
|
||||
|
||||
it('obtains source entities from an alternate graph', function () {
|
||||
var a = iD.osmNode({id: 'a'}),
|
||||
old = iD.coreGraph([a]),
|
||||
base = iD.coreGraph(),
|
||||
action = iD.actionCopyEntities(['a'], old),
|
||||
head = action(base),
|
||||
diff = iD.Difference(base, head);
|
||||
diff.created();
|
||||
var a = iD.osmNode({id: 'a'});
|
||||
var old = iD.coreGraph([a]);
|
||||
var base = iD.coreGraph();
|
||||
var action = iD.actionCopyEntities(['a'], old);
|
||||
var head = action(base);
|
||||
|
||||
expect(head.hasEntity('a')).not.to.be.ok;
|
||||
expect(Object.keys(action.copies())).to.have.length(1);
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
describe('iD.actionOrthogonalize', function () {
|
||||
var projection = d3.geoMercator();
|
||||
var projection = function (l) { return l; };
|
||||
projection.invert = projection;
|
||||
|
||||
it('orthogonalizes a perfect quad', function () {
|
||||
var graph = iD.coreGraph([
|
||||
describe('closed paths', function () {
|
||||
it('orthogonalizes a perfect quad', function () {
|
||||
// d --- c
|
||||
// | |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
@@ -10,37 +15,47 @@ describe('iD.actionOrthogonalize', function () {
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
|
||||
it('orthogonalizes a quad', function () {
|
||||
var graph = iD.coreGraph([
|
||||
it('orthogonalizes a quad', function () {
|
||||
// d --- c
|
||||
// | |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [4, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 2]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
|
||||
it('orthogonalizes a triangle', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
it('orthogonalizes a triangle', function () {
|
||||
// a
|
||||
// | \
|
||||
// | \
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(4);
|
||||
});
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(4);
|
||||
});
|
||||
|
||||
it('deletes empty redundant nodes', function() {
|
||||
var graph = iD.coreGraph([
|
||||
it('deletes empty redundant nodes', function() {
|
||||
// e - d - c
|
||||
// | |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
@@ -49,12 +64,15 @@ describe('iD.actionOrthogonalize', function () {
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.hasEntity('d')).to.eq(undefined);
|
||||
});
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.hasEntity('d')).to.eq(undefined);
|
||||
});
|
||||
|
||||
it('preserves non empty redundant nodes', function() {
|
||||
var graph = iD.coreGraph([
|
||||
it('preserves non empty redundant nodes', function() {
|
||||
// e - d - c
|
||||
// | |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
@@ -63,76 +81,597 @@ describe('iD.actionOrthogonalize', function () {
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(6);
|
||||
expect(graph.hasEntity('d')).to.not.eq(undefined);
|
||||
});
|
||||
|
||||
it('preserves the shape of skinny quads', function () {
|
||||
var tests = [
|
||||
[
|
||||
[-77.0339864831478, 38.8616391227204],
|
||||
[-77.0209775298677, 38.8613609264884],
|
||||
[-77.0210405781065, 38.8607390721519],
|
||||
[-77.0339024188294, 38.8610663645859]
|
||||
],
|
||||
[
|
||||
[-89.4706683, 40.6261177],
|
||||
[-89.4706664, 40.6260574],
|
||||
[-89.4693973, 40.6260830],
|
||||
[-89.4694012, 40.6261355]
|
||||
]
|
||||
];
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: tests[i][0]}),
|
||||
iD.osmNode({id: 'b', loc: tests[i][1]}),
|
||||
iD.osmNode({id: 'c', loc: tests[i][2]}),
|
||||
iD.osmNode({id: 'd', loc: tests[i][3]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]),
|
||||
initialWidth = iD.geoSphericalDistance(graph.entity('a').loc, graph.entity('b').loc),
|
||||
finalWidth;
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(6);
|
||||
expect(graph.hasEntity('d')).to.not.eq(undefined);
|
||||
});
|
||||
|
||||
finalWidth = iD.geoSphericalDistance(graph.entity('a').loc, graph.entity('b').loc);
|
||||
expect(finalWidth / initialWidth).within(0.90, 1.10);
|
||||
}
|
||||
});
|
||||
|
||||
it('only moves nodes which are near right or near straight', function() {
|
||||
var graph = iD.coreGraph([
|
||||
it('only moves nodes which are near right or near straight', function() {
|
||||
// f - e
|
||||
// | \
|
||||
// | d - c
|
||||
// | |
|
||||
// a -------- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3, 0.001]}),
|
||||
iD.osmNode({id: 'b', loc: [3.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [2, 1]}),
|
||||
iD.osmNode({id: 'e', loc: [1, 2]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]),
|
||||
diff = iD.Difference(graph, iD.actionOrthogonalize('-', projection)(graph));
|
||||
]);
|
||||
|
||||
expect(Object.keys(diff.changes()).sort()).to.eql(['a', 'b', 'c', 'f']);
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection)(graph));
|
||||
expect(Object.keys(diff.changes()).sort()).to.eql(['a', 'b', 'c', 'f']);
|
||||
});
|
||||
|
||||
it('does not move or remove self-intersecting nodes', function() {
|
||||
// f -- g
|
||||
// | |
|
||||
// e --- d - c
|
||||
// | |
|
||||
// a -- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [ 0, -1]}),
|
||||
iD.osmNode({id: 'b', loc: [ 1, -1]}),
|
||||
iD.osmNode({id: 'c', loc: [ 0, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [ 0.1, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [-1, 0]}),
|
||||
iD.osmNode({id: 'f', loc: [-1, 1]}),
|
||||
iD.osmNode({id: 'g', loc: [ 0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'd', 'a']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection)(graph));
|
||||
expect(diff.changes().d).to.be.undefined;
|
||||
expect(graph.hasEntity('d')).to.be.ok;
|
||||
});
|
||||
|
||||
it('preserves the shape of skinny quads', function () {
|
||||
var projection = iD.d3.geoMercator();
|
||||
var tests = [[
|
||||
[-77.0339864831478, 38.8616391227204],
|
||||
[-77.0209775298677, 38.8613609264884],
|
||||
[-77.0210405781065, 38.8607390721519],
|
||||
[-77.0339024188294, 38.8610663645859]
|
||||
], [
|
||||
[-89.4706683, 40.6261177],
|
||||
[-89.4706664, 40.6260574],
|
||||
[-89.4693973, 40.6260830],
|
||||
[-89.4694012, 40.6261355]
|
||||
]];
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: tests[i][0]}),
|
||||
iD.osmNode({id: 'b', loc: tests[i][1]}),
|
||||
iD.osmNode({id: 'c', loc: tests[i][2]}),
|
||||
iD.osmNode({id: 'd', loc: tests[i][3]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
var initialWidth = iD.geoSphericalDistance(graph.entity('a').loc, graph.entity('b').loc);
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
var finalWidth = iD.geoSphericalDistance(graph.entity('a').loc, graph.entity('b').loc);
|
||||
expect(finalWidth / initialWidth).within(0.90, 1.10);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('open paths', function () {
|
||||
it('orthogonalizes a perfect quad path', function () {
|
||||
// d --- c
|
||||
// |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(4);
|
||||
});
|
||||
|
||||
it('orthogonalizes a quad path', function () {
|
||||
// d --- c
|
||||
// |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(4);
|
||||
});
|
||||
|
||||
it('orthogonalizes a 3-point path', function () {
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(3);
|
||||
});
|
||||
|
||||
it('deletes empty redundant nodes', function() {
|
||||
// e - d - c
|
||||
// |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [1, 2]}),
|
||||
iD.osmNode({id: 'e', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.hasEntity('d')).to.be.undefined;
|
||||
});
|
||||
|
||||
it('preserves non empty redundant nodes', function() {
|
||||
// e - d - c
|
||||
// |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [1, 2], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'e', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
expect(graph.hasEntity('d')).to.be.ok;
|
||||
});
|
||||
|
||||
it('only moves non-endpoint nodes which are near right or near straight', function() {
|
||||
// f - e
|
||||
// \
|
||||
// d - c
|
||||
// |
|
||||
// a -------- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [2, 1]}),
|
||||
iD.osmNode({id: 'e', loc: [1, 2]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection)(graph));
|
||||
expect(Object.keys(diff.changes()).sort()).to.eql(['b', 'c']);
|
||||
});
|
||||
|
||||
it('does not move or remove self-intersecting nodes', function() {
|
||||
// f -- g
|
||||
// | |
|
||||
// e --- d - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'c', loc: [ 0, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [ 0.1, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [-1, 0]}),
|
||||
iD.osmNode({id: 'f', loc: [-1, 1]}),
|
||||
iD.osmNode({id: 'g', loc: [ 0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['c', 'd', 'e', 'f', 'g', 'd']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection)(graph));
|
||||
expect(diff.changes().d).to.be.undefined;
|
||||
expect(graph.hasEntity('d')).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('vertices', function () {
|
||||
it('orthogonalizes a single vertex in a quad', function () {
|
||||
// d --- c
|
||||
// | |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection, 'b')(graph));
|
||||
expect(diff.changes().a).to.be.undefined;
|
||||
expect(diff.changes().b).to.be.not.undefined;
|
||||
expect(diff.changes().c).to.be.undefined;
|
||||
expect(diff.changes().d).to.be.undefined;
|
||||
});
|
||||
|
||||
it('orthogonalizes a single vertex in a triangle', function () {
|
||||
// a
|
||||
// | \
|
||||
// | \
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'a']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection, 'b')(graph));
|
||||
expect(diff.changes().a).to.be.undefined;
|
||||
expect(diff.changes().b).to.be.not.undefined;
|
||||
expect(diff.changes().c).to.be.undefined;
|
||||
});
|
||||
|
||||
it('orthogonalizes a single vertex in a quad path', function () {
|
||||
// d --- c
|
||||
// |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection, 'b')(graph));
|
||||
expect(diff.changes().a).to.be.undefined;
|
||||
expect(diff.changes().b).to.be.not.undefined;
|
||||
expect(diff.changes().c).to.be.undefined;
|
||||
expect(diff.changes().d).to.be.undefined;
|
||||
});
|
||||
|
||||
it('orthogonalizes a single vertex in a 3-point path', function () {
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
var diff = iD.coreDifference(graph, iD.actionOrthogonalize('-', projection, 'b')(graph));
|
||||
expect(diff.changes().a).to.be.undefined;
|
||||
expect(diff.changes().b).to.be.not.undefined;
|
||||
expect(diff.changes().c).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#disabled', function () {
|
||||
|
||||
describe('closed paths', function () {
|
||||
|
||||
it('returns "square_enough" for a perfect quad', function () {
|
||||
// d ---- c
|
||||
// | |
|
||||
// a ---- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.eql('square_enough');
|
||||
});
|
||||
|
||||
it('returns false for unsquared quad', function () {
|
||||
// d --- c
|
||||
// | |
|
||||
// a ---- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false for unsquared triangle', function () {
|
||||
// a
|
||||
// | \
|
||||
// | \
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false for perfectly square shape with redundant nodes', function () {
|
||||
// e - d - c
|
||||
// | |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [1, 2]}),
|
||||
iD.osmNode({id: 'e', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns "not_squarish" for shape that can not be squared', function () {
|
||||
// e -- d
|
||||
// / \
|
||||
// f c
|
||||
// \ /
|
||||
// a -- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [1, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [4, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 4]}),
|
||||
iD.osmNode({id: 'e', loc: [1, 4]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.eql('not_squarish');
|
||||
});
|
||||
|
||||
it('returns false for non-square self-intersecting shapes', function() {
|
||||
// f -- g
|
||||
// | |
|
||||
// e --- d - c
|
||||
// | |
|
||||
// a -- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [ 0, -1]}),
|
||||
iD.osmNode({id: 'b', loc: [ 1, -1]}),
|
||||
iD.osmNode({id: 'c', loc: [ 0, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [ 0.1, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [-1, 0]}),
|
||||
iD.osmNode({id: 'f', loc: [-1, 1]}),
|
||||
iD.osmNode({id: 'g', loc: [ 0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'd', 'a']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('open paths', function () {
|
||||
|
||||
it('returns "square_enough" for a perfect quad', function () {
|
||||
// d ---- c
|
||||
// |
|
||||
// a ---- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.eql('square_enough');
|
||||
});
|
||||
|
||||
it('returns false for unsquared quad', function () {
|
||||
// d --- c
|
||||
// |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false for unsquared 3-point path', function () {
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0, 0.1]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false for perfectly square shape with redundant nodes', function () {
|
||||
// e - d - c
|
||||
// |
|
||||
// a ----- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [1, 2]}),
|
||||
iD.osmNode({id: 'e', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns "not_squarish" for path that can not be squared', function () {
|
||||
// e -- d
|
||||
// / \
|
||||
// f c
|
||||
// /
|
||||
// a -- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [1, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [4, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 4]}),
|
||||
iD.osmNode({id: 'e', loc: [1, 4]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.eql('not_squarish');
|
||||
});
|
||||
|
||||
it('returns false for non-square self-intersecting paths', function() {
|
||||
// f -- g
|
||||
// | |
|
||||
// e --- d - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'c', loc: [ 0, 1]}),
|
||||
iD.osmNode({id: 'd', loc: [ 0.1, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [-1, 0]}),
|
||||
iD.osmNode({id: 'f', loc: [-1, 1]}),
|
||||
iD.osmNode({id: 'g', loc: [ 0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['c', 'd', 'e', 'f', 'g', 'd']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection).disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('vertex-only', function () {
|
||||
|
||||
it('returns "square_enough" for a vertex in a perfect quad', function () {
|
||||
// d ---- c
|
||||
// |
|
||||
// a ---- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection, 'b').disabled(graph);
|
||||
expect(result).to.eql('square_enough');
|
||||
});
|
||||
|
||||
it('returns false for a vertex in an unsquared quad', function () {
|
||||
// d --- c
|
||||
// |
|
||||
// a --- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [2.1, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [2, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection, 'b').disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false for a vertex in an unsquared 3-point path', function () {
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// b - c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 3]}),
|
||||
iD.osmNode({id: 'b', loc: [0, 0.1]}),
|
||||
iD.osmNode({id: 'c', loc: [3, 0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection, 'b').disabled(graph);
|
||||
expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('returns "not_squarish" for vertex that can not be squared', function () {
|
||||
// e -- d
|
||||
// / \
|
||||
// f c
|
||||
// /
|
||||
// a -- b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [1, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'c', loc: [4, 2]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 4]}),
|
||||
iD.osmNode({id: 'e', loc: [1, 4]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f']})
|
||||
]);
|
||||
|
||||
var result = iD.actionOrthogonalize('-', projection, 'b').disabled(graph);
|
||||
expect(result).to.eql('not_squarish');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('transitions', function () {
|
||||
it('is transitionable', function() {
|
||||
expect(iD.actionOrthogonalize().transitionable).to.be.true;
|
||||
});
|
||||
|
||||
// for all of these:
|
||||
//
|
||||
// f ------------ e
|
||||
// | |
|
||||
// a -- b -- c -- d
|
||||
|
||||
it('orthogonalize at t = 0', function() {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 0);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd', 'e', 'f', 'a']);
|
||||
@@ -145,14 +684,14 @@ describe('iD.actionOrthogonalize', function () {
|
||||
|
||||
it('orthogonalize at t = 0.5', function() {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 0.5);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd', 'e', 'f', 'a']);
|
||||
@@ -164,14 +703,14 @@ describe('iD.actionOrthogonalize', function () {
|
||||
|
||||
it('orthogonalize at t = 1', function() {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
iD.osmNode({id: 'a', loc: [0, 0]}),
|
||||
iD.osmNode({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.osmNode({id: 'c', loc: [2, -0.01]}),
|
||||
iD.osmNode({id: 'd', loc: [3, 0]}),
|
||||
iD.osmNode({id: 'e', loc: [3, 1]}),
|
||||
iD.osmNode({id: 'f', loc: [0, 1]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 1);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd', 'e', 'f', 'a']);
|
||||
|
||||
+177
-177
@@ -1,152 +1,152 @@
|
||||
describe('iD.Difference', function () {
|
||||
describe('iD.coreDifference', function () {
|
||||
describe('#changes', function () {
|
||||
it('includes created entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({n: {base: undefined, head: node}});
|
||||
});
|
||||
|
||||
it('includes undone created entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node),
|
||||
diff = iD.Difference(head, base);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node);
|
||||
var diff = iD.coreDifference(head, base);
|
||||
expect(diff.changes()).to.eql({n: {base: node, head: undefined}});
|
||||
});
|
||||
|
||||
it('includes modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.update({ tags: { yes: 'no' } }),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.update({ tags: { yes: 'no' } });
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({n: {base: n1, head: n2}});
|
||||
});
|
||||
|
||||
it('includes undone modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.update({ tags: { yes: 'no' } }),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(head, base);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.update({ tags: { yes: 'no' } });
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(head, base);
|
||||
expect(diff.changes()).to.eql({n: {base: n2, head: n1}});
|
||||
});
|
||||
|
||||
it('doesn\'t include updated but identical entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.update(),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.update();
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({});
|
||||
});
|
||||
|
||||
it('includes deleted entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({n: {base: node, head: undefined}});
|
||||
});
|
||||
|
||||
it('includes undone deleted entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node),
|
||||
diff = iD.Difference(head, base);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node);
|
||||
var diff = iD.coreDifference(head, base);
|
||||
expect(diff.changes()).to.eql({n: {base: undefined, head: node}});
|
||||
});
|
||||
|
||||
it('doesn\'t include created entities that were subsequently deleted', function () {
|
||||
var node = iD.osmNode(),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node).remove(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode();
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node).remove(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({});
|
||||
});
|
||||
|
||||
it('doesn\'t include created entities that were subsequently reverted', function () {
|
||||
var node = iD.osmNode({id: 'n-1'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node).revert('n-1'),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n-1'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node).revert('n-1');
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({});
|
||||
});
|
||||
|
||||
it('doesn\'t include modified entities that were subsequently reverted', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.update({ tags: { yes: 'no' } }),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2).revert('n'),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.update({ tags: { yes: 'no' } });
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2).revert('n');
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({});
|
||||
});
|
||||
|
||||
it('doesn\'t include deleted entities that were subsequently reverted', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node).revert('n'),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node).revert('n');
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.changes()).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#extantIDs', function () {
|
||||
it('includes the ids of created entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.extantIDs()).to.eql(['n']);
|
||||
});
|
||||
|
||||
it('includes the ids of modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.extantIDs()).to.eql(['n']);
|
||||
});
|
||||
|
||||
it('omits the ids of deleted entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.extantIDs()).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#created', function () {
|
||||
it('returns an array of created entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.created()).to.eql([node]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#modified', function () {
|
||||
it('returns an array of modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.modified()).to.eql([n2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#deleted', function () {
|
||||
it('returns an array of deleted entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.deleted()).to.eql([node]);
|
||||
});
|
||||
});
|
||||
@@ -160,9 +160,9 @@ describe('iD.Difference', function () {
|
||||
]);
|
||||
|
||||
it('reports a created way as created', function() {
|
||||
var way = iD.osmWay({id: '+'}),
|
||||
head = base.replace(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var way = iD.osmWay({id: '+'});
|
||||
var head = base.replace(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'created',
|
||||
@@ -172,9 +172,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a deleted way as deleted', function() {
|
||||
var way = base.entity('-'),
|
||||
head = base.remove(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var way = base.entity('-');
|
||||
var head = base.remove(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'deleted',
|
||||
@@ -184,9 +184,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a modified way as modified', function() {
|
||||
var way = base.entity('-').mergeTags({highway: 'primary'}),
|
||||
head = base.replace(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var way = base.entity('-').mergeTags({highway: 'primary'});
|
||||
var head = base.replace(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -196,9 +196,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a way as modified when a member vertex is moved', function() {
|
||||
var vertex = base.entity('b').move([0,3]),
|
||||
head = base.replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('b').move([0,3]);
|
||||
var head = base.replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -208,10 +208,10 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a way as modified when a member vertex is added', function() {
|
||||
var vertex = iD.osmNode({id: 'c'}),
|
||||
way = base.entity('-').addNode('c'),
|
||||
head = base.replace(vertex).replace(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = iD.osmNode({id: 'c'});
|
||||
var way = base.entity('-').addNode('c');
|
||||
var head = base.replace(vertex).replace(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -221,9 +221,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a way as modified when a member vertex is removed', function() {
|
||||
var way = base.entity('-').removeNode('b'),
|
||||
head = base.replace(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var way = base.entity('-').removeNode('b');
|
||||
var head = base.replace(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -233,10 +233,10 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a created way containing a moved vertex as being created', function() {
|
||||
var vertex = base.entity('b').move([0,3]),
|
||||
way = iD.osmWay({id: '+', nodes: ['b']}),
|
||||
head = base.replace(way).replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('b').move([0,3]);
|
||||
var way = iD.osmWay({id: '+', nodes: ['b']});
|
||||
var head = base.replace(way).replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'created',
|
||||
@@ -250,10 +250,10 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a created way with a created vertex as being created', function() {
|
||||
var vertex = iD.osmNode({id: 'c'}),
|
||||
way = iD.osmWay({id: '+', nodes: ['c']}),
|
||||
head = base.replace(vertex).replace(way),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = iD.osmNode({id: 'c'});
|
||||
var way = iD.osmWay({id: '+', nodes: ['c']});
|
||||
var head = base.replace(vertex).replace(way);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'created',
|
||||
@@ -263,9 +263,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a vertex as modified when it has tags and they are changed', function() {
|
||||
var vertex = base.entity('a').mergeTags({highway: 'traffic_signals'}),
|
||||
head = base.replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('a').mergeTags({highway: 'traffic_signals'});
|
||||
var head = base.replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -275,9 +275,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a vertex as modified when it has tags and is moved', function() {
|
||||
var vertex = base.entity('a').move([1, 2]),
|
||||
head = base.replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('a').move([1, 2]);
|
||||
var head = base.replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -291,9 +291,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('does not report a vertex as modified when it is moved and has no-op tag changes', function() {
|
||||
var vertex = base.entity('b').update({tags: {}, loc: [1, 2]}),
|
||||
head = base.replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('b').update({tags: {}, loc: [1, 2]});
|
||||
var head = base.replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -303,9 +303,9 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a vertex as deleted when it had tags', function() {
|
||||
var vertex = base.entity('v'),
|
||||
head = base.remove(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = base.entity('v');
|
||||
var head = base.remove(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'deleted',
|
||||
@@ -315,10 +315,10 @@ describe('iD.Difference', function () {
|
||||
});
|
||||
|
||||
it('reports a vertex as created when it has tags', function() {
|
||||
var vertex = iD.osmNode({id: 'c', tags: {crossing: 'marked'}}),
|
||||
way = base.entity('-').addNode('c'),
|
||||
head = base.replace(way).replace(vertex),
|
||||
diff = iD.Difference(base, head);
|
||||
var vertex = iD.osmNode({id: 'c', tags: {crossing: 'marked'}});
|
||||
var way = base.entity('-').addNode('c');
|
||||
var head = base.replace(way).replace(vertex);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.summary()).to.eql([{
|
||||
changeType: 'modified',
|
||||
@@ -334,107 +334,107 @@ describe('iD.Difference', function () {
|
||||
|
||||
describe('#complete', function () {
|
||||
it('includes created entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph(),
|
||||
head = base.replace(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph();
|
||||
var head = base.replace(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.complete().n).to.equal(node);
|
||||
});
|
||||
|
||||
it('includes modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
base = iD.coreGraph([n1]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var base = iD.coreGraph([n1]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.complete().n).to.equal(n2);
|
||||
});
|
||||
|
||||
it('includes deleted entities', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
base = iD.coreGraph([node]),
|
||||
head = base.remove(node),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var base = iD.coreGraph([node]);
|
||||
var head = base.remove(node);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
expect(diff.complete()).to.eql({n: undefined});
|
||||
});
|
||||
|
||||
it('includes nodes added to a way', function () {
|
||||
var n1 = iD.osmNode({id: 'n1'}),
|
||||
n2 = iD.osmNode({id: 'n2'}),
|
||||
w1 = iD.osmWay({id: 'w', nodes: ['n1']}),
|
||||
w2 = w1.addNode('n2'),
|
||||
base = iD.coreGraph([n1, n2, w1]),
|
||||
head = base.replace(w2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n1'});
|
||||
var n2 = iD.osmNode({id: 'n2'});
|
||||
var w1 = iD.osmWay({id: 'w', nodes: ['n1']});
|
||||
var w2 = w1.addNode('n2');
|
||||
var base = iD.coreGraph([n1, n2, w1]);
|
||||
var head = base.replace(w2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().n2).to.equal(n2);
|
||||
});
|
||||
|
||||
it('includes nodes removed from a way', function () {
|
||||
var n1 = iD.osmNode({id: 'n1'}),
|
||||
n2 = iD.osmNode({id: 'n2'}),
|
||||
w1 = iD.osmWay({id: 'w', nodes: ['n1', 'n2']}),
|
||||
w2 = w1.removeNode('n2'),
|
||||
base = iD.coreGraph([n1, n2, w1]),
|
||||
head = base.replace(w2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n1'});
|
||||
var n2 = iD.osmNode({id: 'n2'});
|
||||
var w1 = iD.osmWay({id: 'w', nodes: ['n1', 'n2']});
|
||||
var w2 = w1.removeNode('n2');
|
||||
var base = iD.coreGraph([n1, n2, w1]);
|
||||
var head = base.replace(w2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().n2).to.equal(n2);
|
||||
});
|
||||
|
||||
it('includes parent ways of modified nodes', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
way = iD.osmWay({id: 'w', nodes: ['n']}),
|
||||
base = iD.coreGraph([n1, way]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var way = iD.osmWay({id: 'w', nodes: ['n']});
|
||||
var base = iD.coreGraph([n1, way]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().w).to.equal(way);
|
||||
});
|
||||
|
||||
it('includes parent relations of modified entities', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
rel = iD.osmRelation({id: 'r', members: [{id: 'n'}]}),
|
||||
base = iD.coreGraph([n1, rel]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var rel = iD.osmRelation({id: 'r', members: [{id: 'n'}]});
|
||||
var base = iD.coreGraph([n1, rel]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().r).to.equal(rel);
|
||||
});
|
||||
|
||||
it('includes parent relations of modified entities, recursively', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
rel1 = iD.osmRelation({id: 'r1', members: [{id: 'n'}]}),
|
||||
rel2 = iD.osmRelation({id: 'r2', members: [{id: 'r1'}]}),
|
||||
base = iD.coreGraph([n1, rel1, rel2]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var rel1 = iD.osmRelation({id: 'r1', members: [{id: 'n'}]});
|
||||
var rel2 = iD.osmRelation({id: 'r2', members: [{id: 'r1'}]});
|
||||
var base = iD.coreGraph([n1, rel1, rel2]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().r2).to.equal(rel2);
|
||||
});
|
||||
|
||||
it('includes parent relations of parent ways of modified nodes', function () {
|
||||
var n1 = iD.osmNode({id: 'n'}),
|
||||
n2 = n1.move([1, 2]),
|
||||
way = iD.osmWay({id: 'w', nodes: ['n']}),
|
||||
rel = iD.osmRelation({id: 'r', members: [{id: 'w'}]}),
|
||||
base = iD.coreGraph([n1, way, rel]),
|
||||
head = base.replace(n2),
|
||||
diff = iD.Difference(base, head);
|
||||
var n1 = iD.osmNode({id: 'n'});
|
||||
var n2 = n1.move([1, 2]);
|
||||
var way = iD.osmWay({id: 'w', nodes: ['n']});
|
||||
var rel = iD.osmRelation({id: 'r', members: [{id: 'w'}]});
|
||||
var base = iD.coreGraph([n1, way, rel]);
|
||||
var head = base.replace(n2);
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete().r).to.equal(rel);
|
||||
});
|
||||
|
||||
it('copes with recursive relations', function () {
|
||||
var node = iD.osmNode({id: 'n'}),
|
||||
rel1 = iD.osmRelation({id: 'r1', members: [{id: 'n'}, {id: 'r2'}]}),
|
||||
rel2 = iD.osmRelation({id: 'r2', members: [{id: 'r1'}]}),
|
||||
base = iD.coreGraph([node, rel1, rel2]),
|
||||
head = base.replace(node.move([1, 2])),
|
||||
diff = iD.Difference(base, head);
|
||||
var node = iD.osmNode({id: 'n'});
|
||||
var rel1 = iD.osmRelation({id: 'r1', members: [{id: 'n'}, {id: 'r2'}]});
|
||||
var rel2 = iD.osmRelation({id: 'r2', members: [{id: 'r1'}]});
|
||||
var base = iD.coreGraph([node, rel1, rel2]);
|
||||
var head = base.replace(node.move([1, 2]));
|
||||
var diff = iD.coreDifference(base, head);
|
||||
|
||||
expect(diff.complete()).to.be.ok;
|
||||
});
|
||||
|
||||
@@ -76,6 +76,17 @@ describe('iD.geo - vector', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecNormalize', function() {
|
||||
it('gets unit vectors', function() {
|
||||
expect(iD.geoVecNormalize([0, 0])).to.eql([0, 0]);
|
||||
expect(iD.geoVecNormalize([1, 0])).to.eql([1, 0]);
|
||||
expect(iD.geoVecNormalize([5, 0])).to.eql([1, 0]);
|
||||
expect(iD.geoVecNormalize([-5, 0])).to.eql([-1, 0]);
|
||||
expect(iD.geoVecNormalize([1, 1])[0]).to.be.closeTo(Math.sqrt(2)/2, 1e-6);
|
||||
expect(iD.geoVecNormalize([1, 1])[1]).to.be.closeTo(Math.sqrt(2)/2, 1e-6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecAngle', function() {
|
||||
it('returns angle between a and b', function() {
|
||||
expect(iD.geoVecAngle([0, 0], [1, 0])).to.be.closeTo(0, 1e-6);
|
||||
@@ -98,6 +109,24 @@ describe('iD.geo - vector', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecNormalizedDot', function() {
|
||||
it('normalized dot product of right angle is zero', function() {
|
||||
var a = [2, 0];
|
||||
var b = [0, 2];
|
||||
expect(iD.geoVecNormalizedDot(a, b)).to.eql(0);
|
||||
});
|
||||
it('normalized dot product of same vector multiplies unit vectors', function() {
|
||||
var a = [2, 0];
|
||||
var b = [2, 0];
|
||||
expect(iD.geoVecNormalizedDot(a, b)).to.eql(1);
|
||||
});
|
||||
it('normalized dot product of 45 degrees', function() {
|
||||
var a = [0, 2];
|
||||
var b = [2, 2];
|
||||
expect(iD.geoVecNormalizedDot(a, b)).to.be.closeTo(Math.sqrt(2)/2, 1e-6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoVecCross', function() {
|
||||
it('2D cross product of right hand turn is positive', function() {
|
||||
var a = [2, 0];
|
||||
@@ -116,4 +145,49 @@ describe('iD.geo - vector', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('geoVecProject', function() {
|
||||
it('returns null for a degenerate path (no nodes)', function() {
|
||||
expect(iD.geoVecProject([0, 1], [])).to.be.null;
|
||||
});
|
||||
|
||||
it('returns null for a degenerate path (single node)', function() {
|
||||
expect(iD.geoVecProject([0, 1], [0, 0])).to.be.null;
|
||||
});
|
||||
|
||||
it('calculates the orthogonal projection of a point onto a path', function() {
|
||||
// c
|
||||
// |
|
||||
// a --*--- b
|
||||
//
|
||||
// * = [2, 0]
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [2, 1];
|
||||
var choice = iD.geoVecProject(c, [a, b]);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(1);
|
||||
expect(choice.target).to.eql([2, 0]);
|
||||
});
|
||||
|
||||
it('returns the starting vertex when the orthogonal projection is < 0', function() {
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [-3, 4];
|
||||
var choice = iD.geoVecProject(c, [a, b]);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(5);
|
||||
expect(choice.target).to.eql([0, 0]);
|
||||
});
|
||||
|
||||
it('returns the ending vertex when the orthogonal projection is > 1', function() {
|
||||
var a = [0, 0];
|
||||
var b = [5, 0];
|
||||
var c = [8, 4];
|
||||
var choice = iD.geoVecProject(c, [a, b]);
|
||||
expect(choice.index).to.eql(1);
|
||||
expect(choice.distance).to.eql(5);
|
||||
expect(choice.target).to.eql([5, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user