in modules/osm/intersection.js [362:539]
function step(entity, currPath, currRestrictions, matchedRestriction) {
currPath = (currPath || []).slice(); // shallow copy
if (currPath.length >= maxPathLength) return;
currPath.push(entity.id);
currRestrictions = (currRestrictions || []).slice(); // shallow copy
var i, j;
if (entity.type === 'node') {
var parents = vgraph.parentWays(entity);
var nextWays = [];
// which ways can we step into?
for (i = 0; i < parents.length; i++) {
var way = parents[i];
// if next way is a oneway incoming to this vertex, skip
if (way.__oneWay && way.nodes[0] !== entity.id) continue;
// if we have seen it before (allowing for an initial u-turn), skip
if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue;
// Check all "current" restrictions (where we've already walked the `FROM`)
var restrict = null;
for (j = 0; j < currRestrictions.length; j++) {
var restriction = currRestrictions[j];
var f = restriction.memberByRole('from');
var v = restriction.membersByRole('via');
var t = restriction.memberByRole('to');
var isOnly = /^only_/.test(restriction.tags.restriction);
// Does the current path match this turn restriction?
var matchesFrom = (f.id === fromWayId);
var matchesViaTo = false;
var isAlongOnlyPath = false;
if (t.id === way.id) { // match TO
if (v.length === 1 && v[0].type === 'node') { // match VIA node
matchesViaTo = (v[0].id === entity.id && (
(matchesFrom && currPath.length === 2) ||
(!matchesFrom && currPath.length > 2)
));
} else { // match all VIA ways
var pathVias = [];
for (k = 2; k < currPath.length; k +=2 ) { // k = 2 skips FROM
pathVias.push(currPath[k]); // (path goes way-node-way...)
}
var restrictionVias = [];
for (k = 0; k < v.length; k++) {
if (v[k].type === 'way') {
restrictionVias.push(v[k].id);
}
}
var diff = utilArrayDifference(pathVias, restrictionVias);
matchesViaTo = !diff.length;
}
} else if (isOnly) {
for (k = 0; k < v.length; k++) {
// way doesn't match TO, but is one of the via ways along the path of an "only"
if (v[k].type === 'way' && v[k].id === way.id) {
isAlongOnlyPath = true;
break;
}
}
}
if (matchesViaTo) {
if (isOnly) {
restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true };
} else {
restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true };
}
} else { // indirect - caused by a different nearby restriction
if (isAlongOnlyPath) {
restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false };
} else if (isOnly) {
restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true };
}
}
// stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
if (restrict && restrict.direct) break;
}
nextWays.push({ way: way, restrict: restrict });
}
nextWays.forEach(function(nextWay) {
step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
});
} else { // entity.type === 'way'
if (currPath.length >= 3) { // this is a "complete" path..
var turnPath = currPath.slice(); // shallow copy
// an indirect restriction - only include the partial path (starting at FROM)
if (matchedRestriction && matchedRestriction.direct === false) {
for (i = 0; i < turnPath.length; i++) {
if (turnPath[i] === matchedRestriction.from) {
turnPath = turnPath.slice(i);
break;
}
}
}
var turn = pathToTurn(turnPath);
if (turn) {
if (matchedRestriction) {
turn.restrictionID = matchedRestriction.id;
turn.no = matchedRestriction.no;
turn.only = matchedRestriction.only;
turn.direct = matchedRestriction.direct;
}
turns.push(osmTurn(turn));
}
if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
}
if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
// which nodes can we step into?
var n1 = vgraph.entity(entity.first());
var n2 = vgraph.entity(entity.last());
var dist = geoSphericalDistance(n1.loc, n2.loc);
var nextNodes = [];
if (currPath.length > 1) {
if (dist > maxDistance) return; // the next node is too far
if (!entity.__via) return; // this way is a leaf / can't be a via
}
if (!entity.__oneWay && // bidirectional..
keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
currPath.indexOf(n1.id) === -1) { // haven't seen it yet..
nextNodes.push(n1); // can advance to first node
}
if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
currPath.indexOf(n2.id) === -1) { // haven't seen it yet..
nextNodes.push(n2); // can advance to last node
}
nextNodes.forEach(function(nextNode) {
// gather restrictions FROM this way
var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
if (!r.isRestriction()) return false;
var f = r.memberByRole('from');
if (!f || f.id !== entity.id) return false;
var isOnly = /^only_/.test(r.tags.restriction);
if (!isOnly) return true;
// `only_` restrictions only matter along the direction of the VIA - #4849
var isOnlyVia = false;
var v = r.membersByRole('via');
if (v.length === 1 && v[0].type === 'node') { // via node
isOnlyVia = (v[0].id === nextNode.id);
} else { // via way(s)
for (var i = 0; i < v.length; i++) {
if (v[i].type !== 'way') continue;
var viaWay = vgraph.entity(v[i].id);
if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
isOnlyVia = true;
break;
}
}
}
return isOnlyVia;
});
step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
});
}
}