Source: bemap-services-v2/traceroute/bemap-traceroute-request.js

/**
 * BeNomad BeMap JavaScript API - TraceRouteRequest
 *
 * Typed request for `bemap.TraceRoute.compute(request)` and
 * `bemap.TraceRoute.computeRnc(request)`. Map-matches a GPS trace onto the
 * road network; the response is the same shape as `RoutingV2.calculate()`:
 * a `bemap.RoutingResponse`.
 *
 * Wire shape mirrors the BeMap server contract (cross-checked against
 * `bemap-flutter-api/lib/service/trace_route/model/request/trace_route_request.dart`).
 * Endpoint: `POST service/routing/1.0/traceroute`.
 *
 * Required wire fields: `destinations` (min. 2 points), `routingVehicleProfile`.
 *
 * @since 1.5.0
 * @public
 * @constructor
 * @param {Object} [options]
 * @param {Array.<bemap.TraceRouteDestination|bemap.CoordinateSat|bemap.Coordinate|Object>} [options.destinations]
 *   Trace samples in chronological order. Mixed-input types are auto-promoted.
 * @param {bemap.RoutingVehicleProfile} [options.routingVehicleProfile]
 *   Required by the server. Defaults to a generic CAR profile when omitted.
 * @param {Array.<String>} [options.routingCriterias]   Subset of `bemap.RoutingCriteria.*` (e.g. AVOID_TOLLS). Supported server-side for traceroute.
 * @param {Array.<String>} [options.options]            Subset of `bemap.TraceRouteOptions.*`.
 * @param {Date|Number|String} [options.departureTime]  Epoch ms / Date / ISO local datetime.
 * @param {Boolean} [options.adjustEta]                 Adjust ETA from GPS timestamps.
 * @param {Boolean} [options.allowOffRoad]              Default true server-side.
 * @param {Number} [options.corridorRadius]
 * @param {Array.<Object>} [options.fenceShapes]
 * @param {String} [options.language]                   ISO 639-1.
 * @param {Array.<Object>} [options.customData]
 * @param {Array.<Object>} [options.preferredRoads]
 * @param {Array.<Object>} [options.routingRoadBlocks]
 * @param {String} [options.geoserver]
 * @param {Number} [options.switchIndex]
 * @param {String} [options.requestId]
 */
bemap.TraceRouteRequest = function(options) {
    var opts = options || {};
    this.destinations = Array.isArray(opts.destinations) ? opts.destinations.slice() : [];
    this.routingVehicleProfile = opts.routingVehicleProfile || null;
    this.routingCriterias = Array.isArray(opts.routingCriterias) ? opts.routingCriterias.slice() : null;
    this.options = Array.isArray(opts.options) ? opts.options.slice() : null;
    this.departureTime = opts.departureTime || null;
    this.adjustEta = (typeof opts.adjustEta === 'boolean') ? opts.adjustEta : null;
    this.allowOffRoad = (typeof opts.allowOffRoad === 'boolean') ? opts.allowOffRoad : null;
    this.corridorRadius = (typeof opts.corridorRadius === 'number') ? opts.corridorRadius : null;
    this.fenceShapes = Array.isArray(opts.fenceShapes) ? opts.fenceShapes.slice() : null;
    this.language = opts.language || null;
    this.customData = Array.isArray(opts.customData) ? opts.customData.slice() : null;
    this.preferredRoads = Array.isArray(opts.preferredRoads) ? opts.preferredRoads.slice() : null;
    this.routingRoadBlocks = Array.isArray(opts.routingRoadBlocks) ? opts.routingRoadBlocks.slice() : null;
    this.geoserver = opts.geoserver || null;
    this.switchIndex = (typeof opts.switchIndex === 'number') ? opts.switchIndex : null;
    this.requestId = opts.requestId || null;
};

