From 80a998e9c5df9d34d3a9feb75ac24bc0dad01180 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 29 Jan 2018 17:49:08 -0500 Subject: [PATCH] Support multiple semicolon delimited direction values (closes #4755) --- modules/osm/node.js | 73 ++++++++++++++++++++++++++----------------- test/spec/osm/node.js | 24 ++++++++++++++ 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/modules/osm/node.js b/modules/osm/node.js index 5083caada..956abe554 100644 --- a/modules/osm/node.js +++ b/modules/osm/node.js @@ -1,6 +1,7 @@ import _extend from 'lodash-es/extend'; import _map from 'lodash-es/map'; import _some from 'lodash-es/some'; +import _uniq from 'lodash-es/uniq'; import { osmEntity } from './entity'; import { geoAngle, geoExtent } from '../geo'; @@ -73,7 +74,8 @@ _extend(osmNode.prototype, { } } - // swap cardinal for numeric directions + if (val === '') return []; + var cardinal = { north: 0, n: 0, northnortheast: 22, nne: 22, @@ -92,41 +94,56 @@ _extend(osmNode.prototype, { northwest: 315, nw: 315, northnorthwest: 337, nnw: 337 }; - if (cardinal[val] !== undefined) { - val = cardinal[val]; - } - // if direction is numeric, return early - if (val !== '' && !isNaN(+val)) { - return [(+val)]; - } - var lookBackward = - (this.tags['traffic_sign:backward'] || val === 'backward' || val === 'both' || val === 'all'); - var lookForward = - (this.tags['traffic_sign:forward'] || val === 'forward' || val === 'both' || val === 'all'); + var values = val.split(';'); + var results = []; - if (!lookForward && !lookBackward) return []; + values.forEach(function(v) { + // swap cardinal for numeric directions + if (cardinal[v] !== undefined) { + v = cardinal[v]; + } - var nodeIds = {}; - resolver.parentWays(this).forEach(function(parent) { - var nodes = parent.nodes; - for (i = 0; i < nodes.length; i++) { - if (nodes[i] === this.id) { // match current entity - if (lookForward && i > 0) { - nodeIds[nodes[i - 1]] = true; // look back to prev node - } - if (lookBackward && i < nodes.length - 1) { - nodeIds[nodes[i + 1]] = true; // look ahead to next node + // numeric direction - just add to results + if (v !== '' && !isNaN(+v)) { + results.push(+v); + return; + } + + // string direction - inspect parent ways + var lookBackward = + (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all'); + var lookForward = + (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all'); + + if (!lookForward && !lookBackward) return; + + var nodeIds = {}; + resolver.parentWays(this).forEach(function(parent) { + var nodes = parent.nodes; + for (i = 0; i < nodes.length; i++) { + if (nodes[i] === this.id) { // match current entity + if (lookForward && i > 0) { + nodeIds[nodes[i - 1]] = true; // look back to prev node + } + if (lookBackward && i < nodes.length - 1) { + nodeIds[nodes[i + 1]] = true; // look ahead to next node + } } } - } + }, this); + + Object.keys(nodeIds).forEach(function(nodeId) { + // +90 because geoAngle returns angle from X axis, not Y (north) + results.push( + (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90 + ); + }, this); + }, this); - return Object.keys(nodeIds).map(function(nodeId) { - // +90 because geoAngle returns angle from X axis, not Y (north) - return (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90; - }, this); + return _uniq(results); }, diff --git a/test/spec/osm/node.js b/test/spec/osm/node.js index dd005c889..4293c2686 100644 --- a/test/spec/osm/node.js +++ b/test/spec/osm/node.js @@ -565,6 +565,30 @@ describe('iD.osmNode', function () { expect(node2.directions(graph, projection)).to.eql([]); }); + it('supports multiple directions delimited by ;', function () { + var node1 = iD.osmNode({ loc: [0, 0], tags: { direction: '0;45' }}); + var node2 = iD.osmNode({ loc: [0, 0], tags: { direction: '45;north' }}); + var node3 = iD.osmNode({ loc: [0, 0], tags: { direction: 'north;east' }}); + var node4 = iD.osmNode({ loc: [0, 0], tags: { direction: 'n;s;e;w' }}); + var node5 = iD.osmNode({ loc: [0, 0], tags: { direction: 's;wat' }}); + var graph = iD.coreGraph([node1, node2, node3, node4, node5]); + + expect(node1.directions(graph, projection)).to.eql([0, 45], 'numeric 0, numeric 45'); + expect(node2.directions(graph, projection)).to.eql([45, 0], 'numeric 45, cardinal north'); + expect(node3.directions(graph, projection)).to.eql([0, 90], 'cardinal north and east'); + expect(node4.directions(graph, projection)).to.eql([0, 180, 90, 270], 'cardinal n,s,e,w'); + expect(node5.directions(graph, projection)).to.eql([180], 'cardinal 180 and nonsense'); + }); + + it('supports mixing textual, cardinal, numeric directions, delimited by ;', function () { + var node1 = iD.osmNode({ id: 'n1', loc: [-1, 0] }); + var node2 = iD.osmNode({ id: 'n2', loc: [0, 0], tags: { 'camera:direction': 'both;ne;60' }}); + var node3 = iD.osmNode({ id: 'n3', loc: [1, 0] }); + var way = iD.osmWay({ nodes: ['n1','n2','n3'] }); + var graph = iD.coreGraph([node1, node2, node3, way]); + expect(node2.directions(graph, projection)).to.have.members([90, 270, 45, 60]); + }); + }); describe('#asJXON', function () {