2016-09-19 17:13:44 -04:00
'use strict' ;
2016-03-04 15:11:05 -05:00
2016-09-19 17:13:44 -04:00
const fs = require ( 'fs' ) ;
const util = require ( 'util' ) ;
const d3 = require ( 'd3-queue' ) ;
const OSM = require ( '../lib/osm' ) ;
const classes = require ( './data_classes' ) ;
const tableDiff = require ( '../lib/table_diff' ) ;
const ensureDecimal = require ( '../lib/utils' ) . ensureDecimal ;
const errorReason = require ( '../lib/utils' ) . errorReason ;
2016-03-04 15:11:05 -05:00
module . exports = function ( ) {
this . setGridSize = ( meters ) => {
// the constant is calculated (with BigDecimal as: 1.0/(DEG_TO_RAD*EARTH_RADIUS_IN_METERS
// see ApproximateDistance() in ExtractorStructs.h
// it's only accurate when measuring along the equator, or going exactly north-south
this . zoom = parseFloat ( meters ) * 0.8990679362704610899694577444566908445396483347536032203503 E - 5 ;
} ;
this . setOrigin = ( origin ) => {
this . origin = origin ;
} ;
this . buildWaysFromTable = ( table , callback ) => {
// add one unconnected way for each row
var buildRow = ( row , ri , cb ) => {
// comments ported directly from ruby suite:
// NOTE: currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 edges
// this is related to the fact that a oneway dead-end street doesn't make a lot of sense
// if we stack ways on different x coordinates, routability tests get messed up, because osrm might pick a neighboring way if the one test can't be used.
// instead we place all lines as a string on the same y coordinate. this prevents using neighboring ways.
// add some nodes
var makeFakeNode = ( namePrefix , offset ) => {
return new OSM . Node ( this . makeOSMId ( ) , this . OSM _USER , this . OSM _TIMESTAMP ,
this . OSM _UID , this . origin [ 0 ] + ( offset + this . WAY _SPACING * ri ) * this . zoom ,
this . origin [ 1 ] , { name : util . format ( '%s%d' , namePrefix , ri ) } ) ;
} ;
var nodes = [ 'a' , 'b' , 'c' , 'd' , 'e' ] . map ( ( l , i ) => makeFakeNode ( l , i ) ) ;
nodes . forEach ( node => { this . OSMDB . addNode ( node ) ; } ) ;
// ...with a way between them
var way = new OSM . Way ( this . makeOSMId ( ) , this . OSM _USER , this . OSM _TIMESTAMP , this . OSM _UID ) ;
nodes . forEach ( node => { way . addNode ( node ) ; } ) ;
// remove tags that describe expected test result, reject empty tags
var tags = { } ;
for ( var rkey in row ) {
if ( ! rkey . match ( /^forw\b/ ) &&
! rkey . match ( /^backw\b/ ) &&
! rkey . match ( /^bothw\b/ ) &&
row [ rkey ] . length )
tags [ rkey ] = row [ rkey ] ;
}
var wayTags = { highway : 'primary' } ,
nodeTags = { } ;
for ( var key in tags ) {
var nodeMatch = key . match ( /node\/(.*)/ ) ;
if ( nodeMatch ) {
if ( tags [ key ] === '(nil)' ) {
delete nodeTags [ key ] ;
} else {
nodeTags [ nodeMatch [ 1 ] ] = tags [ key ] ;
}
} else {
if ( tags [ key ] === '(nil)' ) {
delete wayTags [ key ] ;
} else {
wayTags [ key ] = tags [ key ] ;
}
}
}
wayTags . name = util . format ( 'w%d' , ri ) ;
way . setTags ( wayTags ) ;
this . OSMDB . addWay ( way ) ;
for ( var k in nodeTags ) {
nodes [ 2 ] . addTag ( k , nodeTags [ k ] ) ;
}
cb ( ) ;
} ;
var q = d3 . queue ( ) ;
table . hashes ( ) . forEach ( ( row , ri ) => {
q . defer ( buildRow , row , ri ) ;
} ) ;
q . awaitAll ( callback ) ;
} ;
this . tableCoordToLonLat = ( ci , ri ) => {
2016-09-19 17:13:44 -04:00
return [ this . origin [ 0 ] + ci * this . zoom , this . origin [ 1 ] - ri * this . zoom ] . map ( ensureDecimal ) ;
2016-03-04 15:11:05 -05:00
} ;
this . addOSMNode = ( name , lon , lat , id ) => {
id = id || this . makeOSMId ( ) ;
var node = new OSM . Node ( id , this . OSM _USER , this . OSM _TIMESTAMP , this . OSM _UID , lon , lat , { name : name } ) ;
this . OSMDB . addNode ( node ) ;
this . nameNodeHash [ name ] = node ;
} ;
this . addLocation = ( name , lon , lat ) => {
this . locationHash [ name ] = new classes . Location ( lon , lat ) ;
} ;
this . findNodeByName = ( s ) => {
if ( s . length !== 1 ) throw new Error ( util . format ( '*** invalid node name "%s", must be single characters' , s ) ) ;
if ( ! s . match ( /[a-z0-9]/ ) ) throw new Error ( util . format ( '*** invalid node name "%s", must be alphanumeric' , s ) ) ;
var fromNode ;
if ( s . match ( /[a-z]/ ) ) {
fromNode = this . nameNodeHash [ s . toString ( ) ] ;
} else {
fromNode = this . locationHash [ s . toString ( ) ] ;
}
return fromNode ;
} ;
2016-11-08 06:52:15 -05:00
// find a node based on an array containing lon/lat
this . findNodeByLocation = ( node _location ) => {
var searched _coordinate = new classes . Location ( node _location [ 0 ] , node _location [ 1 ] ) ;
for ( var node in this . nameNodeHash )
{
var node _coordinate = new classes . Location ( this . nameNodeHash [ node ] . lon , this . nameNodeHash [ node ] . lat ) ;
if ( this . FuzzyMatch . matchCoordinate ( searched _coordinate , node _coordinate , this . zoom ) )
{
return node ;
}
}
return '_' ;
} ;
2016-03-04 15:11:05 -05:00
this . findWayByName = ( s ) => {
return this . nameWayHash [ s . toString ( ) ] || this . nameWayHash [ s . toString ( ) . split ( '' ) . reverse ( ) . join ( '' ) ] ;
} ;
this . makeOSMId = ( ) => {
this . osmID = this . osmID + 1 ;
return this . osmID ;
} ;
this . resetOSM = ( ) => {
this . OSMDB . clear ( ) ;
this . nameNodeHash = { } ;
this . locationHash = { } ;
2016-09-19 17:13:44 -04:00
this . shortcutsHash = { } ;
2016-03-04 15:11:05 -05:00
this . nameWayHash = { } ;
this . osmID = 0 ;
} ;
this . writeOSM = ( callback ) => {
2016-09-19 17:13:44 -04:00
fs . exists ( this . scenarioCacheFile , ( exists ) => {
if ( exists ) callback ( ) ;
else {
this . OSMDB . toXML ( ( xml ) => {
fs . writeFile ( this . scenarioCacheFile , xml , callback ) ;
2016-03-04 15:11:05 -05:00
} ) ;
2016-09-19 17:13:44 -04:00
}
2016-03-04 15:11:05 -05:00
} ) ;
} ;
2016-09-19 17:13:44 -04:00
this . linkOSM = ( callback ) => {
fs . exists ( this . inputCacheFile , ( exists ) => {
if ( exists ) callback ( ) ;
else {
fs . link ( this . scenarioCacheFile , this . inputCacheFile , callback ) ;
2016-03-04 15:11:05 -05:00
}
} ) ;
} ;
2016-09-19 17:13:44 -04:00
this . extractData = ( p , callback ) => {
2017-03-11 03:26:12 -05:00
let stamp = p . processedCacheFile + '.stamp_extract' ;
2016-09-19 17:13:44 -04:00
fs . exists ( stamp , ( exists ) => {
if ( exists ) return callback ( ) ;
2016-03-04 15:11:05 -05:00
2016-09-19 17:13:44 -04:00
this . runBin ( 'osrm-extract' , util . format ( '%s --profile %s %s' , p . extractArgs , p . profileFile , p . inputCacheFile ) , p . environment , ( err ) => {
if ( err ) {
return callback ( new Error ( util . format ( 'osrm-extract %s: %s' , errorReason ( err ) , err . cmd ) ) ) ;
}
fs . writeFile ( stamp , 'ok' , callback ) ;
2016-03-04 15:11:05 -05:00
} ) ;
} ) ;
} ;
2016-09-19 17:13:44 -04:00
this . contractData = ( p , callback ) => {
2017-03-11 03:26:12 -05:00
let stamp = p . processedCacheFile + '.stamp_contract' ;
2016-09-19 17:13:44 -04:00
fs . exists ( stamp , ( exists ) => {
if ( exists ) return callback ( ) ;
2016-03-04 15:11:05 -05:00
2016-09-19 17:13:44 -04:00
this . runBin ( 'osrm-contract' , util . format ( '%s %s' , p . contractArgs , p . processedCacheFile ) , p . environment , ( err ) => {
if ( err ) {
return callback ( new Error ( util . format ( 'osrm-contract %s: %s' , errorReason ( err ) , err ) ) ) ;
2016-05-26 01:05:44 -04:00
}
2016-09-19 17:13:44 -04:00
fs . writeFile ( stamp , 'ok' , callback ) ;
2016-03-04 15:11:05 -05:00
} ) ;
} ) ;
} ;
2017-03-11 03:26:12 -05:00
this . partitionData = ( p , callback ) => {
let stamp = p . processedCacheFile + '.stamp_partition' ;
fs . exists ( stamp , ( exists ) => {
if ( exists ) return callback ( ) ;
this . runBin ( 'osrm-partition' , util . format ( '%s %s' , p . partitionArgs , p . processedCacheFile ) , p . environment , ( err ) => {
if ( err ) {
return callback ( new Error ( util . format ( 'osrm-partition %s: %s' , errorReason ( err ) , err . cmd ) ) ) ;
}
fs . writeFile ( stamp , 'ok' , callback ) ;
} ) ;
} ) ;
} ;
this . customizeData = ( p , callback ) => {
let stamp = p . processedCacheFile + '.stamp_customize' ;
fs . exists ( stamp , ( exists ) => {
if ( exists ) return callback ( ) ;
this . runBin ( 'osrm-customize' , util . format ( '%s %s' , p . customizeArgs , p . processedCacheFile ) , p . environment , ( err ) => {
if ( err ) {
return callback ( new Error ( util . format ( 'osrm-customize %s: %s' , errorReason ( err ) , err ) ) ) ;
}
fs . writeFile ( stamp , 'ok' , callback ) ;
} ) ;
} ) ;
} ;
2017-03-14 09:45:44 -04:00
this . extractContractPartitionAndCustomize = ( callback ) => {
2016-09-19 17:13:44 -04:00
// a shallow copy of scenario parameters to avoid data inconsistency
// if a cucumber timeout occurs during deferred jobs
let p = { extractArgs : this . extractArgs , contractArgs : this . contractArgs ,
2017-03-11 03:26:12 -05:00
partitionArgs : this . partitionArgs , customizeArgs : this . customizeArgs ,
2016-09-19 17:13:44 -04:00
profileFile : this . profileFile , inputCacheFile : this . inputCacheFile ,
processedCacheFile : this . processedCacheFile , environment : this . environment } ;
let queue = d3 . queue ( 1 ) ;
queue . defer ( this . extractData . bind ( this ) , p ) ;
2017-03-11 03:26:12 -05:00
queue . defer ( this . partitionData . bind ( this ) , p ) ;
2017-05-19 18:28:01 -04:00
queue . defer ( this . contractData . bind ( this ) , p ) ;
2017-03-11 03:26:12 -05:00
queue . defer ( this . customizeData . bind ( this ) , p ) ;
queue . awaitAll ( callback ) ;
} ;
2016-09-29 18:11:04 -04:00
this . writeAndLinkOSM = ( callback ) => {
2016-09-19 17:13:44 -04:00
let queue = d3 . queue ( 1 ) ;
queue . defer ( this . writeOSM . bind ( this ) ) ;
queue . defer ( this . linkOSM . bind ( this ) ) ;
2016-09-29 18:11:04 -04:00
queue . awaitAll ( callback ) ;
} ;
this . reprocess = ( callback ) => {
let queue = d3 . queue ( 1 ) ;
queue . defer ( this . writeAndLinkOSM . bind ( this ) ) ;
2017-03-14 09:45:44 -04:00
queue . defer ( this . extractContractPartitionAndCustomize . bind ( this ) ) ;
2017-03-11 03:26:12 -05:00
queue . awaitAll ( callback ) ;
} ;
2016-03-04 15:11:05 -05:00
this . reprocessAndLoadData = ( callback ) => {
2016-09-19 17:13:44 -04:00
let queue = d3 . queue ( 1 ) ;
2016-09-29 18:11:04 -04:00
queue . defer ( this . writeAndLinkOSM . bind ( this ) ) ;
2017-03-14 09:45:44 -04:00
queue . defer ( this . extractContractPartitionAndCustomize . bind ( this ) ) ;
2016-09-19 17:13:44 -04:00
queue . defer ( this . osrmLoader . load . bind ( this . osrmLoader ) , this . processedCacheFile ) ;
queue . awaitAll ( callback ) ;
2016-03-04 15:11:05 -05:00
} ;
this . processRowsAndDiff = ( table , fn , callback ) => {
var q = d3 . queue ( 1 ) ;
2016-03-28 18:39:57 -04:00
table . hashes ( ) . forEach ( ( row , i ) => { q . defer ( fn , row , i ) ; } ) ;
2016-03-04 15:11:05 -05:00
q . awaitAll ( ( err , actual ) => {
if ( err ) return callback ( err ) ;
2016-09-19 17:13:44 -04:00
let diff = tableDiff ( table , actual ) ;
2017-01-30 18:09:32 -05:00
if ( diff ) callback ( diff ) ;
2016-09-19 17:13:44 -04:00
else callback ( ) ;
2016-03-04 15:11:05 -05:00
} ) ;
} ;
} ;