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