diff --git a/modules/geo/vector.js b/modules/geo/vector.js index 0e7929b3b..708bb5f66 100644 --- a/modules/geo/vector.js +++ b/modules/geo/vector.js @@ -13,9 +13,9 @@ export function geoVecSubtract(a, b) { return [ a[0] - b[0], a[1] - b[1] ]; } -// vector multiplication -export function geoVecScale(a, b) { - return [ a[0] * b, a[1] * b ]; +// vector scaling +export function geoVecScale(a, mag) { + return [ a[0] * mag, a[1] * mag ]; } // vector rounding (was: geoRoundCoordinates) diff --git a/modules/osm/way.js b/modules/osm/way.js index 14e4e410c..28dbccf95 100644 --- a/modules/osm/way.js +++ b/modules/osm/way.js @@ -108,8 +108,18 @@ _extend(osmWay.prototype, { isOneWay: function() { // explicit oneway tag.. - if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; } - if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; } + var values = { + 'yes': true, + '1': true, + '-1': true, + 'reversible': true, + 'alternating': true, + 'no': false, + '0': false + }; + if (values[this.tags.oneway] !== undefined) { + return values[this.tags.oneway]; + } // implied oneway tag.. for (var key in this.tags) { diff --git a/modules/svg/defs.js b/modules/svg/defs.js index 65f37a7ba..ff6e2209e 100644 --- a/modules/svg/defs.js +++ b/modules/svg/defs.js @@ -31,7 +31,7 @@ export function svgDefs(context) { .append('marker') .attr('id', 'oneway-marker') .attr('viewBox', '0 0 10 5') - .attr('refX', 5) + .attr('refX', 2.5) .attr('refY', 2.5) .attr('markerWidth', 2) .attr('markerHeight', 2) diff --git a/modules/svg/helpers.js b/modules/svg/helpers.js index 3ca51ea8d..4cee3047b 100644 --- a/modules/svg/helpers.js +++ b/modules/svg/helpers.js @@ -6,7 +6,11 @@ import { geoStream as d3_geoStream } from 'd3-geo'; -import { geoVecLength } from '../geo'; +import { + geoVecAdd, + geoVecAngle, + geoVecLength +} from '../geo'; // Touch targets control which other vertices we can drag a vertex onto. @@ -72,6 +76,8 @@ export function svgOneWaySegments(projection, graph, dt) { coordinates.reverse(); } + var isReversible = (entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating'); + d3_geoStream({ type: 'LineString', coordinates: coordinates @@ -85,27 +91,41 @@ export function svgOneWaySegments(projection, graph, dt) { var span = geoVecLength(a, b) - offset; if (span >= 0) { - var angle = Math.atan2(b[1] - a[1], b[0] - a[0]); - var dx = dt * Math.cos(angle); - var dy = dt * Math.sin(angle); + var heading = geoVecAngle(a, b); + var dx = dt * Math.cos(heading); + var dy = dt * Math.sin(heading); var p = [ - a[0] + offset * Math.cos(angle), - a[1] + offset * Math.sin(angle) + a[0] + offset * Math.cos(heading), + a[1] + offset * Math.sin(heading) ]; - var segment = 'M' + a[0] + ',' + a[1] + 'L' + p[0] + ',' + p[1]; + // gather coordinates + var coord = [a, p]; for (span -= dt; span >= 0; span -= dt) { - p[0] += dx; - p[1] += dy; - segment += 'L' + p[0] + ',' + p[1]; + p = geoVecAdd(p, [dx, dy]); + coord.push(p); } + coord.push(b); - segment += 'L' + b[0] + ',' + b[1]; - segments.push({id: entity.id, index: i, d: segment}); + // generate svg paths + var segment = ''; + var j; + + for (j = 0; j < coord.length; j++) { + segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; + } + segments.push({ id: entity.id, index: i++, d: segment }); + + if (isReversible) { + segment = ''; + for (j = coord.length - 1; j >= 0; j--) { + segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; + } + segments.push({ id: entity.id, index: i++, d: segment }); + } } offset = -span; - i++; } a = b; diff --git a/test/spec/osm/way.js b/test/spec/osm/way.js index 2460f5fc8..2680e0dea 100644 --- a/test/spec/osm/way.js +++ b/test/spec/osm/way.js @@ -293,6 +293,14 @@ describe('iD.osmWay', function() { expect(iD.Way({tags: { oneway: '-1' }}).isOneWay(), 'oneway -1').to.be.true; }); + it('returns true when the way has tag oneway=reversible', function() { + expect(iD.Way({tags: { oneway: 'reversible' }}).isOneWay(), 'oneway reversible').to.be.true; + }); + + it('returns true when the way has tag oneway=alternating', function() { + expect(iD.Way({tags: { oneway: 'alternating' }}).isOneWay(), 'oneway alternating').to.be.true; + }); + it('returns true when the way has implied oneway tag (waterway=river, waterway=stream, etc)', function() { expect(iD.Way({tags: { waterway: 'river' }}).isOneWay(), 'river').to.be.true; expect(iD.Way({tags: { waterway: 'stream' }}).isOneWay(), 'stream').to.be.true;