/* Fluent setters */
bemap.TraceRouteRequest.prototype.addDestination = function(d) { this.destinations.push(d); return this; };
bemap.TraceRouteRequest.prototype.setDestinations = function(arr) {
    this.destinations = Array.isArray(arr) ? arr.slice() : [];
    return this;
};
bemap.TraceRouteRequest.prototype.setRoutingVehicleProfile = function(v) { this.routingVehicleProfile = v; return this; };
bemap.TraceRouteRequest.prototype.setOptions = function(o) {
    this.options = Array.isArray(o) ? o.slice() : null;
    return this;
};
bemap.TraceRouteRequest.prototype.setLanguage = function(l) { this.language = l; return this; };
bemap.TraceRouteRequest.prototype.setDepartureTime = function(d) { this.departureTime = d; return this; };
bemap.TraceRouteRequest.prototype.setAdjustEta = function(b) { this.adjustEta = !!b; return this; };
bemap.TraceRouteRequest.prototype.setAllowOffRoad = function(b) { this.allowOffRoad = !!b; return this; };
bemap.TraceRouteRequest.prototype.setRequestId = function(id) { this.requestId = id; return this; };
bemap.TraceRouteRequest.prototype.setRoutingCriterias = function(c) { this.routingCriterias = Array.isArray(c) ? c.slice() : null; return this; };
bemap.TraceRouteRequest.prototype.setCorridorRadius = function(n) { this.corridorRadius = (typeof n === 'number') ? n : null; return this; };
bemap.TraceRouteRequest.prototype.setFenceShapes = function(a) { this.fenceShapes = Array.isArray(a) ? a.slice() : null; return this; };
bemap.TraceRouteRequest.prototype.setCustomData = function(a) { this.customData = Array.isArray(a) ? a.slice() : null; return this; };
bemap.TraceRouteRequest.prototype.setPreferredRoads = function(a) { this.preferredRoads = Array.isArray(a) ? a.slice() : null; return this; };
bemap.TraceRouteRequest.prototype.setRoutingRoadBlocks = function(a) { this.routingRoadBlocks = Array.isArray(a) ? a.slice() : null; return this; };
bemap.TraceRouteRequest.prototype.setGeoserver = function(g) { this.geoserver = g || null; return this; };
bemap.TraceRouteRequest.prototype.setSwitchIndex = function(n) { this.switchIndex = (typeof n === 'number') ? n : null; return this; };

/** @private */
bemap.TraceRouteRequest._serialisePoint = function(p) {
    if (!p) return null;
    if (p instanceof bemap.TraceRouteDestination) return p.toJson();
    return new bemap.TraceRouteDestination({ coordinateSat: p }).toJson();
};

/** @private */
bemap.TraceRouteRequest._asEpochOrIso = function(v) {
    if (v === null || v === undefined) return null;
    if (v instanceof Date) return v.getTime();
    if (typeof v === 'number') return v;
    return String(v);
};

bemap.TraceRouteRequest.prototype.toJson = function() {
    var body = {};

    // Required wire fields
    var pts = [];
    for (var i = 0; i < this.destinations.length; i++) {
        var s = bemap.TraceRouteRequest._serialisePoint(this.destinations[i]);
        if (s) pts.push(s);
    }
    body.destinations = pts;

    // routingVehicleProfile is required server-side — default to a generic CAR
    // profile when the caller did not supply one. Spec'd in the Flutter SDK doc:
    //   "Default transport mode for TraceRoute is EMERGENCY". The JS API defaults
    //   to CAR for parity with how most demos run the service.
    var vp = this.routingVehicleProfile;
    if (vp) {
        body.routingVehicleProfile = (typeof vp.toJson === 'function') ? vp.toJson() : vp;
    } else {
        body.routingVehicleProfile = { transportMode: bemap.TransportMode.CAR };
    }

    // Optional
    if (this.options && this.options.length) body.options = this.options.slice();
    if (this.routingCriterias && this.routingCriterias.length) body.routingCriterias = this.routingCriterias.slice();
    var dep = bemap.TraceRouteRequest._asEpochOrIso(this.departureTime);
    if (dep !== null) body.departureTime = dep;
    if (this.adjustEta !== null) body.adjustEta = this.adjustEta;
    if (this.allowOffRoad !== null) body.allowOffRoad = this.allowOffRoad;
    if (this.corridorRadius !== null) body.corridorRadius = this.corridorRadius;
    if (this.fenceShapes && this.fenceShapes.length) body.fenceShapes = this.fenceShapes.slice();
    if (this.language) body.language = this.language;
    if (this.customData && this.customData.length) body.customData = this.customData.slice();
    if (this.preferredRoads && this.preferredRoads.length) body.preferredRoads = this.preferredRoads.slice();
    if (this.routingRoadBlocks && this.routingRoadBlocks.length) body.routingRoadBlocks = this.routingRoadBlocks.slice();
    if (this.geoserver) body.geoserver = this.geoserver;
    if (this.switchIndex !== null) body.switchIndex = this.switchIndex;

    return body;
};