diff --git a/css/map.css b/css/map.css index 6cbec597e..3ae6ec183 100644 --- a/css/map.css +++ b/css/map.css @@ -695,17 +695,14 @@ text { opacity: 1; } -.oneway .textpath { - pointer-events: none; - font-size: 7px; - baseline-shift: 2px; - opacity: .7; -} - .oneway .textpath.tag-waterway { fill: #002F35; } +marker#oneway-marker path { + fill:#000; +} + text.tag-oneway { fill:#91CFFF; stroke:#2C6B9B; diff --git a/js/id/svg.js b/js/id/svg.js index 12ffc5c31..ab9fe8e7e 100644 --- a/js/id/svg.js +++ b/js/id/svg.js @@ -13,8 +13,34 @@ iD.svg = { }; }, - LineString: function(projection, graph, dimensions) { - var cache = {}; + resample: function(points, dx) { + var o = []; + for (var i = 0; i < points.length - 1; i++) { + var a = points[i], b = points[i + 1], + span = iD.geo.dist(a, b); + o.push(a); + // if there is space to fit one or more oneway mark + // in this segment + if (span > dx) { + // the angle from a to b + var angle = Math.atan2(b[1] - a[1], b[0] - a[0]), + to = points[i].slice(); + while (iD.geo.dist(a, to) < (span - dx)) { + // a dx-length line segment in that angle + to[0] += Math.cos(angle) * dx; + to[1] += Math.sin(angle) * dx; + o.push(to.slice()); + } + } + o.push(b); + } + return o; + }, + + LineString: function(projection, graph, dimensions, dx) { + var cache = {}, + resample = this.resample; + return function(entity) { if (cache[entity.id] !== undefined) { return cache[entity.id]; @@ -34,6 +60,7 @@ iD.svg = { cache[entity.id] = segments.map(function(points) { + if (dx) points = resample(points, dx); return 'M' + points.map(function(p) { return p[0] + ',' + p[1]; }).join('L'); diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js index 0e91f2ce2..d34e60fb1 100644 --- a/js/id/svg/lines.js +++ b/js/id/svg/lines.js @@ -1,8 +1,5 @@ iD.svg.Lines = function(projection) { - var arrowtext = '►\u3000\u3000\u3000', - alength; - var highway_stack = { motorway: 0, motorway_link: 1, @@ -92,16 +89,6 @@ iD.svg.Lines = function(projection) { return paths; } - if (!alength) { - var container = surface.append('g') - .attr('class', 'oneway'), - arrow = container.append('text') - .attr('class', 'textpath') - .text(arrowtext); - alength = arrow.node().getComputedTextLength(); - container.remove(); - } - var lines = []; for (var i = 0; i < entities.length; i++) { @@ -117,6 +104,7 @@ iD.svg.Lines = function(projection) { lines.sort(waystack); var lineString = iD.svg.LineString(projection, graph, dimensions); + var lineStringResampled = iD.svg.LineString(projection, graph, dimensions, 35); var shadow = surface.select('.layer-shadow'), casing = surface.select('.layer-casing'), @@ -127,47 +115,9 @@ iD.svg.Lines = function(projection) { casings = drawPaths(casing, lines, filter, 'casing', lineString), strokes = drawPaths(stroke, lines, filter, 'stroke', lineString); - // Determine the lengths of oneway paths - var lengths = {}, - oneways = strokes.filter(function(d) { return d.isOneWay(); }).each(function(d) { - lengths[d.id] = Math.floor(this.getTotalLength() / alength); - }).data(); - - var uses = defs.selectAll('path') - .filter(filter) - .data(oneways, iD.Entity.key); - - uses.enter() - .append('path'); - - uses - .attr('id', function(d) { return 'shadow-' + d.id; }) - .attr('d', lineString); - - uses.exit() - .remove(); - - var labels = text.selectAll('text') - .filter(filter) - .data(oneways, iD.Entity.key); - - var tagClasses = iD.svg.TagClasses(); - - var tp = labels.enter() - .append('text') - .attr({ 'class': 'oneway', dy: 4 }) - .append('textPath') - .attr('class', 'textpath') - .call(tagClasses); - - labels.exit().remove(); - - text.selectAll('.textpath') - .filter(filter) - .attr('xlink:href', function(d) { return '#shadow-' + d.id; }) - .text(function(d) { - // adding longer text than necessary, since overflow is hidden - return (new Array(Math.floor(lengths[d.id] * 1.1))).join(arrowtext); - }); + strokes + .filter(function(d) { return d.isOneWay(); }) + .attr('marker-mid', 'url(#oneway-marker)') + .attr('d', lineStringResampled); }; }; diff --git a/js/id/svg/surface.js b/js/id/svg/surface.js index a11f8fbae..469b9e0d5 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/surface.js @@ -1,6 +1,17 @@ iD.svg.Surface = function() { return function drawSurface(selection) { - selection.append('defs'); + var defs = selection.append('defs'); + defs.append('marker') + .attr({ + id: 'oneway-marker', + viewBox: '0 0 10 10', + refY: 2.5, + markerWidth: 2, + markerHeight: 2, + orient: 'auto' + }) + .append('path') + .attr('d', 'M 0 0 L 5 2.5 L 0 5 z'); var layers = selection.selectAll('.layer') .data(['fill', 'shadow', 'casing', 'stroke', 'text', 'hit', 'halo', 'label']); diff --git a/test/spec/svg/areas.js b/test/spec/svg/areas.js index d84abf8f1..a2ede70e4 100644 --- a/test/spec/svg/areas.js +++ b/test/spec/svg/areas.js @@ -14,8 +14,8 @@ describe("iD.svg.Areas", function () { surface.call(iD.svg.Areas(projection), graph, [area], filter); - expect(surface.select('path')).to.be.classed('way'); - expect(surface.select('path')).to.be.classed('area'); + expect(surface.select('path.way')).to.be.classed('way'); + expect(surface.select('path.area')).to.be.classed('area'); }); it("adds tag classes", function () { diff --git a/test/spec/svg/lines.js b/test/spec/svg/lines.js index 0dfa32f67..d38b6f998 100644 --- a/test/spec/svg/lines.js +++ b/test/spec/svg/lines.js @@ -17,8 +17,8 @@ describe("iD.svg.Lines", function () { surface.call(iD.svg.Lines(projection), graph, [line], filter, dimensions); - expect(surface.select('path')).to.be.classed('way'); - expect(surface.select('path')).to.be.classed('line'); + expect(surface.select('path.way')).to.be.classed('way'); + expect(surface.select('path.line')).to.be.classed('line'); }); it("adds tag classes", function () {