mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-16 22:03:37 +02:00
Add support for complex intersection and via way restrictions
This commit is contained in:
+1
-1
@@ -1836,7 +1836,7 @@ input[type=number] {
|
||||
|
||||
.form-field-restrictions .preset-input-wrap {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.form-field-restrictions svg.surface {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { actionSplit } from './split';
|
||||
|
||||
import {
|
||||
osmInferRestriction,
|
||||
osmRelation,
|
||||
osmWay
|
||||
osmRelation
|
||||
} from '../osm';
|
||||
|
||||
|
||||
@@ -11,21 +8,20 @@ import {
|
||||
//
|
||||
// {
|
||||
// from: { node: <node ID>, way: <way ID> },
|
||||
// via: { node: <node ID> },
|
||||
// via: { node: <node ID>, ways: [<way ID>,<way ID>,...] },
|
||||
// to: { node: <node ID>, way: <way ID> },
|
||||
// restriction: <'no_right_turn', 'no_left_turn', etc.>
|
||||
// }
|
||||
//
|
||||
// This specifies a restriction of type `restriction` when traveling from
|
||||
// `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
|
||||
// `from.node` in `from.way` toward `to.node` in `to.way` via `via.node` OR `via.ways`.
|
||||
// (The action does not check that these entities form a valid intersection.)
|
||||
//
|
||||
// If `restriction` is not provided, it is automatically determined by
|
||||
// osmInferRestriction.
|
||||
//
|
||||
// If necessary, the `from` and `to` ways are split. In these cases, `from.node`
|
||||
// and `to.node` are used to determine which portion of the split ways become
|
||||
// members of the restriction.
|
||||
// From, to, and via ways should be split before calling this action.
|
||||
// (old versions of the code would split the ways here, but we no longer do it)
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the new relation.
|
||||
// Normally, this will be undefined and the relation will automatically
|
||||
@@ -34,47 +30,23 @@ import {
|
||||
export function actionRestrictTurn(turn, projection, restrictionId) {
|
||||
|
||||
return function(graph) {
|
||||
var from = graph.entity(turn.from.way),
|
||||
via = graph.entity(turn.via.node),
|
||||
to = graph.entity(turn.to.way);
|
||||
var fromWay = graph.entity(turn.from.way);
|
||||
var toWay = graph.entity(turn.to.way);
|
||||
var viaNode = turn.via.node && graph.entity(turn.via.node);
|
||||
var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); });
|
||||
var members = [];
|
||||
|
||||
function isClosingNode(way, nodeId) {
|
||||
return nodeId === way.first() && nodeId === way.last();
|
||||
members.push({ id: fromWay.id, type: 'way', role: 'from' });
|
||||
|
||||
if (viaNode) {
|
||||
members.push({ id: viaNode.id, type: 'node', role: 'via' });
|
||||
} else if (viaWays) {
|
||||
viaWays.forEach(function(viaWay) {
|
||||
members.push({ id: viaWay.id, type: 'way', role: 'via' });
|
||||
});
|
||||
}
|
||||
|
||||
function split(toOrFrom) {
|
||||
var newID = toOrFrom.newID || osmWay().id;
|
||||
graph = actionSplit(via.id, [newID])
|
||||
.limitWays([toOrFrom.way])(graph);
|
||||
|
||||
var a = graph.entity(newID),
|
||||
b = graph.entity(toOrFrom.way);
|
||||
|
||||
if (a.nodes.indexOf(toOrFrom.node) !== -1) {
|
||||
return [a, b];
|
||||
} else {
|
||||
return [b, a];
|
||||
}
|
||||
}
|
||||
|
||||
if (!from.affix(via.id) || isClosingNode(from, via.id)) {
|
||||
if (turn.from.node === turn.to.node) {
|
||||
// U-turn
|
||||
from = to = split(turn.from)[0];
|
||||
} else if (turn.from.way === turn.to.way) {
|
||||
// Straight-on or circular
|
||||
var s = split(turn.from);
|
||||
from = s[0];
|
||||
to = s[1];
|
||||
} else {
|
||||
// Other
|
||||
from = split(turn.from)[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!to.affix(via.id) || isClosingNode(to, via.id)) {
|
||||
to = split(turn.to)[0];
|
||||
}
|
||||
members.push({ id: toWay.id, type: 'way', role: 'to' });
|
||||
|
||||
return graph.replace(osmRelation({
|
||||
id: restrictionId,
|
||||
@@ -84,15 +56,10 @@ export function actionRestrictTurn(turn, projection, restrictionId) {
|
||||
osmInferRestriction(
|
||||
graph,
|
||||
turn.from,
|
||||
turn.via,
|
||||
turn.to,
|
||||
projection)
|
||||
},
|
||||
members: [
|
||||
{id: from.id, type: 'way', role: 'from'},
|
||||
{id: via.id, type: 'node', role: 'via'},
|
||||
{id: to.id, type: 'way', role: 'to'}
|
||||
]
|
||||
members: members
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,7 +53,10 @@ export function actionSplit(nodeId, newWayIds) {
|
||||
}
|
||||
|
||||
function dist(nA, nB) {
|
||||
return geoSphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
var locA = graph.entity(nA).loc;
|
||||
var locB = graph.entity(nB).loc;
|
||||
var epsilon = 1e-6;
|
||||
return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon;
|
||||
}
|
||||
|
||||
// calculate lengths
|
||||
|
||||
+522
-161
@@ -1,195 +1,556 @@
|
||||
import _each from 'lodash-es/each';
|
||||
import _clone from 'lodash-es/clone';
|
||||
import _every from 'lodash-es/every';
|
||||
import _extend from 'lodash-es/extend';
|
||||
import _find from 'lodash-es/find';
|
||||
import _indexOf from 'lodash-es/indexOf';
|
||||
import _keys from 'lodash-es/keys';
|
||||
import _values from 'lodash-es/values';
|
||||
import _uniq from 'lodash-es/uniq';
|
||||
|
||||
import { geoAngle } from '../geo/index';
|
||||
import { osmWay } from './way';
|
||||
import {
|
||||
actionDeleteRelation,
|
||||
actionReverse,
|
||||
actionSplit
|
||||
} from '../actions';
|
||||
|
||||
import { coreGraph } from '../core';
|
||||
import { geoAngle, geoSphericalDistance } from '../geo';
|
||||
import { osmEntity } from './entity';
|
||||
|
||||
|
||||
export function osmTurn(turn) {
|
||||
if (!(this instanceof osmTurn))
|
||||
if (!(this instanceof osmTurn)) {
|
||||
return new osmTurn(turn);
|
||||
}
|
||||
_extend(this, turn);
|
||||
}
|
||||
|
||||
|
||||
export function osmIntersection(graph, vertexId) {
|
||||
var vertex = graph.entity(vertexId),
|
||||
parentWays = graph.parentWays(vertex),
|
||||
coincident = [],
|
||||
highways = {};
|
||||
export function osmIntersection(graph, startVertexId) {
|
||||
var vgraph = coreGraph(), // virtual graph
|
||||
i, j, k;
|
||||
|
||||
function addHighway(way, adjacentNodeId) {
|
||||
if (highways[adjacentNodeId]) {
|
||||
coincident.push(adjacentNodeId);
|
||||
} else {
|
||||
highways[adjacentNodeId] = way;
|
||||
|
||||
function memberOfRestriction(entity) {
|
||||
return graph.parentRelations(entity)
|
||||
.some(function(r) { return r.isRestriction(); });
|
||||
}
|
||||
|
||||
function isRoad(way) {
|
||||
if (way.isArea() || way.isDegenerate()) return false;
|
||||
var roads = {
|
||||
'motorway': true,
|
||||
'motorway_link': true,
|
||||
'trunk': true,
|
||||
'trunk_link': true,
|
||||
'primary': true,
|
||||
'primary_link': true,
|
||||
'secondary': true,
|
||||
'secondary_link': true,
|
||||
'tertiary': true,
|
||||
'tertiary_link': true,
|
||||
'residential': true,
|
||||
'unclassified': true,
|
||||
'living_street': true,
|
||||
'service': true,
|
||||
'road': true,
|
||||
'track': true
|
||||
};
|
||||
return roads[way.tags.highway];
|
||||
}
|
||||
|
||||
|
||||
var distCutoff = 20; // meters
|
||||
var checkVertices = [graph.entity(startVertexId)];
|
||||
var checkWays;
|
||||
var vertices = [];
|
||||
var vertexIds = [];
|
||||
var vertex;
|
||||
var ways = [];
|
||||
var wayIds = [];
|
||||
var way;
|
||||
var nodes = [];
|
||||
var node;
|
||||
var parents = [];
|
||||
var parent;
|
||||
|
||||
// `actions` will store whatever actions must be performed to satisfy
|
||||
// preconditions for adding a turn restriction to this intersection.
|
||||
// - Remove any existing degenerate turn restrictions (missing from/to, etc)
|
||||
// - Reverse oneways so that they are drawn in the forward direction
|
||||
// - Split ways on key vertices
|
||||
var actions = [];
|
||||
|
||||
|
||||
// STEP 1: walk the graph outwards from starting vertex to search
|
||||
// for more key vertices and ways to include in the intersection..
|
||||
|
||||
while (checkVertices.length) {
|
||||
vertex = checkVertices.pop();
|
||||
|
||||
// check this vertex for parent ways that are roads
|
||||
checkWays = graph.parentWays(vertex);
|
||||
var hasWays = false;
|
||||
for (i = 0; i < checkWays.length; i++) {
|
||||
way = checkWays[i];
|
||||
if (!isRoad(way) && !memberOfRestriction(way)) continue;
|
||||
|
||||
ways.push(way); // it's a road, or it's already in a turn restriction
|
||||
hasWays = true;
|
||||
|
||||
// check the way's children for more key vertices
|
||||
nodes = _uniq(graph.childNodes(way));
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
node = nodes[j];
|
||||
if (node === vertex) continue; // same thing
|
||||
if (vertices.indexOf(node) !== -1) continue; // seen it already
|
||||
if (node.loc && vertex.loc && geoSphericalDistance(node.loc, vertex.loc) > distCutoff) continue; // too far
|
||||
|
||||
// a key vertex will have parents that are also roads
|
||||
var hasParents = false;
|
||||
parents = graph.parentWays(node);
|
||||
for (k = 0; k < parents.length; k++) {
|
||||
parent = parents[k];
|
||||
if (parent === way) continue; // same thing
|
||||
if (ways.indexOf(parent) !== -1) continue; // seen it already
|
||||
if (!isRoad(parent)) continue; // not a road
|
||||
hasParents = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasParents) {
|
||||
checkVertices.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWays) {
|
||||
vertices.push(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-split ways that would need to be split in
|
||||
// order to add a restriction. The real split will
|
||||
// happen when the restriction is added.
|
||||
parentWays.forEach(function(way) {
|
||||
if (!way.tags.highway || way.isArea() || way.isDegenerate())
|
||||
return;
|
||||
|
||||
var isFirst = (vertexId === way.first()),
|
||||
isLast = (vertexId === way.last()),
|
||||
isAffix = (isFirst || isLast),
|
||||
isClosingNode = (isFirst && isLast);
|
||||
|
||||
if (isAffix && !isClosingNode) {
|
||||
var index = (isFirst ? 1 : way.nodes.length - 2);
|
||||
addHighway(way, way.nodes[index]);
|
||||
|
||||
} else {
|
||||
var splitIndex, wayA, wayB, indexA, indexB;
|
||||
if (isClosingNode) {
|
||||
splitIndex = Math.ceil(way.nodes.length / 2); // split at midpoint
|
||||
wayA = osmWay({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
|
||||
wayB = osmWay({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = 1;
|
||||
indexB = way.nodes.length - 2;
|
||||
} else {
|
||||
splitIndex = _indexOf(way.nodes, vertex.id, 1); // split at vertexid
|
||||
wayA = osmWay({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
|
||||
wayB = osmWay({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = splitIndex - 1;
|
||||
indexB = splitIndex + 1;
|
||||
}
|
||||
graph = graph.replace(wayA).replace(wayB);
|
||||
addHighway(wayA, way.nodes[indexA]);
|
||||
addHighway(wayB, way.nodes[indexB]);
|
||||
}
|
||||
});
|
||||
|
||||
// remove any ways from this intersection that are coincident
|
||||
// (i.e. any adjacent node used by more than one intersecting way)
|
||||
coincident.forEach(function (n) {
|
||||
delete highways[n];
|
||||
});
|
||||
vertices = _uniq(vertices);
|
||||
ways = _uniq(ways);
|
||||
|
||||
|
||||
var intersection = {
|
||||
highways: highways,
|
||||
ways: _values(highways),
|
||||
graph: graph
|
||||
};
|
||||
|
||||
|
||||
intersection.adjacentNodeId = function(fromWayId) {
|
||||
return _find(_keys(highways), function(k) {
|
||||
return highways[k].id === fromWayId;
|
||||
// STEP 2: Build a virtual graph containing only the entities in the intersection..
|
||||
// Everything done after this step should act on the virtual graph
|
||||
// Any actions that must be performed later to the main graph go in `actions` array
|
||||
ways.forEach(function(way) {
|
||||
graph.childNodes(way).forEach(function(node) {
|
||||
vgraph = vgraph.replace(node);
|
||||
});
|
||||
};
|
||||
|
||||
vgraph = vgraph.replace(way);
|
||||
|
||||
intersection.turns = function(fromNodeId) {
|
||||
var start = highways[fromNodeId];
|
||||
if (!start)
|
||||
return [];
|
||||
|
||||
if (start.first() === vertex.id && start.tags.oneway === 'yes')
|
||||
return [];
|
||||
if (start.last() === vertex.id && start.tags.oneway === '-1')
|
||||
return [];
|
||||
|
||||
function withRestriction(turn) {
|
||||
graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
|
||||
if (relation.tags.type !== 'restriction')
|
||||
return;
|
||||
|
||||
var f = relation.memberByRole('from'),
|
||||
t = relation.memberByRole('to'),
|
||||
v = relation.memberByRole('via');
|
||||
|
||||
if (f && f.id === turn.from.way &&
|
||||
v && v.id === turn.via.node &&
|
||||
t && t.id === turn.to.way) {
|
||||
turn.restriction = relation.id;
|
||||
} else if (/^only_/.test(relation.tags.restriction) &&
|
||||
f && f.id === turn.from.way &&
|
||||
v && v.id === turn.via.node &&
|
||||
t && t.id !== turn.to.way) {
|
||||
turn.restriction = relation.id;
|
||||
turn.indirect_restriction = true;
|
||||
graph.parentRelations(way).forEach(function(relation) {
|
||||
if (relation.isRestriction()) {
|
||||
if (relation.isValidRestriction(graph)) {
|
||||
vgraph = vgraph.replace(relation);
|
||||
} else if (relation.isComplete(graph)) {
|
||||
actions.push(actionDeleteRelation(relation.id));
|
||||
}
|
||||
});
|
||||
|
||||
return osmTurn(turn);
|
||||
}
|
||||
|
||||
|
||||
var from = {
|
||||
node: fromNodeId,
|
||||
way: start.id.split(/-(a|b)/)[0]
|
||||
},
|
||||
via = { node: vertex.id },
|
||||
turns = [];
|
||||
|
||||
_each(highways, function(end, adjacentNodeId) {
|
||||
if (end === start)
|
||||
return;
|
||||
|
||||
// backward
|
||||
if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
|
||||
turns.push(withRestriction({
|
||||
from: from,
|
||||
via: via,
|
||||
to: {
|
||||
node: adjacentNodeId,
|
||||
way: end.id.split(/-(a|b)/)[0]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// forward
|
||||
if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
|
||||
turns.push(withRestriction({
|
||||
from: from,
|
||||
via: via,
|
||||
to: {
|
||||
node: adjacentNodeId,
|
||||
way: end.id.split(/-(a|b)/)[0]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// STEP 3: Force all oneways to be drawn in the forward direction
|
||||
ways.forEach(function(w) {
|
||||
var way = vgraph.entity(w.id);
|
||||
if (way.tags.oneway === '-1') {
|
||||
var action = actionReverse(way.id, { reverseOneway: true });
|
||||
actions.push(action);
|
||||
vgraph = action(vgraph);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// STEP 4: Split ways on key vertices
|
||||
var origCount = osmEntity.id.next.way;
|
||||
vertices.forEach(function(v) {
|
||||
// This is an odd way to do it, but we need to find all the ways that
|
||||
// will be split here, then split them one at a time to ensure that these
|
||||
// actions can be replayed on the main graph exactly in the same order.
|
||||
// (It is unintuitive, but the order of ways returned from graph.parentWays()
|
||||
// is arbitrary, depending on how the main graph and vgraph were built)
|
||||
var splitAll = actionSplit(v.id);
|
||||
if (!splitAll.disabled(vgraph)) {
|
||||
splitAll.ways(vgraph).forEach(function(way) {
|
||||
var splitOne = actionSplit(v.id).limitWays([way.id]);
|
||||
actions.push(splitOne);
|
||||
vgraph = splitOne(vgraph);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// In here is where we should also split the intersection at nearby junction.
|
||||
// for https://github.com/mapbox/iD-internal/issues/31
|
||||
// nearbyVertices.forEach(function(v) {
|
||||
// });
|
||||
|
||||
// Reasons why we reset the way id count here:
|
||||
// 1. Continuity with way ids created by the splits so that we can replay
|
||||
// these actions later if the user decides to create a turn restriction
|
||||
// 2. Avoids churning way ids just by hovering over a vertex
|
||||
// and displaying the turn restriction editor
|
||||
osmEntity.id.next.way = origCount;
|
||||
|
||||
|
||||
// STEP 5: Update arrays to point to vgraph entities
|
||||
vertexIds = vertices.map(function(v) { return v.id; });
|
||||
vertices = [];
|
||||
ways = [];
|
||||
|
||||
vertexIds.forEach(function(id) {
|
||||
var vertex = vgraph.entity(id);
|
||||
var parents = vgraph.parentWays(vertex);
|
||||
|
||||
vertices.push(vertex);
|
||||
ways = ways.concat(parents);
|
||||
});
|
||||
|
||||
vertices = _uniq(vertices);
|
||||
ways = _uniq(ways);
|
||||
|
||||
vertexIds = vertices.map(function(v) { return v.id; });
|
||||
wayIds = ways.map(function(w) { return w.id; });
|
||||
|
||||
|
||||
// STEP 6: Update the ways with some metadata that will be useful for
|
||||
// walking the intersection graph later and rendering turn arrows.
|
||||
|
||||
function withMetadata(way, vertexIds) {
|
||||
var __oneWay = way.isOneWay();
|
||||
|
||||
// which affixes are key vertices?
|
||||
var __first = (vertexIds.indexOf(way.first()) !== -1);
|
||||
var __last = (vertexIds.indexOf(way.last()) !== -1);
|
||||
|
||||
// what roles is this way eligible for?
|
||||
var __via = (__first && __last);
|
||||
var __from = ((__first && !__oneWay) || __last);
|
||||
var __to = (__first || (__last && !__oneWay));
|
||||
|
||||
return way.update({
|
||||
__first: __first,
|
||||
__last: __last,
|
||||
__from: __from,
|
||||
__via: __via,
|
||||
__to: __to,
|
||||
__oneWay: __oneWay
|
||||
});
|
||||
}
|
||||
|
||||
ways = [];
|
||||
wayIds.forEach(function(id) {
|
||||
var way = withMetadata(vgraph.entity(id), vertexIds);
|
||||
vgraph = vgraph.replace(way);
|
||||
ways.push(way);
|
||||
});
|
||||
|
||||
|
||||
// STEP 7: Simplify - This is an iterative process where we:
|
||||
// 1. Find trivial vertices with only 2 parents
|
||||
// 2. trim off the leaf way from those vertices and remove from vgraph
|
||||
|
||||
var keepGoing;
|
||||
var removeWayIds = [];
|
||||
var removeVertexIds = [];
|
||||
|
||||
do {
|
||||
keepGoing = false;
|
||||
checkVertices = vertexIds.slice();
|
||||
|
||||
for (i = 0; i < checkVertices.length; i++) {
|
||||
var vertexId = checkVertices[i];
|
||||
vertex = vgraph.hasEntity(vertexId);
|
||||
|
||||
if (!vertex) {
|
||||
vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
|
||||
removeVertexIds.push(vertexId);
|
||||
continue;
|
||||
}
|
||||
|
||||
parents = vgraph.parentWays(vertex);
|
||||
if (parents.length < 3) {
|
||||
vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
|
||||
}
|
||||
|
||||
if (parents.length === 2) { // vertex with 2 parents is trivial
|
||||
var a = parents[0];
|
||||
var b = parents[1];
|
||||
var aIsLeaf = a && !a.__via;
|
||||
var bIsLeaf = b && !b.__via;
|
||||
var leaf, survivor;
|
||||
|
||||
if (aIsLeaf && !bIsLeaf) {
|
||||
leaf = a;
|
||||
survivor = b;
|
||||
} else if (!aIsLeaf && bIsLeaf) {
|
||||
leaf = b;
|
||||
survivor = a;
|
||||
}
|
||||
|
||||
if (leaf && survivor) {
|
||||
survivor = withMetadata(survivor, vertexIds); // update survivor way
|
||||
vgraph = vgraph.replace(survivor).remove(leaf); // update graph
|
||||
removeWayIds.push(leaf.id);
|
||||
keepGoing = true;
|
||||
}
|
||||
}
|
||||
|
||||
parents = vgraph.parentWays(vertex);
|
||||
|
||||
if (parents.length < 2) { // vertex is no longer a key vertex
|
||||
vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
|
||||
removeVertexIds.push(vertexId);
|
||||
keepGoing = true;
|
||||
}
|
||||
|
||||
if (parents.length < 1) { // vertex is no longer attached to anything
|
||||
vgraph = vgraph.remove(vertex);
|
||||
}
|
||||
|
||||
}
|
||||
} while (keepGoing);
|
||||
|
||||
|
||||
vertices = vertices
|
||||
.filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; })
|
||||
.map(function(vertex) { return vgraph.entity(vertex.id); });
|
||||
ways = ways
|
||||
.filter(function(way) { return removeWayIds.indexOf(way.id) === -1; })
|
||||
.map(function(way) { return vgraph.entity(way.id); });
|
||||
|
||||
|
||||
// OK! Here is our intersection..
|
||||
var intersection = {
|
||||
graph: vgraph,
|
||||
actions: actions,
|
||||
vertices: vertices,
|
||||
ways: ways,
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Get all the valid turns through this intersection given a starting way id.
|
||||
// This operates on the virtual graph for everything.
|
||||
//
|
||||
// Basically, walk through all possible paths from starting way,
|
||||
// honoring the existing turn restrictions as we go (watch out for loops!)
|
||||
//
|
||||
// For each path found, generate and return a `osmTurn` datastructure.
|
||||
//
|
||||
intersection.turns = function(fromWayId) {
|
||||
if (!fromWayId) return [];
|
||||
|
||||
var vgraph = intersection.graph;
|
||||
var keyVertexIds = intersection.vertices.map(function(v) { return v.id; });
|
||||
var keyWayIds = intersection.ways.map(function(w) { return w.id; });
|
||||
|
||||
var start = vgraph.entity(fromWayId);
|
||||
if (!start || !(start.__from || start.__via)) return [];
|
||||
|
||||
var maxPathLength = 7; // from-*-via-*-via-*-to (2 vias max)
|
||||
var maxStepDist = 20; // meters
|
||||
var turns = [];
|
||||
|
||||
step(start);
|
||||
return turns;
|
||||
|
||||
|
||||
// traverse the intersection graph and find all the valid paths
|
||||
function step(entity, currPath, currRestrictions, matchedRestriction) {
|
||||
currPath = _clone(currPath || []);
|
||||
if (currPath.length >= maxPathLength) return;
|
||||
currPath.push(entity.id);
|
||||
currRestrictions = _clone(currRestrictions || []);
|
||||
|
||||
if (entity.type === 'node') {
|
||||
var parents = vgraph.parentWays(entity);
|
||||
var nextWays = [];
|
||||
|
||||
// which ways can we step into?
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var way = parents[i];
|
||||
|
||||
// if next way is a oneway incoming to this vertex, skip
|
||||
if (way.__oneWay && way.nodes[0] !== entity.id) continue;
|
||||
|
||||
// if we have seen it before (allowing for an initial u-turn), skip
|
||||
if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue;
|
||||
|
||||
// Check all "current" restrictions (where we've already walked the `from`)
|
||||
var restrict = undefined;
|
||||
for (var j = 0; j < currRestrictions.length; j++) {
|
||||
var restriction = currRestrictions[j];
|
||||
var v = restriction.membersByRole('via');
|
||||
var t = restriction.memberByRole('to');
|
||||
var isOnly = /^only_/.test(restriction.tags.restriction);
|
||||
|
||||
// Are all the vias part of this local intersection?
|
||||
// This matters for flagging "indirect" restrictions
|
||||
var isLocalVia;
|
||||
if (v.length === 1 && v[0].type === 'node') {
|
||||
isLocalVia = (keyVertexIds.indexOf(v[0].id) !== -1);
|
||||
} else {
|
||||
isLocalVia = _every(v, function(via) { return keyWayIds.indexOf(via.id) !== -1; });
|
||||
}
|
||||
|
||||
// Does this path match the turn restriction?
|
||||
var isMatch = false;
|
||||
if ( // match via node, to way
|
||||
v.length === 1 &&
|
||||
v[0].type === 'node' &&
|
||||
v[0].id === entity.id &&
|
||||
t.id === way.id
|
||||
) {
|
||||
isMatch = true;
|
||||
} else if ( // match via ways, to way
|
||||
_every(v, function(via) { return currPath.indexOf(via.id) !== -1; }) &&
|
||||
t.id === way.id
|
||||
) {
|
||||
isMatch = true;
|
||||
}
|
||||
|
||||
if (isMatch && isOnly) {
|
||||
restrict = { id: restriction.id, only: true };
|
||||
break;
|
||||
} else if (isMatch && !isOnly) {
|
||||
restrict = { id: restriction.id, direct: true };
|
||||
break;
|
||||
} else if (!isMatch && isOnly && isLocalVia) {
|
||||
restrict = { id: restriction.id, indirect: true };
|
||||
// no break - keep looking for a "better" direct or only
|
||||
}
|
||||
}
|
||||
|
||||
nextWays.push({ way: way, restrict: restrict });
|
||||
}
|
||||
|
||||
nextWays.forEach(function(nextWay) {
|
||||
step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
|
||||
});
|
||||
|
||||
|
||||
} else { // entity.type === 'way'
|
||||
if (currPath.length >= 3) { // this is a "complete" path..
|
||||
var turn = pathToTurn(currPath);
|
||||
if (turn) {
|
||||
if (matchedRestriction) {
|
||||
turn.restriction = matchedRestriction.id;
|
||||
turn.only = matchedRestriction.only;
|
||||
turn.direct = matchedRestriction.direct;
|
||||
turn.indirect = matchedRestriction.indirect;
|
||||
}
|
||||
turns.push(osmTurn(turn));
|
||||
}
|
||||
|
||||
if (currPath[0] === currPath[2]) return; // we made a u-turn - stop here
|
||||
}
|
||||
|
||||
if (matchedRestriction) return; // don't advance any further
|
||||
|
||||
// which nodes can we step into?
|
||||
var n1 = vgraph.entity(entity.first()),
|
||||
n2 = vgraph.entity(entity.last()),
|
||||
dist = n1.loc && n2.loc && geoSphericalDistance(n1.loc, n2.loc),
|
||||
nextNodes = [];
|
||||
|
||||
if (currPath.length > 1) {
|
||||
if (dist > maxStepDist) return; // the next node is too far
|
||||
if (!entity.__via) return; // this way is a leaf / can't be a via
|
||||
}
|
||||
|
||||
if (!entity.__oneWay && // bidirectional..
|
||||
keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
|
||||
currPath.indexOf(n1.id) === -1) { // haven't seen it yet..
|
||||
nextNodes.push(n1); // can advance to first node
|
||||
}
|
||||
if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
|
||||
currPath.indexOf(n2.id) === -1) { // haven't seen it yet..
|
||||
nextNodes.push(n2); // can advance to last node
|
||||
}
|
||||
|
||||
// gather restrictions FROM this way
|
||||
var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
|
||||
if (!r.isRestriction()) return false;
|
||||
var f = r.memberByRole('from');
|
||||
return f && f.id === entity.id;
|
||||
});
|
||||
|
||||
nextNodes.forEach(function(node) {
|
||||
step(node, currPath, currRestrictions.concat(fromRestrictions), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// assumes path is alternating way-node-way of odd length
|
||||
function pathToTurn(path) {
|
||||
if (path.length < 3) return;
|
||||
var fromWayId, fromNodeId, fromVertexId;
|
||||
var toWayId, toNodeId, toVertexId;
|
||||
var viaWayIds, viaNodeId, isUturn;
|
||||
|
||||
fromWayId = path[0];
|
||||
toWayId = path[path.length - 1];
|
||||
|
||||
if (path.length === 3 && fromWayId === toWayId) { // u turn
|
||||
var way = vgraph.entity(fromWayId);
|
||||
if (way.__oneWay) return null;
|
||||
|
||||
isUturn = true;
|
||||
viaNodeId = fromVertexId = toVertexId = path[1];
|
||||
fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
|
||||
|
||||
} else {
|
||||
isUturn = false;
|
||||
fromVertexId = path[1];
|
||||
fromNodeId = adjacentNode(fromWayId, fromVertexId);
|
||||
toVertexId = path[path.length - 2];
|
||||
toNodeId = adjacentNode(toWayId, toVertexId);
|
||||
|
||||
if (path.length === 3) {
|
||||
viaNodeId = path[1];
|
||||
} else {
|
||||
viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; });
|
||||
viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: path.join(','),
|
||||
path: path,
|
||||
from: { node: fromNodeId, way: fromWayId, vertex: fromVertexId },
|
||||
via: { node: viaNodeId, ways: viaWayIds },
|
||||
to: { node: toNodeId, way: toWayId, vertex: toVertexId },
|
||||
u: isUturn
|
||||
};
|
||||
|
||||
|
||||
function adjacentNode(wayId, affixId) {
|
||||
var nodes = vgraph.entity(wayId).nodes;
|
||||
return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
|
||||
}
|
||||
|
||||
// U-turn
|
||||
if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
|
||||
turns.push(withRestriction({
|
||||
from: from,
|
||||
via: via,
|
||||
to: from,
|
||||
u: true
|
||||
}));
|
||||
}
|
||||
|
||||
return turns;
|
||||
};
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
|
||||
export function osmInferRestriction(graph, from, via, to, projection) {
|
||||
var fromWay = graph.entity(from.way),
|
||||
fromNode = graph.entity(from.node),
|
||||
toWay = graph.entity(to.way),
|
||||
toNode = graph.entity(to.node),
|
||||
viaNode = graph.entity(via.node),
|
||||
fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
|
||||
(fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
|
||||
toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
|
||||
(toWay.tags.oneway === '-1' && toWay.last() === via.node),
|
||||
angle = geoAngle(viaNode, fromNode, projection) -
|
||||
geoAngle(viaNode, toNode, projection);
|
||||
export function osmInferRestriction(graph, from, to, projection) {
|
||||
var fromWay = graph.entity(from.way);
|
||||
var fromNode = graph.entity(from.node);
|
||||
var fromVertex = graph.entity(from.vertex);
|
||||
var toWay = graph.entity(to.way);
|
||||
var toNode = graph.entity(to.node);
|
||||
var toVertex = graph.entity(to.vertex);
|
||||
|
||||
var fromOneWay = (fromWay.tags.oneway === 'yes');
|
||||
var toOneWay = (toWay.tags.oneway === 'yes');
|
||||
var angle = geoAngle(fromVertex, fromNode, projection) -
|
||||
geoAngle(toVertex, toNode, projection);
|
||||
|
||||
angle = angle * 180 / Math.PI;
|
||||
|
||||
|
||||
+32
-2
@@ -109,6 +109,16 @@ _extend(osmRelation.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// Same as memberByRole, but returns all members with the given role
|
||||
membersByRole: function(role) {
|
||||
var result = [];
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].role === role) {
|
||||
result.push(_extend({}, this.members[i], {index: i}));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
// Return the first member with the given id. A copy of the member object
|
||||
// is returned, extended with an 'index' property whose value is the member index.
|
||||
@@ -253,6 +263,26 @@ _extend(osmRelation.prototype, {
|
||||
},
|
||||
|
||||
|
||||
isValidRestriction: function() {
|
||||
if (!this.isRestriction()) return false;
|
||||
|
||||
var froms = this.members.filter(function(m) { return m.role === 'from'; });
|
||||
var vias = this.members.filter(function(m) { return m.role === 'via'; });
|
||||
var tos = this.members.filter(function(m) { return m.role === 'to'; });
|
||||
|
||||
if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
|
||||
if (froms.some(function(m) { return m.type !== 'way'; })) return false;
|
||||
|
||||
if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
|
||||
if (tos.some(function(m) { return m.type !== 'way'; })) return false;
|
||||
|
||||
if (vias.length === 0) return false;
|
||||
if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
// Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
|
||||
// where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
|
||||
//
|
||||
@@ -264,8 +294,8 @@ _extend(osmRelation.prototype, {
|
||||
// rings not matched with the intended outer ring.
|
||||
//
|
||||
multipolygon: function(resolver) {
|
||||
var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
|
||||
inners = this.members.filter(function(m) { return 'inner' === m.role; });
|
||||
var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); });
|
||||
var inners = this.members.filter(function(m) { return 'inner' === m.role; });
|
||||
|
||||
outers = osmJoinWays(outers, resolver);
|
||||
inners = osmJoinWays(inners, resolver);
|
||||
|
||||
+19
-17
@@ -1,26 +1,28 @@
|
||||
import { geoAngle } from '../geo';
|
||||
import { geoAngle } from '../geo/index';
|
||||
|
||||
|
||||
export function svgTurns(projection) {
|
||||
|
||||
return function drawTurns(selection, graph, turns) {
|
||||
|
||||
function key(turn) {
|
||||
return [turn.from.node + turn.via.node + turn.to.node].join('-');
|
||||
}
|
||||
|
||||
function icon(turn) {
|
||||
var u = turn.u ? '-u' : '';
|
||||
if (!turn.restriction)
|
||||
return '#turn-yes' + u;
|
||||
var restriction = graph.entity(turn.restriction).tags.restriction;
|
||||
return '#turn-' +
|
||||
(!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
|
||||
if (turn.direct || turn.indirect) return '#turn-no' + u;
|
||||
if (turn.only) return '#turn-only' + u;
|
||||
return '#turn-yes' + u;
|
||||
}
|
||||
|
||||
var layer = selection.selectAll('.layer-points .layer-points-turns');
|
||||
var layer = selection.selectAll('.data-layer-osm').selectAll('.layer-turns')
|
||||
.data([0]);
|
||||
|
||||
layer = layer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-osm layer-turns')
|
||||
.merge(layer);
|
||||
|
||||
|
||||
var groups = layer.selectAll('g.turn')
|
||||
.data(turns, key);
|
||||
.data(turns, function(d) { return d.key; });
|
||||
|
||||
groups.exit()
|
||||
.remove();
|
||||
@@ -61,11 +63,11 @@ export function svgTurns(projection) {
|
||||
|
||||
groups
|
||||
.attr('transform', function (turn) {
|
||||
var v = graph.entity(turn.via.node),
|
||||
t = graph.entity(turn.to.node),
|
||||
a = geoAngle(v, t, projection),
|
||||
p = projection(v.loc),
|
||||
r = turn.u ? 0 : 60;
|
||||
var t = graph.entity(turn.to.node);
|
||||
var v = graph.entity(turn.to.vertex);
|
||||
var a = geoAngle(v, t, projection);
|
||||
var p = projection(v.loc);
|
||||
var r = turn.u ? 0 : 60;
|
||||
|
||||
return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
|
||||
'rotate(' + a * 180 / Math.PI + ')';
|
||||
|
||||
@@ -6,44 +6,14 @@ import {
|
||||
} from 'd3-selection';
|
||||
|
||||
import { t } from '../../util/locale';
|
||||
|
||||
import {
|
||||
behaviorBreathe,
|
||||
behaviorHover
|
||||
} from '../../behavior';
|
||||
|
||||
import {
|
||||
osmEntity,
|
||||
osmIntersection,
|
||||
osmInferRestriction,
|
||||
osmTurn
|
||||
} from '../../osm';
|
||||
|
||||
import {
|
||||
actionRestrictTurn,
|
||||
actionUnrestrictTurn
|
||||
} from '../../actions';
|
||||
|
||||
import {
|
||||
geoExtent,
|
||||
geoRawMercator,
|
||||
geoZoomToScale
|
||||
} from '../../geo';
|
||||
|
||||
import {
|
||||
svgLayers,
|
||||
svgLines,
|
||||
svgTurns,
|
||||
svgVertices
|
||||
} from '../../svg';
|
||||
|
||||
import { actionRestrictTurn, actionUnrestrictTurn } from '../../actions';
|
||||
import { behaviorBreathe, behaviorHover } from '../../behavior';
|
||||
import { geoExtent, geoRawMercator } from '../../geo';
|
||||
import { osmIntersection, osmInferRestriction, osmTurn, osmWay } from '../../osm';
|
||||
import { svgLabels, svgLayers, svgLines, svgTurns, svgVertices } from '../../svg';
|
||||
import { utilRebind } from '../../util/rebind';
|
||||
import { utilFunctor } from '../../util';
|
||||
|
||||
import {
|
||||
utilGetDimensions,
|
||||
utilSetDimensions
|
||||
} from '../../util/dimensions';
|
||||
import { utilGetDimensions, utilSetDimensions } from '../../util/dimensions';
|
||||
|
||||
|
||||
export function uiFieldRestrictions(field, context) {
|
||||
@@ -51,19 +21,31 @@ export function uiFieldRestrictions(field, context) {
|
||||
var breathe = behaviorBreathe(context);
|
||||
var hover = behaviorHover(context);
|
||||
var initialized = false;
|
||||
var graph;
|
||||
var vertexID;
|
||||
var fromNodeID;
|
||||
var fromWayID;
|
||||
|
||||
|
||||
function restrictions(selection) {
|
||||
function restrictions(selection, intersection) {
|
||||
// if form field is hidden or has detached from dom, clean up.
|
||||
if (!d3_select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
|
||||
if (!d3_select('.inspector-wrap.inspector-hidden').empty() ||
|
||||
!selection.node().parentNode || !selection.node().parentNode.parentNode) {
|
||||
selection.call(restrictions.off);
|
||||
return;
|
||||
}
|
||||
|
||||
// try to reuse the intersection, but always rebuild it if the graph has changed
|
||||
if (context.graph() !== graph || !intersection) {
|
||||
graph = context.graph();
|
||||
intersection = osmIntersection(graph, vertexID);
|
||||
}
|
||||
var ok = (intersection.vertices.length && intersection.ways.length);
|
||||
|
||||
var wrap = selection.selectAll('.preset-input-wrap')
|
||||
.data([0]);
|
||||
.data(ok ? [0] : []);
|
||||
|
||||
wrap.exit()
|
||||
.remove();
|
||||
|
||||
var enter = wrap.enter()
|
||||
.append('div')
|
||||
@@ -73,31 +55,35 @@ export function uiFieldRestrictions(field, context) {
|
||||
.append('div')
|
||||
.attr('class', 'restriction-help');
|
||||
|
||||
// hack: no actual intersection exists here, just dont show the field
|
||||
if (!ok) return;
|
||||
|
||||
var intersection = osmIntersection(context.graph(), vertexID);
|
||||
var graph = intersection.graph;
|
||||
var vertex = graph.entity(vertexID);
|
||||
var vgraph = intersection.graph;
|
||||
var filter = utilFunctor(true);
|
||||
var extent = geoExtent();
|
||||
var projection = geoRawMercator();
|
||||
|
||||
var d = utilGetDimensions(wrap.merge(enter));
|
||||
var c = [d[0] / 2, d[1] / 2];
|
||||
var z = 24;
|
||||
var z = intersection.vertices.length === 1 ? 22 : 20;
|
||||
|
||||
projection
|
||||
.scale(geoZoomToScale(z));
|
||||
.scale(256 * Math.pow(2, z) / (2 * Math.PI));
|
||||
|
||||
var s = projection(vertex.loc);
|
||||
// fit extent to include all key vertices
|
||||
for (var i = 0; i < intersection.vertices.length; i++) {
|
||||
extent._extend(intersection.vertices[i].extent());
|
||||
}
|
||||
var s = projection(extent.center());
|
||||
|
||||
projection
|
||||
.translate([c[0] - s[0], c[1] - s[1]])
|
||||
.clipExtent([[0, 0], d]);
|
||||
|
||||
var extent = geoExtent(projection.invert([0, d[1]]), projection.invert([d[0], 0]));
|
||||
|
||||
var drawLayers = svgLayers(projection, context).only('osm').dimensions(d);
|
||||
var drawVertices = svgVertices(projection, context);
|
||||
var drawLines = svgLines(projection, context);
|
||||
// var drawLabels = svgLabels(projection, context, true);
|
||||
var drawTurns = svgTurns(projection, context);
|
||||
|
||||
enter
|
||||
@@ -117,9 +103,10 @@ export function uiFieldRestrictions(field, context) {
|
||||
|
||||
surface
|
||||
.call(utilSetDimensions, d)
|
||||
.call(drawVertices, graph, [vertex], filter, extent, true)
|
||||
.call(drawLines, graph, intersection.ways, filter)
|
||||
.call(drawTurns, graph, intersection.turns(fromNodeID));
|
||||
.call(drawVertices, vgraph, intersection.vertices, filter, extent, z)
|
||||
.call(drawLines, vgraph, intersection.ways, filter)
|
||||
// .call(drawLabels, vgraph, intersection.ways, filter, d, true)
|
||||
.call(drawTurns, vgraph, intersection.turns(fromWayID));
|
||||
|
||||
surface
|
||||
.on('click.restrictions', click)
|
||||
@@ -130,9 +117,9 @@ export function uiFieldRestrictions(field, context) {
|
||||
.selectAll('.selected')
|
||||
.classed('selected', false);
|
||||
|
||||
if (fromNodeID) {
|
||||
if (fromWayID) {
|
||||
surface
|
||||
.selectAll('.' + intersection.highways[fromNodeID].id)
|
||||
.selectAll('.' + fromWayID)
|
||||
.classed('selected', true);
|
||||
}
|
||||
|
||||
@@ -155,69 +142,111 @@ export function uiFieldRestrictions(field, context) {
|
||||
|
||||
var datum = d3_event.target.__data__;
|
||||
var entity = datum && datum.properties && datum.properties.entity;
|
||||
if (entity) datum = entity;
|
||||
if (entity) {
|
||||
datum = entity;
|
||||
}
|
||||
|
||||
if (datum instanceof osmEntity) {
|
||||
fromNodeID = intersection.adjacentNodeId(datum.id);
|
||||
if (datum instanceof osmWay && (datum.__from || datum.__via)) {
|
||||
fromWayID = datum.id;
|
||||
render();
|
||||
|
||||
} else if (datum instanceof osmTurn) {
|
||||
var actions;
|
||||
if (datum.restriction) {
|
||||
context.perform(
|
||||
actions = intersection.actions.concat([
|
||||
actionUnrestrictTurn(datum, projection),
|
||||
t('operations.restriction.annotation.delete')
|
||||
);
|
||||
]);
|
||||
} else {
|
||||
context.perform(
|
||||
actions = intersection.actions.concat([
|
||||
actionRestrictTurn(datum, projection),
|
||||
t('operations.restriction.annotation.create')
|
||||
);
|
||||
]);
|
||||
}
|
||||
context.perform.apply(context, actions);
|
||||
|
||||
} else {
|
||||
fromWayID = null;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mouseover() {
|
||||
var datum = d3_event.target.__data__;
|
||||
if (datum instanceof osmTurn) {
|
||||
var graph = context.graph();
|
||||
var presets = context.presets();
|
||||
var preset;
|
||||
var entity = datum && datum.properties && datum.properties.entity;
|
||||
if (entity) {
|
||||
datum = entity;
|
||||
}
|
||||
|
||||
if (datum instanceof osmWay) {
|
||||
wrap.selectAll('.restriction-help')
|
||||
.text(datum.id);
|
||||
|
||||
} else if (datum instanceof osmTurn) {
|
||||
|
||||
//DEBUG
|
||||
var str = '';
|
||||
if (datum.restriction) {
|
||||
preset = presets.match(graph.entity(datum.restriction), graph);
|
||||
} else {
|
||||
preset = presets.item('type/restriction/' +
|
||||
osmInferRestriction(
|
||||
graph,
|
||||
datum.from,
|
||||
datum.via,
|
||||
datum.to,
|
||||
projection
|
||||
)
|
||||
);
|
||||
if (datum.only) { str += 'ONLY_ '; }
|
||||
if (datum.direct) { str += 'NO_ '; }
|
||||
if (datum.indirect) { str += 'indirect '; }
|
||||
str += datum.restriction;
|
||||
}
|
||||
|
||||
str += ' FROM ' + datum.from.way +
|
||||
' VIA ' + (datum.via.node || datum.via.ways.join(',')) +
|
||||
' TO ' + datum.to.way;
|
||||
|
||||
wrap.selectAll('.restriction-help')
|
||||
.text(t('operations.restriction.help.' +
|
||||
(datum.restriction ? 'toggle_off' : 'toggle_on'),
|
||||
{ restriction: preset.name() })
|
||||
);
|
||||
.text(str);
|
||||
|
||||
// return;
|
||||
// var presets = context.presets(),
|
||||
// preset;
|
||||
|
||||
// if (datum.restriction) {
|
||||
// preset = presets.match(vgraph.entity(datum.restriction), vgraph);
|
||||
// } else {
|
||||
// preset = presets.item('type/restriction/' +
|
||||
// osmInferRestriction(
|
||||
// vgraph,
|
||||
// datum.from,
|
||||
// datum.to,
|
||||
// projection
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
// wrap.selectAll('.restriction-help')
|
||||
// .text(t('operations.restriction.help.' +
|
||||
// (datum.restriction ? 'toggle_off' : 'toggle_on'),
|
||||
// { restriction: preset.name() })
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mouseout() {
|
||||
wrap.selectAll('.restriction-help')
|
||||
.text(t('operations.restriction.help.' +
|
||||
(fromNodeID ? 'toggle' : 'select'))
|
||||
);
|
||||
|
||||
if (fromWayID) {
|
||||
wrap.selectAll('.restriction-help')
|
||||
.text('FROM ' + fromWayID);
|
||||
} else {
|
||||
wrap.selectAll('.restriction-help')
|
||||
.text('Click to select the FROM way');
|
||||
}
|
||||
|
||||
// wrap.selectAll('.restriction-help')
|
||||
// .text(t('operations.restriction.help.' +
|
||||
// (fromWayID ? 'toggle' : 'select'))
|
||||
// );
|
||||
}
|
||||
|
||||
|
||||
function render() {
|
||||
if (context.hasEntity(vertexID)) {
|
||||
restrictions(selection);
|
||||
restrictions(selection, intersection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,7 +254,7 @@ export function uiFieldRestrictions(field, context) {
|
||||
|
||||
restrictions.entity = function(_) {
|
||||
if (!vertexID || vertexID !== _.id) {
|
||||
fromNodeID = null;
|
||||
fromWayID = null;
|
||||
vertexID = _.id;
|
||||
}
|
||||
};
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ export function uiInit(context) {
|
||||
container
|
||||
.append('div')
|
||||
.attr('id', 'sidebar')
|
||||
.attr('class', 'col4')
|
||||
.attr('class', 'col5')
|
||||
.call(ui.sidebar);
|
||||
|
||||
var content = container
|
||||
@@ -94,7 +94,7 @@ export function uiInit(context) {
|
||||
|
||||
bar
|
||||
.append('div')
|
||||
.attr('class', 'spacer col4');
|
||||
.attr('class', 'spacer col5');
|
||||
|
||||
var limiter = bar.append('div')
|
||||
.attr('class', 'limiter');
|
||||
|
||||
+258
-453
@@ -1,483 +1,288 @@
|
||||
describe('iD.actionRestrictTurn', function() {
|
||||
var projection = d3.geoMercator().scale(250 / Math.PI);
|
||||
|
||||
it('adds a restriction to an unrestricted turn', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
describe('via node', function() {
|
||||
|
||||
it('adds a via node restriction to an unrestricted turn', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*']}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w']})
|
||||
]);
|
||||
var action = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'},
|
||||
restriction: 'no_right_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('=');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('-');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
|
||||
|
||||
it('splits the from way when necessary (forward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
var f = r.memberByRole('from');
|
||||
expect(f.id).to.eql('=');
|
||||
expect(f.type).to.eql('way');
|
||||
|
||||
var v = r.memberByRole('via');
|
||||
expect(v.id).to.eql('*');
|
||||
expect(v.type).to.eql('node');
|
||||
|
||||
var t = r.memberByRole('to');
|
||||
expect(t.id).to.eql('-');
|
||||
expect(t.type).to.eql('way');
|
||||
});
|
||||
|
||||
//TODO?
|
||||
it.skip('infers the restriction type based on the turn angle', function() {
|
||||
// u====*~~~~w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u', loc: [-1, 0]}),
|
||||
iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
iD.osmNode({id: 'w', loc: [ 1, 0]}),
|
||||
iD.osmNode({id: 'x', loc: [ 0, -1]}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*']}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'x']}),
|
||||
iD.osmWay({id: '~', nodes: ['*', 'w']})
|
||||
]);
|
||||
|
||||
var r1 = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'},
|
||||
restriction: 'no_right_turn'
|
||||
}, projection, 'r');
|
||||
to: {node: 'x', way: '-'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r1.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('=');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('-');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from way when necessary (backward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'w', way: '=', newID: '=='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'},
|
||||
restriction: 'no_left_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('==');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('-');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from way when necessary (straight on forward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '=', newID: '=='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '='},
|
||||
restriction: 'no_straight_on'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_straight_on'});
|
||||
expect(r.memberByRole('from').id).to.eql('=');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('==');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from way when necessary (straight on backward)', function() {
|
||||
// u<===*====w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['w', '*', 'u']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '=', newID: '=='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '='},
|
||||
restriction: 'no_straight_on'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_straight_on'});
|
||||
expect(r.memberByRole('from').id).to.eql('==');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('=');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from way when necessary (vertex closes from)', function() {
|
||||
//
|
||||
// b -- c
|
||||
// | |
|
||||
// a -- * === w
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [-1, 0]}),
|
||||
iD.Node({id: 'b', loc: [-1, 1]}),
|
||||
iD.Node({id: 'c', loc: [ 0, 1]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'w', loc: [ 1, 0]}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*']}),
|
||||
iD.Way({id: '=', nodes: ['*', 'w']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'c', way: '-', newID: '--'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '='},
|
||||
restriction: 'no_left_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('--');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('=');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from/to way when necessary (vertex closes from/to)', function() {
|
||||
//
|
||||
// b -- c
|
||||
// | |
|
||||
// a -- * === w
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [-1, 0]}),
|
||||
iD.Node({id: 'b', loc: [-1, 1]}),
|
||||
iD.Node({id: 'c', loc: [ 0, 1]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'w', loc: [ 1, 0]}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*']}),
|
||||
iD.Way({id: '=', nodes: ['*', 'w']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'a', way: '-', newID: '--'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-'},
|
||||
restriction: 'no_left_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('-');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('--');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the to way when necessary (forward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
var r2 = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '=', newID: '=='},
|
||||
restriction: 'no_right_turn'
|
||||
}, projection, 'r');
|
||||
to: {node: 'w', way: '~'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r2.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('-');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('==');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the to way when necessary (backward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
var l1 = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
restriction: 'no_left_turn'
|
||||
}, projection, 'r');
|
||||
to: {node: 'u', way: '='}
|
||||
}, projection, 'r')(graph);
|
||||
expect(l1.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('-');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('=');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the to way when necessary (vertex closes to)', function() {
|
||||
//
|
||||
// b -- c
|
||||
// | |
|
||||
// a -- * === w
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [-1, 0]}),
|
||||
iD.Node({id: 'b', loc: [-1, 1]}),
|
||||
iD.Node({id: 'c', loc: [ 0, 1]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'w', loc: [ 1, 0]}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*']}),
|
||||
iD.Way({id: '=', nodes: ['*', 'w']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'w', way: '='},
|
||||
var l2 = iD.actionRestrictTurn({
|
||||
from: {node: 'w', way: '~'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-', newID: '--'},
|
||||
restriction: 'no_right_turn'
|
||||
}, projection, 'r');
|
||||
to: {node: 'x', way: '-'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(l2.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('=');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('--');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('splits the from/to way of a U-turn (forward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
var s = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
to: {node: 'w', way: '~'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(s.entity('r').tags.restriction).to.equal('no_straight_on');
|
||||
|
||||
var u = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
}, projection, 'r')(graph);
|
||||
expect(u.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
|
||||
//TODO?
|
||||
it.skip('infers no_u_turn from acute angle made by forward oneways', function() {
|
||||
// *
|
||||
// / \
|
||||
// w2/ \w1
|
||||
// / \
|
||||
// u x
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u', loc: [-1, -20]}),
|
||||
iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
iD.osmNode({id: 'x', loc: [ 1, -20]}),
|
||||
iD.osmWay({id: 'w1', nodes: ['x', '*'], tags: {oneway: 'yes'}}),
|
||||
iD.osmWay({id: 'w2', nodes: ['*', 'u'], tags: {oneway: 'yes'}})
|
||||
]);
|
||||
|
||||
var r = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: 'w1'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: 'w2'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
|
||||
//TODO?
|
||||
it.skip('infers no_u_turn from acute angle made by reverse oneways', function() {
|
||||
// *
|
||||
// / \
|
||||
// w2/ \w1
|
||||
// / \
|
||||
// u x
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u', loc: [-1, -20]}),
|
||||
iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
iD.osmNode({id: 'x', loc: [ 1, -20]}),
|
||||
iD.osmWay({id: 'w1', nodes: ['*', 'x'], tags: {oneway: '-1'}}),
|
||||
iD.osmWay({id: 'w2', nodes: ['u', '*'], tags: {oneway: '-1'}})
|
||||
]);
|
||||
|
||||
var r = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: 'w1'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: 'w2'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('via way', function() {
|
||||
|
||||
it('adds a via way restriction to an unrestricted turn', function() {
|
||||
// u ==== VIA ---> w
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: 'V1'}),
|
||||
iD.osmNode({id: 'V2'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', 'V1']}),
|
||||
iD.osmWay({id: 'VIA', nodes: ['V1', 'V2']}),
|
||||
iD.osmWay({id: '-', nodes: ['V2', 'w']})
|
||||
]);
|
||||
var action = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {ways: ['VIA']},
|
||||
to: {node: 'w', way: '-'},
|
||||
restriction: 'no_u_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_u_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('=');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('=');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_u_turn'});
|
||||
|
||||
var f = r.memberByRole('from');
|
||||
expect(f.id).to.eql('=');
|
||||
expect(f.type).to.eql('way');
|
||||
|
||||
var v = r.memberByRole('via');
|
||||
expect(v.id).to.eql('VIA');
|
||||
expect(v.type).to.eql('way');
|
||||
|
||||
var t = r.memberByRole('to');
|
||||
expect(t.id).to.eql('-');
|
||||
expect(t.type).to.eql('way');
|
||||
});
|
||||
|
||||
|
||||
// TODO?
|
||||
|
||||
// it('infers the restriction type based on the turn angle', function() {
|
||||
// // u====*~~~~w
|
||||
// // |
|
||||
// // x
|
||||
// var graph = iD.coreGraph([
|
||||
// iD.osmNode({id: 'u', loc: [-1, 0]}),
|
||||
// iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
// iD.osmNode({id: 'w', loc: [ 1, 0]}),
|
||||
// iD.osmNode({id: 'x', loc: [ 0, -1]}),
|
||||
// iD.osmWay({id: '=', nodes: ['u', '*']}),
|
||||
// iD.osmWay({id: '-', nodes: ['*', 'x']}),
|
||||
// iD.osmWay({id: '~', nodes: ['*', 'w']})
|
||||
// ]);
|
||||
|
||||
// var r1 = iD.actionRestrictTurn({
|
||||
// from: {node: 'u', way: '='},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'x', way: '-'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(r1.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
// var r2 = iD.actionRestrictTurn({
|
||||
// from: {node: 'x', way: '-'},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'w', way: '~'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(r2.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
// var l1 = iD.actionRestrictTurn({
|
||||
// from: {node: 'x', way: '-'},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'u', way: '='}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(l1.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
// var l2 = iD.actionRestrictTurn({
|
||||
// from: {node: 'w', way: '~'},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'x', way: '-'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(l2.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
// var s = iD.actionRestrictTurn({
|
||||
// from: {node: 'u', way: '='},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'w', way: '~'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(s.entity('r').tags.restriction).to.equal('no_straight_on');
|
||||
|
||||
// var u = iD.actionRestrictTurn({
|
||||
// from: {node: 'u', way: '='},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'u', way: '='}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(u.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
// });
|
||||
|
||||
// it('infers no_u_turn from acute angle made by forward oneways', function() {
|
||||
// // *
|
||||
// // / \
|
||||
// // w2/ \w1
|
||||
// // / \
|
||||
// // u x
|
||||
// var graph = iD.coreGraph([
|
||||
// iD.osmNode({id: 'u', loc: [-1, -20]}),
|
||||
// iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
// iD.osmNode({id: 'x', loc: [ 1, -20]}),
|
||||
// iD.osmWay({id: 'w1', nodes: ['x', '*'], tags: {oneway: 'yes'}}),
|
||||
// iD.osmWay({id: 'w2', nodes: ['*', 'u'], tags: {oneway: 'yes'}})
|
||||
// ]);
|
||||
|
||||
// var r = iD.actionRestrictTurn({
|
||||
// from: {node: 'x', way: 'w1'},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'u', way: 'w2'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
// });
|
||||
|
||||
// it('infers no_u_turn from acute angle made by reverse oneways', function() {
|
||||
// // *
|
||||
// // / \
|
||||
// // w2/ \w1
|
||||
// // / \
|
||||
// // u x
|
||||
// var graph = iD.coreGraph([
|
||||
// iD.osmNode({id: 'u', loc: [-1, -20]}),
|
||||
// iD.osmNode({id: '*', loc: [ 0, 0]}),
|
||||
// iD.osmNode({id: 'x', loc: [ 1, -20]}),
|
||||
// iD.osmWay({id: 'w1', nodes: ['*', 'x'], tags: {oneway: '-1'}}),
|
||||
// iD.osmWay({id: 'w2', nodes: ['u', '*'], tags: {oneway: '-1'}})
|
||||
// ]);
|
||||
|
||||
// var r = iD.actionRestrictTurn({
|
||||
// from: {node: 'x', way: 'w1'},
|
||||
// via: {node: '*'},
|
||||
// to: {node: 'u', way: 'w2'}
|
||||
// }, projection, 'r')(graph);
|
||||
// expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
// });
|
||||
});
|
||||
|
||||
it('splits the from/to way of a U-turn (backward)', function() {
|
||||
// u====*===>w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']})
|
||||
]),
|
||||
action = iD.actionRestrictTurn({
|
||||
from: {node: 'w', way: '=', newID: '=='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '=', newID: '~~'},
|
||||
restriction: 'no_u_turn'
|
||||
}, projection, 'r');
|
||||
|
||||
graph = action(graph);
|
||||
|
||||
var r = graph.entity('r');
|
||||
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_u_turn'});
|
||||
expect(r.memberByRole('from').id).to.eql('==');
|
||||
expect(r.memberByRole('from').type).to.eql('way');
|
||||
expect(r.memberByRole('via').id).to.eql('*');
|
||||
expect(r.memberByRole('via').type).to.eql('node');
|
||||
expect(r.memberByRole('to').id).to.eql('==');
|
||||
expect(r.memberByRole('to').type).to.eql('way');
|
||||
});
|
||||
|
||||
it('infers the restriction type based on the turn angle', function() {
|
||||
// u====*~~~~w
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u', loc: [-1, 0]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'w', loc: [ 1, 0]}),
|
||||
iD.Node({id: 'x', loc: [ 0, -1]}),
|
||||
iD.Way({id: '=', nodes: ['u', '*']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'x']}),
|
||||
iD.Way({id: '~', nodes: ['*', 'w']})
|
||||
]);
|
||||
|
||||
var r1 = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r1.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
var r2 = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '~'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r2.entity('r').tags.restriction).to.equal('no_right_turn');
|
||||
|
||||
var l1 = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
}, projection, 'r')(graph);
|
||||
expect(l1.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
var l2 = iD.actionRestrictTurn({
|
||||
from: {node: 'w', way: '~'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(l2.entity('r').tags.restriction).to.equal('no_left_turn');
|
||||
|
||||
var s = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '~'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(s.entity('r').tags.restriction).to.equal('no_straight_on');
|
||||
|
||||
var u = iD.actionRestrictTurn({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
}, projection, 'r')(graph);
|
||||
expect(u.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
|
||||
it('infers no_u_turn from acute angle made by forward oneways', function() {
|
||||
// *
|
||||
// / \
|
||||
// w2/ \w1
|
||||
// / \
|
||||
// u x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u', loc: [-1, -20]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'x', loc: [ 1, -20]}),
|
||||
iD.Way({id: 'w1', nodes: ['x', '*'], tags: {oneway: 'yes'}}),
|
||||
iD.Way({id: 'w2', nodes: ['*', 'u'], tags: {oneway: 'yes'}})
|
||||
]);
|
||||
|
||||
var r = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: 'w1'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: 'w2'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
|
||||
it('infers no_u_turn from acute angle made by reverse oneways', function() {
|
||||
// *
|
||||
// / \
|
||||
// w2/ \w1
|
||||
// / \
|
||||
// u x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u', loc: [-1, -20]}),
|
||||
iD.Node({id: '*', loc: [ 0, 0]}),
|
||||
iD.Node({id: 'x', loc: [ 1, -20]}),
|
||||
iD.Way({id: 'w1', nodes: ['*', 'x'], tags: {oneway: '-1'}}),
|
||||
iD.Way({id: 'w2', nodes: ['u', '*'], tags: {oneway: '-1'}})
|
||||
]);
|
||||
|
||||
var r = iD.actionRestrictTurn({
|
||||
from: {node: 'x', way: 'w1'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: 'w2'}
|
||||
}, projection, 'r')(graph);
|
||||
expect(r.entity('r').tags.restriction).to.equal('no_u_turn');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+390
-403
@@ -1,108 +1,114 @@
|
||||
describe('iD.osmIntersection', function() {
|
||||
describe('highways', function() {
|
||||
it('excludes non-highways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*']}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w']})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*']}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w']})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').ways).to.eql([]);
|
||||
});
|
||||
|
||||
it('excludes degenerate highways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*'], tags: {highway: 'residential'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
var ids = iD.osmIntersection(graph, '*').ways.map(function (w) { return w.id; });
|
||||
expect(ids).to.have.same.members(['=']);
|
||||
var result = iD.osmIntersection(graph, '*').ways;
|
||||
expect(result.map(function(i) { return i.id; })).to.eql(['=']);
|
||||
});
|
||||
|
||||
it('excludes coincident highways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['u', '*'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').ways).to.eql([]);
|
||||
});
|
||||
//TODO?
|
||||
// it('excludes coincident highways', function() {
|
||||
// var graph = iD.coreGraph([
|
||||
// iD.osmNode({id: 'u'}),
|
||||
// iD.osmNode({id: '*'}),
|
||||
// iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
// iD.osmWay({id: '-', nodes: ['u', '*'], tags: {highway: 'residential'}})
|
||||
// ]);
|
||||
// expect(iD.osmIntersection(graph, '*').ways).to.eql([]);
|
||||
// });
|
||||
|
||||
it('includes line highways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w']})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w']})
|
||||
]);
|
||||
var ids = iD.osmIntersection(graph, '*').ways.map(function (w) { return w.id; });
|
||||
expect(ids).to.have.same.members(['=']);
|
||||
var result = iD.osmIntersection(graph, '*').ways;
|
||||
expect(result.map(function(i) { return i.id; })).to.eql(['=']);
|
||||
});
|
||||
|
||||
it('excludes area highways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'pedestrian', area: 'yes'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'pedestrian', area: 'yes'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').ways).to.eql([]);
|
||||
});
|
||||
|
||||
it('auto-splits highways at the intersection', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'residential'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
var ids = iD.osmIntersection(graph, '*').ways.map(function (w) { return w.id; });
|
||||
expect(ids).to.have.ordered.members(['=-a', '=-b']);
|
||||
expect(iD.osmIntersection(graph, '*').ways.length).to.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#turns', function() {
|
||||
it('permits turns onto a way forward', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns onto a way backward', function() {
|
||||
// u====*<---w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns from a way that must be split', function() {
|
||||
@@ -111,33 +117,29 @@ describe('iD.osmIntersection', function() {
|
||||
// u===*
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('w');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmNode({id: 'x'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('-');
|
||||
expect(turns.length).to.eql(3);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'w', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'w', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'}
|
||||
});
|
||||
expect(turns[2]).to.eql({
|
||||
from: {node: 'w', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('-,*,=');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('-,*,-');
|
||||
expect(turns[1].u).to.be.true;
|
||||
|
||||
expect(turns[2]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[2].key).to.match(/^-,\*,w-\d+$/); // new way
|
||||
expect(turns[2].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns to a way that must be split', function() {
|
||||
@@ -146,244 +148,243 @@ describe('iD.osmIntersection', function() {
|
||||
// u===*
|
||||
// |
|
||||
// x
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: 'x'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmNode({id: 'x'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(3);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'x', way: '-'}
|
||||
});
|
||||
expect(turns[2]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
|
||||
expect(turns[2]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[2].key).to.match(/^=,\*,w-\d+$/); // new way
|
||||
expect(turns[2].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns from a oneway forward', function() {
|
||||
// u===>v----w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
expect(turns).to.eql([{
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
}]);
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(1);
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns from a reverse oneway backward', function() {
|
||||
// u<===*----w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
expect(turns).to.eql([{
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
}]);
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(1);
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('omits turns from a oneway backward', function() {
|
||||
// u<===*----w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').turns('u')).to.eql([]);
|
||||
});
|
||||
|
||||
it('omits turns from a reverse oneway forward', function() {
|
||||
// u===>*----w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').turns('u')).to.eql([]);
|
||||
});
|
||||
|
||||
it('permits turns onto a oneway forward', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: 'yes'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: 'yes'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns onto a reverse oneway backward', function() {
|
||||
// u====*<---w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: '-1'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: '-1'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('omits turns onto a oneway backward', function() {
|
||||
// u====*<---w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: 'yes'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: 'yes'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').turns('u').length).to.eql(1);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(1);
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
});
|
||||
|
||||
it('omits turns onto a reverse oneway forward', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: '-1'}})
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: '-1'}})
|
||||
]);
|
||||
expect(iD.osmIntersection(graph, '*').turns('u').length).to.eql(1);
|
||||
});
|
||||
|
||||
it('includes U-turns', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(1);
|
||||
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
u: true
|
||||
});
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
});
|
||||
|
||||
it('restricts turns with a restriction relation', function() {
|
||||
// u====*--->w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}}),
|
||||
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{id: '=', role: 'from', type: 'way'},
|
||||
{id: '-', role: 'to', type: 'way'},
|
||||
{id: '*', role: 'via', type: 'node'}
|
||||
]})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{id: '=', role: 'from', type: 'way'},
|
||||
{id: '-', role: 'to', type: 'way'},
|
||||
{id: '*', role: 'via', type: 'node'}
|
||||
]})
|
||||
]);
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'},
|
||||
restriction: 'r'
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,-');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
expect(turns[1].restriction).to.eql('r');
|
||||
expect(turns[1].direct).to.be.true;
|
||||
expect(turns[1].indirect).to.be.not.ok;
|
||||
expect(turns[1].only).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('restricts turns affected by an only_* restriction relation', function() {
|
||||
// u====*~~~~v
|
||||
// |
|
||||
// w
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Node({id: 'v'}),
|
||||
iD.Node({id: 'w'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '~', nodes: ['v', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Relation({id: 'r', tags: {type: 'restriction', restriction: 'only_right_turn'}, members: [
|
||||
{id: '=', role: 'from', type: 'way'},
|
||||
{id: '-', role: 'to', type: 'way'},
|
||||
{id: '*', role: 'via', type: 'node'}
|
||||
]})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmNode({id: 'v'}),
|
||||
iD.osmNode({id: 'w'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmWay({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '~', nodes: ['v', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction', restriction: 'only_right_turn'}, members: [
|
||||
{id: '=', role: 'from', type: 'way'},
|
||||
{id: '-', role: 'to', type: 'way'},
|
||||
{id: '*', role: 'via', type: 'node'}
|
||||
]})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(3);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'v', way: '~'},
|
||||
restriction: 'r',
|
||||
indirect_restriction: true
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'w', way: '-'},
|
||||
restriction: 'r'
|
||||
});
|
||||
expect(turns[2]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
restriction: 'r',
|
||||
indirect_restriction: true,
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,=');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,~');
|
||||
expect(turns[1].restriction).to.eql('r');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
expect(turns[1].direct).to.be.not.ok;
|
||||
expect(turns[1].indirect).to.be.true;
|
||||
expect(turns[1].only).to.be.not.ok;
|
||||
|
||||
expect(turns[2]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[2].key).to.eql('=,*,-');
|
||||
expect(turns[2].restriction).to.eql('r');
|
||||
expect(turns[2].u).to.be.not.ok;
|
||||
expect(turns[2].direct).to.be.not.ok;
|
||||
expect(turns[2].indirect).to.be.not.ok;
|
||||
expect(turns[2].only).to.be.true;
|
||||
});
|
||||
|
||||
it('permits turns to a circular way', function() {
|
||||
@@ -392,34 +393,30 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(3);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'a', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-'}
|
||||
});
|
||||
expect(turns[2]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,=');
|
||||
expect(turns[1].u).to.be.true;
|
||||
|
||||
expect(turns[2]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[2].key).to.match(/^=,\*,w-\d+$/); // new way
|
||||
expect(turns[2].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns from a circular way', function() {
|
||||
@@ -428,34 +425,30 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('a');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('-');
|
||||
expect(turns.length).to.eql(3);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'a', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'a', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
});
|
||||
expect(turns[2]).to.eql({
|
||||
from: {node: 'a', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'a', way: '-'},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('-,*,-');
|
||||
expect(turns[0].u).to.be.true;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('-,*,=');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
|
||||
expect(turns[2]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[2].key).to.match(/^-,\*,w-\d+$/); // new way
|
||||
expect(turns[2].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns to a oneway circular way', function() {
|
||||
@@ -464,29 +457,26 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'a', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,=');
|
||||
expect(turns[1].u).to.be.true;
|
||||
});
|
||||
|
||||
it('permits turns to a reverse oneway circular way', function() {
|
||||
@@ -495,29 +485,26 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('u');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var turns = iD.osmIntersection(graph, '*').turns('=');
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'u', way: '='},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='},
|
||||
u: true
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql('=,*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql('=,*,=');
|
||||
expect(turns[1].u).to.be.true;
|
||||
});
|
||||
|
||||
it('permits turns from a oneway circular way', function() {
|
||||
@@ -526,28 +513,28 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('c');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var intersection = iD.osmIntersection(graph, '*');
|
||||
var newWay = intersection.ways.find(function(w) { return /^w-\d+$/.test(w.id); });
|
||||
var turns = iD.osmIntersection(graph, '*').turns(newWay.id);
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'c', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'a', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'c', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql(newWay.id + ',*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql(newWay.id + ',*,=');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('permits turns from a reverse oneway circular way', function() {
|
||||
@@ -556,28 +543,28 @@ describe('iD.osmIntersection', function() {
|
||||
// | |
|
||||
// a -- * === u
|
||||
//
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a'}),
|
||||
iD.Node({id: 'b'}),
|
||||
iD.Node({id: 'c'}),
|
||||
iD.Node({id: '*'}),
|
||||
iD.Node({id: 'u'}),
|
||||
iD.Way({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]),
|
||||
turns = iD.osmIntersection(graph, '*').turns('a');
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: '*'}),
|
||||
iD.osmNode({id: 'u'}),
|
||||
iD.osmWay({id: '-', nodes: ['*', 'a', 'b', 'c', '*'], tags: {highway: 'residential', oneway: '-1'}}),
|
||||
iD.osmWay({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential'}})
|
||||
]);
|
||||
|
||||
var intersection = iD.osmIntersection(graph, '*');
|
||||
var newWay = intersection.ways.find(function(w) { return /^w-\d+$/.test(w.id); });
|
||||
var turns = iD.osmIntersection(graph, '*').turns(newWay.id);
|
||||
expect(turns.length).to.eql(2);
|
||||
expect(turns[0]).to.eql({
|
||||
from: {node: 'a', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'c', way: '-'}
|
||||
});
|
||||
expect(turns[1]).to.eql({
|
||||
from: {node: 'a', way: '-'},
|
||||
via: {node: '*'},
|
||||
to: {node: 'u', way: '='}
|
||||
});
|
||||
|
||||
expect(turns[0]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[0].key).to.eql(newWay.id + ',*,-');
|
||||
expect(turns[0].u).to.be.not.ok;
|
||||
|
||||
expect(turns[1]).to.be.an.instanceOf(iD.osmTurn);
|
||||
expect(turns[1].key).to.eql(newWay.id + ',*,=');
|
||||
expect(turns[1].u).to.be.not.ok;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+409
-257
@@ -1,98 +1,98 @@
|
||||
describe('iD.osmRelation', function () {
|
||||
if (iD.debug) {
|
||||
it('freezes nodes', function () {
|
||||
expect(Object.isFrozen(iD.Relation().members)).to.be.true;
|
||||
expect(Object.isFrozen(iD.osmRelation().members)).to.be.true;
|
||||
});
|
||||
}
|
||||
|
||||
it('returns a relation', function () {
|
||||
expect(iD.Relation()).to.be.an.instanceOf(iD.Relation);
|
||||
expect(iD.Relation().type).to.equal('relation');
|
||||
expect(iD.osmRelation()).to.be.an.instanceOf(iD.osmRelation);
|
||||
expect(iD.osmRelation().type).to.equal('relation');
|
||||
});
|
||||
|
||||
it('defaults members to an empty array', function () {
|
||||
expect(iD.Relation().members).to.eql([]);
|
||||
expect(iD.osmRelation().members).to.eql([]);
|
||||
});
|
||||
|
||||
it('sets members as specified', function () {
|
||||
expect(iD.Relation({members: ['n-1']}).members).to.eql(['n-1']);
|
||||
expect(iD.osmRelation({members: ['n-1']}).members).to.eql(['n-1']);
|
||||
});
|
||||
|
||||
it('defaults tags to an empty object', function () {
|
||||
expect(iD.Relation().tags).to.eql({});
|
||||
expect(iD.osmRelation().tags).to.eql({});
|
||||
});
|
||||
|
||||
it('sets tags as specified', function () {
|
||||
expect(iD.Relation({tags: {foo: 'bar'}}).tags).to.eql({foo: 'bar'});
|
||||
expect(iD.osmRelation({tags: {foo: 'bar'}}).tags).to.eql({foo: 'bar'});
|
||||
});
|
||||
|
||||
describe('#copy', function () {
|
||||
it('returns a new Relation', function () {
|
||||
var r = iD.Relation({id: 'r'}),
|
||||
result = r.copy(null, {});
|
||||
var r = iD.osmRelation({id: 'r'});
|
||||
var result = r.copy(null, {});
|
||||
|
||||
expect(result).to.be.an.instanceof(iD.Relation);
|
||||
expect(result).to.be.an.instanceof(iD.osmRelation);
|
||||
expect(result).not.to.equal(r);
|
||||
});
|
||||
|
||||
it('adds the new Relation to input object', function () {
|
||||
var r = iD.Relation({id: 'r'}),
|
||||
copies = {},
|
||||
result = r.copy(null, copies);
|
||||
var r = iD.osmRelation({id: 'r'});
|
||||
var copies = {};
|
||||
var result = r.copy(null, copies);
|
||||
expect(Object.keys(copies)).to.have.length(1);
|
||||
expect(copies.r).to.equal(result);
|
||||
});
|
||||
|
||||
it('returns an existing copy in input object', function () {
|
||||
var r = iD.Relation({id: 'r'}),
|
||||
copies = {},
|
||||
result1 = r.copy(null, copies),
|
||||
result2 = r.copy(null, copies);
|
||||
var r = iD.osmRelation({id: 'r'});
|
||||
var copies = {};
|
||||
var result1 = r.copy(null, copies);
|
||||
var result2 = r.copy(null, copies);
|
||||
expect(Object.keys(copies)).to.have.length(1);
|
||||
expect(result1).to.equal(result2);
|
||||
});
|
||||
|
||||
it('deep copies members', function () {
|
||||
var a = iD.Node({id: 'a'}),
|
||||
b = iD.Node({id: 'b'}),
|
||||
c = iD.Node({id: 'c'}),
|
||||
w = iD.Way({id: 'w', nodes: ['a','b','c','a']}),
|
||||
r = iD.Relation({id: 'r', members: [{id: 'w', role: 'outer'}]}),
|
||||
graph = iD.Graph([a, b, c, w, r]),
|
||||
copies = {},
|
||||
result = r.copy(graph, copies);
|
||||
var a = iD.osmNode({id: 'a'});
|
||||
var b = iD.osmNode({id: 'b'});
|
||||
var c = iD.osmNode({id: 'c'});
|
||||
var w = iD.osmWay({id: 'w', nodes: ['a','b','c','a']});
|
||||
var r = iD.osmRelation({id: 'r', members: [{id: 'w', role: 'outer'}]});
|
||||
var graph = iD.coreGraph([a, b, c, w, r]);
|
||||
var copies = {};
|
||||
var result = r.copy(graph, copies);
|
||||
|
||||
expect(Object.keys(copies)).to.have.length(5);
|
||||
expect(copies.w).to.be.an.instanceof(iD.Way);
|
||||
expect(copies.a).to.be.an.instanceof(iD.Node);
|
||||
expect(copies.b).to.be.an.instanceof(iD.Node);
|
||||
expect(copies.c).to.be.an.instanceof(iD.Node);
|
||||
expect(copies.w).to.be.an.instanceof(iD.osmWay);
|
||||
expect(copies.a).to.be.an.instanceof(iD.osmNode);
|
||||
expect(copies.b).to.be.an.instanceof(iD.osmNode);
|
||||
expect(copies.c).to.be.an.instanceof(iD.osmNode);
|
||||
expect(result.members[0].id).not.to.equal(r.members[0].id);
|
||||
expect(result.members[0].role).to.equal(r.members[0].role);
|
||||
});
|
||||
|
||||
it('deep copies non-tree relation graphs without duplicating children', function () {
|
||||
var w = iD.Way({id: 'w'}),
|
||||
r1 = iD.Relation({id: 'r1', members: [{id: 'r2'}, {id: 'w'}]}),
|
||||
r2 = iD.Relation({id: 'r2', members: [{id: 'w'}]}),
|
||||
graph = iD.Graph([w, r1, r2]),
|
||||
copies = {};
|
||||
var w = iD.osmWay({id: 'w'});
|
||||
var r1 = iD.osmRelation({id: 'r1', members: [{id: 'r2'}, {id: 'w'}]});
|
||||
var r2 = iD.osmRelation({id: 'r2', members: [{id: 'w'}]});
|
||||
var graph = iD.coreGraph([w, r1, r2]);
|
||||
var copies = {};
|
||||
r1.copy(graph, copies);
|
||||
|
||||
expect(Object.keys(copies)).to.have.length(3);
|
||||
expect(copies.r1).to.be.an.instanceof(iD.Relation);
|
||||
expect(copies.r2).to.be.an.instanceof(iD.Relation);
|
||||
expect(copies.w).to.be.an.instanceof(iD.Way);
|
||||
expect(copies.r1).to.be.an.instanceof(iD.osmRelation);
|
||||
expect(copies.r2).to.be.an.instanceof(iD.osmRelation);
|
||||
expect(copies.w).to.be.an.instanceof(iD.osmWay);
|
||||
expect(copies.r1.members[0].id).to.equal(copies.r2.id);
|
||||
expect(copies.r1.members[1].id).to.equal(copies.w.id);
|
||||
expect(copies.r2.members[0].id).to.equal(copies.w.id);
|
||||
});
|
||||
|
||||
it('deep copies cyclical relation graphs without issue', function () {
|
||||
var r1 = iD.Relation({id: 'r1', members: [{id: 'r2'}]}),
|
||||
r2 = iD.Relation({id: 'r2', members: [{id: 'r1'}]}),
|
||||
graph = iD.Graph([r1, r2]),
|
||||
copies = {};
|
||||
var r1 = iD.osmRelation({id: 'r1', members: [{id: 'r2'}]});
|
||||
var r2 = iD.osmRelation({id: 'r2', members: [{id: 'r1'}]});
|
||||
var graph = iD.coreGraph([r1, r2]);
|
||||
var copies = {};
|
||||
r1.copy(graph, copies);
|
||||
|
||||
expect(Object.keys(copies)).to.have.length(2);
|
||||
@@ -101,9 +101,9 @@ describe('iD.osmRelation', function () {
|
||||
});
|
||||
|
||||
it('deep copies self-referencing relations without issue', function () {
|
||||
var r = iD.Relation({id: 'r', members: [{id: 'r'}]}),
|
||||
graph = iD.Graph([r]),
|
||||
copies = {};
|
||||
var r = iD.osmRelation({id: 'r', members: [{id: 'r'}]});
|
||||
var graph = iD.coreGraph([r]);
|
||||
var copies = {};
|
||||
r.copy(graph, copies);
|
||||
|
||||
expect(Object.keys(copies)).to.have.length(1);
|
||||
@@ -113,53 +113,53 @@ describe('iD.osmRelation', function () {
|
||||
|
||||
describe('#extent', function () {
|
||||
it('returns the minimal extent containing the extents of all members', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [5, 10]}),
|
||||
r = iD.Relation({members: [{id: a.id}, {id: b.id}]}),
|
||||
graph = iD.Graph([a, b, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [5, 10]});
|
||||
var r = iD.osmRelation({members: [{id: a.id}, {id: b.id}]});
|
||||
var graph = iD.coreGraph([a, b, r]);
|
||||
|
||||
expect(r.extent(graph).equals([[0, 0], [5, 10]])).to.be.ok;
|
||||
});
|
||||
|
||||
it('returns the known extent of incomplete relations', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [5, 10]}),
|
||||
r = iD.Relation({members: [{id: a.id}, {id: b.id}]}),
|
||||
graph = iD.Graph([a, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [5, 10]});
|
||||
var r = iD.osmRelation({members: [{id: a.id}, {id: b.id}]});
|
||||
var graph = iD.coreGraph([a, r]);
|
||||
|
||||
expect(r.extent(graph).equals([[0, 0], [0, 0]])).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not error on self-referencing relations', function () {
|
||||
var r = iD.Relation();
|
||||
var r = iD.osmRelation();
|
||||
r = r.addMember({id: r.id});
|
||||
expect(r.extent(iD.Graph([r]))).to.eql(iD.geoExtent());
|
||||
expect(r.extent(iD.coreGraph([r]))).to.eql(iD.geoExtent());
|
||||
});
|
||||
});
|
||||
|
||||
describe('#geometry', function () {
|
||||
it('returns \'area\' for multipolygons', function () {
|
||||
expect(iD.Relation({tags: {type: 'multipolygon'}}).geometry(iD.Graph())).to.equal('area');
|
||||
expect(iD.osmRelation({tags: {type: 'multipolygon'}}).geometry(iD.coreGraph())).to.equal('area');
|
||||
});
|
||||
|
||||
it('returns \'relation\' for other relations', function () {
|
||||
expect(iD.Relation().geometry(iD.Graph())).to.equal('relation');
|
||||
expect(iD.osmRelation().geometry(iD.coreGraph())).to.equal('relation');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isDegenerate', function () {
|
||||
it('returns true for a relation without members', function () {
|
||||
expect(iD.Relation().isDegenerate()).to.equal(true);
|
||||
expect(iD.osmRelation().isDegenerate()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns false for a relation with members', function () {
|
||||
expect(iD.Relation({members: [{id: 'a', role: 'inner'}]}).isDegenerate()).to.equal(false);
|
||||
expect(iD.osmRelation({members: [{id: 'a', role: 'inner'}]}).isDegenerate()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#memberByRole', function () {
|
||||
it('returns the first member with the given role', function () {
|
||||
var r = iD.Relation({members: [
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: 'a', role: 'inner'},
|
||||
{id: 'b', role: 'outer'},
|
||||
{id: 'c', role: 'outer'}]});
|
||||
@@ -167,13 +167,13 @@ describe('iD.osmRelation', function () {
|
||||
});
|
||||
|
||||
it('returns undefined if no members have the given role', function () {
|
||||
expect(iD.Relation().memberByRole('outer')).to.be.undefined;
|
||||
expect(iD.osmRelation().memberByRole('outer')).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#memberById', function () {
|
||||
it('returns the first member with the given id', function () {
|
||||
var r = iD.Relation({members: [
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: 'a', role: 'outer'},
|
||||
{id: 'b', role: 'outer'},
|
||||
{id: 'b', role: 'inner'}]});
|
||||
@@ -181,101 +181,247 @@ describe('iD.osmRelation', function () {
|
||||
});
|
||||
|
||||
it('returns undefined if no members have the given role', function () {
|
||||
expect(iD.Relation().memberById('b')).to.be.undefined;
|
||||
expect(iD.osmRelation().memberById('b')).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isRestriction', function () {
|
||||
it('returns true for \'restriction\' type', function () {
|
||||
expect(iD.Relation({tags: {type: 'restriction'}}).isRestriction()).to.be.true;
|
||||
expect(iD.osmRelation({tags: {type: 'restriction'}}).isRestriction()).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true for \'restriction:type\' types', function () {
|
||||
expect(iD.Relation({tags: {type: 'restriction:bus'}}).isRestriction()).to.be.true;
|
||||
expect(iD.osmRelation({tags: {type: 'restriction:bus'}}).isRestriction()).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false otherwise', function () {
|
||||
expect(iD.Relation().isRestriction()).to.be.false;
|
||||
expect(iD.Relation({tags: {type: 'multipolygon'}}).isRestriction()).to.be.false;
|
||||
expect(iD.osmRelation().isRestriction()).to.be.false;
|
||||
expect(iD.osmRelation({tags: {type: 'multipolygon'}}).isRestriction()).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isValidRestriction', function () {
|
||||
it('not a restriction', function () {
|
||||
var r = iD.osmRelation({ id: 'r', tags: { type: 'multipolygon' }});
|
||||
var graph = iD.coreGraph([r]);
|
||||
expect(r.isValidRestriction(graph)).to.be.false;
|
||||
});
|
||||
|
||||
it('typical restriction (from way, via node, to way) is valid', function () {
|
||||
var f = iD.osmWay({id: 'f'});
|
||||
var v = iD.osmNode({id: 'v'});
|
||||
var t = iD.osmWay({id: 't'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_left_turn' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'to', id: 't', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f, v, t, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.true;
|
||||
});
|
||||
|
||||
it('multiple froms, normal restriction is invalid', function () {
|
||||
var f1 = iD.osmWay({id: 'f1'});
|
||||
var f2 = iD.osmWay({id: 'f2'});
|
||||
var v = iD.osmNode({id: 'v'});
|
||||
var t = iD.osmWay({id: 't'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_left_turn' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f1', type: 'way' },
|
||||
{ role: 'from', id: 'f2', type: 'way' },
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'to', id: 't', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f1, f2, v, t, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.false;
|
||||
});
|
||||
|
||||
it('multiple froms, no_entry restriction is valid', function () {
|
||||
var f1 = iD.osmWay({id: 'f1'});
|
||||
var f2 = iD.osmWay({id: 'f2'});
|
||||
var v = iD.osmNode({id: 'v'});
|
||||
var t = iD.osmWay({id: 't'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_entry' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f1', type: 'way' },
|
||||
{ role: 'from', id: 'f2', type: 'way' },
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'to', id: 't', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f1, f2, v, t, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.true;
|
||||
});
|
||||
|
||||
it('multiple tos, normal restriction is invalid', function () {
|
||||
var f = iD.osmWay({id: 'f'});
|
||||
var v = iD.osmNode({id: 'v'});
|
||||
var t1 = iD.osmWay({id: 't1'});
|
||||
var t2 = iD.osmWay({id: 't2'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_left_turn' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'to', id: 't1', type: 'way' },
|
||||
{ role: 'to', id: 't2', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f, v, t1, t2, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.false;
|
||||
});
|
||||
|
||||
it('multiple tos, no_exit restriction is valid', function () {
|
||||
var f = iD.osmWay({id: 'f'});
|
||||
var v = iD.osmNode({id: 'v'});
|
||||
var t1 = iD.osmWay({id: 't1'});
|
||||
var t2 = iD.osmWay({id: 't2'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_exit' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'to', id: 't1', type: 'way' },
|
||||
{ role: 'to', id: 't2', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f, v, t1, t2, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.true;
|
||||
});
|
||||
|
||||
it('multiple vias, with some as node is invalid', function () {
|
||||
var f = iD.osmWay({id: 'f'});
|
||||
var v1 = iD.osmNode({id: 'v1'});
|
||||
var v2 = iD.osmWay({id: 'v2'});
|
||||
var t = iD.osmWay({id: 't'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_left_turn' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'via', id: 'v1', type: 'node' },
|
||||
{ role: 'via', id: 'v2', type: 'way' },
|
||||
{ role: 'to', id: 't', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f, v1, v2, t, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.false;
|
||||
});
|
||||
|
||||
it('multiple vias, with all as way is valid', function () {
|
||||
var f = iD.osmWay({id: 'f'});
|
||||
var v1 = iD.osmWay({id: 'v1'});
|
||||
var v2 = iD.osmWay({id: 'v2'});
|
||||
var t = iD.osmWay({id: 't'});
|
||||
var r = iD.osmRelation({
|
||||
id: 'r',
|
||||
tags: { type: 'restriction', restriction: 'no_left_turn' },
|
||||
members: [
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'via', id: 'v1', type: 'way' },
|
||||
{ role: 'via', id: 'v2', type: 'way' },
|
||||
{ role: 'to', id: 't', type: 'way' },
|
||||
]
|
||||
});
|
||||
var graph = iD.coreGraph([f, v1, v2, t, r]);
|
||||
|
||||
expect(r.isValidRestriction(graph)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#indexedMembers', function () {
|
||||
it('returns an array of members extended with indexes', function () {
|
||||
var r = iD.Relation({members: [{id: '1'}, {id: '3'}]});
|
||||
var r = iD.osmRelation({members: [{id: '1'}, {id: '3'}]});
|
||||
expect(r.indexedMembers()).to.eql([{id: '1', index: 0}, {id: '3', index: 1}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#addMember', function () {
|
||||
it('adds a member at the end of the relation', function () {
|
||||
var r = iD.Relation();
|
||||
var r = iD.osmRelation();
|
||||
expect(r.addMember({id: '1'}).members).to.eql([{id: '1'}]);
|
||||
});
|
||||
|
||||
it('adds a member at index 0', function () {
|
||||
var r = iD.Relation({members: [{id: '1'}]});
|
||||
var r = iD.osmRelation({members: [{id: '1'}]});
|
||||
expect(r.addMember({id: '2'}, 0).members).to.eql([{id: '2'}, {id: '1'}]);
|
||||
});
|
||||
|
||||
it('adds a member at a positive index', function () {
|
||||
var r = iD.Relation({members: [{id: '1'}, {id: '3'}]});
|
||||
var r = iD.osmRelation({members: [{id: '1'}, {id: '3'}]});
|
||||
expect(r.addMember({id: '2'}, 1).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
|
||||
it('adds a member at a negative index', function () {
|
||||
var r = iD.Relation({members: [{id: '1'}, {id: '3'}]});
|
||||
var r = iD.osmRelation({members: [{id: '1'}, {id: '3'}]});
|
||||
expect(r.addMember({id: '2'}, -1).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateMember', function () {
|
||||
it('updates the properties of the relation member at the specified index', function () {
|
||||
var r = iD.Relation({members: [{role: 'forward'}]});
|
||||
var r = iD.osmRelation({members: [{role: 'forward'}]});
|
||||
expect(r.updateMember({role: 'backward'}, 0).members).to.eql([{role: 'backward'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeMember', function () {
|
||||
it('removes the member at the specified index', function () {
|
||||
var r = iD.Relation({members: [{id: 'a'}, {id: 'b'}, {id: 'c'}]});
|
||||
var r = iD.osmRelation({members: [{id: 'a'}, {id: 'b'}, {id: 'c'}]});
|
||||
expect(r.removeMember(1).members).to.eql([{id: 'a'}, {id: 'c'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeMembersWithID', function () {
|
||||
it('removes members with the given ID', function () {
|
||||
var r = iD.Relation({members: [{id: 'a'}, {id: 'b'}, {id: 'a'}]});
|
||||
var r = iD.osmRelation({members: [{id: 'a'}, {id: 'b'}, {id: 'a'}]});
|
||||
expect(r.removeMembersWithID('a').members).to.eql([{id: 'b'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#replaceMember', function () {
|
||||
it('returns self if self does not contain needle', function () {
|
||||
var r = iD.Relation({members: []});
|
||||
var r = iD.osmRelation({members: []});
|
||||
expect(r.replaceMember({id: 'a'}, {id: 'b'})).to.equal(r);
|
||||
});
|
||||
|
||||
it('replaces a member which doesn\'t already exist', function () {
|
||||
var r = iD.Relation({members: [{id: 'a', role: 'a'}]});
|
||||
var r = iD.osmRelation({members: [{id: 'a', role: 'a'}]});
|
||||
expect(r.replaceMember({id: 'a'}, {id: 'b', type: 'node'}).members)
|
||||
.to.eql([{id: 'b', role: 'a', type: 'node'}]);
|
||||
});
|
||||
|
||||
it('preserves the existing role', function () {
|
||||
var r = iD.Relation({members: [{id: 'a', role: 'a', type: 'node'}]});
|
||||
var r = iD.osmRelation({members: [{id: 'a', role: 'a', type: 'node'}]});
|
||||
expect(r.replaceMember({id: 'a'}, {id: 'b', type: 'node'}).members)
|
||||
.to.eql([{id: 'b', role: 'a', type: 'node'}]);
|
||||
});
|
||||
|
||||
it('uses the replacement type', function () {
|
||||
var r = iD.Relation({members: [{id: 'a', role: 'a', type: 'node'}]});
|
||||
var r = iD.osmRelation({members: [{id: 'a', role: 'a', type: 'node'}]});
|
||||
expect(r.replaceMember({id: 'a'}, {id: 'b', type: 'way'}).members)
|
||||
.to.eql([{id: 'b', role: 'a', type: 'way'}]);
|
||||
});
|
||||
|
||||
it('removes members if replacing them would produce duplicates', function () {
|
||||
var r = iD.Relation({members: [
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: 'a', role: 'b', type: 'node'},
|
||||
{id: 'b', role: 'b', type: 'node'}
|
||||
]});
|
||||
@@ -283,7 +429,7 @@ describe('iD.osmRelation', function () {
|
||||
.to.eql([{id: 'b', role: 'b', type: 'node'}]);
|
||||
});
|
||||
it('keeps duplicate members if `keepDuplicates = true`', function () {
|
||||
var r = iD.Relation({members: [
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: 'a', role: 'b', type: 'node'},
|
||||
{id: 'b', role: 'b', type: 'node'}
|
||||
]});
|
||||
@@ -294,7 +440,7 @@ describe('iD.osmRelation', function () {
|
||||
|
||||
describe('#asJXON', function () {
|
||||
it('converts a relation to jxon', function() {
|
||||
var relation = iD.Relation({id: 'r-1', members: [{id: 'w1', role: 'forward', type: 'way'}], tags: {type: 'route'}});
|
||||
var relation = iD.osmRelation({id: 'r-1', members: [{id: 'w1', role: 'forward', type: 'way'}], tags: {type: 'route'}});
|
||||
expect(relation.asJXON()).to.eql({relation: {
|
||||
'@id': '-1',
|
||||
'@version': 0,
|
||||
@@ -303,56 +449,56 @@ describe('iD.osmRelation', function () {
|
||||
});
|
||||
|
||||
it('includes changeset if provided', function() {
|
||||
expect(iD.Relation().asJXON('1234').relation['@changeset']).to.equal('1234');
|
||||
expect(iD.osmRelation().asJXON('1234').relation['@changeset']).to.equal('1234');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#asGeoJSON', function (){
|
||||
describe('#asGeoJSON', function () {
|
||||
it('converts a multipolygon to a GeoJSON MultiPolygon geometry', function() {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]),
|
||||
json = r.asGeoJSON(g);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var r = iD.osmRelation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w, r]);
|
||||
var json = r.asGeoJSON(g);
|
||||
|
||||
expect(json.type).to.equal('MultiPolygon');
|
||||
expect(json.coordinates).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
it('forces clockwise winding order for outer multipolygon ways', function() {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
w = iD.Way({nodes: [a.id, c.id, b.id, a.id]}),
|
||||
r = iD.Relation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]),
|
||||
json = r.asGeoJSON(g);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var w = iD.osmWay({nodes: [a.id, c.id, b.id, a.id]});
|
||||
var r = iD.osmRelation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w, r]);
|
||||
var json = r.asGeoJSON(g);
|
||||
|
||||
expect(json.coordinates[0][0]).to.eql([a.loc, b.loc, c.loc, a.loc]);
|
||||
});
|
||||
|
||||
it('forces counterclockwise winding order for inner multipolygon ways', function() {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.1, 0.2]}),
|
||||
f = iD.Node({loc: [0.2, 0.1]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, outer, inner, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var d = iD.osmNode({loc: [0.1, 0.1]});
|
||||
var e = iD.osmNode({loc: [0.1, 0.2]});
|
||||
var f = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var outer = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var inner = iD.osmWay({nodes: [d.id, e.id, f.id, d.id]});
|
||||
var r = iD.osmRelation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, d, e, f, outer, inner, r]);
|
||||
|
||||
expect(r.multipolygon(g)[0][1]).to.eql([d.loc, f.loc, e.loc, d.loc]);
|
||||
});
|
||||
|
||||
it('converts a relation to a GeoJSON FeatureCollection', function() {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
r = iD.Relation({tags: {type: 'type'}, members: [{id: a.id, role: 'role'}]}),
|
||||
g = iD.Graph([a, r]),
|
||||
json = r.asGeoJSON(g);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var r = iD.osmRelation({tags: {type: 'type'}, members: [{id: a.id, role: 'role'}]});
|
||||
var g = iD.coreGraph([a, r]);
|
||||
var json = r.asGeoJSON(g);
|
||||
|
||||
expect(json.type).to.equal('FeatureCollection');
|
||||
expect(json.properties).to.eql({type: 'type'});
|
||||
@@ -365,214 +511,220 @@ describe('iD.osmRelation', function () {
|
||||
|
||||
describe('#multipolygon', function () {
|
||||
specify('single polygon consisting of a single way', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var r = iD.osmRelation({members: [{id: w.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('single polygon consisting of multiple ways', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id]}),
|
||||
w2 = iD.Way({nodes: [b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id]});
|
||||
var w2 = iD.osmWay({nodes: [b.id, c.id, a.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('single polygon consisting of multiple ways, one needing reversal', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id]}),
|
||||
w2 = iD.Way({nodes: [a.id, c.id, b.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id]});
|
||||
var w2 = iD.osmWay({nodes: [a.id, c.id, b.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('multiple polygons consisting of single ways', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
e = iD.Node({loc: [6, 6]}),
|
||||
f = iD.Node({loc: [5, 5]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
w2 = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var d = iD.osmNode({loc: [4, 4]});
|
||||
var e = iD.osmNode({loc: [6, 6]});
|
||||
var f = iD.osmNode({loc: [5, 5]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var w2 = iD.osmWay({nodes: [d.id, e.id, f.id, d.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, d, e, f, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]], [[d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unclosed ring consisting of a single way', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w = iD.osmWay({nodes: [a.id, b.id, c.id]});
|
||||
var r = iD.osmRelation({members: [{id: w.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unclosed ring consisting of multiple ways', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [3, 3]}),
|
||||
c = iD.Node({loc: [2, 2]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id]}),
|
||||
w2 = iD.Way({nodes: [b.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [3, 3]});
|
||||
var c = iD.osmNode({loc: [2, 2]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id]});
|
||||
var w2 = iD.osmWay({nodes: [b.id, c.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unclosed ring consisting of multiple ways, alternate order', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [c.id, d.id]}),
|
||||
w2 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [2, 2]});
|
||||
var c = iD.osmNode({loc: [3, 3]});
|
||||
var d = iD.osmNode({loc: [4, 4]});
|
||||
var w1 = iD.osmWay({nodes: [c.id, d.id]});
|
||||
var w2 = iD.osmWay({nodes: [a.id, b.id, c.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[d.loc, c.loc, b.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unclosed ring consisting of multiple ways, one needing reversal', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way({nodes: [d.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [2, 2]});
|
||||
var c = iD.osmNode({loc: [3, 3]});
|
||||
var d = iD.osmNode({loc: [4, 4]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id, c.id]});
|
||||
var w2 = iD.osmWay({nodes: [d.id, c.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[d.loc, c.loc, b.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unclosed ring consisting of multiple ways, one needing reversal, alternate order', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [c.id, d.id]}),
|
||||
w2 = iD.Way({nodes: [c.id, b.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [2, 2]});
|
||||
var c = iD.osmNode({loc: [3, 3]});
|
||||
var d = iD.osmNode({loc: [4, 4]});
|
||||
var w1 = iD.osmWay({nodes: [c.id, d.id]});
|
||||
var w2 = iD.osmWay({nodes: [c.id, b.id, a.id]});
|
||||
var r = iD.osmRelation({members: [{id: w1.id, type: 'way'}, {id: w2.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[d.loc, c.loc, b.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('single polygon with single single-way inner', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, outer, inner, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var d = iD.osmNode({loc: [0.1, 0.1]});
|
||||
var e = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var f = iD.osmNode({loc: [0.1, 0.2]});
|
||||
var outer = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var inner = iD.osmWay({nodes: [d.id, e.id, f.id, d.id]});
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner.id, role: 'inner', type: 'way'}
|
||||
]});
|
||||
var g = iD.coreGraph([a, b, c, d, e, f, outer, inner, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify('single polygon with single multi-way inner', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.2, 0.1]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner1 = iD.Way({nodes: [d.id, e.id]}),
|
||||
inner2 = iD.Way({nodes: [e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, outer, inner1, inner2, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var d = iD.osmNode({loc: [0.1, 0.1]});
|
||||
var e = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var f = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var outer = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var inner1 = iD.osmWay({nodes: [d.id, e.id]});
|
||||
var inner2 = iD.osmWay({nodes: [e.id, f.id, d.id]});
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'}
|
||||
]});
|
||||
var graph = iD.coreGraph([a, b, c, d, e, f, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify('single polygon with multiple single-way inners', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
g = iD.Node({loc: [0.2, 0.2]}),
|
||||
h = iD.Node({loc: [0.3, 0.2]}),
|
||||
i = iD.Node({loc: [0.2, 0.3]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner1 = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
inner2 = iD.Way({nodes: [g.id, h.id, i.id, g.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer, inner1, inner2, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var d = iD.osmNode({loc: [0.1, 0.1]});
|
||||
var e = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var f = iD.osmNode({loc: [0.1, 0.2]});
|
||||
var g = iD.osmNode({loc: [0.2, 0.2]});
|
||||
var h = iD.osmNode({loc: [0.3, 0.2]});
|
||||
var i = iD.osmNode({loc: [0.2, 0.3]});
|
||||
var outer = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var inner1 = iD.osmWay({nodes: [d.id, e.id, f.id, d.id]});
|
||||
var inner2 = iD.osmWay({nodes: [g.id, h.id, i.id, g.id]});
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'}
|
||||
]});
|
||||
var graph = iD.coreGraph([a, b, c, d, e, f, g, h, i, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc], [g.loc, h.loc, i.loc, g.loc]]]);
|
||||
});
|
||||
|
||||
specify('multiple polygons with single single-way inner', function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [0, 1]}),
|
||||
c = iD.Node({loc: [1, 0]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
g = iD.Node({loc: [0, 0]}),
|
||||
h = iD.Node({loc: [0, -1]}),
|
||||
i = iD.Node({loc: [-1, 0]}),
|
||||
outer1 = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
outer2 = iD.Way({nodes: [g.id, h.id, i.id, g.id]}),
|
||||
inner = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer1.id, type: 'way'},
|
||||
{id: outer2.id, type: 'way'},
|
||||
{id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer1, outer2, inner, r]);
|
||||
var a = iD.osmNode({loc: [0, 0]});
|
||||
var b = iD.osmNode({loc: [0, 1]});
|
||||
var c = iD.osmNode({loc: [1, 0]});
|
||||
var d = iD.osmNode({loc: [0.1, 0.1]});
|
||||
var e = iD.osmNode({loc: [0.2, 0.1]});
|
||||
var f = iD.osmNode({loc: [0.1, 0.2]});
|
||||
var g = iD.osmNode({loc: [0, 0]});
|
||||
var h = iD.osmNode({loc: [0, -1]});
|
||||
var i = iD.osmNode({loc: [-1, 0]});
|
||||
var outer1 = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var outer2 = iD.osmWay({nodes: [g.id, h.id, i.id, g.id]});
|
||||
var inner = iD.osmWay({nodes: [d.id, e.id, f.id, d.id]});
|
||||
var r = iD.osmRelation({members: [
|
||||
{id: outer1.id, type: 'way'},
|
||||
{id: outer2.id, type: 'way'},
|
||||
{id: inner.id, role: 'inner', type: 'way'}
|
||||
]});
|
||||
var graph = iD.coreGraph([a, b, c, d, e, f, g, h, i, outer1, outer2, inner, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]], [[g.loc, h.loc, i.loc, g.loc]]]);
|
||||
});
|
||||
|
||||
specify('invalid geometry: unmatched inner', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [2, 2]});
|
||||
var c = iD.osmNode({loc: [3, 3]});
|
||||
var w = iD.osmWay({nodes: [a.id, b.id, c.id, a.id]});
|
||||
var r = iD.osmRelation({members: [{id: w.id, role: 'inner', type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify('incomplete relation', function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way(),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w1, r]);
|
||||
var a = iD.osmNode({loc: [1, 1]});
|
||||
var b = iD.osmNode({loc: [2, 2]});
|
||||
var c = iD.osmNode({loc: [3, 3]});
|
||||
var w1 = iD.osmWay({nodes: [a.id, b.id, c.id]});
|
||||
var w2 = iD.osmWay();
|
||||
var r = iD.osmRelation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]});
|
||||
var g = iD.coreGraph([a, b, c, w1, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc]]]);
|
||||
});
|
||||
@@ -580,17 +732,17 @@ describe('iD.osmRelation', function () {
|
||||
|
||||
describe('.creationOrder comparator', function () {
|
||||
specify('orders existing relations newest-first', function () {
|
||||
var a = iD.Relation({ id: 'r1' }),
|
||||
b = iD.Relation({ id: 'r2' });
|
||||
expect(iD.Relation.creationOrder(a, b)).to.be.above(0);
|
||||
expect(iD.Relation.creationOrder(b, a)).to.be.below(0);
|
||||
var a = iD.osmRelation({ id: 'r1' });
|
||||
var b = iD.osmRelation({ id: 'r2' });
|
||||
expect(iD.osmRelation.creationOrder(a, b)).to.be.above(0);
|
||||
expect(iD.osmRelation.creationOrder(b, a)).to.be.below(0);
|
||||
});
|
||||
|
||||
specify('orders new relations newest-first', function () {
|
||||
var a = iD.Relation({ id: 'r-1' }),
|
||||
b = iD.Relation({ id: 'r-2' });
|
||||
expect(iD.Relation.creationOrder(a, b)).to.be.above(0);
|
||||
expect(iD.Relation.creationOrder(b, a)).to.be.below(0);
|
||||
var a = iD.osmRelation({ id: 'r-1' });
|
||||
var b = iD.osmRelation({ id: 'r-2' });
|
||||
expect(iD.osmRelation.creationOrder(a, b)).to.be.above(0);
|
||||
expect(iD.osmRelation.creationOrder(b, a)).to.be.below(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user