osrm-backend/features/support/route.js
Michael Bell db7946d762
Add support for disabling feature datasets (#6666)
This change adds support for disabling datasets, such that specific
files are not loaded into memory when running OSRM. This enables users
to not pay the memory cost for features they do not intend to use.

Initially, there are two options:
- ROUTE_GEOMETRY, for disabling overview, steps, annotations and waypoints.
- ROUTE_STEPS, for disabling steps only.

Attempts to query features for which the datasets are disabled will
lead to a DisabledDatasetException being returned.
2023-08-04 18:43:37 +01:00

307 lines
11 KiB
JavaScript

'use strict';
const Timeout = require('node-timeout');
const request = require('request');
const ensureDecimal = require('../lib/utils').ensureDecimal;
module.exports = function () {
this.requestPath = (service, params, callback) => {
var uri;
if (service == 'timestamp') {
uri = [this.HOST, service].join('/');
} else {
uri = [this.HOST, service, 'v1', this.profile].join('/');
}
return this.sendRequest(uri, params, callback);
};
this.requestUrl = (path, callback) => {
var uri = this.query = [this.HOST, path].join('/'),
limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } });
function runRequest (cb) {
request(uri, cb);
}
runRequest(limit((err, res, body) => {
if (err) {
if (err.statusCode === 408) return callback(this.RoutedError('*** osrm-routed did not respond'));
else if (err.code === 'ECONNREFUSED')
return callback(this.RoutedError('*** osrm-routed is not running'));
} else
return callback(err, res, body);
}));
};
// Overwrites the default values in defaults
// e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]]
this.overwriteParams = (defaults, other) => {
var otherMap = {};
for (var key in other) otherMap[key] = other[key];
return Object.assign({}, defaults, otherMap);
};
var encodeWaypoints = (waypoints) => {
return waypoints.map(w => [w.lon, w.lat].map(ensureDecimal).join(','));
};
this.requestRoute = (waypoints, bearings, approaches, userParams, callback) => {
if (bearings.length && bearings.length !== waypoints.length) throw new Error('*** number of bearings does not equal the number of waypoints');
if (approaches.length && approaches.length !== waypoints.length) throw new Error('*** number of approaches does not equal the number of waypoints');
var defaults = {
output: 'json',
steps: 'true',
alternatives: 'false'
},
params = this.overwriteParams(defaults, userParams),
encodedWaypoints = encodeWaypoints(waypoints);
params.coordinates = encodedWaypoints;
if (bearings.length) {
params.bearings = bearings.map(b => {
var bs = b.split(',');
if (bs.length === 2) return b;
else return b += ',10';
}).join(';');
}
if (approaches.length) {
params.approaches = approaches.join(';');
}
return this.requestPath('route', params, callback);
};
this.requestNearest = (node, userParams, callback) => {
var defaults = {
output: 'json'
},
params = this.overwriteParams(defaults, userParams);
params.coordinates = [[node.lon, node.lat].join(',')];
return this.requestPath('nearest', params, callback);
};
this.requestTable = (waypoints, userParams, callback) => {
var defaults = {
output: 'json'
},
params = this.overwriteParams(defaults, userParams);
params.coordinates = waypoints.map(w => [w.coord.lon, w.coord.lat].join(','));
var srcs = waypoints.map((w, i) => [w.type, i]).filter(w => w[0] === 'src').map(w => w[1]),
dsts = waypoints.map((w, i) => [w.type, i]).filter(w => w[0] === 'dst').map(w => w[1]);
if (srcs.length) params.sources = srcs.join(';');
if (dsts.length) params.destinations = dsts.join(';');
return this.requestPath('table', params, callback);
};
this.requestTrip = (waypoints, userParams, callback) => {
var defaults = {
output: 'json',
steps: 'true'
},
params = this.overwriteParams(defaults, userParams);
params.coordinates = encodeWaypoints(waypoints);
return this.requestPath('trip', params, callback);
};
this.requestMatching = (waypoints, timestamps, userParams, callback) => {
var defaults = {
output: 'json'
},
params = this.overwriteParams(defaults, userParams);
params.coordinates = encodeWaypoints(waypoints);
if (timestamps.length) {
params.timestamps = timestamps.join(';');
}
return this.requestPath('match', params, callback);
};
this.extractInstructionList = (instructions, keyFinder) => {
if (instructions) {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map(keyFinder)
.join(',');
}
};
this.summary = (instructions) => {
if (instructions) {
return instructions.legs.map(l => l.summary).join(';');
}
};
this.wayList = (instructions) => {
return this.extractInstructionList(instructions, s => s.name);
};
this.refList = (instructions) => {
return this.extractInstructionList(instructions, s => s.ref || '');
};
this.pronunciationList = (instructions) => {
return this.extractInstructionList(instructions, s => s.pronunciation || '');
};
this.destinationsList = (instructions) => {
return this.extractInstructionList(instructions, s => s.destinations || '');
};
this.exitsList = (instructions) => {
return this.extractInstructionList(instructions, s => s.exits || '');
};
this.reverseBearing = (bearing) => {
if (bearing >= 180)
return bearing - 180.;
return bearing + 180;
};
this.bearingList = (instructions) => {
return this.extractInstructionList(instructions, s => ('in' in s.intersections[0] ? this.reverseBearing(s.intersections[0].bearings[s.intersections[0].in]) : 0)
+ '->' +
('out' in s.intersections[0] ? s.intersections[0].bearings[s.intersections[0].out] : 0));
};
this.lanesList = (instructions) => {
return this.extractInstructionList(instructions, s => {
return s.intersections.map( i => {
if(i.lanes)
{
return i.lanes.map( l => {
let indications = l.indications.join(';');
return indications + ':' + (l.valid ? 'true' : 'false');
}).join(' ');
}
else
{
return '';
}
}).join(';');
});
};
this.approachList = (instructions) => {
return this.extractInstructionList(instructions, s => s.approaches || '');
};
this.annotationList = (instructions) => {
if (!('annotation' in instructions.legs[0]))
return '';
var merged = {};
instructions.legs.map(l => {
Object.keys(l.annotation).filter(a => !a.match(/metadata/)).forEach(a => {
if (!merged[a]) merged[a] = [];
merged[a].push(l.annotation[a].join(':'));
});
if (l.annotation.metadata) {
merged.metadata = {};
Object.keys(l.annotation.metadata).forEach(a => {
if (!merged.metadata[a]) merged.metadata[a] = [];
merged.metadata[a].push(l.annotation.metadata[a].join(':'));
});
}
});
Object.keys(merged).filter(k => !k.match(/metadata/)).map(a => {
merged[a] = merged[a].join(',');
});
if (merged.metadata) {
Object.keys(merged.metadata).map(a => {
merged.metadata[a] = merged.metadata[a].join(',');
});
}
return merged;
};
this.alternativesList = (instructions) => {
// alternatives_count come from tracepoints list
return instructions.tracepoints.map(t => t.alternatives_count.toString()).join(',');
};
this.turnList = (instructions) => {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map(v => {
switch (v.maneuver.type) {
case 'depart':
case 'arrive':
return v.maneuver.type;
case 'on ramp':
case 'off ramp':
return v.maneuver.type + ' ' + v.maneuver.modifier;
case 'roundabout':
return 'roundabout-exit-' + v.maneuver.exit;
case 'rotary':
if( 'rotary_name' in v )
return v.rotary_name + '-exit-' + v.maneuver.exit;
else
return 'rotary-exit-' + v.maneuver.exit;
case 'roundabout turn':
return v.maneuver.type + ' ' + v.maneuver.modifier + ' exit-' + v.maneuver.exit;
// FIXME this is a little bit over-simplistic for merge/fork instructions
default:
return v.maneuver.type + ' ' + v.maneuver.modifier;
}
})
.join(',');
};
this.locations = (instructions) => {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map(v => {
return this.findNodeByLocation(v.maneuver.location);
})
.join(',');
};
this.intersectionList = (instructions) => {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map( v => {
return v.intersections
.map( intersection => {
var string = intersection.entry[0]+':'+intersection.bearings[0], i;
for( i = 1; i < intersection.bearings.length; ++i )
string = string + ' ' + intersection.entry[i]+':'+intersection.bearings[i];
return string;
}).join(',');
}).join(';');
};
this.modeList = (instructions) => {
return this.extractInstructionList(instructions, s => s.mode);
};
this.drivingSideList = (instructions) => {
return this.extractInstructionList(instructions, s => s.driving_side);
};
this.classesList = (instructions) => {
return this.extractInstructionList(instructions, s => '[' + s.intersections.map(i => '(' + (i.classes ? i.classes.join(',') : '') + ')').join(',') + ']');
};
this.timeList = (instructions) => {
return this.extractInstructionList(instructions, s => s.duration + 's');
};
this.distanceList = (instructions) => {
return this.extractInstructionList(instructions, s => s.distance + 'm');
};
this.weightName = (instructions) => {
return instructions ? instructions.weight_name : '';
};
this.weightList = (instructions) => {
return this.extractInstructionList(instructions, s => s.weight);
};
};