From 200098dff9b1f8778639ac84dccd069502af1bbf Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 19 Feb 2018 21:30:47 -0500 Subject: [PATCH] Render restriction paths with red/green/blue shadow --- css/20_map.css | 32 +++++++- css/80_app.css | 4 +- modules/osm/intersection.js | 18 ++++- modules/svg/turns.js | 20 ++--- modules/ui/fields/restrictions.js | 124 ++++++++++++++++++++++-------- 5 files changed, 146 insertions(+), 52 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index 5cd5771ac..e4531ed65 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -26,6 +26,11 @@ pointer-events: none; } +.lasso #map { + pointer-events: visibleStroke; +} + + /* `.target` objects are interactive */ /* They can be picked up, clicked, hovered, or things can connect to them */ .node.target { @@ -242,7 +247,7 @@ text.point { } -/* Turns */ +/* Turn Restrictions */ g.turn rect, g.turn circle { @@ -255,9 +260,30 @@ g.turn circle { pointer-events: none; } -.lasso #map { - pointer-events: visibleStroke; +/* Turn restriction paths and vertices */ +.surface.tr path.shadow.selected, +.surface.tr path.shadow.related, +.surface.tr g.vertex.selected .shadow, +.surface.tr g.vertex.related .shadow { + stroke-opacity: 0.7; + stroke: #777; } +.surface.tr path.shadow.related.allow, +.surface.tr g.vertex.related.allow .shadow { + stroke: #7a4; + /*stroke: #8cd05f;*/ +} +.surface.tr path.shadow.related.restrict, +.surface.tr g.vertex.related.restrict .shadow { + stroke: #d75; + /*stroke: #e06d5f;*/ +} +.surface.tr path.shadow.related.only, +.surface.tr g.vertex.related.only .shadow { + stroke: #78f; + /*stroke: #7092ff;*/ +} + /* GPX Paths */ diff --git a/css/80_app.css b/css/80_app.css index ee4a32e3d..0a43ff642 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1910,10 +1910,10 @@ input[type=number] { font-weight: bold; } .restriction-help span.qualifier.allow { - color: #8b5; + color: #7a4; } .restriction-help span.qualifier.restrict { - color: #d76; + color: #d75; } .restriction-help span.qualifier.only { color: #78f; diff --git a/modules/osm/intersection.js b/modules/osm/intersection.js index 170552a70..2c337f7ed 100644 --- a/modules/osm/intersection.js +++ b/modules/osm/intersection.js @@ -210,7 +210,6 @@ export function osmIntersection(graph, startVertexId, maxDistance) { vertexIds.forEach(function(id) { var vertex = vgraph.entity(id); var parents = vgraph.parentWays(vertex); - vertices.push(vertex); ways = ways.concat(parents); }); @@ -243,8 +242,21 @@ export function osmIntersection(graph, startVertexId, maxDistance) { __from: __from, __via: __via, __to: __to, - __oneWay: __oneWay + __oneWay: __oneWay, + __fromOnly: fromOnly(way) }); + + function fromOnly(way) { + var parents = vgraph.parentRelations(way); + for (var i = 0; i < parents.length; i++) { + var r = parents[i]; + var f = r.memberByRole('from'); + if (r.isRestriction() && /^only_/.test(r.tags.restriction) && f.id === way.id) { + return r.id; + } + } + return null; + } } ways = []; @@ -583,7 +595,7 @@ export function osmIntersection(graph, startVertexId, maxDistance) { } return { - key: path.join(','), + key: path.join('_'), path: path, from: { node: fromNodeId, way: fromWayId, vertex: fromVertexId }, via: { node: viaNodeId, ways: viaWayIds }, diff --git a/modules/svg/turns.js b/modules/svg/turns.js index 1ed10706b..007733afc 100644 --- a/modules/svg/turns.js +++ b/modules/svg/turns.js @@ -30,10 +30,10 @@ export function svgTurns(projection) { var enter = groups.enter() .append('g') - .attr('class', 'turn'); + .attr('class', function(d) { return 'turn ' + d.key; }); var nEnter = enter - .filter(function (turn) { return !turn.u; }); + .filter(function(d) { return !d.u; }); nEnter.append('rect') .attr('transform', 'translate(-22, -12)') @@ -47,7 +47,7 @@ export function svgTurns(projection) { var uEnter = enter - .filter(function (turn) { return turn.u; }); + .filter(function(d) { return d.u; }); uEnter.append('circle') .attr('r', '16'); @@ -62,23 +62,23 @@ export function svgTurns(projection) { .merge(enter); groups - .attr('opacity', function (turn) { - return turn.direct === false ? '0.7' : null; + .attr('opacity', function(d) { + return d.direct === false ? '0.7' : null; }) - .attr('transform', function (turn) { + .attr('transform', function(d) { var pxRadius = 50; - var toWay = graph.entity(turn.to.way); + var toWay = graph.entity(d.to.way); var toPoints = graph.childNodes(toWay) .map(function (n) { return n.loc; }) .map(projection); var toLength = geoPathLength(toPoints); var mid = toLength / 2; // midpoint of destination way - var toNode = graph.entity(turn.to.node); - var toVertex = graph.entity(turn.to.vertex); + var toNode = graph.entity(d.to.node); + var toVertex = graph.entity(d.to.vertex); var a = geoAngle(toVertex, toNode, projection); var o = projection(toVertex.loc); - var r = turn.u ? 0 // u-turn: no radius + var r = d.u ? 0 // u-turn: no radius : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways diff --git a/modules/ui/fields/restrictions.js b/modules/ui/fields/restrictions.js index f29caccbb..1044b72a7 100644 --- a/modules/ui/fields/restrictions.js +++ b/modules/ui/fields/restrictions.js @@ -283,7 +283,8 @@ export function uiFieldRestrictions(field, context) { selection .call(drawLayers); - var surface = selection.selectAll('.surface'); + var surface = selection.selectAll('.surface') + .classed('tr', true); if (firstTime) { _initialized = true; @@ -314,20 +315,26 @@ export function uiFieldRestrictions(field, context) { surface .on('click.restrictions', click) - .on('mouseover.restrictions', mouseover) - .on('mouseout.restrictions', mouseout); + .on('mouseover.restrictions', mouseover); surface .selectAll('.selected') .classed('selected', false); + surface + .selectAll('.related') + .classed('related', false); + if (_fromWayID) { + var way = vgraph.entity(_fromWayID); surface .selectAll('.' + _fromWayID) - .classed('selected', true); + .classed('selected', true) + .classed('related', true) + .classed('only', !!way.__fromOnly); } - mouseout(); + updateHelp(null); function click() { @@ -351,6 +358,7 @@ export function uiFieldRestrictions(field, context) { if (datum.restrictionID && !datum.direct) { return; + } else if (datum.restrictionID && !datum.only) { // cycle thru the `only_` state var datumOnly = _cloneDeep(datum); datumOnly.only = true; @@ -371,8 +379,15 @@ export function uiFieldRestrictions(field, context) { t('operations.restriction.annotation.create') ]); } + context.perform.apply(context, actions); + // At this point the datum will be changed, but will have same key.. + // Refresh it and update the help.. + var s = surface.selectAll('.' + datum.key); + datum = s.empty() ? null : s.datum(); + updateHelp(datum); + } else { _fromWayID = null; redraw(); @@ -386,24 +401,6 @@ export function uiFieldRestrictions(field, context) { } - function mouseout() { - var help = _container.selectAll('.restriction-help').html(''); - var div = help.append('div'); - var d; - - if (_fromWayID) { - d = display(vgraph.entity(_fromWayID), vgraph); - div.append('span').attr('class', 'qualifier').text('FROM'); - div.append('span').text(d.name || d.type); - - } else { - div.append('span').text('Click to select the'); - div.append('span').attr('class', 'qualifier').text('FROM'); - div.append('span').text('way'); - } - } - - function redraw() { if (context.hasEntity(_vertexID)) { _container.call(renderViewer); @@ -413,7 +410,7 @@ export function uiFieldRestrictions(field, context) { function updateHelp(datum) { var help = _container.selectAll('.restriction-help').html(''); - var div, d; + var div, d, turnType, r; var entity = datum && datum.properties && datum.properties.entity; if (entity) { @@ -421,24 +418,44 @@ export function uiFieldRestrictions(field, context) { } surface.selectAll('.related') - .classed('related', false); + .classed('related', false) + .classed('allow', false) + .classed('restrict', false) + .classed('only', false); + if (datum instanceof osmWay) { + surface.selectAll('.' + datum.id) + .classed('related', true) + .classed('only', !!datum.__fromOnly); + + if (datum.__fromOnly) { + r = vgraph.entity(datum.__fromOnly); + + turnType = { + 'only_left_turn': 'Left Turn', + 'only_right_turn': 'Right Turn', + 'only_u_turn': 'U-Turn', + 'only_straight_on': 'Straight On' + }[r.tags.restriction]; + + div = help.append('div'); + div.append('span').attr('class', 'qualifier only').text('ONLY ' + turnType); + } + d = display(vgraph.entity(datum.id), vgraph); div = help.append('div'); div.append('span').attr('class', 'qualifier').text('FROM'); div.append('span').text(d.name || d.type); - } else if (datum instanceof osmTurn) { - surface.selectAll(utilEntitySelector(datum.key.split(','))) - .classed('related', true); + } else if (datum instanceof osmTurn) { var fromWayID = datum.from.way; var viaWayIDs = datum.via.ways; var toWayID = datum.to.way; var restrictionType = osmInferRestriction(vgraph, datum, projection); - var turnType = { + turnType = { 'no_left_turn': 'Left Turn', 'no_right_turn': 'Right Turn', 'no_u_turn': 'U-Turn', @@ -450,13 +467,19 @@ export function uiFieldRestrictions(field, context) { if (datum.no) { restrictType = 'NO'; klass = 'restrict'; } if (datum.only) { restrictType = 'ONLY'; klass = 'only'; } + var alongIDs = datum.path.slice(); + surface.selectAll(utilEntitySelector(alongIDs)) + .classed('related', true) + .classed('allow', (klass === 'allow')) + .classed('restrict', (klass === 'restrict')) + .classed('only', (klass === 'only')); + + var s = (klass === 'allow' ? turnType + ' Allowed' : restrictType + ' ' + turnType); if (datum.direct === false) { s += ' (indirect)'; } div = help.append('div'); - div.append('span') - .attr('class', 'qualifier ' + klass) - .text(s); + div.append('span').attr('class', 'qualifier ' + klass).text(s); div = help.append('div'); d = display(vgraph.entity(fromWayID), vgraph); @@ -482,6 +505,40 @@ export function uiFieldRestrictions(field, context) { prev = curr; } } + + } else { // datum is empty surface + if (_fromWayID) { + var way = vgraph.entity(_fromWayID); + surface + .selectAll('.' + _fromWayID) + .classed('selected', true) + .classed('related', true) + .classed('only', !!way.__fromOnly); + + if (way.__fromOnly) { + r = vgraph.entity(way.__fromOnly); + + turnType = { + 'only_left_turn': 'Left Turn', + 'only_right_turn': 'Right Turn', + 'only_u_turn': 'U-Turn', + 'only_straight_on': 'Straight On' + }[r.tags.restriction]; + + div = help.append('div'); + div.append('span').attr('class', 'qualifier only').text('ONLY ' + turnType); + } + + d = display(vgraph.entity(_fromWayID), vgraph); + div = help.append('div'); + div.append('span').attr('class', 'qualifier').text('FROM'); + div.append('span').text(d.name || d.type); + } else { + div = help.append('div'); + div.append('span').text('Click to select the'); + div.append('span').attr('class', 'qualifier').text('FROM'); + div.append('span').text('way'); + } } } @@ -516,8 +573,7 @@ export function uiFieldRestrictions(field, context) { .call(hover.off) .call(breathe.off) .on('click.restrictions', null) - .on('mouseover.restrictions', null) - .on('mouseout.restrictions', null); + .on('mouseover.restrictions', null); d3_select(window) .on('resize.restrictions', null);