From 43777d5259baf66b12902117f11dfc5efa5c136f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 2 Sep 2014 16:43:31 -0400 Subject: [PATCH] Better inferRestriction for no_u_turn * where from.node === to.node * where from.way and to.way are oneways joined at a narrow angle --- js/id/actions/restrict_turn.js | 17 ++++------- js/id/geo/intersection.js | 26 ++++++++++++----- js/id/ui/preset/restrictions.js | 7 +++-- test/spec/actions/restrict_turn.js | 47 +++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/js/id/actions/restrict_turn.js b/js/id/actions/restrict_turn.js index a797a544a..c2430908a 100644 --- a/js/id/actions/restrict_turn.js +++ b/js/id/actions/restrict_turn.js @@ -11,14 +11,8 @@ // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`. // (The action does not check that these entities form a valid intersection.) // -// If `restriction` is not provided, it is automatically determined by the -// angle of the turn: -// -// 0-5 degrees: no_u_turn -// 5-158 degrees: no_right_turn -// 158-202 degrees: no_straight_on -// 202-355 degrees: no_left_turn -// 355-360 degrees: no_u_turn +// If `restriction` is not provided, it is automatically determined by +// iD.geo.inferRestriction. // // 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 @@ -74,9 +68,10 @@ iD.actions.RestrictTurn = function(turn, projection, restrictionId) { type: 'restriction', restriction: turn.restriction || iD.geo.inferRestriction( - graph.entity(turn.from.node), - via, - graph.entity(turn.to.node), + graph, + turn.from, + turn.via, + turn.to, projection) }, members: [ diff --git a/js/id/geo/intersection.js b/js/id/geo/intersection.js index 3efe9670b..05f70ef01 100644 --- a/js/id/geo/intersection.js +++ b/js/id/geo/intersection.js @@ -118,23 +118,33 @@ iD.geo.Intersection = function(graph, vertexId) { return intersection; }; -iD.geo.inferRestriction = function(from, via, to, projection) { - var angle = iD.geo.angle(via, from, projection) - - iD.geo.angle(via, to, projection); + +iD.geo.inferRestriction = function(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 = iD.geo.angle(viaNode, fromNode, projection) - + iD.geo.angle(viaNode, toNode, projection); angle = angle * 180 / Math.PI; while (angle < 0) angle += 360; - if (angle < 5) + if (fromNode === toNode) + return 'no_u_turn'; + if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) return 'no_u_turn'; if (angle < 158) return 'no_right_turn'; - if (angle < 202) - return 'no_straight_on'; - if (angle < 355) + if (angle > 202) return 'no_left_turn'; - return 'no_u_turn'; + return 'no_straight_on'; }; diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index ae7ec22ef..7f9824958 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -99,9 +99,10 @@ iD.ui.preset.restrictions = function(field, context) { } else { preset = presets.item('type/restriction/' + iD.geo.inferRestriction( - graph.entity(datum.from.node), - graph.entity(datum.via.node), - graph.entity(datum.to.node), + graph, + datum.from, + datum.via, + datum.to, projection)); } diff --git a/test/spec/actions/restrict_turn.js b/test/spec/actions/restrict_turn.js index 2ed2e9b20..d093c3bd4 100644 --- a/test/spec/actions/restrict_turn.js +++ b/test/spec/actions/restrict_turn.js @@ -250,7 +250,7 @@ describe("iD.actions.RestrictTurn", function() { expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: 'way'}); }); - it('guesses the restriction type based on the turn angle', function() { + it('infers the restriction type based on the turn angle', function() { // u====*~~~~w // | // x @@ -306,4 +306,49 @@ describe("iD.actions.RestrictTurn", function() { }, 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.actions.RestrictTurn({ + 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.actions.RestrictTurn({ + 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'); + }); + });