diff --git a/index.html b/index.html
index 7f192a48d..aa156ee5e 100644
--- a/index.html
+++ b/index.html
@@ -47,9 +47,9 @@
+
-
diff --git a/js/id/geo/intersection.js b/js/id/geo/intersection.js
new file mode 100644
index 000000000..c53e6db2a
--- /dev/null
+++ b/js/id/geo/intersection.js
@@ -0,0 +1,114 @@
+iD.geo.Turn = function(turn) {
+ turn = _.clone(turn);
+
+ turn.key = function() {
+ var components = [turn.from, turn.to, turn.via, turn.toward];
+ if (turn.restriction)
+ components.push(turn.restriction);
+ return components.map(iD.Entity.key).join('-');
+ };
+
+ turn.angle = function(projection) {
+ var v = projection(turn.via.loc),
+ t = projection(turn.toward.loc);
+
+ return Math.atan2(t[1] - v[1], t[0] - v[0]);
+ };
+
+ return turn;
+};
+
+iD.geo.Intersection = function(graph, vertexId) {
+ var vertex = graph.entity(vertexId),
+ highways = [];
+
+ // 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.
+ graph.parentWays(vertex).forEach(function(way) {
+ if (!way.tags.highway || way.isArea() || way.isDegenerate())
+ return;
+
+ if (way.affix(vertexId)) {
+ highways.push(way);
+ } else {
+ var idx = _.indexOf(way.nodes, vertex.id, 1),
+ wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, idx + 1)}),
+ wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(idx)});
+
+ graph = graph.replace(wayA);
+ graph = graph.replace(wayB);
+
+ highways.push(wayA);
+ highways.push(wayB);
+ }
+ });
+
+ var intersection = {
+ highways: highways,
+ graph: graph
+ };
+
+ intersection.turns = function(wayId) {
+ if (!wayId)
+ return [];
+
+ var way = graph.entity(wayId);
+ if (way.first() === vertex.id && way.tags.oneway === 'yes')
+ return [];
+ if (way.last() === vertex.id && way.tags.oneway === '-1')
+ return [];
+
+ function withRestriction(turn) {
+ graph.parentRelations(turn.from).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.id &&
+ t && t.id === turn.to.id &&
+ v && v.id === turn.via.id) {
+ turn.restriction = relation;
+ }
+ });
+
+ return iD.geo.Turn(turn);
+ }
+
+ var turns = [];
+
+ highways.forEach(function(parent) {
+ if (parent === way)
+ return;
+
+ var index = parent.nodes.indexOf(vertex.id);
+
+ // backward
+ if (parent.first() !== vertex.id && parent.tags.oneway !== 'yes') {
+ turns.push(withRestriction({
+ from: way,
+ to: parent,
+ via: vertex,
+ toward: graph.entity(parent.nodes[index - 1])
+ }));
+ }
+
+ // forward
+ if (parent.last() !== vertex.id && parent.tags.oneway !== '-1') {
+ turns.push(withRestriction({
+ from: way,
+ to: parent,
+ via: vertex,
+ toward: graph.entity(parent.nodes[index + 1])
+ }));
+ }
+ });
+
+ return turns;
+ };
+
+ return intersection;
+};
diff --git a/js/id/geo/turn.js b/js/id/geo/turn.js
deleted file mode 100644
index 5776000e9..000000000
--- a/js/id/geo/turn.js
+++ /dev/null
@@ -1,82 +0,0 @@
-iD.geo.Turn = function(turn) {
- turn = _.clone(turn);
-
- turn.key = function() {
- var components = [turn.from, turn.to, turn.via, turn.toward];
- if (turn.restriction)
- components.push(turn.restriction);
- return components.map(iD.Entity.key).join('-');
- };
-
- turn.angle = function(projection) {
- var v = projection(turn.via.loc),
- t = projection(turn.toward.loc);
-
- return Math.atan2(t[1] - v[1], t[0] - v[0]);
- };
-
- return turn;
-};
-
-iD.geo.turns = function(graph, entityID) {
- var way = graph.entity(entityID);
- if (way.type !== 'way' || !way.tags.highway || way.isArea())
- return [];
-
- function withRestriction(turn) {
- graph.parentRelations(turn.from).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.id &&
- t && t.id === turn.to.id &&
- v && v.id === turn.via.id) {
- turn.restriction = relation;
- }
- });
-
- return iD.geo.Turn(turn);
- }
-
- var turns = [];
-
- [way.first(), way.last()].forEach(function(nodeID) {
- var node = graph.entity(nodeID);
- graph.parentWays(node).forEach(function(parent) {
- if (parent === way || parent.isDegenerate() || !parent.tags.highway)
- return;
- if (way.first() === node.id && way.tags.oneway === 'yes')
- return;
- if (way.last() === node.id && way.tags.oneway === '-1')
- return;
-
- var index = parent.nodes.indexOf(node.id);
-
- // backward
- if (parent.first() !== node.id && parent.tags.oneway !== 'yes') {
- turns.push(withRestriction({
- from: way,
- to: parent,
- via: node,
- toward: graph.entity(parent.nodes[index - 1])
- }));
- }
-
- // forward
- if (parent.last() !== node.id && parent.tags.oneway !== '-1') {
- turns.push(withRestriction({
- from: way,
- to: parent,
- via: node,
- toward: graph.entity(parent.nodes[index + 1])
- }));
- }
- });
- });
-
- return turns;
-};
diff --git a/js/id/svg/turns.js b/js/id/svg/turns.js
index f86518ce3..dce3ca924 100644
--- a/js/id/svg/turns.js
+++ b/js/id/svg/turns.js
@@ -1,7 +1,5 @@
iD.svg.Turns = function(projection) {
- return function(surface, graph, wayID) {
- var turns = wayID ? iD.geo.turns(graph, wayID) : [];
-
+ return function(surface, graph, turns) {
var groups = surface.select('.layer-hit').selectAll('g.turn')
.data(turns, function(turn) { return turn.key(); });
diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js
index 9d72bd406..c604d04bd 100644
--- a/js/id/ui/preset/restrictions.js
+++ b/js/id/ui/preset/restrictions.js
@@ -1,6 +1,6 @@
iD.ui.preset.restrictions = function(field, context) {
var event = d3.dispatch('change'),
- entity,
+ vertex,
selectedID;
function restrictions(selection) {
@@ -23,7 +23,7 @@ iD.ui.preset.restrictions = function(field, context) {
var projection = iD.geo.RawMercator()
.scale(256 * Math.pow(2, z) / (2 * Math.PI));
- var s = projection(entity ? entity.loc : [0, 0]);
+ var s = projection(vertex.loc);
projection
.translate([c[0] - s[0], c[1] - s[1]])
@@ -32,28 +32,16 @@ iD.ui.preset.restrictions = function(field, context) {
var surface = wrap.selectAll('svg'),
filter = function () { return true; },
extent = iD.geo.Extent(),
- entities = [],
- graph = context.graph(),
+ intersection = iD.geo.Intersection(context.graph(), vertex.id),
+ graph = intersection.graph,
lines = iD.svg.Lines(projection, context),
vertices = iD.svg.Vertices(projection, context),
turns = iD.svg.Turns(projection, context);
- if (entity) {
- entities = graph.parentWays(entity).filter(function (parent) {
- return parent.type === 'way' && parent.tags.highway && !parent.isArea();
- });
-
- entities.push(entity);
- }
-
- if (!selectedID && entities.length) {
- selectedID = entities[0].id;
- }
-
surface
- .call(vertices, graph, entities, filter, extent, z)
- .call(lines, graph, entities, filter)
- .call(turns, graph, selectedID);
+ .call(vertices, graph, [vertex], filter, extent, z)
+ .call(lines, graph, intersection.highways, filter)
+ .call(turns, graph, intersection.turns(selectedID));
surface.on('click.select', function() {
var datum = d3.event.target.__data__;
@@ -85,9 +73,9 @@ iD.ui.preset.restrictions = function(field, context) {
}
restrictions.entity = function(_) {
- if (!entity || entity.id !== _.id) {
+ if (!vertex || vertex.id !== _.id) {
selectedID = null;
- entity = _;
+ vertex = _;
}
};
diff --git a/test/index.html b/test/index.html
index 708e20622..5b6469948 100644
--- a/test/index.html
+++ b/test/index.html
@@ -45,9 +45,9 @@
+
-
@@ -57,14 +57,14 @@
+
+
-
-
@@ -234,8 +234,8 @@
+
-
@@ -258,7 +258,6 @@
-
diff --git a/test/index_packaged.html b/test/index_packaged.html
index 55c067b52..0f83951a5 100644
--- a/test/index_packaged.html
+++ b/test/index_packaged.html
@@ -49,8 +49,8 @@
+
-
@@ -73,7 +73,6 @@
-
diff --git a/test/spec/geo/intersection.js b/test/spec/geo/intersection.js
new file mode 100644
index 000000000..c77b50c78
--- /dev/null
+++ b/test/spec/geo/intersection.js
@@ -0,0 +1,276 @@
+describe('iD.geo.Turn', function() {
+ describe('#angle', function() {
+ it("calculates the angle of via to toward", function() {
+ function projection(x) { return x; }
+
+ var turn = iD.geo.Turn({
+ via: iD.Node({id: 'v', loc: [1, 0]}),
+ toward: iD.Node({id: 'w', loc: [1, 1]})
+ });
+
+ expect(turn.angle(projection)).to.eql(Math.PI / 2);
+ });
+ });
+});
+
+describe("iD.geo.Intersection", 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']})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').highways).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'}})
+ ]);
+ expect(_.pluck(iD.geo.Intersection(graph, '*').highways, 'id')).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']})
+ ]);
+ expect(_.pluck(iD.geo.Intersection(graph, '*').highways, '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'}})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').highways).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'}})
+ ]);
+ expect(_.pluck(iD.geo.Intersection(graph, '*').highways, 'id')).to.eql(['=-a', '=-b']);
+ });
+ });
+
+ describe('#turns', function() {
+ function ids(turns) {
+ return turns.map(function (turn) {
+ var result = {
+ from: turn.from.id,
+ to: turn.to.id,
+ via: turn.via.id,
+ toward: turn.toward.id
+ };
+
+ if (turn.restriction)
+ result.restriction = turn.restriction.id;
+
+ return result;
+ });
+ }
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{
+ from: '=',
+ to: '-',
+ via: '*',
+ toward: 'w'
+ }]);
+ });
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{from: '=', to: '-', via: '*', toward: 'w'}]);
+ });
+
+ it("permits turns onto a way in both directions", function() {
+ // w
+ // |
+ // 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([
+ {from: '=', to: '--a', via: '*', toward: 'w'},
+ {from: '=', to: '--b', via: '*', toward: 'x'}
+ ]);
+ });
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{from: '=', to: '-', via: '*', toward: 'w'}]);
+ });
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{from: '=', to: '-', via: '*', toward: 'w'}]);
+ });
+
+ 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'}})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').turns('=')).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'}})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').turns('=')).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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{from: '=', to: '-', via: '*', toward: 'w'}]);
+ });
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{from: '=', to: '-', via: '*', toward: 'w'}]);
+ });
+
+ 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'}})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').turns('=')).to.eql([]);
+ });
+
+ 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'}})
+ ]);
+ expect(iD.geo.Intersection(graph, '*').turns('=')).to.eql([]);
+ });
+
+ 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.geo.Intersection(graph, '*').turns('=');
+
+ expect(ids(turns)).to.eql([{
+ from: '=',
+ to: '-',
+ via: '*',
+ toward: 'w',
+ restriction: 'r'
+ }]);
+ });
+ });
+
+ // 'no' vs 'only'
+ // U-turns
+ // Self-intersections
+});
diff --git a/test/spec/geo/turn.js b/test/spec/geo/turn.js
deleted file mode 100644
index 44f95dc92..000000000
--- a/test/spec/geo/turn.js
+++ /dev/null
@@ -1,312 +0,0 @@
-describe('iD.geo.Turn', function() {
- describe('#angle', function() {
- it("calculates the angle of via to toward", function() {
- function projection(x) { return x; }
-
- var turn = iD.geo.Turn({
- via: iD.Node({id: 'v', loc: [1, 0]}),
- toward: iD.Node({id: 'w', loc: [1, 1]})
- });
-
- expect(turn.angle(projection)).to.eql(Math.PI / 2);
- });
- });
-});
-
-describe("iD.geo.turns", function() {
- function properties(turns) {
- return turns.map(function (turn) { return _.pick(turn, 'from', 'to', 'via', 'toward', 'restriction') });
- }
-
- it("returns an empty array for non-ways", function() {
- var graph = iD.Graph([
- iD.Node({id: 'n'})
- ]);
- expect(iD.geo.turns(graph, 'n')).to.eql([]);
- });
-
- it("returns an empty array for non-lines", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', area: 'yes'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("returns an empty array for an unconnected way", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Way({id: '=', nodes: ['u', 'v']})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns onto degenerate ways", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v'], tags: {highway: 'residential'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns from non-highways", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v']}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns onto non-highways", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w']})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns onto non-lines", function() {
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Node({id: 'x'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w', 'x', 'v'], tags: {highway: 'residential', area: 'yes'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("permits turns onto a way forward", function() {
- // u====v--->w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("permits turns onto a way backward", function() {
- // u====v<---w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("permits turns onto a way in both directions", function() {
- // w
- // |
- // u===v
- // |
- // x
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Node({id: 'x'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['w', 'v', 'x'], tags: {highway: 'residential'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }, {
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('x')
- }]);
- });
-
- it("permits turns from a oneway forward", function() {
- // u===>v----w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', oneway: 'yes'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("permits turns from a reverse oneway backward", function() {
- // u<===v----w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['v', 'u'], tags: {highway: 'residential', oneway: '-1'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("omits turns from a oneway backward", function() {
- // u<===v----w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['v', 'u'], tags: {highway: 'residential', oneway: 'yes'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns from a reverse oneway forward", function() {
- // u===>v----w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', oneway: '-1'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("permits turns onto a oneway forward", function() {
- // u====v--->w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential', oneway: 'yes'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("permits turns onto a reverse oneway backward", function() {
- // u====v<---w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential', oneway: '-1'}})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w')
- }]);
- });
-
- it("omits turns onto a oneway backward", function() {
- // u====v<---w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential', oneway: 'yes'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("omits turns onto a reverse oneway forward", function() {
- // u====v--->w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential', oneway: '-1'}})
- ]);
- expect(iD.geo.turns(graph, '=')).to.eql([]);
- });
-
- it("restricts turns with a restriction relation", function() {
- // u====v--->w
- var graph = iD.Graph([
- iD.Node({id: 'u'}),
- iD.Node({id: 'v'}),
- iD.Node({id: 'w'}),
- iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
- iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}}),
- iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
- {id: '=', role: 'from', type: 'way'},
- {id: '-', role: 'to', type: 'way'},
- {id: 'v', role: 'via', type: 'node'}
- ]})
- ]),
- turns = iD.geo.turns(graph, '=');
-
- expect(properties(turns)).to.eql([{
- from: graph.entity('='),
- to: graph.entity('-'),
- via: graph.entity('v'),
- toward: graph.entity('w'),
- restriction: graph.entity('r')
- }]);
- });
-
- // 'no' vs 'only'
- // U-turns
- // Self-intersections
- // Split point
-});
diff --git a/test/spec/svg/restrictions.js b/test/spec/svg/restrictions.js
deleted file mode 100644
index 5d4f5e5e3..000000000
--- a/test/spec/svg/restrictions.js
+++ /dev/null
@@ -1,2 +0,0 @@
-describe("iD.svg.Restrictions", function() {
-});