/**
* BeNomad BeMap JavaScript API - RoutingRoute
*
* A single calculated route. A response can contain several when the request
* asked for alternatives or used MODE_1_TO_N / N_TO_1 / N_TO_N.
*
* Wire shape from the BeMap server (cross-checked against an actual response
* payload):
* {
* length: long, // total length in metres
* duration: long, // total duration in seconds (no traffic)
* totalDuration: long, // total duration in seconds (with traffic, if requested)
* trafficDelay: long,
* departureTime: long, // epoch ms (0 when not set)
* arrivalTime: long, // epoch ms (0 when not set)
* averageSpeed: double, // km/h
* maximumSpeed: double,
* startUTurnThreshold: int,
* routingInstructions: [...], // turn-by-turn
* boundingBox: { minLon, minLat, maxLon, maxLat },
* startStopInfo: { start, stop, distanceFirstMatched, distanceLastMatched, interDests },
* polyline: [{ lon, lat }, ...] // array of coordinate objects
* }
*
* Extras returned by the server but not modelled here are preserved on
* `extra` so advanced consumers can still reach them.
*
* For consumer convenience, `getSummary()` returns a synthetic
* `bemap.RoutingSummary` built from the route-level totals (distance,
* duration, traffic delay, ETA).
*
* @since 1.5.0
* @public
* @constructor
*/
bemap.RoutingRoute = function(options) {
var opts = options || {};
// Route-level totals
this.length = (typeof opts.length === 'number') ? opts.length : 0;
this.duration = (typeof opts.duration === 'number') ? opts.duration : 0;
this.totalDuration = (typeof opts.totalDuration === 'number') ? opts.totalDuration : null;
this.trafficDelay = (typeof opts.trafficDelay === 'number') ? opts.trafficDelay : null;
this.departureTime = (typeof opts.departureTime === 'number' && opts.departureTime > 0) ? opts.departureTime : null;
this.arrivalTime = (typeof opts.arrivalTime === 'number' && opts.arrivalTime > 0) ? opts.arrivalTime : null;
this.averageSpeed = (typeof opts.averageSpeed === 'number') ? opts.averageSpeed : null;
this.maximumSpeed = (typeof opts.maximumSpeed === 'number') ? opts.maximumSpeed : null;
// Geometry / structure
this.polyline = (opts.polyline !== undefined && opts.polyline !== null) ? opts.polyline : null;
this.routingInstructions = Array.isArray(opts.routingInstructions) ? opts.routingInstructions : [];
this.boundingBox = opts.boundingBox || null;
this.startStopInfo = opts.startStopInfo || null;
// Catch-all for unmodelled fields (events, off-roads, toll breakdown, etc.)
this.extra = opts.extra || null;
};
/** @public — total length in metres. */
bemap.RoutingRoute.prototype.getLength = function() { return this.length; };
/** @public — total duration in seconds (free-flow). */
bemap.RoutingRoute.prototype.getDuration = function() { return this.duration; };
/** @public — total duration including current traffic, when TRAFFIC option requested. */
bemap.RoutingRoute.prototype.getTotalDuration = function() { return this.totalDuration; };
/** @public — delay added by current traffic, in seconds. */
bemap.RoutingRoute.prototype.getTrafficDelay = function() { return this.trafficDelay; };
/** @public — departure epoch ms when set on the request. */
bemap.RoutingRoute.prototype.getDepartureTime = function() { return this.departureTime; };
/** @public — arrival epoch ms when set on the request. */
bemap.RoutingRoute.prototype.getArrivalTime = function() { return this.arrivalTime; };
/** @public — average speed in km/h. */
bemap.RoutingRoute.prototype.getAverageSpeed = function() { return this.averageSpeed; };
/** @public — maximum speed in km/h. */
bemap.RoutingRoute.prototype.getMaximumSpeed = function() { return this.maximumSpeed; };
/**
* Returns the polyline as an Array of `bemap.Coordinate`. The BeMap server
* emits the polyline as an array of `{lon, lat}` objects when the POLYLINE
* option is requested; we coerce to `bemap.Coordinate` for ergonomics.
*
* Returns `null` when the polyline was not requested.
*
* @public
* @return {Array.<bemap.Coordinate>|null}
*/
bemap.RoutingRoute.prototype.getPolyline = function() {
if (!this.polyline) return null;
// Already an array of Coordinate?
if (Array.isArray(this.polyline) && this.polyline[0] instanceof bemap.Coordinate) return this.polyline;
// Array of {lon,lat}
if (Array.isArray(this.polyline)) {
return this.polyline.map(function(p) {
var lon = (p.lon !== undefined) ? p.lon : p.longitude;
var lat = (p.lat !== undefined) ? p.lat : p.latitude;
return new bemap.Coordinate(lon, lat);
});
}
// Raw value (e.g. encoded polyline string) — return as-is for the
// power-user to decode.
return this.polyline;
};
/** @public — raw polyline value as the server sent it (array or string). */
bemap.RoutingRoute.prototype.getPolylineRaw = function() { return this.polyline; };
/** @public — array of `bemap.RoutingInstruction`. */
bemap.RoutingRoute.prototype.getInstructions = function() { return this.routingInstructions; };
bemap.RoutingRoute.prototype.getRoutingInstructions = function() { return this.routingInstructions; };
bemap.RoutingRoute.prototype.getBoundingBox = function() { return this.boundingBox; };
bemap.RoutingRoute.prototype.getStartStopInfo = function() { return this.startStopInfo; };
bemap.RoutingRoute.prototype.getExtra = function() { return this.extra; };
/**
* Synthetic summary object: distance, duration, traffic delay, ETA. Built
* from the route-level totals — preserved for parity with consumers that
* expect a `summary` object on the route.
*
* @public
* @return {bemap.RoutingSummary}
*/
bemap.RoutingRoute.prototype.getSummary = function() {
if (this._summary) return this._summary;
this._summary = new bemap.RoutingSummary({
distanceM: this.length,
durationS: this.duration,
durationInTrafficS: (this.totalDuration !== null) ? this.totalDuration : this.duration,
eta: this.arrivalTime || null
});
return this._summary;
};
bemap.RoutingRoute.fromJson = function(raw) {
if (!raw) return new bemap.RoutingRoute();
var instructions = [];
var rawInstr = raw.routingInstructions || raw.instructions || raw.roadSheet || [];
for (var j = 0; j < rawInstr.length; j++) {
instructions.push(bemap.RoutingInstruction.fromJson(rawInstr[j]));
}
var bbox = null;
if (raw.boundingBox) {
bbox = new bemap.BoundingBox(
raw.boundingBox.minLon !== undefined ? raw.boundingBox.minLon : raw.boundingBox.minX,
raw.boundingBox.minLat !== undefined ? raw.boundingBox.minLat : raw.boundingBox.minY,
raw.boundingBox.maxLon !== undefined ? raw.boundingBox.maxLon : raw.boundingBox.maxX,
raw.boundingBox.maxLat !== undefined ? raw.boundingBox.maxLat : raw.boundingBox.maxY
);
}
var extra = {};
var modelledKeys = {
length: 1, duration: 1, totalDuration: 1, trafficDelay: 1,
departureTime: 1, arrivalTime: 1, averageSpeed: 1, maximumSpeed: 1,
startUTurnThreshold: 1,
polyline: 1, routingInstructions: 1, instructions: 1, roadSheet: 1,
boundingBox: 1, startStopInfo: 1,
summary: 1
};
var hasExtra = false;
for (var k in raw) {
if (Object.prototype.hasOwnProperty.call(raw, k) && !modelledKeys[k]) {
extra[k] = raw[k];
hasExtra = true;
}
}
return new bemap.RoutingRoute({
length: raw.length,
duration: raw.duration,
totalDuration: raw.totalDuration,
trafficDelay: raw.trafficDelay,
departureTime: raw.departureTime,
arrivalTime: raw.arrivalTime,
averageSpeed: raw.averageSpeed,
maximumSpeed: raw.maximumSpeed,
polyline: raw.polyline || null,
routingInstructions: instructions,
boundingBox: bbox,
startStopInfo: raw.startStopInfo || null,
extra: hasExtra ? extra : null
});
};