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