laneify
Version:
Create offset GeoJSON LineString features (lanes) based on an OSM way (road)
128 lines (105 loc) • 3.54 kB
JavaScript
;
var clone = require('clone')
var merge = require('object-merge')
var assert = require('assert')
var geoTools = require('./geoTools')
var translateSegment = require('./translateSegment')
var DEFAULTS = {
laneOffset: 0.0001
}
/**
* Strips out the unneeded properties
* @param {object} road GeoJSON object to sanitize
* @return {object} Sanitized road
*/
function sanitizeRoadData(road) {
var whitelist = ['highway', 'name', 'lanes', 'oneway']
delete road.id
Object.keys(road.properties).forEach(function (property) {
if (whitelist.indexOf(property) < 0) {
delete road.properties[property]
}
})
}
/**
* Modifies the coordinates of a lane to be the translated (offset) values
* @param {object} lane OSM GeoJSON LineString
* @param {Number} laneOffset distance to offset by
*/
function createLaneGeometry(lane, laneOffset) {
var lastSegment
var laneCoords = []
var allNodes = lane.geometry.coordinates
allNodes.forEach(function (point1Array, index) {
if (index !== allNodes.length - 1) {
var point2Array = allNodes[index + 1]
var p1 = geoTools.createPoint(point1Array[0], point1Array[1])
var p2 = geoTools.createPoint(point2Array[0], point2Array[1])
var centerline = geoTools.createLine(p1, p2)
lastSegment = translateSegment(centerline, laneOffset, lane.properties.direction === 'with')
laneCoords.push([lastSegment.p1.x, lastSegment.p1.y])
} else {
var lastPoint = lastSegment.p2
laneCoords.push([lastPoint.x, lastPoint.y])
}
})
lane.geometry.coordinates = laneCoords
}
/**
* Clones the given OSM GeoJSON way into 2 and offsets their geometries
* @param {object} road OSM GeoJSON LineString
* @param {Number} laneOffset distance to offset by
* @return {array} the 2 offset GeoJSON objects
*/
function createLanes(road, laneOffset) {
var lanes = [road, clone(road)]
var leftLane = lanes[0]
var rightLane = lanes[1]
leftLane.properties.direction = 'with';
rightLane.properties.direction = 'against';
createLaneGeometry(leftLane, laneOffset)
createLaneGeometry(rightLane, laneOffset)
return [leftLane, rightLane]
}
/**
* Check if this is a one way road (by OSM's standard)
* @param {object} road OSM GeoJSON LineString
* @return {Boolean}
*/
function isOneWay(road) {
return !!road.properties.oneway && road.properties.oneway === 'yes'
}
/**
* Check if this is a road we're interested in
* @param {object} road OSM GeoJSON LineString
* @return {Boolean}
*/
function isRoad(feature, types) {
var validRoadTypes = new RegExp(types.join('|'))
var highway = feature.properties.highway
return highway && validRoadTypes.test(highway)
}
function isLineString(feature) {
return feature.geometry.type === 'LineString'
}
module.exports = function splitter(road, options) {
options = merge(DEFAULTS, options || {})
assert(!!road, '`road` must be supplied (GeoJSON object)')
assert(isLineString(road), '`road` must be a GeoJSON LineString')
if (options.highwayTypes) {
assert(isRoad(road, options.highwayTypes), '`road` supplied must be a LineString with valid `highway` property')
}
// Don't modify what we're given
road = clone(road)
var featureCollection = {
type: 'FeatureCollection',
features: []
}
sanitizeRoadData(road)
if (isOneWay(road)) {
featureCollection.features.push(road)
} else {
featureCollection.features = createLanes(road, options.laneOffset)
}
return featureCollection;
}