modules/osm/lanes.js (118 lines of code) (raw):
export function osmLanes(entity) {
if (entity.type !== 'way') return null;
if (!entity.tags.highway) return null;
var tags = entity.tags;
var isOneWay = entity.isOneWay();
var laneCount = getLaneCount(tags, isOneWay);
var maxspeed = parseMaxspeed(tags);
var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
var forward = laneDirections.forward;
var backward = laneDirections.backward;
var bothways = laneDirections.bothways;
// parse the piped string 'x|y|z' format
var turnLanes = {};
turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
var maxspeedLanes = {};
maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
var psvLanes = {};
psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
var busLanes = {};
busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
var taxiLanes = {};
taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
var hovLanes = {};
hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
var hgvLanes = {};
hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
var bicyclewayLanes = {};
bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
var lanesObj = {
forward: [],
backward: [],
unspecified: []
};
// map forward/backward/unspecified of each lane type to lanesObj
mapToLanesObj(lanesObj, turnLanes, 'turnLane');
mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
mapToLanesObj(lanesObj, psvLanes, 'psv');
mapToLanesObj(lanesObj, busLanes, 'bus');
mapToLanesObj(lanesObj, taxiLanes, 'taxi');
mapToLanesObj(lanesObj, hovLanes, 'hov');
mapToLanesObj(lanesObj, hgvLanes, 'hgv');
mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
return {
metadata: {
count: laneCount,
oneway: isOneWay,
forward: forward,
backward: backward,
bothways: bothways,
turnLanes: turnLanes,
maxspeed: maxspeed,
maxspeedLanes: maxspeedLanes,
psvLanes: psvLanes,
busLanes: busLanes,
taxiLanes: taxiLanes,
hovLanes: hovLanes,
hgvLanes: hgvLanes,
bicyclewayLanes: bicyclewayLanes
},
lanes: lanesObj
};
}
function getLaneCount(tags, isOneWay) {
var count;
if (tags.lanes) {
count = parseInt(tags.lanes, 10);
if (count > 0) {
return count;
}
}
switch (tags.highway) {
case 'trunk':
case 'motorway':
count = isOneWay ? 2 : 4;
break;
default:
count = isOneWay ? 1 : 2;
break;
}
return count;
}
function parseMaxspeed(tags) {
var maxspeed = tags.maxspeed;
if (!maxspeed) return;
var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
if (!maxspeedRegex.test(maxspeed)) return;
return parseInt(maxspeed, 10);
}
function parseLaneDirections(tags, isOneWay, laneCount) {
var forward = parseInt(tags['lanes:forward'], 10);
var backward = parseInt(tags['lanes:backward'], 10);
var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
if (parseInt(tags.oneway, 10) === -1) {
forward = 0;
bothways = 0;
backward = laneCount;
}
else if (isOneWay) {
forward = laneCount;
bothways = 0;
backward = 0;
}
else if (isNaN(forward) && isNaN(backward)) {
backward = Math.floor((laneCount - bothways) / 2);
forward = laneCount - bothways - backward;
}
else if (isNaN(forward)) {
if (backward > laneCount - bothways) {
backward = laneCount - bothways;
}
forward = laneCount - bothways - backward;
}
else if (isNaN(backward)) {
if (forward > laneCount - bothways) {
forward = laneCount - bothways;
}
backward = laneCount - bothways - forward;
}
return {
forward: forward,
backward: backward,
bothways: bothways
};
}
function parseTurnLanes(tag){
if (!tag) return;
var validValues = [
'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
];
return tag.split('|')
.map(function (s) {
if (s === '') s = 'none';
return s.split(';')
.map(function (d) {
return validValues.indexOf(d) === -1 ? 'unknown': d;
});
});
}
function parseMaxspeedLanes(tag, maxspeed) {
if (!tag) return;
return tag.split('|')
.map(function (s) {
if (s === 'none') return s;
var m = parseInt(s, 10);
if (s === '' || m === maxspeed) return null;
return isNaN(m) ? 'unknown': m;
});
}
function parseMiscLanes(tag) {
if (!tag) return;
var validValues = [
'yes', 'no', 'designated'
];
return tag.split('|')
.map(function (s) {
if (s === '') s = 'no';
return validValues.indexOf(s) === -1 ? 'unknown': s;
});
}
function parseBicycleWay(tag) {
if (!tag) return;
var validValues = [
'yes', 'no', 'designated', 'lane'
];
return tag.split('|')
.map(function (s) {
if (s === '') s = 'no';
return validValues.indexOf(s) === -1 ? 'unknown': s;
});
}
function mapToLanesObj(lanesObj, data, key) {
if (data.forward) data.forward.forEach(function(l, i) {
if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
lanesObj.forward[i][key] = l;
});
if (data.backward) data.backward.forEach(function(l, i) {
if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
lanesObj.backward[i][key] = l;
});
if (data.unspecified) data.unspecified.forEach(function(l, i) {
if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
lanesObj.unspecified[i][key] = l;
});
}