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({ from: {node: 'u', way: '='}, via: {node: '*'}, to: {node: 'w', way: '-'}, restriction: 'no_right_turn' }, projection, 'r'); graph = action(graph); var r = graph.entity('r'); expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'}); expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: 'way'}); }); 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({ from: {node: 'u', way: '='}, via: {node: '*'}, to: {node: 'x', way: '-'}, restriction: 'no_right_turn' }, projection, 'r'); graph = action(graph); var r = graph.entity('r'); expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'}); expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: '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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: '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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: '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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: '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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '--', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: '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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '-', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '--', type: '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({ from: {node: 'x', way: '-'}, via: {node: '*'}, to: {node: 'w', way: '=', newID: '=='}, restriction: 'no_right_turn' }, projection, 'r'); graph = action(graph); var r = graph.entity('r'); expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'}); expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '-', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: '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({ from: {node: 'x', way: '-'}, via: {node: '*'}, to: {node: 'u', 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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '-', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: '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: '='}, via: {node: '*'}, to: {node: 'c', way: '-', newID: '--'}, restriction: 'no_right_turn' }, projection, 'r'); graph = action(graph); var r = graph.entity('r'); expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'}); expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '--', type: '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({ from: {node: 'u', way: '='}, via: {node: '*'}, to: {node: 'u', way: '='}, 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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: 'way'}); }); 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(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'}); expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'}); expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: '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'); }); });