UNPKG

mobility-toolbox-js

Version:

Toolbox for JavaScript applications in the domains of mobility and logistics.

694 lines (693 loc) 31.2 kB
/* eslint-disable @typescript-eslint/unbound-method */ import { Feature } from 'ol'; import Control from 'ol/control/Control'; import { click } from 'ol/events/condition'; import BaseEvent from 'ol/events/Event'; import { buffer } from 'ol/extent'; import { GeoJSON } from 'ol/format'; import { LineString, Point } from 'ol/geom'; import { Modify } from 'ol/interaction'; import VectorLayer from 'ol/layer/Vector'; import { unByKey } from 'ol/Observable'; import { fromLonLat, toLonLat } from 'ol/proj'; import VectorSource from 'ol/source/Vector'; import { RoutingAPI } from '../../api'; // Examples for a single hop: // basel sbb a station named "basel sbb" // ZUE, station "Zürich HB" by its common abbreviation // Zürich Hauptbahnhof or HBF Zürich are all valid synonyms für "Zürich HB" // @47.37811,8.53935 a station at position 47.37811, 8.53935 // @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935 // zürich hb@47.37811,8.53935$8 track 8 in station "Zürich HB" at position 47.37811, 8.53935 const REGEX_VIA_POINT = /^([^@$!\n]*)(@?([\d.]+),([\d.]+))?(\$?([a-zA-Z0-9]{0,2}))$/; // Examples for a single hop: // // 47.37811,8.53935 a position 47.37811, 8.53935 const REGEX_VIA_POINT_COORD = /^([\d.]+),([\d.]+)$/; // Examples for a single hop: // // !8596126 a station with id 8596126 // !8596126$4 a station with id 8596126 const REGEX_VIA_POINT_STATION_ID = /^!([^$]*)(\$?([a-zA-Z0-9]{0,2}))$/; const STOP_FETCH_ABORT_CONTROLLER_KEY = 'stop-fetch'; const getFlatCoordinatesFromSegments = (segmentArray) => { const coords = []; segmentArray.forEach((seg) => { var _a; const coordArr = (_a = seg.getGeometry()) === null || _a === void 0 ? void 0 : _a.getCoordinates(); if (coordArr === null || coordArr === void 0 ? void 0 : coordArr.length) { coords.push(...coordArr); } }); return coords; }; /** * This OpenLayers control allows the user to add and modifiy via points to * a map and request a route from the [geOps Routing API](https://developer.geops.io/apis/routing/). * * @example * import { Map } from 'ol'; * import { RoutingControl } from 'mobility-toolbox-js/ol'; * * const map = new Map({ * target: 'map' * }); * * const control = new RoutingControl(); * * map.addControl(control); * * @classproperty {string} mot - Mean of transport to be used for routing. * @classproperty {VectorLayer} routingLayer - Layer for adding route features. * @classproperty {boolean} loading - True if the control is requesting the backend. * * @see <a href="/example/ol-routing">Openlayers routing example</a> * * @extends {ol/control/Control~Control} * * @public */ class RoutingControl extends Control { get active() { return this.get('active'); } set active(newValue) { this.set('active', newValue); } get loading() { return this.get('loading'); } set loading(newValue) { this.set('loading', newValue); } get modify() { return this.get('modify'); } set modify(newValue) { this.set('modify', newValue); } get mot() { return this.get('mot'); } set mot(newValue) { this.set('mot', newValue); } /** * Constructor. * * @param {Object} options * @param {string} options.apiKey Access key for [geOps APIs](https://developer.geops.io/). * @param {Object} options.apiParams Request parameters. See [geOps Routing API documentation](https://developer.geops.io/apis/routing/). * @param {Array.<Array<graph="osm", minZoom=0, maxZoom=99>>} [options.graphs=[['osm', 0, 99]]] - Array of routing graphs and min/max zoom levels. If you use the control in combination with the [geOps Maps API](https://developer.geops.io/apis/maps/), you may want to use the optimal level of generalizations: "[['gen4', 0, 8], ['gen3', 8, 9], ['gen2', 9, 11], ['gen1', 11, 13], ['osm', 13, 99]]". * @param {string} [options.mot="bus"] Mean of transport to be used for routing. * @param {function} options.onRouteError Callback on request errors. * @param {VectorLayer} [options.routingLayer=new VectorLayer()] Vector layer for adding route features. * @param {boolean} [options.snapToClosestStation=false] If true, the routing will snap the coordinate to the closest station. Default to false. * @param {StyleLike} options.style Style of the default vector layer. * @param {boolean} [options.useRawViaPoints=fale] Experimental property. If true, it allows the user to add via points using different kind of string. See "via" parameter defined by the [geOps Routing API](https://developer.geops.io/apis/routing/). Default to false, only array of coordinates and station's id are supported as via points. * @param {string} [options.url='https://api.geops.io/routing/v1/'] [geOps Realtime API](https://developer.geops.io/apis/realtime/) url. * @public */ constructor(options = {}) { var _a; super(options); this.abortControllers = {}; this.cacheStationData = {}; this.format = new GeoJSON({ featureProjection: 'EPSG:3857' }); this.graphs = []; this.initialRouteDrag = {}; this.segments = []; this.snapToClosestStation = false; this.useRawViaPoints = false; this.viaPoints = []; if (!this.element) { this.createDefaultElement(); } /** True if the control is requesting the backend. */ this.loading = false; this.active = options.active || true; this.graphs = (_a = options.graphs) !== null && _a !== void 0 ? _a : [['osm', 0, 99]]; this.mot = options.mot || 'bus'; this.modify = options.modify !== false; this.apiParams = options.apiParams; this.useRawViaPoints = options.useRawViaPoints || false; this.snapToClosestStation = options.snapToClosestStation || false; this.apiKey = options.apiKey; this.stopsApiKey = options.stopsApiKey || this.apiKey; this.stopsApiUrl = options.stopsApiUrl || 'https://api.geops.io/stops/v1/'; this.api = new RoutingAPI(Object.assign({}, options)); this.routingLayer = options.routingLayer || new VectorLayer({ source: new VectorSource(), style: options.style, }); this.onRouteError = options.onRouteError || ((error) => { this.dispatchEvent(new BaseEvent('change:route')); this.reset(); console.error(error); }); this.onMapClick = this.onMapClick.bind(this); this.onModifyEnd = this.onModifyEnd.bind(this); this.onModifyStart = this.onModifyStart.bind(this); this.createModifyInteraction(); this.on('propertychange', (evt) => { if (evt.key === 'active') { this.onActiveChange(); } if (evt.key === 'mot') { if (this.viaPoints) { void this.drawRoute(); } } }); } /** * Calculate at which resolutions corresponds each generalizations. * * @private */ static getGraphsResolutions(graphs, map) { const view = map.getView(); return graphs.map(([, minZoom, maxZoom]) => { return [ view.getResolutionForZoom(minZoom), view.getResolutionForZoom(maxZoom || minZoom + 1), ]; }); } /** * Aborts viapoint and route requests * @private */ abortRequests() { var _a; // Abort Routing API requests this.graphs.forEach((graph) => { const graphName = graph[0] || ''; if (this.abortControllers[graphName]) { this.abortControllers[graphName].abort(); } this.abortControllers[graphName] = new AbortController(); }); // Abort Stops API requests (_a = this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]) === null || _a === void 0 ? void 0 : _a.abort(); this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY] = new AbortController(); this.loading = false; } activate() { var _a; const map = this.getMap(); if (map) { this.format = new GeoJSON({ featureProjection: map.getView().getProjection(), }); this.graphsResolutions = RoutingControl.getGraphsResolutions(this.graphs, map); // Clean the modifyInteraction if present if (this.modifyInteraction) { map.removeInteraction(this.modifyInteraction); } // Add modify interaction, RoutingLayer and listeners // this.routingLayer?.attachToMap(this.getMap()); if (this.modifyInteraction) { map.addInteraction(this.modifyInteraction); } (_a = this.modifyInteraction) === null || _a === void 0 ? void 0 : _a.setActive(this.modify); this.addListeners(); } } /** * Add click listener to map. * @private */ addListeners() { var _a; if (!this.modify) { return; } this.removeListeners(); // @ts-expect-error improve ts types this.onMapClickKey = (_a = this.getMap()) === null || _a === void 0 ? void 0 : _a.on('singleclick', this.onMapClick); } /** * Adds/Replaces a viaPoint to the viaPoints array and redraws route: * Adds a viaPoint at end of array by default. * If an index is passed a viaPoint is added at the specified index. * If an index is passed and overwrite x is > 0, x viaPoints at the specified * index are replaced with a single new viaPoint. * @param {string | Coordinate} coordinatesOrString Array of coordinates or a string representing a station * @param {number} [index=-1] Integer representing the index of the added viaPoint. If not specified, the viaPoint is added at the end of the array. * @param {number} [overwrite=0] Marks the number of viaPoints that are removed at the specified index on add. * @public */ addViaPoint(coordinatesOrString, index = -1, overwrite = 0) { /* Add/Insert/Overwrite viapoint and redraw route */ this.viaPoints.splice(index === -1 ? this.viaPoints.length : index, overwrite, coordinatesOrString); void this.drawRoute(); this.dispatchEvent(new BaseEvent('change:route')); } /** * Define a default element. * * @private */ createDefaultElement() { this.element = document.createElement('button'); this.element.id = 'ol-toggle-routing'; this.element.innerHTML = 'Toggle Route Control'; this.element.onclick = () => { return this.active ? this.deactivate() : this.activate(); }; Object.assign(this.element.style, { position: 'absolute', right: '10px', top: '10px', }); } /** * Create the interaction used to modify vertexes of features. * @private */ createModifyInteraction() { var _a; /** * @type {ol.interaction.Modify} * @private */ // Define and add modify interaction this.modifyInteraction = new Modify({ // hitDetection: this.routingLayer, // TODO: wait for ol, fixed in https://github.com/openlayers/openlayers/pull/16393 deleteCondition: (e) => { var _a; const feats = // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access ((_a = e.target) === null || _a === void 0 ? void 0 : _a.getFeaturesAtPixel(e.pixel, { hitTolerance: 5, })) || []; const viaPoint = feats.find((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point' && feat.get('index'); }); if (click(e) && viaPoint) { // Remove node & viaPoint if an existing viaPoint was clicked this.removeViaPoint(viaPoint.get('index')); return true; } return false; }, pixelTolerance: 6, source: ((_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.getSource()) || undefined, }); this.modifyInteraction.on('modifystart', this.onModifyStart); this.modifyInteraction.on('modifyend', this.onModifyEnd); this.modifyInteraction.setActive(false); } deactivate() { const map = this.getMap(); if (map) { // Remove modify interaction, RoutingLayer, listeners and viaPoints // this.routingLayer?.detachFromMap(); if (this.modifyInteraction) { map.removeInteraction(this.modifyInteraction); } this.removeListeners(); this.reset(); } } /** * Draws route on map using an array of coordinates: * If a single coordinate is passed a single point feature is added to map. * If two or more coordinates are passed a request to the RoutingAPI fetches * the route using the passed coordinates and the current mot. * @private */ drawRoute() { var _a, _b; /* Calls RoutingAPI to draw a route using the viaPoints array */ this.abortRequests(); (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.clear(); if (!this.viaPoints.length) { return null; } if (this.viaPoints.length === 1) { // Add point for first node return this.drawViaPoint(this.viaPoints[0], 0, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]); } const formattedViaPoints = this.viaPoints.map((viaPoint) => { var _a; if (Array.isArray(viaPoint)) { const projection = (_a = this.getMap()) === null || _a === void 0 ? void 0 : _a.getView().getProjection(); // viaPoint is a coordinate // Coordinates need to be reversed as required by the backend RoutingAPI const [lon, lat] = toLonLat(viaPoint, projection); return this.snapToClosestStation ? [`@${lat}`, lon] : [lat, lon]; } // viaPoint is a string to use as it is return this.useRawViaPoints ? viaPoint : `!${viaPoint}`; }); this.loading = true; // Create point features for the viaPoints this.viaPoints.forEach((viaPoint, idx) => { void this.drawViaPoint(viaPoint, idx, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]); }); return Promise.all(this.graphs.map(([graph], index) => { const { signal } = this.abortControllers[graph || '']; if (!this.api) { return Promise.resolve([]); } return this.api .route(Object.assign({ 'coord-punish': 1000.0, 'coord-radius': 100.0, elevation: false, // @ts-expect-error improve type graph must include osm in the enum graph, mot: this.mot, 'resolve-hops': false, via: `${formattedViaPoints.join('|')}` }, (this.apiParams || {})), { signal }) .then((featureCollection) => { var _a, _b, _c; this.segments = this.format.readFeatures(featureCollection); if (this.mot === 'foot') { // Extract unique values from viaPoint target value const uniqueVias = this.segments.reduce((resultVias, currentFeat) => { const segTrg = currentFeat.get('trg'); return resultVias.find((via) => { return via[0] === segTrg[0] && via[1] === segTrg[1]; }) ? resultVias : [...resultVias, segTrg]; }, []); // Create LineString features from segments with same unique value this.segments = uniqueVias.map((via) => { const viaSegments = this.segments.filter((seg) => { const segTrg = seg.get('trg'); return segTrg[0] === via[0] && segTrg[1] === via[1]; }); const coords = getFlatCoordinatesFromSegments(viaSegments); return new Feature({ geometry: new LineString(coords), }); }); } // Create the new route. This route will be modifiable by the Modifiy interaction. const coords = getFlatCoordinatesFromSegments(this.segments); const routeFeature = new Feature({ geometry: new LineString(coords), }); routeFeature.set('graph', graph); routeFeature.set('mot', this.mot); if (this.graphsResolutions && ((_a = this.graphsResolutions[index]) === null || _a === void 0 ? void 0 : _a.length) >= 2) { routeFeature.set('minResolution', this.graphsResolutions[index][0]); routeFeature.set('maxResolution', this.graphsResolutions[index][1]); } (_c = (_b = this.routingLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.addFeature(routeFeature); this.loading = false; }) .catch((error) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access if (/AbortError/.test(error === null || error === void 0 ? void 0 : error.name)) { // Ignore abort error return; } this.segments = []; // Dispatch error event and execute error function this.dispatchEvent(new BaseEvent('error')); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument this.onRouteError(error, this); this.loading = false; }); })); } /** * Draw a via point. This function can parse all the possibilitiies * * @private */ drawViaPoint(viaPoint, idx, abortController) { var _a, _b, _c, _d, _e, _f, _g, _h; const pointFeature = new Feature(); pointFeature.set('viaPointIdx', idx); // The via point is a coordinate using the current map's projection if (Array.isArray(viaPoint)) { pointFeature.setGeometry(new Point(viaPoint)); (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.addFeature(pointFeature); return Promise.resolve(pointFeature); } // Possibility to parse: // // !8596126 a station with id 8596126 // !8596126$4 a station with id 8596126 if (!this.useRawViaPoints || REGEX_VIA_POINT_STATION_ID.test(viaPoint)) { let stationId; let track; if (this.useRawViaPoints) { [, stationId, , track] = REGEX_VIA_POINT_STATION_ID.exec(viaPoint) || []; } else { [stationId, track] = viaPoint.split('$'); } return fetch(`${this.stopsApiUrl}lookup/${stationId}?key=${this.stopsApiKey}`, { signal: abortController.signal }) .then((res) => { return res.json(); }) .then((stationData) => { var _a, _b, _c, _d; const { coordinates } = ((_b = (_a = stationData === null || stationData === void 0 ? void 0 : stationData.features) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.geometry) || {}; if (!coordinates) { console.log(`No coordinates found for station ${stationId}`, stationData); } this.cacheStationData[viaPoint] = fromLonLat(coordinates); pointFeature.set('viaPointTrack', track); pointFeature.setGeometry(new Point(fromLonLat(coordinates))); (_d = (_c = this.routingLayer) === null || _c === void 0 ? void 0 : _c.getSource()) === null || _d === void 0 ? void 0 : _d.addFeature(pointFeature); return pointFeature; }) .catch((error) => { var _a, _b; // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access if (/AbortError/.test(error.message)) { // Ignore abort error return; } // Dispatch error event and execute error function this.dispatchEvent(new BaseEvent('error')); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument this.onRouteError(error, this); (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.clear(); this.loading = false; }); } // Only when this.useRawViaPoints is true. // Possibility to parse: // // 47.37811,8.53935 a position 47.37811, 8.53935 if (this.useRawViaPoints && REGEX_VIA_POINT_COORD.test(viaPoint)) { const [lat, lon] = REGEX_VIA_POINT_COORD.exec(viaPoint) || []; if (lon && lat) { const floatLon = parseFloat(lon); const floatLat = parseFloat(lat); const coordinates = fromLonLat([floatLon, floatLat], (_c = this.getMap()) === null || _c === void 0 ? void 0 : _c.getView().getProjection()); pointFeature.setGeometry(new Point(coordinates)); (_e = (_d = this.routingLayer) === null || _d === void 0 ? void 0 : _d.getSource()) === null || _e === void 0 ? void 0 : _e.addFeature(pointFeature); return Promise.resolve(pointFeature); } } // Only when this.useRawViaPoints is true. // It will parse the via point to find some name, id, track coordinates. // // Possibility to parse: // // @47.37811,8.53935 a station at position 47.37811, 8.53935 // @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935 // zürich hb@47.37811,8.53935$8 track 8 in station "Zürich HB" at position 47.37811, 8.53935 const [, stationName, , lat, lon, , track] = REGEX_VIA_POINT.exec(viaPoint) || []; if (lon && lat) { const coordinates = fromLonLat([parseFloat(lon), parseFloat(lat)], (_f = this.getMap()) === null || _f === void 0 ? void 0 : _f.getView().getProjection()); pointFeature.set('viaPointTrack', track); pointFeature.setGeometry(new Point(coordinates)); (_h = (_g = this.routingLayer) === null || _g === void 0 ? void 0 : _g.getSource()) === null || _h === void 0 ? void 0 : _h.addFeature(pointFeature); return Promise.resolve(pointFeature); } if (stationName) { return fetch(`${this.stopsApiUrl}?key=${this.stopsApiKey}&q=${stationName}&limit=1`, { signal: abortController.signal }) .then((res) => { return res.json(); }) .then((stationData) => { var _a, _b, _c; const { coordinates } = ((_a = stationData === null || stationData === void 0 ? void 0 : stationData.features) === null || _a === void 0 ? void 0 : _a[0].geometry) || {}; if (!coordinates) { throw new Error(`No coordinates found for station ${stationName}`); } this.cacheStationData[viaPoint] = fromLonLat(coordinates); pointFeature.set('viaPointTrack', track); pointFeature.setGeometry(new Point(fromLonLat(coordinates))); (_c = (_b = this.routingLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.addFeature(pointFeature); return pointFeature; }) .catch((error) => { // Dispatch error event and execute error function this.dispatchEvent(new BaseEvent('error')); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument this.onRouteError(error, this); this.loading = false; return null; }); } return Promise.resolve(null); } /** * Activet7deactivate the control when activ eproperty changes * @private */ onActiveChange() { if (this.get('active')) { this.activate(); } else { this.deactivate(); } this.render(); } /** * Used on click on map while control is active: * By default adds a viaPoint to the end of array. * If an existing viaPoint is clicked removes the clicked viaPoint. * @private */ onMapClick(evt) { const feats = evt.target.getFeaturesAtPixel(evt.pixel, { hitTolerance: 5, layerFilter: (layer) => { return layer === this.routingLayer; }, }); const viaPoint = feats.find((feat) => { var _a; return (((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point' && feat.get('viaPointIdx') !== undefined); }); if (viaPoint) { // Remove existing viaPoint on click and abort viaPoint add this.removeViaPoint(viaPoint.get('viaPointIdx')); return; } this.addViaPoint(evt.coordinate); } /** * Used on end of the modify interaction. Resolves feature modification: * Line drag creates new viaPoint at the final coordinate of drag. * Point drag replaces old viaPoint. * @private */ onModifyEnd(evt) { const coord = evt.mapBrowserEvent.coordinate; const { oldRoute, segmentIndex, viaPoint } = this.initialRouteDrag || {}; // If viaPoint is being relocated overwrite the old viaPoint if (viaPoint) { return this.addViaPoint(coord, viaPoint.get('viaPointIdx'), 1); } // In case there is no route overwrite first coordinate if (!oldRoute) { return this.addViaPoint(coord, 0, 1); } // We can't add a via point because we haven't found which segment has been modified. if (segmentIndex === -1) { return Promise.reject(new Error('No segment found')); } // Insert new viaPoint at the modified segment index + 1 return this.addViaPoint(coord, (segmentIndex || 0) + 1); } /** * Used on start of the modify interaction. Stores relevant data * in this.initialRouteDrag object * @private */ onModifyStart(evt) { var _a; // When modify start, we search the index of the segment that is modifying. let segmentIndex = -1; const route = evt.features.getArray().find((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'LineString'; }); // Find the segment index that is being modified if ((route === null || route === void 0 ? void 0 : route.getGeometry()) && evt.mapBrowserEvent.coordinate) { // We use a buff extent to fix floating issues , see https://github.com/openlayers/openlayers/issues/7130#issuecomment-535856422 const closestExtent = buffer(new Point( // @ts-expect-error bad def (_a = route.getGeometry()) === null || _a === void 0 ? void 0 : _a.getClosestPoint(evt.mapBrowserEvent.coordinate)).getExtent(), 0.001); segmentIndex = this.segments.findIndex((segment) => { var _a; return (_a = segment.getGeometry()) === null || _a === void 0 ? void 0 : _a.intersectsExtent(closestExtent); }); } // Find the viaPoint that is being modified const viaPoint = (evt.features.getArray().filter((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point'; }) || [])[0]; // Write object with modify info this.initialRouteDrag = { oldRoute: route === null || route === void 0 ? void 0 : route.clone(), segmentIndex, viaPoint, }; } /** * Remove click listener from map. * @private */ removeListeners() { if (this.onMapClickKey) { unByKey(this.onMapClickKey); } } /** * Removes a viaPoint at the passed array index and redraws route * By default the last viaPoint is removed. * @param {number} index Integer representing the index of the viaPoint to delete. * @public */ removeViaPoint(index = (this.viaPoints || []).length - 1) { /* Remove viapoint and redraw route */ if (this.viaPoints.length && this.viaPoints[index]) { this.viaPoints.splice(index, 1); } void this.drawRoute(); this.dispatchEvent(new BaseEvent('change:route')); } render() { } /** * Removes all viaPoints, clears the source and triggers a change event * @public */ reset() { var _a, _b; // Clear viaPoints and source this.abortRequests(); this.viaPoints = []; (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.clear(); this.dispatchEvent(new BaseEvent('change:route')); } setMap(map) { super.setMap(map); if (map && this.active) { this.activate(); } else if (!map) { this.active = false; } } /** * Replaces the current viaPoints with a new coordinate array. * @param {Array<Array<number>>} coordinateArray Array of nested coordinates * @public */ setViaPoints(coordinateArray) { this.viaPoints = [...coordinateArray]; void this.drawRoute(); this.dispatchEvent(new BaseEvent('change:route')); } } export default RoutingControl;