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() { -});