From b07bf8eaa214518e4df468789a6f300952873c26 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 12 Oct 2016 00:10:46 -0400 Subject: [PATCH] Extract lane parsing code from way.js into lanes.js --- modules/osm/index.js | 8 +- modules/osm/lanes.js | 246 ++++++++ modules/osm/way.js | 229 +------- test/index.html | 1 + test/spec/osm/lanes.js | 1218 ++++++++++++++++++++++++++++++++++++++++ test/spec/osm/way.js | 1218 ---------------------------------------- 6 files changed, 1473 insertions(+), 1447 deletions(-) create mode 100644 modules/osm/lanes.js create mode 100644 test/spec/osm/lanes.js diff --git a/modules/osm/index.js b/modules/osm/index.js index aa9effc79..38bc88520 100644 --- a/modules/osm/index.js +++ b/modules/osm/index.js @@ -7,13 +7,17 @@ export { osmIntersection, osmTurn, osmInferRestriction -} from './intersection.js'; +} from './intersection'; + +export { + osmLanes +} from './lanes'; export { osmIsSimpleMultipolygonOuterMember, osmSimpleMultipolygonOuterMember, osmJoinWays -} from './multipolygon.js'; +} from './multipolygon'; export { osmOneWayTags, diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js new file mode 100644 index 000000000..f8c136d8d --- /dev/null +++ b/modules/osm/lanes.js @@ -0,0 +1,246 @@ +import _ from 'lodash'; + + +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 (_.isNumber(maxspeed)) return maxspeed; + if (_.isString(maxspeed)) { + maxspeed = maxspeed.match(/^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/g); + if (!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; + }); +} diff --git a/modules/osm/way.js b/modules/osm/way.js index aa6ef190c..7f328894c 100644 --- a/modules/osm/way.js +++ b/modules/osm/way.js @@ -2,6 +2,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { geoExtent, geoCross } from '../geo/index'; import { osmEntity } from './entity'; +import { osmLanes } from './lanes'; import { osmOneWayTags } from './tags'; import { areaKeys } from '../core/context'; @@ -116,233 +117,7 @@ _.extend(osmWay.prototype, { lanes: function() { - function getLaneCount() { - var count; - // fill laneCount with defaults - switch (tags.highway) { - case 'trunk': - case 'motorway': - count = oneway ? 2 : 4; - break; - default: - count = oneway ? 1 : 2; - break; - } - - if (tags.lanes) count = parseInt(tags.lanes, 10); - return count; - } - - function parseMaxspeed() { - var maxspeed = tags.maxspeed; - if (_.isNumber(maxspeed)) return maxspeed; - if (_.isString(maxspeed)) { - maxspeed = maxspeed.match(/^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/g); - if (!maxspeed) return; - return parseInt(maxspeed, 10); - } - } - - function parseLaneDirections() { - 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 (oneway) { - 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(key){ - var validValues = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' - ]; - var str = tags[key]; - if (!str) return; - var parsedArray = str.split('|') - .map(function (s) { - if (s === '') s = 'none'; - return s.split(';') - .map(function (d) { - return validValues.indexOf(d) === -1 ? 'unknown': d; - }); - }); - return parsedArray; - } - - function parseMaxspeedLanes(key) { - var str = tags[key]; - if (!str) return; - var parsedArray = str.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; - }); - return parsedArray; - } - - function parseMiscLanes(key) { - var validValues = [ - 'yes', 'no', 'designated' - ]; - var str = tags[key]; - if (!str) return; - var parsedArray = str.split('|') - .map(function (s) { - if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; - }); - return parsedArray; - } - - function parseBicycleWay(key) { - var validValues = [ - 'yes', 'no', 'designated', 'lane' - ]; - var str = tags[key]; - if (!str) return; - var parsedArray = str.split('|') - .map(function (s) { - if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; - }); - return parsedArray; - } - - function mapToLanesObj(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; - }); - } - - if (!this.tags.highway) return null; - - var tags = this.tags; - var oneway = this.isOneWay(); - var laneCount = getLaneCount(); - var maxspeed = parseMaxspeed(); - - var laneDirections = parseLaneDirections(); - 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('turn:lanes'); - turnLanes.forward = parseTurnLanes('turn:lanes:forward'); - turnLanes.backward = parseTurnLanes('turn:lanes:backward'); - - var maxspeedLanes = {}; - maxspeedLanes.unspecified = parseMaxspeedLanes('maxspeed:lanes'); - maxspeedLanes.forward = parseMaxspeedLanes('maxspeed:lanes:forward'); - maxspeedLanes.backward = parseMaxspeedLanes('maxspeed:lanes:backward'); - - var psvLanes = {}; - psvLanes.unspecified = parseMiscLanes('psv:lanes'); - psvLanes.forward = parseMiscLanes('psv:lanes:forward'); - psvLanes.backward = parseMiscLanes('psv:lanes:backward'); - - var busLanes = {}; - busLanes.unspecified = parseMiscLanes('bus:lanes'); - busLanes.forward = parseMiscLanes('bus:lanes:forward'); - busLanes.backward = parseMiscLanes('bus:lanes:backward'); - - var taxiLanes = {}; - taxiLanes.unspecified = parseMiscLanes('taxi:lanes'); - taxiLanes.forward = parseMiscLanes('taxi:lanes:forward'); - taxiLanes.backward = parseMiscLanes('taxi:lanes:backward'); - - var hovLanes = {}; - hovLanes.unspecified = parseMiscLanes('hov:lanes'); - hovLanes.forward = parseMiscLanes('hov:lanes:forward'); - hovLanes.backward = parseMiscLanes('hov:lanes:backward'); - - var hgvLanes = {}; - hgvLanes.unspecified = parseMiscLanes('hgv:lanes'); - hgvLanes.forward = parseMiscLanes('hgv:lanes:forward'); - hgvLanes.backward = parseMiscLanes('hgv:lanes:backward'); - - var bicyclewayLanes = {}; - bicyclewayLanes.unspecified = parseBicycleWay('bicycleway:lanes'); - bicyclewayLanes.forward = parseBicycleWay('bicycleway:lanes:forward'); - bicyclewayLanes.backward = parseBicycleWay('bicycleway:lanes:backward'); - - var lanesObj = { - forward: [], - backward: [], - unspecified: [] - }; - - // map forward/backward/unspecified of each lane type to lanesObj - mapToLanesObj(turnLanes, 'turnLane'); - mapToLanesObj(maxspeedLanes, 'maxspeed'); - mapToLanesObj(psvLanes, 'psv'); - mapToLanesObj(busLanes, 'bus'); - mapToLanesObj(taxiLanes, 'taxi'); - mapToLanesObj(hovLanes, 'hov'); - mapToLanesObj(hgvLanes, 'hgv'); - mapToLanesObj(bicyclewayLanes, 'bicycleway'); - - return { - metadata: { - count: laneCount, - oneway: oneway, - 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 - }; + return osmLanes(this); }, diff --git a/test/index.html b/test/index.html index 23dfd3eab..24c233ab4 100644 --- a/test/index.html +++ b/test/index.html @@ -84,6 +84,7 @@ + diff --git a/test/spec/osm/lanes.js b/test/spec/osm/lanes.js new file mode 100644 index 000000000..4a072a28c --- /dev/null +++ b/test/spec/osm/lanes.js @@ -0,0 +1,1218 @@ +describe('iD.Lanes', function() { + + describe('default lane tags', function() { + + describe('motorway', function() { + + it('returns 2 lanes for highway=motorway', function() { + expect(iD.Way({tags: { highway: 'motorway' }}).lanes().metadata.count, 'motorway lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'motorway', oneway: 'yes' }}).lanes().metadata.count, 'motorway lanes') + .to.eql(2); + }); + + it('returns 4 lanes for highway=motorway and oneway=no', function() { + expect(iD.Way({tags: { highway: 'motorway', oneway: 'no' }}).lanes().metadata.count, 'motorway lanes') + .to.eql(4); + }); + + it('returns 1 lane for highway=motorway_link', function() { + expect(iD.Way({tags: { highway: 'motorway_link' }}).lanes().metadata.count, 'motorway_link lanes') + .to.eql(1); + expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'yes' }}).lanes().metadata.count, 'motorway_link lanes') + .to.eql(1); + }); + + it('returns 2 lanes for highway=motorway_link and oneway=no', function() { + expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'no' }}).lanes().metadata.count, 'motorway_link lanes') + .to.eql(2); + }); + + }); + + describe('trunk', function() { + + it('returns 4 lanes for highway=trunk', function() { + expect(iD.Way({tags: { highway: 'trunk' }}).lanes().metadata.count, 'trunk lanes') + .to.eql(4); + expect(iD.Way({tags: { highway: 'trunk', oneway: 'no' }}).lanes().metadata.count, 'trunk lanes') + .to.eql(4); + }); + + it('returns 2 lanes for highway=trunk and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'trunk', oneway: 'yes' }}).lanes().metadata.count, 'trunk lanes') + .to.eql(2); + }); + + it('returns 2 lanes for highway=trunk_link', function() { + expect(iD.Way({tags: { highway: 'trunk_link' }}).lanes().metadata.count, 'trunk_link lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'no' }}).lanes().metadata.count, 'trunk_link lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=trunk_link and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'yes' }}).lanes().metadata.count, 'trunk_link lanes') + .to.eql(1); + }); + }); + + describe('primary', function() { + + it('returns 2 lanes for highway=primary', function() { + expect(iD.Way({tags: { highway: 'primary' }}).lanes().metadata.count, 'primary lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'primary', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=primary and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'primary', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') + .to.eql(1); + }); + + it('returns 2 lanes for highway=primary_link', function() { + expect(iD.Way({tags: { highway: 'primary_link' }}).lanes().metadata.count, 'primary lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'primary_link', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=primary_link and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'primary_link', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') + .to.eql(1); + }); + }); + + describe('seconday', function() { + + it('returns 2 lanes for highway=secondary', function() { + expect(iD.Way({tags: { highway: 'secondary' }}).lanes().metadata.count, 'secondary lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'secondary', oneway: 'no' }}).lanes().metadata.count, 'secondary lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=secondary and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'secondary', oneway: 'yes' }}).lanes().metadata.count, 'secondary lanes') + .to.eql(1); + }); + + it('returns 2 lane for highway=secondary_link', function() { + expect(iD.Way({tags: { highway: 'secondary_link' }}).lanes().metadata.count, 'secondary_link lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'no' }}).lanes().metadata.count, 'secondary_link lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=secondary_link and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'yes' }}).lanes().metadata.count, 'secondary_link lanes') + .to.eql(1); + }); + }); + + describe('tertiary', function() { + + it('returns 2 lanes for highway=tertiary', function() { + expect(iD.Way({tags: { highway: 'tertiary' }}).lanes().metadata.count, 'tertiary lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'tertiary', oneway: 'no' }}).lanes().metadata.count, 'tertiary lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=tertiary and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'tertiary', oneway: 'yes' }}).lanes().metadata.count, 'tertiary lanes') + .to.eql(1); + }); + + it('returns 2 lane for highway=tertiary_link', function() { + expect(iD.Way({tags: { highway: 'tertiary_link' }}).lanes().metadata.count, 'tertiary_link lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'no' }}).lanes().metadata.count, 'tertiary_link lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=tertiary_link and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'yes' }}).lanes().metadata.count, 'tertiary_link lanes') + .to.eql(1); + }); + }); + + describe('residential', function() { + + it('returns 2 lanes for highway=residential', function() { + expect(iD.Way({tags: { highway: 'residential' }}).lanes().metadata.count, 'residential lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.count, 'residential lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=residential and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.count, 'residential lanes') + .to.eql(1); + }); + }); + + describe('service', function() { + + it('returns 2 lanes for highway=service', function() { + expect(iD.Way({tags: { highway: 'service' }}).lanes().metadata.count, 'service lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'service', oneway: 'no' }}).lanes().metadata.count, 'service lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=service and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'service', oneway: 'yes' }}).lanes().metadata.count, 'service lanes') + .to.eql(1); + }); + }); + + describe('track', function() { + + it('returns 2 lanes for highway=track', function() { + expect(iD.Way({tags: { highway: 'track' }}).lanes().metadata.count, 'track lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'track', oneway: 'no' }}).lanes().metadata.count, 'track lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=track and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'track', oneway: 'yes' }}).lanes().metadata.count, 'track lanes') + .to.eql(1); + }); + }); + + describe('path', function() { + + it('returns 2 lanes for highway=path', function() { + expect(iD.Way({tags: { highway: 'path' }}).lanes().metadata.count, 'path lanes') + .to.eql(2); + expect(iD.Way({tags: { highway: 'path', oneway: 'no' }}).lanes().metadata.count, 'path lanes') + .to.eql(2); + }); + + it('returns 1 lane for highway=path and oneway=yes', function() { + expect(iD.Way({tags: { highway: 'path', oneway: 'yes' }}).lanes().metadata.count, 'path lanes') + .to.eql(1); + }); + }); + }); + + describe('oneway tags', function() { + it('returns correctly oneway when tagged as oneway', function() { + expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.oneway, 'residential lanes') + .to.be.true; + expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.oneway, 'residential lanes') + .to.be.false; + }); + }); + + describe('lane direction', function() { + + it('returns correctly the lane:forward and lane:backward count', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 2, + oneway: false, + forward: 1, + backward: 1, + bothways: 0 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:forward': 3, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 4, + oneway: false, + forward: 3, + backward: 1, + bothways: 0 + }); + }); + + it('returns correctly the count if erroneous values are supplied', function() { + expect(iD.Way({tags: { highway: 'trunk', lanes: 2, 'lanes:forward': 3 }}).lanes().metadata, 'trunk lanes') + .to.include({ + count: 2, + oneway: false, + forward: 2, + backward: 0, + bothways: 0 + }); + }); + + it('returns correctly forward count when oneway=yes', function() { + expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes' }}).lanes().metadata, 'trunk lanes') + .to.include({ + count: 2, + oneway: true, + forward: 2, + backward: 0, + bothways: 0 + }); + }); + + it('returns correctly backward count the when oneway=-1', function() { + expect(iD.Way({tags: { highway: 'primary', lanes: 4, oneway: '-1' }}).lanes().metadata, 'primary lanes') + .to.include({ + count: 4, + oneway: true, + backward: 4, + forward: 0, + bothways: 0 + }); + }); + + it('skips provided lanes:forward value when oneway=yes', function() { + expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:forward': 1 }}).lanes().metadata, 'trunk lanes') + .to.include({ + count: 2, + oneway: true, + forward: 2, + backward: 0, + bothways: 0 + }); + }); + + it('skips provided lanes:backward value when oneway=yes', function() { + expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:backward': 1 }}).lanes().metadata, 'trunk lanes') + .to.include({ + count: 2, + oneway: true, + forward: 2, + backward: 0, + bothways: 0 + }); + }); + + it('returns correctly forward count if only backward is supplied', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 3, + oneway: false, + forward: 2, + backward: 1, + bothways: 0 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:backward': 3, }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 4, + oneway: false, + forward: 1, + backward: 3, + bothways: 0 + }); + }); + + it('returns correctly backward count if only forward is supplied', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 3, + oneway: false, + forward: 1, + backward: 2, + bothways: 0 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 2, + oneway: false, + forward: 1, + backward: 1, + bothways: 0 + }); + }); + + it('returns correctly backward count if forward and both_ways are supplied', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 3, + oneway: false, + forward: 1, + backward: 1, + bothways: 1 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 5, + oneway: false, + forward: 1, + backward: 3, + bothways: 1 + }); + }); + + it('returns correctly forward count if backward and both_ways are supplied', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 3, + oneway: false, + forward: 1, + backward: 1, + bothways: 1 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 5, + oneway: false, + forward: 3, + backward: 1, + bothways: 1 + }); + }); + + it('returns correctly the lane:both_ways count as 1', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 2, + oneway: false, + forward: 1, + backward: 0, + bothways: 1 + }); + }); + + it('returns correctly when lane:both_ways>1', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 2, 'lanes:backward': 2 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 5, + oneway: false, + forward: 2, + backward: 2, + bothways: 1 + }); + }); + + it('returns correctly when lane:both_ways is 0 or Not a Number', function() { + expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 5, + oneway: false, + forward: 2, + backward: 3, + bothways: 0 + }); + expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 'none' }}).lanes().metadata, 'residential lanes') + .to.include({ + count: 2, + oneway: false, + forward: 1, + backward: 1, + bothways: 0 + }); + }); + + }); + + describe.skip('lanes array', function() { + it('should have correct number of direction elements', function() { + var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().lanes; + var forward = lanes.filter(function(l) { + return l.direction === 'forward'; + }); + var backward = lanes.filter(function(l) { + return l.direction === 'backward'; + }); + var bothways = lanes.filter(function(l) { + return l.direction === 'bothways'; + }); + expect(forward.length).to.eql(2); + expect(backward.length).to.eql(3); + expect(bothways.length).to.eql(0); + + }); + it('should have corrent number of direction elements', function() { + var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().lanes; + var forward = lanes.filter(function(l) { + return l.direction === 'forward'; + }); + var backward = lanes.filter(function(l) { + return l.direction === 'backward'; + }); + var bothways = lanes.filter(function(l) { + return l.direction === 'bothways'; + }); + expect(forward.length).to.eql(3); + expect(backward.length).to.eql(1); + expect(bothways.length).to.eql(1); + }); + }); + + describe('turn lanes', function() { + it('returns correctly when oneway=yes', function() { + var metadata = iD.Way({ + tags: { + highway: 'trunk', + oneway: 'yes', + 'turn:lanes': 'none|slight_right' + } + }).lanes().metadata; + expect(metadata.turnLanes.unspecified) + .to.deep.equal([ + ['none'], ['slight_right'] + ]); + }); + + it('returns correctly when oneway=yes and lanes=2', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + oneway: 'yes', + lanes: '2', + 'turn:lanes': 'none|slight_right' + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.deep.equal([ + ['none'], ['slight_right'] + ]); + }); + + it('returns correctly when lanes=5 and both_ways=1', function() { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:forward': 1, + 'lanes:both_ways': 1, + 'turn:lanes:forward': 'slight_left', + 'turn:lanes:backward': 'none|through|through;slight_right', + } + }).lanes().metadata; + expect(metadata.turnLanes.forward) + .to.deep.equal([ + ['slight_left'] + ]); + expect(metadata.turnLanes.backward) + .to.deep.equal([ + ['none'], ['through'], ['through', 'slight_right'] + ]); + }); + + it('returns correctly when multiple values are present in a lane and oneway=yes', function() { + var lanesData = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'yes', + 'turn:lanes': 'slight_left;reverse;left|slight_left;left;through|through|none|through;right', + } + }).lanes(); + + expect(lanesData.metadata.turnLanes.unspecified) + .to.deep.equal([ + ['slight_left', 'reverse', 'left'], + ['slight_left', 'left', 'through'], + ['through'], + ['none'], + ['through', 'right'] + ]); + }); + + it('returns correctly when multiple values are present in a lane and oneway=no', function() { + var lanesData = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'no', + 'lanes:forward': 3, + 'lanes:backward': 2, + 'turn:lanes:forward': 'slight_left;reverse;left|slight_left;left;through|through', + 'turn:lanes:backward': 'none|through;left' + } + }).lanes(); + expect(lanesData.metadata.turnLanes.forward) + .to.deep.equal([ + ['slight_left', 'reverse', 'left'], + ['slight_left', 'left', 'through'], + ['through'] + ]); + expect(lanesData.metadata.turnLanes.backward) + .to.deep.equal([ + ['none'], + ['through', 'left'] + ]); + }); + + it('returns unknown for every invalid value in turn:lanes', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 3, + oneway: 'yes', + 'turn:lanes': '||straight;NO_LEFT', + } + }).lanes().metadata; + expect(metadata.turnLanes.unspecified) + .to.deep.equal([ + ['none'], ['none'], ['unknown', 'unknown'] + ]); + }); + + it('returns unknown for every invalid value in turn:lanes:forward & turn:lanes:backward', function() { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:forward': 1, + 'lanes:both_ways': 1, + 'turn:lanes:forward': 'sligh_left', + 'turn:lanes:backward': 'none|through|though;slight_right', + } + }).lanes().metadata; + expect(metadata.turnLanes.forward) + .to.deep.equal([ + ['unknown'] + ]); + expect(metadata.turnLanes.backward) + .to.deep.equal([ + ['none'], ['through'], ['unknown', 'slight_right'] + ]); + }); + + it.skip('fills with [\'unknown\'] when given turn:lanes are less than lanes count', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'yes', + 'turn:lanes': 'slight_left|', + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.deep.equal([ + ['slight_left'], ['none'] + ]); + }); + + it.skip('fills with [\'unknown\'] when given turn:lanes:forward are less than lanes forward count', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'no', + 'lanes:forward': 3, + 'turn:lanes:forward': 'slight_left', + 'turn:lanes:backward': 'through', + } + }).lanes().metadata; + + expect(metadata.turnLanes.forward) + .to.deep.equal([ + ['slight_left'], ['unknown'], ['unknown'] + ]); + expect(metadata.turnLanes.backward) + .to.deep.equal([ + ['through'], ['unknown'] + ]); + }); + + it.skip('clips when turn lane information is more than lane count', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 2, + oneway: 'yes', + 'turn:lanes': 'through|through;slight_right|slight_right', + } + }).lanes().metadata; + + expect(metadata.turnLanes) + .to.deep.equal([ + ['through'], ['through', 'slight_right'] + ]); + }); + + it('turnLanes is undefined when not present', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 2, + oneway: 'yes' + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.equal(undefined); + expect(metadata.turnLanes.forward) + .to.equal(undefined); + expect(metadata.turnLanes.backward) + .to.equal(undefined); + }); + + it('turnLanes.forward and turnLanes.backward are both undefined when both are not provided', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 2, + oneway: 'yes', + 'turn:lanes': 'through|through;slight_right', + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.deep.equal([['through'], ['through', 'slight_right']]); + expect(metadata.turnLanes.forward) + .to.equal(undefined); + expect(metadata.turnLanes.backward) + .to.equal(undefined); + }); + + it('parses turnLane correctly when lanes:both_ways=1', function() { + var lanes = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'no', + 'lanes:forward': 3, + 'lanes:both_ways': 1, + 'lanes:backward': 1, + 'turn:lanes:backward': 'slight_right', + 'turn:lanes:forward': 'slight_left||', + } + }).lanes(); + expect(lanes.metadata.turnLanes.backward) + .to.deep.equal([['slight_right']]); + expect(lanes.metadata.turnLanes.forward) + .to.deep.equal([['slight_left'], ['none'], ['none']]); + }); + + it('parses turnLane correctly when lanes:both_ways=1 & lanes:forward < lanes:backward', function() { + var lanes = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'no', + 'lanes:forward': 1, + 'lanes:both_ways': 1, + 'lanes:backward': 3, + 'turn:lanes:forward': 'through', + 'turn:lanes:backward': 'slight_left||', + } + }).lanes(); + expect(lanes.metadata.turnLanes.forward) + .to.deep.equal([['through']]); + expect(lanes.metadata.turnLanes.backward) + .to.deep.equal([['slight_left'], ['none'], ['none']]); + }); + + it('parses correctly when turn:lanes= ||x', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 3, + oneway: 'yes', + 'turn:lanes': '||through;slight_right', + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.deep.equal([['none'], ['none'], ['through', 'slight_right']]); + }); + + it('parses correctly when turn:lanes= |x|', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + 'turn:lanes': '|through|', + } + }).lanes().metadata; + + expect(metadata.turnLanes.unspecified) + .to.deep.equal([['none'], ['through'], ['none']]); + }); + + it('parses correctly when turn:lanes:forward= ||x', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 4, + oneway: 'no', + 'lanes:forward': 3, + 'lanes:backward': 1, + 'turn:lanes:forward': '||through;slight_right', + 'turn:lanes:backward': 'none', + } + }).lanes().metadata; + + expect(metadata.turnLanes.forward) + .to.deep.equal([['none'], ['none'], ['through', 'slight_right']]); + expect(metadata.turnLanes.backward) + .to.deep.equal([['none']]); + }); + + it('parses correctly when turn:lanes:backward= |', function() { + var metadata = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'no', + 'lanes:forward': 3, + 'lanes:backward': 2, + 'turn:lanes:backward': '|', + 'turn:lanes:forward': 'slight_left||', + } + }).lanes().metadata; + + expect(metadata.turnLanes.forward) + .to.deep.equal([['slight_left'], ['none'], ['none']]); + expect(metadata.turnLanes.backward) + .to.deep.equal([['none'], ['none']]); + }); + + it('fills lanes.unspecified with key \'turnLane\' correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + oneway: 'yes', + 'turn:lanes': 'slight_left||through|through;slight_right|slight_right' + } + }).lanes().lanes; + var turnLanesUnspecified = lanes.unspecified.map(function(l) { return l.turnLane; }); + expect(turnLanesUnspecified).to.deep.equal([ + ['slight_left'], ['none'], ['through'], ['through', 'slight_right'], ['slight_right'] + ]); + expect(lanes.forward).to.deep.equal([]); + expect(lanes.backward).to.deep.equal([]); + }); + + it('fills lanes.forward & lanes.backward with key \'turnLane\' correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'tertiary', + lanes: 5, + 'lanes:forward': 3, + 'lanes:backward': 2, + 'turn:lanes:backward': 'none|slight_right', + 'turn:lanes:forward': 'slight_left||', + } + }).lanes().lanes; + expect(lanes.unspecified).to.deep.equal([]); + var turnLanesForward = lanes.forward.map(function(l) { return l.turnLane; }); + var turnLanesBackward = lanes.backward.map(function(l) { return l.turnLane; }); + expect(turnLanesForward).to.deep.equal([ + ['slight_left'], ['none'], ['none'] + ]); + expect(turnLanesBackward).to.deep.equal([ + ['none'], ['slight_right'] + ]); + }); + }); + + describe('maxspeed', function() { + it('should parse maxspeed without any units correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': 70 + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + }); + + it('should parse maxspeed with km/h correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70 km/h' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + }); + + it('should parse maxspeed with kmh correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70kmh' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + }); + + it('should parse maxspeed with kph correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70 kph' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + }); + + it('should parse maxspeed with mph correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70mph' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(70); + }); + + it('should parse maxspeed with knots correctly', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '50knots' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(50); + }); + + it('should return undefined when incorrect maxspeed unit provided ', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': '70km' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(undefined); + }); + + it('should return undefined when incorrect maxspeed value provided ', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed': 'a70knots' + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(undefined); + }); + + it('should return undefined when maxspeed not provided ', function() { + var maxspeed = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + } + }).lanes().metadata.maxspeed; + expect(maxspeed).to.equal(undefined); + }); + }); + + describe('maxspeed:lanes', function() { + + it('should parse correctly', function() { + var maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed:lanes': '30|40|40|40|40' + } + }).lanes().metadata.maxspeedLanes.unspecified; + expect(maxspeedLanes).to.deep.equal([ + 30, 40, 40, 40, 40 + ]); + }); + + it('should parse maxspeed:lanes:forward/backward correctly', function() { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: 30, + 'lanes:forward': 4, + 'lanes:backward': 1, + 'maxspeed:lanes:forward': '30|40|40|40', + 'maxspeed:lanes:backward': '30' + } + }).lanes().metadata; + expect(metadata.maxspeedLanes.forward).to.deep.equal([ + null, 40, 40, 40 + ]); + expect(metadata.maxspeedLanes.backward).to.deep.equal([ + null + ]); + }); + + it('should parse correctly when some values maxspeed:lanes are implied by x||y notation', function() { + var maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 4, + maxspeed: '40kmh', + 'maxspeed:lanes': '30|||40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + 30, null, null, null + ]); + }); + + it('should parse correctly when some values maxspeed:lanes are implied by x||| notation', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:forward': 1, + 'lanes:both_ways': 1, + 'turn:lanes:forward': 'slight_left', + 'turn:lanes:backward': 'none|through|through;slight_right', + maxspeed: '60kmh', + 'maxspeed:lanes': '30|||' + } + }).lanes(); + expect(lanes.metadata.maxspeedLanes.unspecified).to.deep.equal([ + 30, null, null, null + ]); + }); + + it('should return none for each maxspeed:lanes which equals maxspeed', function() { + var maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '40kmh', + 'maxspeed:lanes': '30|40|40|40|40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + 30, null, null, null, null + ]); + maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '50kmh', + 'maxspeed:lanes': '30|40|40|40|40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + 30, 40, 40, 40, 40 + ]); + maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30knots', + 'maxspeed:lanes': '30|40|40|40|40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + null, 40, 40, 40, 40 + ]); + }); + + it('should return \'unknown\' for every invalid maxspeed:lane value', function() { + var maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30kmh', + 'maxspeed:lanes': '30|40|fourty|40|40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + null, 40, 'unknown', 40, 40 + ]); + maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30kmh', + 'maxspeed:lanes': '30|40|fourty|40|random' + } + }).lanes().metadata.maxspeedLanes; + + expect(maxspeedLanes.unspecified).to.deep.equal([ + null, 40, 'unknown', 40, 'unknown' + ]); + }); + + it('should parse maxspeed when none', function() { + var maxspeedLanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'maxspeed:lanes': '30|40|none|40|40' + } + }).lanes().metadata.maxspeedLanes; + expect(maxspeedLanes.unspecified).to.deep.equal([ + 30, 40, 'none', 40, 40 + ]); + }); + + it('fills lanes.unspecified with key \'maxspeed\' correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30kmh', + 'maxspeed:lanes': '30|40|fourty|40|40' + } + }).lanes().lanes; + var maxspeedLanes = lanes.unspecified.map(function (l) { + return l.maxspeed; + }); + expect(maxspeedLanes).to.deep.equal([ + null, 40, 'unknown', 40, 40 + ]); + }); + }); + + describe('bicycle lanes', function() { + it('should parse bicycle:lanes correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 3, + 'lanes:bicycleway': 2, + 'bicycleway:lanes': 'no|yes|no|designated|no', + maxspeed: '30kmh', + 'turn:lanes': 'left|||through|right' + } + }).lanes(); + expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ + 'no','yes','no', 'designated', 'no' + ]); + var bicyclewayLanes = lanes.lanes.unspecified.map(function(l) { + return l.bicycleway; + }); + expect(bicyclewayLanes).to.deep.equal([ + 'no','yes','no', 'designated', 'no' + ]); + }); + + it('should parse bicycle:lanes:forward/backward correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + 'lanes:forward': 4, + 'lanes:backward': 3, + 'turn:lanes:forward': 'left;through|left;through|through|right;through|right', + 'bicycleway:lanes:forward': 'lane|no|no|no|no', + 'bicycleway:lanes:backward': 'lane|no|no|no' + } + }).lanes(); + expect(lanes.metadata.bicyclewayLanes.forward).to.deep.equal([ + 'lane','no','no', 'no', 'no' + ]); + expect(lanes.metadata.bicyclewayLanes.backward).to.deep.equal([ + 'lane','no','no', 'no' + ]); + var bicyclewayLanesForward = lanes.lanes.forward.map(function(l) { + return l.bicycleway; + }); + expect(bicyclewayLanesForward).to.deep.equal([ + 'lane','no','no', 'no', 'no' + ]); + var bicyclewayLanesBackward = lanes.lanes.backward.map(function(l) { + return l.bicycleway; + }); + expect(bicyclewayLanesBackward).to.deep.equal([ + 'lane','no','no', 'no' + ]); + }); + + it('should replace any invalid value with unknown', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 3, + maxspeed: '30kmh', + 'bicycleway:lanes': 'no|share|no|designated|no', + 'turn:lanes': 'left|||through|right' + } + }).lanes(); + expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ + 'no','unknown','no', 'designated', 'no' + ]); + var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + return l.bicycleway; + }); + expect(psvLanesForward).to.deep.equal([ + 'no','unknown','no', 'designated', 'no' + ]); + }); + }); + + describe('miscellaneous lanes', function() { + it('should parse psv:lanes correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30kmh', + 'psv:lanes': 'yes|no||no|no' + } + }).lanes(); + expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ + 'yes','no','no', 'no', 'no' + ]); + var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + return l.psv; + }); + expect(psvLanesForward).to.deep.equal([ + 'yes','no','no', 'no', 'no' + ]); + }); + it('should parse psv:lanes:forward/backward correctly', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + maxspeed: '30kmh', + 'psv:lanes:forward': 'no|no|', + 'psv:lanes:backward': 'yes|designated', + } + }).lanes(); + expect(lanes.metadata.psvLanes.forward).to.deep.equal([ + 'no','no','no' + ]); + expect(lanes.metadata.psvLanes.backward).to.deep.equal([ + 'yes', 'designated' + ]); + var psvLanesForward = lanes.lanes.forward.map(function(l) { + return l.psv; + }); + var psvLanesBackward = lanes.lanes.backward.map(function(l) { + return l.psv; + }); + expect(psvLanesForward).to.deep.equal([ + 'no','no','no' + ]); + expect(psvLanesBackward).to.deep.equal([ + 'yes', 'designated' + ]); + }); + it('should replace any invalid value with unknown', function() { + var lanes = iD.Way({ + tags: { + highway: 'residential', + lanes: 3, + maxspeed: '30kmh', + 'psv:lanes': 'yes|no|garbage' + } + }).lanes(); + expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ + 'yes','no', 'unknown' + ]); + var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + return l.psv; + }); + expect(psvLanesForward).to.deep.equal([ + 'yes','no', 'unknown' + ]); + }); + }); +}); diff --git a/test/spec/osm/way.js b/test/spec/osm/way.js index 4635daaf6..78141ff94 100644 --- a/test/spec/osm/way.js +++ b/test/spec/osm/way.js @@ -560,1222 +560,4 @@ describe('iD.osmWay', function() { }); }); - describe('iD.Lanes', function() { - - describe('default lane tags', function() { - - describe('motorway', function() { - - it('returns 2 lanes for highway=motorway', function() { - expect(iD.Way({tags: { highway: 'motorway' }}).lanes().metadata.count, 'motorway lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'motorway', oneway: 'yes' }}).lanes().metadata.count, 'motorway lanes') - .to.eql(2); - }); - - it('returns 4 lanes for highway=motorway and oneway=no', function() { - expect(iD.Way({tags: { highway: 'motorway', oneway: 'no' }}).lanes().metadata.count, 'motorway lanes') - .to.eql(4); - }); - - it('returns 1 lane for highway=motorway_link', function() { - expect(iD.Way({tags: { highway: 'motorway_link' }}).lanes().metadata.count, 'motorway_link lanes') - .to.eql(1); - expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'yes' }}).lanes().metadata.count, 'motorway_link lanes') - .to.eql(1); - }); - - it('returns 2 lanes for highway=motorway_link and oneway=no', function() { - expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'no' }}).lanes().metadata.count, 'motorway_link lanes') - .to.eql(2); - }); - - }); - - describe('trunk', function() { - - it('returns 4 lanes for highway=trunk', function() { - expect(iD.Way({tags: { highway: 'trunk' }}).lanes().metadata.count, 'trunk lanes') - .to.eql(4); - expect(iD.Way({tags: { highway: 'trunk', oneway: 'no' }}).lanes().metadata.count, 'trunk lanes') - .to.eql(4); - }); - - it('returns 2 lanes for highway=trunk and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', oneway: 'yes' }}).lanes().metadata.count, 'trunk lanes') - .to.eql(2); - }); - - it('returns 2 lanes for highway=trunk_link', function() { - expect(iD.Way({tags: { highway: 'trunk_link' }}).lanes().metadata.count, 'trunk_link lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'no' }}).lanes().metadata.count, 'trunk_link lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=trunk_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'yes' }}).lanes().metadata.count, 'trunk_link lanes') - .to.eql(1); - }); - }); - - describe('primary', function() { - - it('returns 2 lanes for highway=primary', function() { - expect(iD.Way({tags: { highway: 'primary' }}).lanes().metadata.count, 'primary lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'primary', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=primary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'primary', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') - .to.eql(1); - }); - - it('returns 2 lanes for highway=primary_link', function() { - expect(iD.Way({tags: { highway: 'primary_link' }}).lanes().metadata.count, 'primary lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'primary_link', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=primary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'primary_link', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') - .to.eql(1); - }); - }); - - describe('seconday', function() { - - it('returns 2 lanes for highway=secondary', function() { - expect(iD.Way({tags: { highway: 'secondary' }}).lanes().metadata.count, 'secondary lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'secondary', oneway: 'no' }}).lanes().metadata.count, 'secondary lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=secondary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'secondary', oneway: 'yes' }}).lanes().metadata.count, 'secondary lanes') - .to.eql(1); - }); - - it('returns 2 lane for highway=secondary_link', function() { - expect(iD.Way({tags: { highway: 'secondary_link' }}).lanes().metadata.count, 'secondary_link lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'no' }}).lanes().metadata.count, 'secondary_link lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=secondary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'yes' }}).lanes().metadata.count, 'secondary_link lanes') - .to.eql(1); - }); - }); - - describe('tertiary', function() { - - it('returns 2 lanes for highway=tertiary', function() { - expect(iD.Way({tags: { highway: 'tertiary' }}).lanes().metadata.count, 'tertiary lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'tertiary', oneway: 'no' }}).lanes().metadata.count, 'tertiary lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=tertiary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'tertiary', oneway: 'yes' }}).lanes().metadata.count, 'tertiary lanes') - .to.eql(1); - }); - - it('returns 2 lane for highway=tertiary_link', function() { - expect(iD.Way({tags: { highway: 'tertiary_link' }}).lanes().metadata.count, 'tertiary_link lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'no' }}).lanes().metadata.count, 'tertiary_link lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=tertiary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'yes' }}).lanes().metadata.count, 'tertiary_link lanes') - .to.eql(1); - }); - }); - - describe('residential', function() { - - it('returns 2 lanes for highway=residential', function() { - expect(iD.Way({tags: { highway: 'residential' }}).lanes().metadata.count, 'residential lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.count, 'residential lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=residential and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.count, 'residential lanes') - .to.eql(1); - }); - }); - - describe('service', function() { - - it('returns 2 lanes for highway=service', function() { - expect(iD.Way({tags: { highway: 'service' }}).lanes().metadata.count, 'service lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'service', oneway: 'no' }}).lanes().metadata.count, 'service lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=service and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'service', oneway: 'yes' }}).lanes().metadata.count, 'service lanes') - .to.eql(1); - }); - }); - - describe('track', function() { - - it('returns 2 lanes for highway=track', function() { - expect(iD.Way({tags: { highway: 'track' }}).lanes().metadata.count, 'track lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'track', oneway: 'no' }}).lanes().metadata.count, 'track lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=track and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'track', oneway: 'yes' }}).lanes().metadata.count, 'track lanes') - .to.eql(1); - }); - }); - - describe('path', function() { - - it('returns 2 lanes for highway=path', function() { - expect(iD.Way({tags: { highway: 'path' }}).lanes().metadata.count, 'path lanes') - .to.eql(2); - expect(iD.Way({tags: { highway: 'path', oneway: 'no' }}).lanes().metadata.count, 'path lanes') - .to.eql(2); - }); - - it('returns 1 lane for highway=path and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'path', oneway: 'yes' }}).lanes().metadata.count, 'path lanes') - .to.eql(1); - }); - }); - }); - - describe('oneway tags', function() { - it('returns correctly oneway when tagged as oneway', function() { - expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.oneway, 'residential lanes') - .to.be.true; - expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.oneway, 'residential lanes') - .to.be.false; - }); - }); - - describe('lane direction', function() { - - it('returns correctly the lane:forward and lane:backward count', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 2, - oneway: false, - forward: 1, - backward: 1, - bothways: 0 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:forward': 3, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 4, - oneway: false, - forward: 3, - backward: 1, - bothways: 0 - }); - }); - - it('returns correctly the count if erroneous values are supplied', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, 'lanes:forward': 3 }}).lanes().metadata, 'trunk lanes') - .to.include({ - count: 2, - oneway: false, - forward: 2, - backward: 0, - bothways: 0 - }); - }); - - it('returns correctly forward count when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes' }}).lanes().metadata, 'trunk lanes') - .to.include({ - count: 2, - oneway: true, - forward: 2, - backward: 0, - bothways: 0 - }); - }); - - it('returns correctly backward count the when oneway=-1', function() { - expect(iD.Way({tags: { highway: 'primary', lanes: 4, oneway: '-1' }}).lanes().metadata, 'primary lanes') - .to.include({ - count: 4, - oneway: true, - backward: 4, - forward: 0, - bothways: 0 - }); - }); - - it('skips provided lanes:forward value when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:forward': 1 }}).lanes().metadata, 'trunk lanes') - .to.include({ - count: 2, - oneway: true, - forward: 2, - backward: 0, - bothways: 0 - }); - }); - - it('skips provided lanes:backward value when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:backward': 1 }}).lanes().metadata, 'trunk lanes') - .to.include({ - count: 2, - oneway: true, - forward: 2, - backward: 0, - bothways: 0 - }); - }); - - it('returns correctly forward count if only backward is supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 3, - oneway: false, - forward: 2, - backward: 1, - bothways: 0 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:backward': 3, }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 4, - oneway: false, - forward: 1, - backward: 3, - bothways: 0 - }); - }); - - it('returns correctly backward count if only forward is supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 3, - oneway: false, - forward: 1, - backward: 2, - bothways: 0 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 2, - oneway: false, - forward: 1, - backward: 1, - bothways: 0 - }); - }); - - it('returns correctly backward count if forward and both_ways are supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 3, - oneway: false, - forward: 1, - backward: 1, - bothways: 1 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 5, - oneway: false, - forward: 1, - backward: 3, - bothways: 1 - }); - }); - - it('returns correctly forward count if backward and both_ways are supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 3, - oneway: false, - forward: 1, - backward: 1, - bothways: 1 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 5, - oneway: false, - forward: 3, - backward: 1, - bothways: 1 - }); - }); - - it('returns correctly the lane:both_ways count as 1', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 2, - oneway: false, - forward: 1, - backward: 0, - bothways: 1 - }); - }); - - it('returns correctly when lane:both_ways>1', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 2, 'lanes:backward': 2 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 5, - oneway: false, - forward: 2, - backward: 2, - bothways: 1 - }); - }); - - it('returns correctly when lane:both_ways is 0 or Not a Number', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 5, - oneway: false, - forward: 2, - backward: 3, - bothways: 0 - }); - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 'none' }}).lanes().metadata, 'residential lanes') - .to.include({ - count: 2, - oneway: false, - forward: 1, - backward: 1, - bothways: 0 - }); - }); - - }); - - describe.skip('lanes array', function() { - it('should have correct number of direction elements', function() { - var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().lanes; - var forward = lanes.filter(function(l) { - return l.direction === 'forward'; - }); - var backward = lanes.filter(function(l) { - return l.direction === 'backward'; - }); - var bothways = lanes.filter(function(l) { - return l.direction === 'bothways'; - }); - expect(forward.length).to.eql(2); - expect(backward.length).to.eql(3); - expect(bothways.length).to.eql(0); - - }); - it('should have corrent number of direction elements', function() { - var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().lanes; - var forward = lanes.filter(function(l) { - return l.direction === 'forward'; - }); - var backward = lanes.filter(function(l) { - return l.direction === 'backward'; - }); - var bothways = lanes.filter(function(l) { - return l.direction === 'bothways'; - }); - expect(forward.length).to.eql(3); - expect(backward.length).to.eql(1); - expect(bothways.length).to.eql(1); - }); - }); - - describe('turn lanes', function() { - it('returns correctly when oneway=yes', function() { - var metadata = iD.Way({ - tags: { - highway: 'trunk', - oneway: 'yes', - 'turn:lanes': 'none|slight_right' - } - }).lanes().metadata; - expect(metadata.turnLanes.unspecified) - .to.deep.equal([ - ['none'], ['slight_right'] - ]); - }); - - it('returns correctly when oneway=yes and lanes=2', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - oneway: 'yes', - lanes: '2', - 'turn:lanes': 'none|slight_right' - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.deep.equal([ - ['none'], ['slight_right'] - ]); - }); - - it('returns correctly when lanes=5 and both_ways=1', function() { - var metadata = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'lanes:forward': 1, - 'lanes:both_ways': 1, - 'turn:lanes:forward': 'slight_left', - 'turn:lanes:backward': 'none|through|through;slight_right', - } - }).lanes().metadata; - expect(metadata.turnLanes.forward) - .to.deep.equal([ - ['slight_left'] - ]); - expect(metadata.turnLanes.backward) - .to.deep.equal([ - ['none'], ['through'], ['through', 'slight_right'] - ]); - }); - - it('returns correctly when multiple values are present in a lane and oneway=yes', function() { - var lanesData = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'yes', - 'turn:lanes': 'slight_left;reverse;left|slight_left;left;through|through|none|through;right', - } - }).lanes(); - - expect(lanesData.metadata.turnLanes.unspecified) - .to.deep.equal([ - ['slight_left', 'reverse', 'left'], - ['slight_left', 'left', 'through'], - ['through'], - ['none'], - ['through', 'right'] - ]); - }); - - it('returns correctly when multiple values are present in a lane and oneway=no', function() { - var lanesData = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'no', - 'lanes:forward': 3, - 'lanes:backward': 2, - 'turn:lanes:forward': 'slight_left;reverse;left|slight_left;left;through|through', - 'turn:lanes:backward': 'none|through;left' - } - }).lanes(); - expect(lanesData.metadata.turnLanes.forward) - .to.deep.equal([ - ['slight_left', 'reverse', 'left'], - ['slight_left', 'left', 'through'], - ['through'] - ]); - expect(lanesData.metadata.turnLanes.backward) - .to.deep.equal([ - ['none'], - ['through', 'left'] - ]); - }); - - it('returns unknown for every invalid value in turn:lanes', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 3, - oneway: 'yes', - 'turn:lanes': '||straight;NO_LEFT', - } - }).lanes().metadata; - expect(metadata.turnLanes.unspecified) - .to.deep.equal([ - ['none'], ['none'], ['unknown', 'unknown'] - ]); - }); - - it('returns unknown for every invalid value in turn:lanes:forward & turn:lanes:backward', function() { - var metadata = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'lanes:forward': 1, - 'lanes:both_ways': 1, - 'turn:lanes:forward': 'sligh_left', - 'turn:lanes:backward': 'none|through|though;slight_right', - } - }).lanes().metadata; - expect(metadata.turnLanes.forward) - .to.deep.equal([ - ['unknown'] - ]); - expect(metadata.turnLanes.backward) - .to.deep.equal([ - ['none'], ['through'], ['unknown', 'slight_right'] - ]); - }); - - it.skip('fills with [\'unknown\'] when given turn:lanes are less than lanes count', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'yes', - 'turn:lanes': 'slight_left|', - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.deep.equal([ - ['slight_left'], ['none'] - ]); - }); - - it.skip('fills with [\'unknown\'] when given turn:lanes:forward are less than lanes forward count', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'no', - 'lanes:forward': 3, - 'turn:lanes:forward': 'slight_left', - 'turn:lanes:backward': 'through', - } - }).lanes().metadata; - - expect(metadata.turnLanes.forward) - .to.deep.equal([ - ['slight_left'], ['unknown'], ['unknown'] - ]); - expect(metadata.turnLanes.backward) - .to.deep.equal([ - ['through'], ['unknown'] - ]); - }); - - it.skip('clips when turn lane information is more than lane count', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 2, - oneway: 'yes', - 'turn:lanes': 'through|through;slight_right|slight_right', - } - }).lanes().metadata; - - expect(metadata.turnLanes) - .to.deep.equal([ - ['through'], ['through', 'slight_right'] - ]); - }); - - it('turnLanes is undefined when not present', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 2, - oneway: 'yes' - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.equal(undefined); - expect(metadata.turnLanes.forward) - .to.equal(undefined); - expect(metadata.turnLanes.backward) - .to.equal(undefined); - }); - - it('turnLanes.forward and turnLanes.backward are both undefined when both are not provided', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 2, - oneway: 'yes', - 'turn:lanes': 'through|through;slight_right', - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.deep.equal([['through'], ['through', 'slight_right']]); - expect(metadata.turnLanes.forward) - .to.equal(undefined); - expect(metadata.turnLanes.backward) - .to.equal(undefined); - }); - - it('parses turnLane correctly when lanes:both_ways=1', function() { - var lanes = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'no', - 'lanes:forward': 3, - 'lanes:both_ways': 1, - 'lanes:backward': 1, - 'turn:lanes:backward': 'slight_right', - 'turn:lanes:forward': 'slight_left||', - } - }).lanes(); - expect(lanes.metadata.turnLanes.backward) - .to.deep.equal([['slight_right']]); - expect(lanes.metadata.turnLanes.forward) - .to.deep.equal([['slight_left'], ['none'], ['none']]); - }); - - it('parses turnLane correctly when lanes:both_ways=1 & lanes:forward < lanes:backward', function() { - var lanes = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'no', - 'lanes:forward': 1, - 'lanes:both_ways': 1, - 'lanes:backward': 3, - 'turn:lanes:forward': 'through', - 'turn:lanes:backward': 'slight_left||', - } - }).lanes(); - expect(lanes.metadata.turnLanes.forward) - .to.deep.equal([['through']]); - expect(lanes.metadata.turnLanes.backward) - .to.deep.equal([['slight_left'], ['none'], ['none']]); - }); - - it('parses correctly when turn:lanes= ||x', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 3, - oneway: 'yes', - 'turn:lanes': '||through;slight_right', - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.deep.equal([['none'], ['none'], ['through', 'slight_right']]); - }); - - it('parses correctly when turn:lanes= |x|', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - 'turn:lanes': '|through|', - } - }).lanes().metadata; - - expect(metadata.turnLanes.unspecified) - .to.deep.equal([['none'], ['through'], ['none']]); - }); - - it('parses correctly when turn:lanes:forward= ||x', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 4, - oneway: 'no', - 'lanes:forward': 3, - 'lanes:backward': 1, - 'turn:lanes:forward': '||through;slight_right', - 'turn:lanes:backward': 'none', - } - }).lanes().metadata; - - expect(metadata.turnLanes.forward) - .to.deep.equal([['none'], ['none'], ['through', 'slight_right']]); - expect(metadata.turnLanes.backward) - .to.deep.equal([['none']]); - }); - - it('parses correctly when turn:lanes:backward= |', function() { - var metadata = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'no', - 'lanes:forward': 3, - 'lanes:backward': 2, - 'turn:lanes:backward': '|', - 'turn:lanes:forward': 'slight_left||', - } - }).lanes().metadata; - - expect(metadata.turnLanes.forward) - .to.deep.equal([['slight_left'], ['none'], ['none']]); - expect(metadata.turnLanes.backward) - .to.deep.equal([['none'], ['none']]); - }); - - it('fills lanes.unspecified with key \'turnLane\' correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - oneway: 'yes', - 'turn:lanes': 'slight_left||through|through;slight_right|slight_right' - } - }).lanes().lanes; - var turnLanesUnspecified = lanes.unspecified.map(function(l) { return l.turnLane; }); - expect(turnLanesUnspecified).to.deep.equal([ - ['slight_left'], ['none'], ['through'], ['through', 'slight_right'], ['slight_right'] - ]); - expect(lanes.forward).to.deep.equal([]); - expect(lanes.backward).to.deep.equal([]); - }); - - it('fills lanes.forward & lanes.backward with key \'turnLane\' correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'tertiary', - lanes: 5, - 'lanes:forward': 3, - 'lanes:backward': 2, - 'turn:lanes:backward': 'none|slight_right', - 'turn:lanes:forward': 'slight_left||', - } - }).lanes().lanes; - expect(lanes.unspecified).to.deep.equal([]); - var turnLanesForward = lanes.forward.map(function(l) { return l.turnLane; }); - var turnLanesBackward = lanes.backward.map(function(l) { return l.turnLane; }); - expect(turnLanesForward).to.deep.equal([ - ['slight_left'], ['none'], ['none'] - ]); - expect(turnLanesBackward).to.deep.equal([ - ['none'], ['slight_right'] - ]); - }); - }); - - describe('maxspeed', function() { - it('should parse maxspeed without any units correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': 70 - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - }); - - it('should parse maxspeed with km/h correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70 km/h' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - }); - - it('should parse maxspeed with kmh correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70kmh' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - }); - - it('should parse maxspeed with kph correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70 kph' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - }); - - it('should parse maxspeed with mph correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70mph' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(70); - }); - - it('should parse maxspeed with knots correctly', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '50knots' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(50); - }); - - it('should return undefined when incorrect maxspeed unit provided ', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': '70km' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(undefined); - }); - - it('should return undefined when incorrect maxspeed value provided ', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed': 'a70knots' - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(undefined); - }); - - it('should return undefined when maxspeed not provided ', function() { - var maxspeed = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - } - }).lanes().metadata.maxspeed; - expect(maxspeed).to.equal(undefined); - }); - }); - - describe('maxspeed:lanes', function() { - - it('should parse correctly', function() { - var maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed:lanes': '30|40|40|40|40' - } - }).lanes().metadata.maxspeedLanes.unspecified; - expect(maxspeedLanes).to.deep.equal([ - 30, 40, 40, 40, 40 - ]); - }); - - it('should parse maxspeed:lanes:forward/backward correctly', function() { - var metadata = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: 30, - 'lanes:forward': 4, - 'lanes:backward': 1, - 'maxspeed:lanes:forward': '30|40|40|40', - 'maxspeed:lanes:backward': '30' - } - }).lanes().metadata; - expect(metadata.maxspeedLanes.forward).to.deep.equal([ - null, 40, 40, 40 - ]); - expect(metadata.maxspeedLanes.backward).to.deep.equal([ - null - ]); - }); - - it('should parse correctly when some values maxspeed:lanes are implied by x||y notation', function() { - var maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 4, - maxspeed: '40kmh', - 'maxspeed:lanes': '30|||40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - 30, null, null, null - ]); - }); - - it('should parse correctly when some values maxspeed:lanes are implied by x||| notation', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'lanes:forward': 1, - 'lanes:both_ways': 1, - 'turn:lanes:forward': 'slight_left', - 'turn:lanes:backward': 'none|through|through;slight_right', - maxspeed: '60kmh', - 'maxspeed:lanes': '30|||' - } - }).lanes(); - expect(lanes.metadata.maxspeedLanes.unspecified).to.deep.equal([ - 30, null, null, null - ]); - }); - - it('should return none for each maxspeed:lanes which equals maxspeed', function() { - var maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '40kmh', - 'maxspeed:lanes': '30|40|40|40|40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - 30, null, null, null, null - ]); - maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '50kmh', - 'maxspeed:lanes': '30|40|40|40|40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - 30, 40, 40, 40, 40 - ]); - maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30knots', - 'maxspeed:lanes': '30|40|40|40|40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - null, 40, 40, 40, 40 - ]); - }); - - it('should return \'unknown\' for every invalid maxspeed:lane value', function() { - var maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30kmh', - 'maxspeed:lanes': '30|40|fourty|40|40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - null, 40, 'unknown', 40, 40 - ]); - maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30kmh', - 'maxspeed:lanes': '30|40|fourty|40|random' - } - }).lanes().metadata.maxspeedLanes; - - expect(maxspeedLanes.unspecified).to.deep.equal([ - null, 40, 'unknown', 40, 'unknown' - ]); - }); - - it('should parse maxspeed when none', function() { - var maxspeedLanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - 'maxspeed:lanes': '30|40|none|40|40' - } - }).lanes().metadata.maxspeedLanes; - expect(maxspeedLanes.unspecified).to.deep.equal([ - 30, 40, 'none', 40, 40 - ]); - }); - - it('fills lanes.unspecified with key \'maxspeed\' correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30kmh', - 'maxspeed:lanes': '30|40|fourty|40|40' - } - }).lanes().lanes; - var maxspeedLanes = lanes.unspecified.map(function (l) { - return l.maxspeed; - }); - expect(maxspeedLanes).to.deep.equal([ - null, 40, 'unknown', 40, 40 - ]); - }); - }); - - describe('bicycle lanes', function() { - it('should parse bicycle:lanes correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 3, - 'lanes:bicycleway': 2, - 'bicycleway:lanes': 'no|yes|no|designated|no', - maxspeed: '30kmh', - 'turn:lanes': 'left|||through|right' - } - }).lanes(); - expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ - 'no','yes','no', 'designated', 'no' - ]); - var bicyclewayLanes = lanes.lanes.unspecified.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanes).to.deep.equal([ - 'no','yes','no', 'designated', 'no' - ]); - }); - - it('should parse bicycle:lanes:forward/backward correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - 'lanes:forward': 4, - 'lanes:backward': 3, - 'turn:lanes:forward': 'left;through|left;through|through|right;through|right', - 'bicycleway:lanes:forward': 'lane|no|no|no|no', - 'bicycleway:lanes:backward': 'lane|no|no|no' - } - }).lanes(); - expect(lanes.metadata.bicyclewayLanes.forward).to.deep.equal([ - 'lane','no','no', 'no', 'no' - ]); - expect(lanes.metadata.bicyclewayLanes.backward).to.deep.equal([ - 'lane','no','no', 'no' - ]); - var bicyclewayLanesForward = lanes.lanes.forward.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanesForward).to.deep.equal([ - 'lane','no','no', 'no', 'no' - ]); - var bicyclewayLanesBackward = lanes.lanes.backward.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanesBackward).to.deep.equal([ - 'lane','no','no', 'no' - ]); - }); - - it('should replace any invalid value with unknown', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 3, - maxspeed: '30kmh', - 'bicycleway:lanes': 'no|share|no|designated|no', - 'turn:lanes': 'left|||through|right' - } - }).lanes(); - expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ - 'no','unknown','no', 'designated', 'no' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.bicycleway; - }); - expect(psvLanesForward).to.deep.equal([ - 'no','unknown','no', 'designated', 'no' - ]); - }); - }); - - describe('miscellaneous lanes', function() { - it('should parse psv:lanes correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30kmh', - 'psv:lanes': 'yes|no||no|no' - } - }).lanes(); - expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ - 'yes','no','no', 'no', 'no' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'yes','no','no', 'no', 'no' - ]); - }); - it('should parse psv:lanes:forward/backward correctly', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 5, - maxspeed: '30kmh', - 'psv:lanes:forward': 'no|no|', - 'psv:lanes:backward': 'yes|designated', - } - }).lanes(); - expect(lanes.metadata.psvLanes.forward).to.deep.equal([ - 'no','no','no' - ]); - expect(lanes.metadata.psvLanes.backward).to.deep.equal([ - 'yes', 'designated' - ]); - var psvLanesForward = lanes.lanes.forward.map(function(l) { - return l.psv; - }); - var psvLanesBackward = lanes.lanes.backward.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'no','no','no' - ]); - expect(psvLanesBackward).to.deep.equal([ - 'yes', 'designated' - ]); - }); - it('should replace any invalid value with unknown', function() { - var lanes = iD.Way({ - tags: { - highway: 'residential', - lanes: 3, - maxspeed: '30kmh', - 'psv:lanes': 'yes|no|garbage' - } - }).lanes(); - expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ - 'yes','no', 'unknown' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'yes','no', 'unknown' - ]); - }); - }); - }); });