Render restriction paths with red/green/blue shadow

This commit is contained in:
Bryan Housel
2018-02-19 21:30:47 -05:00
parent d940200ef0
commit 200098dff9
5 changed files with 146 additions and 52 deletions

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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 },

View File

@@ -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

View File

@@ -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);