Source: bemap-services-v2/routing/bemap-routing-route.js

/**
 * 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
    });
};