/**
* BeNomad BeMap JavaScript API - EvReachableArea service (v1.5)
*
* Promise-based wrapper around `POST service/evreachablearea/1.0`. Given
* a start coordinate, an EV vehicle, and an initial battery level, returns
* the polygon of every point reachable from the start.
*
* Auth: `ROLE_EVREACHABLEAREA`.
*
* Typical usage:
*
* var era = new bemap.EvReachableArea(ctx);
* var req = new bemap.EvReachableAreaRequest({
* start: new bemap.Coordinate(2.35, 48.85),
* vehicle: 'a204c816-...',
* temperature: 18,
* initBatteryLevel: 80
* });
* era.compute(req).then(function(response) {
* var polygon = new bemap.Polygon(response.getPolygon(), {
* style: new bemap.PolygonStyle({ fillColor: new bemap.Color(142, 68, 173, 0.2) })
* });
* map.addPolygon(polygon);
* });
*
* @since 1.5.0
* @public
* @constructor
* @extends {bemap.ServiceV2}
* @param {bemap.Context} context
* @param {Object} [options]
*/
bemap.EvReachableArea = function(context, options) {
bemap.ServiceV2.call(this, context, options);
};
bemap.inherits(bemap.EvReachableArea, bemap.ServiceV2);
/**
* Compute the reachable-area polygon.
*
* @public
* @param {bemap.EvReachableAreaRequest} request
* @param {Object} [options]
* @param {AbortSignal} [options.signal]
* @return {Promise<bemap.EvReachableAreaResponse>}
*/
bemap.EvReachableArea.prototype.compute = function(request, options) {
if (!(request instanceof bemap.EvReachableAreaRequest)) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.compute requires a bemap.EvReachableAreaRequest'
}));
}
var opts = options || {};
var body = request.toJson();
if (!body.geoserver) body.geoserver = this.ctx.getGeoserver();
// Client-side validation — `== null` accepts lon=0/lat=0 (Prime Meridian / Equator).
if (body.startLon == null || body.startLat == null) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.compute: request.start coordinate is required'
}));
}
if (!body.vehicle) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.compute: request.vehicle (UUID key) is required'
}));
}
if (body.temperature == null) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.compute: request.temperature (°C) is required by the server'
}));
}
return this._post('service/evreachablearea/1.0', body, { signal: opts.signal })
.then(function(json) {
var resp = bemap.EvReachableAreaResponse.fromJson(json);
if (!resp.getPolygon().length) {
throw new bemap.Error({
code: bemap.Error.REACHABLE_AREA_FAILED,
message: 'No reachable area could be computed for the supplied vehicle / battery / temperature',
context: json
});
}
return resp;
});
};
/**
* Convenience: issue N parallel `compute()` calls — one per battery level —
* for an "overlay multiple budgets" visualisation (e.g. 50 / 70 / 90 %).
* Returns the results in the same order as the input, each tagged with
* the battery level used.
*
* @public
* @param {bemap.EvReachableAreaRequest} request Template — `initBatteryLevel` will be overridden.
* @param {Array<Number>} batteryLevels Percent values 0-100.
* @param {Object} [options]
* @param {AbortSignal} [options.signal]
* @return {Promise<Array<{batteryLevel: Number, response: bemap.EvReachableAreaResponse}>>}
*/
bemap.EvReachableArea.prototype.combineMultipleBatteryLevels = function(request, batteryLevels, options) {
if (!(request instanceof bemap.EvReachableAreaRequest)) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.combineMultipleBatteryLevels requires a template Request'
}));
}
if (!Array.isArray(batteryLevels) || !batteryLevels.length) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvReachableArea.combineMultipleBatteryLevels: batteryLevels must be a non-empty array'
}));
}
var self = this;
var opts = options || {};
var promises = batteryLevels.map(function(level) {
// Clone the request with the level swapped in.
var cloned = new bemap.EvReachableAreaRequest({
start: request.start,
stop: request.stop,
vehicle: request.vehicle,
initBatteryLevel: level,
temperature: request.temperature,
payload: request.payload,
criterias: request.criterias,
weather: request.weather,
weatherProvider: request.weatherProvider,
polyline: request.polyline,
encodedPolyline: request.encodedPolyline,
geoserver: request.geoserver
});
return self.compute(cloned, { signal: opts.signal })
.then(function(response) { return { batteryLevel: level, response: response }; });
});
return Promise.all(promises);
};