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

/**
 * BeNomad BeMap JavaScript API - RoutingV2 (v1.5)
 *
 * Modern, Promise-based router. Single endpoint
 * `POST service/routing/1.0` covers point-to-point routing (with vias),
 * matrix and isochrone calculations — `routingMode` discriminates.
 *
 * Wire shape mirrors the BeMap server contract (cross-checked against
 * `bemap-flutter-api/lib/service/routing/provider/routing_server.dart`).
 *
 * In-flight requests are tracked by `requestId` and aborted via `cancel(id)`.
 *
 * @since 1.5.0
 * @public
 * @constructor
 * @extends {bemap.ServiceV2}
 * @param {bemap.Context} context BeMap-JS-API Context. Mandatory.
 * @param {Object} [options]
 */
bemap.RoutingV2 = function(context, options) {
    bemap.ServiceV2.call(this, context, options);
    /** @private */
    this._pending = {}; // requestId -> AbortController
};
bemap.inherits(bemap.RoutingV2, bemap.ServiceV2);

/**
 * Calculate one or several routes (or a matrix, or an isochrone, depending
 * on `request.routingMode`).
 *
 * @public
 * @param {bemap.RoutingRequest} request
 * @param {Object} [options]
 * @param {AbortSignal} [options.signal]
 * @return {Promise<bemap.RoutingResponse>}
 */
bemap.RoutingV2.prototype.calculate = function(request, options) {
    if (!(request instanceof bemap.RoutingRequest)) {
        return Promise.reject(new bemap.Error({
            code: bemap.Error.ROUTING_FAILED,
            message: 'RoutingV2.calculate requires a bemap.RoutingRequest'
        }));
    }
    var opts = options || {};
    var body = request.toJson();
    if (!body.geoserver) body.geoserver = this.ctx.getGeoserver();

    var controller = null;
    if (request.requestId && typeof AbortController === 'function') {
        controller = new AbortController();
        this._pending[request.requestId] = controller;
    }
    var signal = opts.signal || (controller ? controller.signal : undefined);

    var self = this;
    var url = 'service/routing/1.0';
    if (request.requestId) url += '?id=' + encodeURIComponent(request.requestId);

    return this._post(url, body, { signal: signal, headers: opts.headers })
        .then(function(json) {
            if (request.requestId) delete self._pending[request.requestId];
            var resp = bemap.RoutingResponse.fromJson(json);
            if (!resp.requestId && request.requestId) resp.requestId = request.requestId;
            var mode = body.routingMode || bemap.RoutingMode.MODE_VIAS;
            if (!resp.getRoutes().length
                && mode !== bemap.RoutingMode.MODE_MATRIX
                && mode !== bemap.RoutingMode.MODE_ISOCHRONE) {
                throw new bemap.Error({
                    code: bemap.Error.ROUTING_NO_ROUTE,
                    message: 'No route was found between the supplied points'
                });
            }
            return resp;
        }).catch(function(err) {
            if (request.requestId) delete self._pending[request.requestId];
            throw err;
        });
};

/**
 * Compute a duration / distance matrix between every origin and every
 * destination. The Worker uses `matrixStartCount` to discriminate origins
 * from destinations inside the flat `destinations` list.
 *
 * @public
 * @param {Array.<bemap.RoutingDestination|bemap.Coordinate|bemap.CoordinateSat>} originList
 * @param {Array.<bemap.RoutingDestination|bemap.Coordinate|bemap.CoordinateSat>} destinationList
 * @param {Object} [options]
 * @param {Array.<String>} [options.routingCriterias]
 * @param {Array.<String>} [options.options]
 * @param {bemap.RoutingVehicleProfile} [options.routingVehicleProfile]
 * @param {String} [options.outputLanguage]
 * @param {Date|Number|String} [options.departureTime]
 * @param {AbortSignal} [options.signal]
 * @param {String} [options.geoserver]
 * @param {String} [options.requestId]
 * @return {Promise<bemap.RoutingResponse>}
 */
bemap.RoutingV2.prototype.matrix = function(originList, destinationList, options) {
    var opts = options || {};
    var origins = Array.isArray(originList) ? originList : [];
    var dests = Array.isArray(destinationList) ? destinationList : [];

    var req = new bemap.RoutingRequest({
        routingMode: bemap.RoutingMode.MODE_MATRIX,
        destinations: origins.concat(dests),
        matrixStartCount: origins.length,
        routingCriterias: opts.routingCriterias,
        options: opts.options || [bemap.RoutingOptions.POLYLINE],
        routingVehicleProfile: opts.routingVehicleProfile,
        outputLanguage: opts.outputLanguage,
        departureTime: opts.departureTime,
        geoserver: opts.geoserver,
        requestId: opts.requestId
    });
    return this.calculate(req, { signal: opts.signal });
};

/**
 * Compute the reachable-area polygon for a given budget (time in seconds,
 * distance in metres or — with an EV profile — energy in Wh).
 *
 * @public
 * @param {bemap.Coordinate|bemap.CoordinateSat|bemap.RoutingDestination} origin
 * @param {Object} criterion
 * @param {Number} criterion.budget                Budget value passed as `isoChroneLimit`.
 * @param {Array.<String>} [criterion.criterias]   Defaults to [FASTEST] (time-based budget).
 * @param {Object} [options]
 * @param {bemap.RoutingVehicleProfile} [options.routingVehicleProfile]
 * @param {Array.<String>} [options.options]
 * @param {String} [options.outputLanguage]
 * @param {Date|Number|String} [options.departureTime]
 * @param {AbortSignal} [options.signal]
 * @param {String} [options.geoserver]
 * @param {String} [options.requestId]
 * @return {Promise<bemap.RoutingResponse>}
 */
bemap.RoutingV2.prototype.isochrone = function(origin, criterion, options) {
    var crit = criterion || {};
    var opts = options || {};
    var req = new bemap.RoutingRequest({
        routingMode: bemap.RoutingMode.MODE_ISOCHRONE,
        destinations: origin ? [origin] : [],
        routingCriterias: Array.isArray(crit.criterias) ? crit.criterias : [bemap.RoutingCriteria.FASTEST],
        options: opts.options || [bemap.RoutingOptions.POLYLINE],
        routingVehicleProfile: opts.routingVehicleProfile,
        outputLanguage: opts.outputLanguage,
        departureTime: opts.departureTime,
        isoChroneLimit: crit.budget,
        geoserver: opts.geoserver,
        requestId: opts.requestId
    });
    return this.calculate(req, { signal: opts.signal });
};

/**
 * Cancel an in-flight request. Returns `true` when the request was found
 * and aborted, `false` otherwise.
 *
 * @public
 * @param {String} requestId
 * @return {Boolean}
 */
bemap.RoutingV2.prototype.cancel = function(requestId) {
    var controller = this._pending[requestId];
    if (!controller) return false;
    try { controller.abort(); } catch (e) { /* ignore */ }
    delete this._pending[requestId];
    return true;
};