diff --git a/doc/pages/example/[example].js b/doc/pages/example/[example].js index 78b01b2f..b3764fbd 100644 --- a/doc/pages/example/[example].js +++ b/doc/pages/example/[example].js @@ -10,6 +10,7 @@ const useStyles = makeStyles({ paddingBottom: 115, margin: 'auto', marginTop: 30, + maxWidth: '100% !important', }, }); diff --git a/doc/public/static/examples/ol-tracker-worker.html b/doc/public/static/examples/ol-tracker-worker.html new file mode 100644 index 00000000..9aba5a6d --- /dev/null +++ b/doc/public/static/examples/ol-tracker-worker.html @@ -0,0 +1 @@ +
diff --git a/doc/public/static/examples/ol-tracker-worker.js b/doc/public/static/examples/ol-tracker-worker.js new file mode 100644 index 00000000..6411c107 --- /dev/null +++ b/doc/public/static/examples/ol-tracker-worker.js @@ -0,0 +1,46 @@ +import { Map, View } from 'ol'; +import { + RealtimeLayer, + MaplibreLayer, + CopyrightControl, +} from 'mobility-toolbox-js/ol'; +import 'ol/ol.css'; + +export default () => { + const map = new Map({ + target: 'map', + view: new View({ + center: [831634, 5933959], + zoom: 13, + minZoom: 5, + }), + controls: [], + }); + + const control = new CopyrightControl(); + control.attachToMap(map); + + const layer = new MaplibreLayer({ + url: 'https://maps.geops.io/styles/travic_v2/style.json', + apiKey: window.apiKey, + }); + layer.attachToMap(map); + + const tracker = new RealtimeLayer({ + url: 'wss://api.geops.io/tracker-ws/v1/', + apiKey: window.apiKey, + allowRenderWhenAnimating: + new URL(window.location.href)?.searchParams?.get( + 'allowRenderWhenAnimating', + ) === 'true', + useWorker: true, + }); + tracker.attachToMap(map); + + tracker.onClick(([feature]) => { + if (feature) { + // eslint-disable-next-line no-console + console.log(feature.getProperties()); + } + }); +}; diff --git a/doc/public/static/examples/ol-tracker-worker.md b/doc/public/static/examples/ol-tracker-worker.md new file mode 100644 index 00000000..26a3f627 --- /dev/null +++ b/doc/public/static/examples/ol-tracker-worker.md @@ -0,0 +1 @@ +Follow [this link](https://developer.geops.io/) to get information about the API and how to get an API key. diff --git a/doc/public/static/examples/ol-tracker.js b/doc/public/static/examples/ol-tracker.js index 2d31b11d..044f410a 100644 --- a/doc/public/static/examples/ol-tracker.js +++ b/doc/public/static/examples/ol-tracker.js @@ -29,6 +29,10 @@ export default () => { const tracker = new RealtimeLayer({ url: 'wss://api.geops.io/tracker-ws/v1/', apiKey: window.apiKey, + allowRenderWhenAnimating: + new URL(window.location.href)?.searchParams?.get( + 'allowRenderWhenAnimating', + ) === 'true', }); tracker.attachToMap(map); diff --git a/doc/src/components/CodeSandboxButton.js b/doc/src/components/CodeSandboxButton.js index 22f457c7..d5357f0f 100644 --- a/doc/src/components/CodeSandboxButton.js +++ b/doc/src/components/CodeSandboxButton.js @@ -41,7 +41,7 @@ function CodeSandboxButton({ html, js, extraFiles, ...props }) { 'mapbox-gl': '1', 'maplibre-gl': '2', 'mobility-toolbox-js': 'latest@beta', - ol: '6.3.1', + ol: '6', }, devDependencies: { '@babel/core': '7.2.0', diff --git a/doc/src/components/Esdoc/index.json b/doc/src/components/Esdoc/index.json index be410ab2..41088ade 100644 --- a/doc/src/components/Esdoc/index.json +++ b/doc/src/components/Esdoc/index.json @@ -2311,7 +2311,7 @@ "__docId__": 118, "kind": "file", "name": "build/api/RoutingAPI.js", - "content": "import HttpAPI from '../common/api/HttpAPI';\n/**\n * Access to the [geOps Routing service](https://developer.geops.io/apis/routing).\n *\n * @example\n * import { RoutingAPI } from 'mobility-toolbox-js';\n *\n * const api = new RoutingAPI({\n * apiKey: [yourApiKey]\n * });\n *\n */\nclass RoutingAPI extends HttpAPI {\n /**\n * Constructor\n *\n * @param {RoutingAPIOptions} options Options.\n * @param {string} [options.url='https://api.geops.io/routing/v1/'] Service url.\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/).\n */\n constructor(options = {}) {\n super(Object.assign({ url: 'https://api.geops.io/routing/v1/' }, options));\n }\n /**\n * Route.\n *\n * @param {RoutingParameters} params Request parameters. See [Routing service documentation](https://developer.geops.io/apis/routing/).\n * @param {RequestInit} config Options for the fetch request.\n * @return {Promise} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n route(params, config) {\n return this.fetch('', params, config);\n }\n}\nexport default RoutingAPI;\n", + "content": "import HttpAPI from '../common/api/HttpAPI';\n/**\n * Access to the [geOps Routing service](https://developer.geops.io/apis/routing).\n *\n * @example\n * import { RoutingAPI } from 'mobility-toolbox-js';\n *\n * const api = new RoutingAPI({\n * apiKey: [yourApiKey]\n * });\n *\n */\nclass RoutingAPI extends HttpAPI {\n /**\n * Constructor\n *\n * @param {RoutingAPIOptions} options Options.\n * @param {string} [options.url='https://api.geops.io/routing/v1/'] Service url.\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/).\n */\n constructor(options = {}) {\n super({ url: 'https://api.geops.io/routing/v1/', ...options });\n }\n /**\n * Route.\n *\n * @param {RoutingParameters} params Request parameters. See [Routing service documentation](https://developer.geops.io/apis/routing/).\n * @param {RequestInit} config Options for the fetch request.\n * @return {Promise} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n route(params, config) {\n return this.fetch('', params, config);\n }\n}\nexport default RoutingAPI;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/api/RoutingAPI.js", "access": "public", @@ -2433,7 +2433,7 @@ "__docId__": 122, "kind": "file", "name": "build/api/StopsAPI.js", - "content": "import HttpAPI from '../common/api/HttpAPI';\n/**\n * Access to the [Stops service](https://developer.geops.io/apis/5dcbd702a256d90001cf1361/).\n *\n * @example\n * import { StopsAPI } from 'mobility-toolbox-js/api';\n *\n * const api = new StopsAPI({\n * url: 'https://api.geops.io/stops/v1/',\n * apiKey: [yourApiKey]\n * });\n *\n */\nclass StopsAPI extends HttpAPI {\n /**\n * Constructor\n *\n * @param {StopsAPIOptions} options Options.\n * @param {string} [options.url='https://api.geops.io/stops/v1/'] Service url.\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/).\n */\n constructor(options = {}) {\n super(Object.assign({ url: 'https://api.geops.io/stops/v1/' }, options));\n }\n /**\n * Search fo stops.\n *\n * @param {StopsParameters} params Request parameters. See [Stops service documentation](https://developer.geops.io/apis/stops).\n * @param {RequestInit} config Options for the fetch request.\n * @return {Promise} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n search(params, config) {\n return this.fetch('', params, config);\n }\n}\nexport default StopsAPI;\n", + "content": "import HttpAPI from '../common/api/HttpAPI';\n/**\n * Access to the [Stops service](https://developer.geops.io/apis/5dcbd702a256d90001cf1361/).\n *\n * @example\n * import { StopsAPI } from 'mobility-toolbox-js/api';\n *\n * const api = new StopsAPI({\n * url: 'https://api.geops.io/stops/v1/',\n * apiKey: [yourApiKey]\n * });\n *\n */\nclass StopsAPI extends HttpAPI {\n /**\n * Constructor\n *\n * @param {StopsAPIOptions} options Options.\n * @param {string} [options.url='https://api.geops.io/stops/v1/'] Service url.\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/).\n */\n constructor(options = {}) {\n super({ url: 'https://api.geops.io/stops/v1/', ...options });\n }\n /**\n * Search fo stops.\n *\n * @param {StopsParameters} params Request parameters. See [Stops service documentation](https://developer.geops.io/apis/stops).\n * @param {RequestInit} config Options for the fetch request.\n * @return {Promise} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n search(params, config) {\n return this.fetch('', params, config);\n }\n}\nexport default StopsAPI;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/api/StopsAPI.js", "access": "public", @@ -2565,6 +2565,65 @@ { "__docId__": 127, "kind": "file", + "name": "build/api/trajserv/fetchTrajectories.worker.js", + "content": "import { translateTrajCollResponse } from './TrajservAPIUtils';\nlet abortController = new AbortController();\n// eslint-disable-next-line no-restricted-globals, func-names\nself.onmessage = function (evt) {\n // console.log('Worker: Message received from main script', evt.data);\n abortController.abort();\n abortController = new AbortController();\n fetch(evt.data, {\n signal: abortController.signal,\n })\n .then((res) => res.json())\n .then((data) => {\n const a = translateTrajCollResponse(data.features);\n // eslint-disable-next-line no-restricted-globals\n self.postMessage(a);\n })\n .catch(() => {\n // eslint-disable-next-line no-restricted-globals\n // self.postMessage(null);\n });\n};\n", + "static": true, + "longname": "/home/olivier/GIT/mobility-toolbox-js/build/api/trajserv/fetchTrajectories.worker.js", + "access": "public", + "description": null, + "lineNumber": 1 + }, + { + "__docId__": 128, + "kind": "variable", + "name": "abortController", + "memberof": "build/api/trajserv/fetchTrajectories.worker.js", + "static": true, + "longname": "build/api/trajserv/fetchTrajectories.worker.js~abortController", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/api/trajserv/fetchTrajectories.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 2, + "undocument": true, + "type": { + "types": [ + "*" + ] + }, + "ignore": true + }, + { + "__docId__": 129, + "kind": "function", + "name": "onmessage", + "memberof": "build/api/trajserv/fetchTrajectories.worker.js", + "generator": false, + "async": false, + "static": true, + "longname": "build/api/trajserv/fetchTrajectories.worker.js~onmessage", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/api/trajserv/fetchTrajectories.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 4, + "undocument": true, + "params": [ + { + "name": "evt", + "types": [ + "*" + ] + } + ], + "return": null, + "ignore": true + }, + { + "__docId__": 130, + "kind": "file", "name": "build/api/typedefs.js", "content": "/**\n * @typedef {Object} Departure\n * @property {number} time Timestamp in ms.\n * @property {boolean} no_stop_between\n * @property {number} train_number\n * @property {string[]} to\n * @property {number} ris_aimed_time Timestamp in ms.\n * @property {number} updated_at Timestamp in ms.\n * @property {boolean} new_to\n * @property {number} min_arrival_time Timestamp in ms.\n * @property {string[]} next_stoppoints List of next stops. Like value in at_station_ds100.\n * @property {number} ris_estimated_time Timestamp in ms.\n * @property {NetworkLine} line\n * @property {boolean} has_fzo if true this departure has realtime data.\n * @property {number} train_id\n * @property {string} platform\n * @property {?*} state\n * @property {number} fzo_estimated_time Timestamp in ms.\n * @property {?*} formation\n * @property {?*} no_stop_till\n * @property {number} train_type\n * @property {number} call_id\n * @property {string} created_at Timestamp in ms.\n * @property {string} at_station_ds100\n * @property {number} timediff Timestamp in ms.\n *\n */\n/**\n * @typedef {GeoJSONFeature} Station\n * @property {StationProperties} properties Returns the station's properties.\n * @property {GeoJSONPoint} geometry Returns a point.\n */\n/**\n * @typedef {Object} StationProperties\n * @property {Transfer[]} transfers\n * @property {boolean} elevatorOutOfOrder\n * @property {number} uic\n * @property {string} name\n * @property {NetworkLine[]} networkLines\n * @property {boolean} hasElevator\n * @property {boolean} hasZOB\n * @property {boolean} hasAccessibility\n * @property {string} type\n */\n/**\n * @typedef {Object} NetworkLine\n * @property {number} id Identifier of the line.\n * @property {string} color Color of the line (CSS color string).\n * @property {string} stroke Stroke color of the line (CSS color string).\n * @property {string} name Name of the line.\n * @property {string} text_color Text color of the line (CSS color string).\n */\n/**\n * @typedef {Object} Transfer\n * @property {string} mot Mode of transportation (ex: U-Bahn).\n * @property {string[]} lines Array of lines name (ex: [\"U4\", \"U5\"]).\n */\n/**\n * @typedef {GeoJSONFeature} StopSequence\n */\n/**\n * @typedef {GeoJSONFeature} RealtimeTrajectory\n */\n/**\n * @typedef {GeoJSONFeature} FullTrajectory\n */\n/**\n * @typedef {GeoJSONFeature} Vehicle\n */\n/**\n * @typedef {GeoJSONFeature} ExtraGeom\n */\n// These lines is to block TypeScript to add \"use strict;\" in the outputed file.\nconst dummy = () => { };\nexport default dummy;\n", "static": true, @@ -2574,7 +2633,7 @@ "lineNumber": 1 }, { - "__docId__": 128, + "__docId__": 131, "kind": "typedef", "name": "Departure", "memberof": "build/api/typedefs.js", @@ -2823,7 +2882,7 @@ } }, { - "__docId__": 129, + "__docId__": 132, "kind": "typedef", "name": "Station", "memberof": "build/api/typedefs.js", @@ -2862,7 +2921,7 @@ } }, { - "__docId__": 130, + "__docId__": 133, "kind": "typedef", "name": "StationProperties", "memberof": "build/api/typedefs.js", @@ -2971,7 +3030,7 @@ } }, { - "__docId__": 131, + "__docId__": 134, "kind": "typedef", "name": "NetworkLine", "memberof": "build/api/typedefs.js", @@ -3040,7 +3099,7 @@ } }, { - "__docId__": 132, + "__docId__": 135, "kind": "typedef", "name": "Transfer", "memberof": "build/api/typedefs.js", @@ -3079,7 +3138,7 @@ } }, { - "__docId__": 133, + "__docId__": 136, "kind": "typedef", "name": "StopSequence", "memberof": "build/api/typedefs.js", @@ -3096,7 +3155,7 @@ } }, { - "__docId__": 134, + "__docId__": 137, "kind": "typedef", "name": "RealtimeTrajectory", "memberof": "build/api/typedefs.js", @@ -3113,7 +3172,7 @@ } }, { - "__docId__": 135, + "__docId__": 138, "kind": "typedef", "name": "FullTrajectory", "memberof": "build/api/typedefs.js", @@ -3130,7 +3189,7 @@ } }, { - "__docId__": 136, + "__docId__": 139, "kind": "typedef", "name": "Vehicle", "memberof": "build/api/typedefs.js", @@ -3147,7 +3206,7 @@ } }, { - "__docId__": 137, + "__docId__": 140, "kind": "typedef", "name": "ExtraGeom", "memberof": "build/api/typedefs.js", @@ -3165,10 +3224,10 @@ } }, { - "__docId__": 138, + "__docId__": 141, "kind": "file", "name": "build/common/api/HttpAPI.js", - "content": "import BaseObject from 'ol/Object';\nimport getUrlWithParams from '../utils/getUrlWithParams';\n/**\n * Common class to access to a geOps api using http.\n *\n * @example\n * import { API } from 'mobility-toolbox-js/api';\n *\n * const api = new HttpApi({\n * url: [yourUrl],\n * apiKey: [yourApiKey]\n * });\n *\n * @classproperty {string} url Url of the service.\n * @classproperty {string} apiKey Api key to access the service.\n */\nclass HttpAPI extends BaseObject {\n constructor(options) {\n super();\n /** @ignore */\n this.url = options.url;\n /** @ignore */\n this.apiKey = options.apiKey;\n }\n /**\n * Append the apiKey before sending the request.\n * @ignore\n */\n fetch(path, params, config) {\n if (!this.url) {\n // eslint-disable-next-line no-console\n return Promise.reject(new Error(`No url defined for request to ${this.url}/${path}`));\n }\n if (!this.url && !this.apiKey && !/key=/.test(this.url)) {\n // eslint-disable-next-line no-console\n return Promise.reject(new Error(`No apiKey defined for request to ${this.url}`));\n }\n // Clean requets parameters, removing undefined and null values.\n const searchParams = params || {};\n const url = getUrlWithParams(`${this.url}${path || ''}`, Object.assign({ key: this.apiKey }, searchParams));\n // We use toString because of TYpeScript bug that only accept a string in fetch method.\n return fetch(url.toString(), config).then((response) => {\n try {\n return response.json().then((data) => {\n if (data.error) {\n throw new Error(data.error);\n }\n return data;\n });\n }\n catch (err) {\n return Promise.reject(new Error(err));\n }\n });\n }\n}\nexport default HttpAPI;\n", + "content": "import BaseObject from 'ol/Object';\nimport getUrlWithParams from '../utils/getUrlWithParams';\n/**\n * Common class to access to a geOps api using http.\n *\n * @example\n * import { API } from 'mobility-toolbox-js/api';\n *\n * const api = new HttpApi({\n * url: [yourUrl],\n * apiKey: [yourApiKey]\n * });\n *\n * @classproperty {string} url Url of the service.\n * @classproperty {string} apiKey Api key to access the service.\n */\nclass HttpAPI extends BaseObject {\n constructor(options) {\n super();\n /** @ignore */\n this.url = options.url;\n /** @ignore */\n this.apiKey = options.apiKey;\n }\n /**\n * Append the apiKey before sending the request.\n * @ignore\n */\n fetch(path, params, config) {\n if (!this.url) {\n // eslint-disable-next-line no-console\n return Promise.reject(new Error(`No url defined for request to ${this.url}/${path}`));\n }\n if (!this.url && !this.apiKey && !/key=/.test(this.url)) {\n // eslint-disable-next-line no-console\n return Promise.reject(new Error(`No apiKey defined for request to ${this.url}`));\n }\n // Clean requets parameters, removing undefined and null values.\n const searchParams = params || {};\n const url = getUrlWithParams(`${this.url}${path || ''}`, {\n key: this.apiKey,\n ...searchParams,\n });\n // We use toString because of TYpeScript bug that only accept a string in fetch method.\n return fetch(url.toString(), config).then((response) => {\n try {\n return response.json().then((data) => {\n if (data.error) {\n throw new Error(data.error);\n }\n return data;\n });\n }\n catch (err) {\n return Promise.reject(new Error(err));\n }\n });\n }\n}\nexport default HttpAPI;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/api/HttpAPI.js", "access": "public", @@ -3176,7 +3235,7 @@ "lineNumber": 1 }, { - "__docId__": 139, + "__docId__": 142, "kind": "class", "name": "HttpAPI", "memberof": "build/common/api/HttpAPI.js", @@ -3207,7 +3266,7 @@ ] }, { - "__docId__": 140, + "__docId__": 143, "kind": "constructor", "name": "constructor", "memberof": "build/common/api/HttpAPI.js~HttpAPI", @@ -3221,7 +3280,7 @@ "undocument": true }, { - "__docId__": 141, + "__docId__": 144, "kind": "member", "name": "url", "memberof": "build/common/api/HttpAPI.js~HttpAPI", @@ -3238,7 +3297,7 @@ } }, { - "__docId__": 142, + "__docId__": 145, "kind": "member", "name": "apiKey", "memberof": "build/common/api/HttpAPI.js~HttpAPI", @@ -3255,7 +3314,7 @@ } }, { - "__docId__": 143, + "__docId__": 146, "kind": "method", "name": "fetch", "memberof": "build/common/api/HttpAPI.js~HttpAPI", @@ -3294,10 +3353,10 @@ } }, { - "__docId__": 144, + "__docId__": 147, "kind": "file", "name": "build/common/api/WebSocketAPI.js", - "content": "/**\n * Class used to facilitate connection to a WebSocketAPI and\n * also to manage properly messages send to the WebSocketAPI.\n * This class must not contain any specific implementation.\n */\nclass WebSocketAPI {\n constructor() {\n this.defineProperties();\n }\n defineProperties() {\n Object.defineProperties(this, {\n closed: {\n get: () => !!(!this.websocket ||\n this.websocket.readyState === this.websocket.CLOSED),\n },\n closing: {\n get: () => !!(this.websocket &&\n this.websocket.readyState === this.websocket.CLOSING),\n },\n connecting: {\n get: () => !!(this.websocket &&\n this.websocket.readyState === this.websocket.CONNECTING),\n },\n open: {\n get: () => !!(this.websocket && this.websocket.readyState === this.websocket.OPEN),\n },\n /**\n * Array of message to send on open.\n * @type {Array}\n * @private\n */\n messagesOnOpen: {\n value: [],\n writable: true,\n },\n /**\n * Array of subscriptions.\n * @type {Array}\n * @private\n */\n subscriptions: {\n value: [],\n writable: true,\n },\n /**\n * List of channels subscribed.\n * @type {WebSocketSubscribed}\n * @private\n */\n subscribed: {\n value: {},\n writable: true,\n },\n });\n }\n /**\n * Get the websocket request string.\n *\n * @param {string} method Request mehtod {GET, SUB}.\n * @param {WebSocketParameters} params Request parameters.\n * @param {string} params.channel Channel name\n * @param {string} [params.args] Request arguments\n * @param {Number|string} [params.id] Request identifier\n * @return {string} request string\n * @private\n */\n static getRequestString(method, params = {}) {\n let reqStr = `${method} ${params.channel}`;\n reqStr += params.args ? ` ${params.args}` : '';\n reqStr += params.id ? ` ${params.id}` : '';\n return reqStr.trim();\n }\n /**\n * (Re)connect the websocket.\n *\n * @param {string} url Websocket url.\n * @param {function} onOpen Callback called when the websocket connection is opened and before subscriptions of previous subscriptions.\n * @private\n */\n connect(url, onOpen = () => { }) {\n if (this.websocket && !this.closed) {\n if (!this.closing && this.websocket.url !== url) {\n this.websocket.close();\n }\n else if (this.connecting) {\n return;\n }\n }\n /** @ignore */\n this.websocket = new WebSocket(url);\n if (!this.open) {\n this.websocket.addEventListener('open', () => {\n onOpen();\n this.subscribePreviousSubscriptions();\n });\n }\n else {\n onOpen();\n this.subscribePreviousSubscriptions();\n }\n }\n /**\n * Close the websocket definitively.\n *\n * @private\n */\n close() {\n if (this.websocket && (this.open || this.connecting)) {\n this.websocket.onclose = () => { };\n this.websocket.close();\n this.messagesOnOpen = [];\n }\n }\n /**\n * Sends a message to the websocket.\n *\n * @param {message} message Message to send.\n * @private\n */\n send(message) {\n if (!this.websocket) {\n return;\n }\n const send = () => {\n var _a;\n (_a = this.websocket) === null || _a === void 0 ? void 0 : _a.send(message);\n };\n if (!this.open) {\n // This 'if' avoid sending 2 identical BBOX message on open,\n if (!this.messagesOnOpen.includes(message)) {\n this.messagesOnOpen.push(message);\n this.websocket.addEventListener('open', () => {\n this.messagesOnOpen = [];\n send();\n });\n this.websocket.addEventListener('close', () => {\n this.messagesOnOpen = [];\n });\n }\n }\n else if (!this.messagesOnOpen.includes(message)) {\n send();\n }\n }\n addEvents(onMessage, onError) {\n if (this.websocket) {\n this.websocket.addEventListener('message', onMessage);\n if (onError) {\n this.websocket.addEventListener('error', onError);\n this.websocket.addEventListener('close', onError);\n }\n }\n }\n removeEvents(onMessage, onError) {\n if (this.websocket) {\n this.websocket.removeEventListener('message', onMessage);\n if (onError) {\n this.websocket.removeEventListener('error', onError);\n this.websocket.removeEventListener('close', onError);\n }\n }\n }\n /**\n * Listen to websocket messages.\n *\n * @param {WebSocketParameters} params Parameters for the websocket get request\n * @param {function} cb callback on listen\n * @param {function} errorCb Callback on error\n * @return {{onMessage: function, errorCb: function}} Object with onMessage and error callbacks\n * @private\n */\n listen(params, cb, errorCb) {\n // Remove the previous identical callback\n this.unlisten(params, cb);\n // We wrap the message callback to be sure we only propagate the message if it is for the right channel.\n const onMessage = (evt) => {\n let data;\n try {\n data = JSON.parse(evt.data);\n }\n catch (err) {\n // eslint-disable-next-line no-console\n console.error('WebSocket: unable to parse JSON data', err, evt.data);\n return;\n }\n let source = params.channel;\n source += params.args ? ` ${params.args}` : '';\n // Buffer channel message return a list of other channels to propagate to proper callbacks.\n let contents;\n if (data.source === 'buffer') {\n contents = data\n .content;\n }\n else {\n contents = [data];\n }\n contents.forEach((content) => {\n // Because of backend optimization, the last content is null.\n if ((content === null || content === void 0 ? void 0 : content.source) === source &&\n (!params.id || params.id === data.client_reference)) {\n cb(content);\n }\n });\n };\n this.addEvents(onMessage, errorCb);\n return { onMessageCb: onMessage, onErrorCb: errorCb };\n }\n /**\n * Unlisten websocket messages.\n *\n * @param {Object} params Parameters for the websocket get request.\n * @param {function} cb Callback used when listen.\n * @private\n */\n unlisten(params, cb) {\n [...(this.subscriptions || []), ...(this.requests || [])]\n .filter((s) => s.params.channel === params.channel && (!cb || s.cb === cb))\n .forEach(({ onMessageCb, onErrorCb }) => {\n this.removeEvents(onMessageCb, onErrorCb);\n });\n }\n /**\n * Sends a get request to the websocket.\n * The callback is called only once, when the response is received or when the call returns an error.\n *\n * @param {Object} params Parameters for the websocket get request\n * @param {function} onMessage callback on message event\n * @param {function} onError Callback on error and close event\n * @private\n */\n get(params, cb, errorCb) {\n const requestString = WebSocketAPI.getRequestString('GET', params);\n this.send(requestString);\n // We wrap the callbacks to make sure they are called only once.\n const once = (callback) => \n // @ts-ignore: Spread error\n (...args) => {\n // @ts-ignore: Spread error\n callback(...args);\n const index = this.requests.findIndex((request) => requestString === request.requestString && cb === request.cb);\n const { onMessageCb, onErrorCb } = this.requests[index];\n this.removeEvents(onMessageCb, onErrorCb);\n this.requests.splice(index, 1);\n };\n const { onMessageCb, onErrorCb } = this.listen(params, once(cb), errorCb && once(errorCb));\n // Store requests and callbacks to be able to remove them.\n if (!this.requests) {\n this.requests = [];\n }\n const index = this.requests.findIndex((request) => requestString === request.requestString && cb === request.cb);\n const newReq = {\n params,\n requestString,\n cb,\n errorCb,\n onMessageCb,\n onErrorCb,\n };\n if (index > -1) {\n this.requests[index] = newReq;\n }\n else {\n this.requests.push(newReq);\n }\n }\n /**\n * Subscribe to a given channel.\n *\n * @param {Object} params Parameters for the websocket get request\n * @param {function} cb callback on listen\n * @param {function} errorCb Callback on error\n * @param {boolean} quiet if false, no GET or SUB requests are send, only the callback is registered.\n * @private\n */\n subscribe(params, cb, errorCb, quiet = false) {\n const { onMessageCb, onErrorCb } = this.listen(params, cb, errorCb);\n const reqStr = WebSocketAPI.getRequestString('', params);\n const index = this.subscriptions.findIndex((subcr) => params.channel === subcr.params.channel && cb === subcr.cb);\n const newSubscr = { params, cb, errorCb, onMessageCb, onErrorCb, quiet };\n if (index > -1) {\n this.subscriptions[index] = newSubscr;\n }\n else {\n this.subscriptions.push(newSubscr);\n }\n if (!this.subscribed[reqStr]) {\n if (!newSubscr.quiet) {\n this.send(`GET ${reqStr}`);\n this.send(`SUB ${reqStr}`);\n }\n this.subscribed[reqStr] = true;\n }\n }\n /**\n * Unsubscribe from a channel.\n * @param {string} source source to unsubscribe from\n * @param {function} cb Callback function to unsubscribe. If null all subscriptions for the channel will be unsubscribed.\n * @private\n */\n unsubscribe(source, cb) {\n const toRemove = this.subscriptions.filter((s) => s.params.channel === source && (!cb || s.cb === cb));\n toRemove.forEach(({ onMessageCb, onErrorCb }) => {\n this.removeEvents(onMessageCb, onErrorCb);\n });\n this.subscriptions = this.subscriptions.filter((s) => s.params.channel !== source || (cb && s.cb !== cb));\n // If there is no more subscriptions to this channel, and the removed subscriptions didn't register quietly,\n // we DEL it.\n if (source &&\n this.subscribed[source] &&\n !this.subscriptions.find((s) => s.params.channel === source) &&\n toRemove.find((subscr) => !subscr.quiet)) {\n this.send(`DEL ${source}`);\n this.subscribed[source] = false;\n }\n }\n /**\n * After an auto reconnection we need to re-subscribe to the channels.\n */\n subscribePreviousSubscriptions() {\n // Before to subscribe previous subscriptions we make sure they\n // are all defined as unsubscribed, because this code is asynchrone\n // and a subscription could have been added in between.\n Object.keys(this.subscribed).forEach((key) => {\n this.subscribed[key] = false;\n });\n // Subscribe all previous subscriptions.\n [...this.subscriptions].forEach((s) => {\n this.subscribe(s.params, s.cb, s.errorCb, s.quiet);\n });\n }\n}\nexport default WebSocketAPI;\n", + "content": "/**\n * Class used to facilitate connection to a WebSocketAPI and\n * also to manage properly messages send to the WebSocketAPI.\n * This class must not contain any specific implementation.\n */\nclass WebSocketAPI {\n constructor() {\n this.defineProperties();\n }\n defineProperties() {\n Object.defineProperties(this, {\n closed: {\n get: () => !!(!this.websocket ||\n this.websocket.readyState === this.websocket.CLOSED),\n },\n closing: {\n get: () => !!(this.websocket &&\n this.websocket.readyState === this.websocket.CLOSING),\n },\n connecting: {\n get: () => !!(this.websocket &&\n this.websocket.readyState === this.websocket.CONNECTING),\n },\n open: {\n get: () => !!(this.websocket && this.websocket.readyState === this.websocket.OPEN),\n },\n /**\n * Array of message to send on open.\n * @type {Array}\n * @private\n */\n messagesOnOpen: {\n value: [],\n writable: true,\n },\n /**\n * Array of subscriptions.\n * @type {Array}\n * @private\n */\n subscriptions: {\n value: [],\n writable: true,\n },\n /**\n * List of channels subscribed.\n * @type {WebSocketSubscribed}\n * @private\n */\n subscribed: {\n value: {},\n writable: true,\n },\n });\n }\n /**\n * Get the websocket request string.\n *\n * @param {string} method Request mehtod {GET, SUB}.\n * @param {WebSocketParameters} params Request parameters.\n * @param {string} params.channel Channel name\n * @param {string} [params.args] Request arguments\n * @param {Number|string} [params.id] Request identifier\n * @return {string} request string\n * @private\n */\n static getRequestString(method, params = {}) {\n let reqStr = `${method} ${params.channel}`;\n reqStr += params.args ? ` ${params.args}` : '';\n reqStr += params.id ? ` ${params.id}` : '';\n return reqStr.trim();\n }\n /**\n * (Re)connect the websocket.\n *\n * @param {string} url Websocket url.\n * @param {function} onOpen Callback called when the websocket connection is opened and before subscriptions of previous subscriptions.\n * @private\n */\n connect(url, onOpen = () => { }) {\n if (this.websocket && !this.closed) {\n if (!this.closing && this.websocket.url !== url) {\n this.websocket.close();\n }\n else if (this.connecting) {\n return;\n }\n }\n /** @ignore */\n this.websocket = new WebSocket(url);\n if (!this.open) {\n this.websocket.addEventListener('open', () => {\n onOpen();\n this.subscribePreviousSubscriptions();\n });\n }\n else {\n onOpen();\n this.subscribePreviousSubscriptions();\n }\n }\n /**\n * Close the websocket definitively.\n *\n * @private\n */\n close() {\n if (this.websocket && (this.open || this.connecting)) {\n this.websocket.onclose = () => { };\n this.websocket.close();\n this.messagesOnOpen = [];\n }\n }\n /**\n * Sends a message to the websocket.\n *\n * @param {message} message Message to send.\n * @private\n */\n send(message) {\n if (!this.websocket) {\n return;\n }\n const send = () => {\n this.websocket?.send(message);\n };\n if (!this.open) {\n // This 'if' avoid sending 2 identical BBOX message on open,\n if (!this.messagesOnOpen.includes(message)) {\n this.messagesOnOpen.push(message);\n this.websocket.addEventListener('open', () => {\n this.messagesOnOpen = [];\n send();\n });\n this.websocket.addEventListener('close', () => {\n this.messagesOnOpen = [];\n });\n }\n }\n else if (!this.messagesOnOpen.includes(message)) {\n send();\n }\n }\n addEvents(onMessage, onError) {\n if (this.websocket) {\n this.websocket.addEventListener('message', onMessage);\n if (onError) {\n this.websocket.addEventListener('error', onError);\n this.websocket.addEventListener('close', onError);\n }\n }\n }\n removeEvents(onMessage, onError) {\n if (this.websocket) {\n this.websocket.removeEventListener('message', onMessage);\n if (onError) {\n this.websocket.removeEventListener('error', onError);\n this.websocket.removeEventListener('close', onError);\n }\n }\n }\n /**\n * Listen to websocket messages.\n *\n * @param {WebSocketParameters} params Parameters for the websocket get request\n * @param {function} cb callback on listen\n * @param {function} errorCb Callback on error\n * @return {{onMessage: function, errorCb: function}} Object with onMessage and error callbacks\n * @private\n */\n listen(params, cb, errorCb) {\n // Remove the previous identical callback\n this.unlisten(params, cb);\n // We wrap the message callback to be sure we only propagate the message if it is for the right channel.\n const onMessage = (evt) => {\n let data;\n try {\n data = JSON.parse(evt.data);\n }\n catch (err) {\n // eslint-disable-next-line no-console\n console.error('WebSocket: unable to parse JSON data', err, evt.data);\n return;\n }\n let source = params.channel;\n source += params.args ? ` ${params.args}` : '';\n // Buffer channel message return a list of other channels to propagate to proper callbacks.\n let contents;\n if (data.source === 'buffer') {\n contents = data\n .content;\n }\n else {\n contents = [data];\n }\n contents.forEach((content) => {\n // Because of backend optimization, the last content is null.\n if (content?.source === source &&\n (!params.id || params.id === data.client_reference)) {\n cb(content);\n }\n });\n };\n this.addEvents(onMessage, errorCb);\n return { onMessageCb: onMessage, onErrorCb: errorCb };\n }\n /**\n * Unlisten websocket messages.\n *\n * @param {Object} params Parameters for the websocket get request.\n * @param {function} cb Callback used when listen.\n * @private\n */\n unlisten(params, cb) {\n [...(this.subscriptions || []), ...(this.requests || [])]\n .filter((s) => s.params.channel === params.channel && (!cb || s.cb === cb))\n .forEach(({ onMessageCb, onErrorCb }) => {\n this.removeEvents(onMessageCb, onErrorCb);\n });\n }\n /**\n * Sends a get request to the websocket.\n * The callback is called only once, when the response is received or when the call returns an error.\n *\n * @param {Object} params Parameters for the websocket get request\n * @param {function} onMessage callback on message event\n * @param {function} onError Callback on error and close event\n * @private\n */\n get(params, cb, errorCb) {\n const requestString = WebSocketAPI.getRequestString('GET', params);\n this.send(requestString);\n // We wrap the callbacks to make sure they are called only once.\n const once = (callback) => \n // @ts-ignore: Spread error\n (...args) => {\n // @ts-ignore: Spread error\n callback(...args);\n const index = this.requests.findIndex((request) => requestString === request.requestString && cb === request.cb);\n const { onMessageCb, onErrorCb } = this.requests[index];\n this.removeEvents(onMessageCb, onErrorCb);\n this.requests.splice(index, 1);\n };\n const { onMessageCb, onErrorCb } = this.listen(params, once(cb), errorCb && once(errorCb));\n // Store requests and callbacks to be able to remove them.\n if (!this.requests) {\n this.requests = [];\n }\n const index = this.requests.findIndex((request) => requestString === request.requestString && cb === request.cb);\n const newReq = {\n params,\n requestString,\n cb,\n errorCb,\n onMessageCb,\n onErrorCb,\n };\n if (index > -1) {\n this.requests[index] = newReq;\n }\n else {\n this.requests.push(newReq);\n }\n }\n /**\n * Subscribe to a given channel.\n *\n * @param {Object} params Parameters for the websocket get request\n * @param {function} cb callback on listen\n * @param {function} errorCb Callback on error\n * @param {boolean} quiet if false, no GET or SUB requests are send, only the callback is registered.\n * @private\n */\n subscribe(params, cb, errorCb, quiet = false) {\n const { onMessageCb, onErrorCb } = this.listen(params, cb, errorCb);\n const reqStr = WebSocketAPI.getRequestString('', params);\n const index = this.subscriptions.findIndex((subcr) => params.channel === subcr.params.channel && cb === subcr.cb);\n const newSubscr = { params, cb, errorCb, onMessageCb, onErrorCb, quiet };\n if (index > -1) {\n this.subscriptions[index] = newSubscr;\n }\n else {\n this.subscriptions.push(newSubscr);\n }\n if (!this.subscribed[reqStr]) {\n if (!newSubscr.quiet) {\n this.send(`GET ${reqStr}`);\n this.send(`SUB ${reqStr}`);\n }\n this.subscribed[reqStr] = true;\n }\n }\n /**\n * Unsubscribe from a channel.\n * @param {string} source source to unsubscribe from\n * @param {function} cb Callback function to unsubscribe. If null all subscriptions for the channel will be unsubscribed.\n * @private\n */\n unsubscribe(source, cb) {\n const toRemove = this.subscriptions.filter((s) => s.params.channel === source && (!cb || s.cb === cb));\n toRemove.forEach(({ onMessageCb, onErrorCb }) => {\n this.removeEvents(onMessageCb, onErrorCb);\n });\n this.subscriptions = this.subscriptions.filter((s) => s.params.channel !== source || (cb && s.cb !== cb));\n // If there is no more subscriptions to this channel, and the removed subscriptions didn't register quietly,\n // we DEL it.\n if (source &&\n this.subscribed[source] &&\n !this.subscriptions.find((s) => s.params.channel === source) &&\n toRemove.find((subscr) => !subscr.quiet)) {\n this.send(`DEL ${source}`);\n this.subscribed[source] = false;\n }\n }\n /**\n * After an auto reconnection we need to re-subscribe to the channels.\n */\n subscribePreviousSubscriptions() {\n // Before to subscribe previous subscriptions we make sure they\n // are all defined as unsubscribed, because this code is asynchrone\n // and a subscription could have been added in between.\n Object.keys(this.subscribed).forEach((key) => {\n this.subscribed[key] = false;\n });\n // Subscribe all previous subscriptions.\n [...this.subscriptions].forEach((s) => {\n this.subscribe(s.params, s.cb, s.errorCb, s.quiet);\n });\n }\n}\nexport default WebSocketAPI;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/api/WebSocketAPI.js", "access": "public", @@ -3305,7 +3364,7 @@ "lineNumber": 1 }, { - "__docId__": 145, + "__docId__": 148, "kind": "class", "name": "WebSocketAPI", "memberof": "build/common/api/WebSocketAPI.js", @@ -3320,7 +3379,7 @@ "interface": false }, { - "__docId__": 146, + "__docId__": 149, "kind": "constructor", "name": "constructor", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3334,7 +3393,7 @@ "undocument": true }, { - "__docId__": 147, + "__docId__": 150, "kind": "method", "name": "defineProperties", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3350,7 +3409,7 @@ "return": null }, { - "__docId__": 148, + "__docId__": 151, "kind": "method", "name": "getRequestString", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3424,7 +3483,7 @@ } }, { - "__docId__": 149, + "__docId__": 152, "kind": "method", "name": "connect", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3460,7 +3519,7 @@ "return": null }, { - "__docId__": 150, + "__docId__": 153, "kind": "member", "name": "websocket", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3477,7 +3536,7 @@ } }, { - "__docId__": 151, + "__docId__": 154, "kind": "method", "name": "close", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3492,7 +3551,7 @@ "return": null }, { - "__docId__": 152, + "__docId__": 155, "kind": "member", "name": "messagesOnOpen", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3509,7 +3568,7 @@ } }, { - "__docId__": 153, + "__docId__": 156, "kind": "method", "name": "send", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3535,7 +3594,7 @@ "return": null }, { - "__docId__": 156, + "__docId__": 159, "kind": "method", "name": "addEvents", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3545,7 +3604,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#addEvents", "access": "public", "description": null, - "lineNumber": 145, + "lineNumber": 144, "undocument": true, "params": [ { @@ -3564,7 +3623,7 @@ "return": null }, { - "__docId__": 157, + "__docId__": 160, "kind": "method", "name": "removeEvents", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3574,7 +3633,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#removeEvents", "access": "public", "description": null, - "lineNumber": 154, + "lineNumber": 153, "undocument": true, "params": [ { @@ -3593,7 +3652,7 @@ "return": null }, { - "__docId__": 158, + "__docId__": 161, "kind": "method", "name": "listen", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3603,7 +3662,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#listen", "access": "private", "description": "Listen to websocket messages.", - "lineNumber": 172, + "lineNumber": 171, "params": [ { "nullable": null, @@ -3646,7 +3705,7 @@ } }, { - "__docId__": 159, + "__docId__": 162, "kind": "method", "name": "unlisten", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3656,7 +3715,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#unlisten", "access": "private", "description": "Unlisten websocket messages.", - "lineNumber": 215, + "lineNumber": 214, "params": [ { "nullable": null, @@ -3682,7 +3741,7 @@ "return": null }, { - "__docId__": 160, + "__docId__": 163, "kind": "method", "name": "get", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3692,7 +3751,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#get", "access": "private", "description": "Sends a get request to the websocket.\nThe callback is called only once, when the response is received or when the call returns an error.", - "lineNumber": 231, + "lineNumber": 230, "params": [ { "nullable": null, @@ -3728,7 +3787,7 @@ "return": null }, { - "__docId__": 161, + "__docId__": 164, "kind": "member", "name": "requests", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3736,7 +3795,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#requests", "access": "public", "description": null, - "lineNumber": 248, + "lineNumber": 247, "undocument": true, "type": { "types": [ @@ -3745,7 +3804,7 @@ } }, { - "__docId__": 162, + "__docId__": 165, "kind": "method", "name": "subscribe", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3755,7 +3814,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#subscribe", "access": "private", "description": "Subscribe to a given channel.", - "lineNumber": 275, + "lineNumber": 274, "params": [ { "nullable": null, @@ -3801,7 +3860,7 @@ "return": null }, { - "__docId__": 163, + "__docId__": 166, "kind": "method", "name": "unsubscribe", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3811,7 +3870,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#unsubscribe", "access": "private", "description": "Unsubscribe from a channel.", - "lineNumber": 300, + "lineNumber": 299, "params": [ { "nullable": null, @@ -3837,7 +3896,7 @@ "return": null }, { - "__docId__": 164, + "__docId__": 167, "kind": "member", "name": "subscriptions", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3845,7 +3904,7 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#subscriptions", "access": "public", "description": null, - "lineNumber": 305, + "lineNumber": 304, "undocument": true, "type": { "types": [ @@ -3854,7 +3913,7 @@ } }, { - "__docId__": 165, + "__docId__": 168, "kind": "method", "name": "subscribePreviousSubscriptions", "memberof": "build/common/api/WebSocketAPI.js~WebSocketAPI", @@ -3864,15 +3923,15 @@ "longname": "build/common/api/WebSocketAPI.js~WebSocketAPI#subscribePreviousSubscriptions", "access": "public", "description": "After an auto reconnection we need to re-subscribe to the channels.", - "lineNumber": 319, + "lineNumber": 318, "params": [], "return": null }, { - "__docId__": 166, + "__docId__": 169, "kind": "file", "name": "build/common/controls/ControlCommon.js", - "content": "import BaseObject from 'ol/Object';\n/**\n * A class representing a control to display on map.\n *\n * @example\n * const control = new Control();\n *\n * @classproperty {ol/Map~Map|mapboxgl.Map} map - The map which the control refers to.\n * @classproperty {boolean} active - Active the control.\n * @classproperty {HTMLElement} element - The HTML element used to render the control.\n * @classproperty {HTMLElement} target - The HTML element where to render the element property. Default is the map's element. Read only.\n */\nclass ControlCommon extends BaseObject {\n /**\n * Constructor\n *\n * @param {Object} [options] Control options.\n * @param {boolean} [options.active = true] Whether the control is active or not.\n * @param {HTMLElement} [options.element] The HTML element used to render the control.\n * @param {HTMLElement} [options.target] The HTML element where to render the element property. Default is the map's element.\n * @param {function} [options.render] Render function called whenever the control needs to be rerendered.\n */\n constructor(options = {}) {\n super(options);\n this.defineProperties(options);\n const { active } = Object.assign({ active: options.active !== false }, options);\n /**\n * @ignore\n */\n this.active = active;\n }\n /**\n * Define control's properties.\n *\n * @private\n * @ignore\n */\n defineProperties(options) {\n const { target, element, render } = Object.assign({}, options);\n Object.defineProperties(this, {\n active: {\n get: () => this.get('active'),\n set: (newActive) => {\n this.set('active', newActive);\n if (newActive) {\n this.activate();\n }\n else {\n this.deactivate();\n }\n this.render();\n },\n },\n map: {\n get: () => this.get('map'),\n set: (map) => {\n // Remove previous node.\n if (this.map && this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n // Clean listeners\n this.deactivate();\n this.set('map', map);\n if (this.map) {\n // Add new node\n const targett = this.target ||\n (this.map.getTargetElement &&\n this.map.getTargetElement()) ||\n (this.map.getContainer &&\n this.map.getContainer());\n if (!this.element) {\n this.createDefaultElement();\n }\n if (this.element) {\n targett.appendChild(this.element);\n }\n // Add listeners\n if (this.active) {\n this.activate();\n }\n }\n this.render();\n },\n },\n target: {\n value: target,\n },\n element: {\n value: element,\n writable: true,\n },\n render: {\n /** @ignore */\n value: render || this.render,\n writable: true,\n },\n });\n }\n /**\n * Attach the control to the map. Add events, html element ...\n */\n attachToMap(map) {\n this.map = map;\n }\n /**\n * Detach the control From the map. Remove events, html element ..\n */\n detachFromMap() {\n this.map = undefined;\n }\n /**\n * Add listeners then renders the control.\n * To be defined in inherited classes.\n */\n activate() {\n this.deactivate();\n }\n /**\n * Remove listeners added by activate() function then renders the control.\n * To be defined in inherited classes.\n */\n // eslint-disable-next-line class-methods-use-this\n deactivate() {\n // eslint-disable-next-line no-console\n console.error('The function deactivate() must be implemented in subclasses');\n }\n /**\n * The default render function. It renders content in the HTML element.\n * To be defined in inherited classes.\n *\n * @private\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n render(options) {\n // eslint-disable-next-line no-console\n console.error('The function render() must be implemented in subclasses');\n }\n /**\n * The default element to display if this.element is not defined.\n * To be defined in inherited classes.\n *\n * @private\n */\n // eslint-disable-next-line class-methods-use-this\n createDefaultElement() {\n // eslint-disable-next-line no-console\n console.error('The function createDefaultElement() must be implemented in subclasses');\n }\n}\nexport default ControlCommon;\n", + "content": "import BaseObject from 'ol/Object';\n/**\n * A class representing a control to display on map.\n *\n * @example\n * const control = new Control();\n *\n * @classproperty {ol/Map~Map|mapboxgl.Map} map - The map which the control refers to.\n * @classproperty {boolean} active - Active the control.\n * @classproperty {HTMLElement} element - The HTML element used to render the control.\n * @classproperty {HTMLElement} target - The HTML element where to render the element property. Default is the map's element. Read only.\n */\nclass ControlCommon extends BaseObject {\n /**\n * Constructor\n *\n * @param {Object} [options] Control options.\n * @param {boolean} [options.active = true] Whether the control is active or not.\n * @param {HTMLElement} [options.element] The HTML element used to render the control.\n * @param {HTMLElement} [options.target] The HTML element where to render the element property. Default is the map's element.\n * @param {function} [options.render] Render function called whenever the control needs to be rerendered.\n */\n constructor(options = {}) {\n super(options);\n this.defineProperties(options);\n const { active } = {\n active: options.active !== false,\n ...options,\n };\n /**\n * @ignore\n */\n this.active = active;\n }\n /**\n * Define control's properties.\n *\n * @private\n * @ignore\n */\n defineProperties(options) {\n const { target, element, render } = {\n ...options,\n };\n Object.defineProperties(this, {\n active: {\n get: () => this.get('active'),\n set: (newActive) => {\n this.set('active', newActive);\n if (newActive) {\n this.activate();\n }\n else {\n this.deactivate();\n }\n this.render();\n },\n },\n map: {\n get: () => this.get('map'),\n set: (map) => {\n // Remove previous node.\n if (this.map && this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n // Clean listeners\n this.deactivate();\n this.set('map', map);\n if (this.map) {\n // Add new node\n const targett = this.target ||\n (this.map.getTargetElement &&\n this.map.getTargetElement()) ||\n (this.map.getContainer &&\n this.map.getContainer());\n if (!this.element) {\n this.createDefaultElement();\n }\n if (this.element) {\n targett.appendChild(this.element);\n }\n // Add listeners\n if (this.active) {\n this.activate();\n }\n }\n this.render();\n },\n },\n target: {\n value: target,\n },\n element: {\n value: element,\n writable: true,\n },\n render: {\n /** @ignore */\n value: render || this.render,\n writable: true,\n },\n });\n }\n /**\n * Attach the control to the map. Add events, html element ...\n */\n attachToMap(map) {\n this.map = map;\n }\n /**\n * Detach the control From the map. Remove events, html element ..\n */\n detachFromMap() {\n this.map = undefined;\n }\n /**\n * Add listeners then renders the control.\n * To be defined in inherited classes.\n */\n activate() {\n this.deactivate();\n }\n /**\n * Remove listeners added by activate() function then renders the control.\n * To be defined in inherited classes.\n */\n // eslint-disable-next-line class-methods-use-this\n deactivate() {\n // eslint-disable-next-line no-console\n console.error('The function deactivate() must be implemented in subclasses');\n }\n /**\n * The default render function. It renders content in the HTML element.\n * To be defined in inherited classes.\n *\n * @private\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n render(options) {\n // eslint-disable-next-line no-console\n console.error('The function render() must be implemented in subclasses');\n }\n /**\n * The default element to display if this.element is not defined.\n * To be defined in inherited classes.\n *\n * @private\n */\n // eslint-disable-next-line class-methods-use-this\n createDefaultElement() {\n // eslint-disable-next-line no-console\n console.error('The function createDefaultElement() must be implemented in subclasses');\n }\n}\nexport default ControlCommon;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/controls/ControlCommon.js", "access": "public", @@ -3880,7 +3939,7 @@ "lineNumber": 1 }, { - "__docId__": 167, + "__docId__": 170, "kind": "class", "name": "ControlCommon", "memberof": "build/common/controls/ControlCommon.js", @@ -3919,7 +3978,7 @@ ] }, { - "__docId__": 168, + "__docId__": 171, "kind": "constructor", "name": "constructor", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -3986,7 +4045,7 @@ ] }, { - "__docId__": 169, + "__docId__": 172, "kind": "member", "name": "active", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -3994,7 +4053,7 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#active", "access": "public", "description": "", - "lineNumber": 30, + "lineNumber": 33, "ignore": true, "type": { "types": [ @@ -4003,7 +4062,7 @@ } }, { - "__docId__": 170, + "__docId__": 173, "kind": "method", "name": "defineProperties", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4013,7 +4072,7 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#defineProperties", "access": "private", "description": "Define control's properties.", - "lineNumber": 38, + "lineNumber": 41, "ignore": true, "params": [ { @@ -4026,7 +4085,7 @@ "return": null }, { - "__docId__": 171, + "__docId__": 174, "kind": "method", "name": "attachToMap", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4036,7 +4095,7 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#attachToMap", "access": "public", "description": "Attach the control to the map. Add events, html element ...", - "lineNumber": 102, + "lineNumber": 107, "params": [ { "name": "map", @@ -4048,7 +4107,7 @@ "return": null }, { - "__docId__": 172, + "__docId__": 175, "kind": "member", "name": "map", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4056,7 +4115,7 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#map", "access": "public", "description": null, - "lineNumber": 103, + "lineNumber": 108, "undocument": true, "type": { "types": [ @@ -4065,7 +4124,7 @@ } }, { - "__docId__": 173, + "__docId__": 176, "kind": "method", "name": "detachFromMap", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4075,12 +4134,12 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#detachFromMap", "access": "public", "description": "Detach the control From the map. Remove events, html element ..", - "lineNumber": 108, + "lineNumber": 113, "params": [], "return": null }, { - "__docId__": 175, + "__docId__": 178, "kind": "method", "name": "activate", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4090,12 +4149,12 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#activate", "access": "public", "description": "Add listeners then renders the control.\nTo be defined in inherited classes.", - "lineNumber": 115, + "lineNumber": 120, "params": [], "return": null }, { - "__docId__": 176, + "__docId__": 179, "kind": "method", "name": "deactivate", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4105,12 +4164,12 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#deactivate", "access": "public", "description": "Remove listeners added by activate() function then renders the control.\nTo be defined in inherited classes.", - "lineNumber": 123, + "lineNumber": 128, "params": [], "return": null }, { - "__docId__": 177, + "__docId__": 180, "kind": "method", "name": "render", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4120,7 +4179,7 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#render", "access": "private", "description": "The default render function. It renders content in the HTML element.\nTo be defined in inherited classes.", - "lineNumber": 134, + "lineNumber": 139, "params": [ { "name": "options", @@ -4132,7 +4191,7 @@ "return": null }, { - "__docId__": 178, + "__docId__": 181, "kind": "method", "name": "createDefaultElement", "memberof": "build/common/controls/ControlCommon.js~ControlCommon", @@ -4142,12 +4201,12 @@ "longname": "build/common/controls/ControlCommon.js~ControlCommon#createDefaultElement", "access": "private", "description": "The default element to display if this.element is not defined.\nTo be defined in inherited classes.", - "lineNumber": 145, + "lineNumber": 150, "params": [], "return": null }, { - "__docId__": 179, + "__docId__": 182, "kind": "file", "name": "build/common/controls/CopyrightControlCommon.js", "content": "/* eslint-disable max-classes-per-file */\nimport ControlCommon from './ControlCommon';\n/**\n * A class representing a copyright control to display on map.\n * This class only draw an html element, with an empty string in it.\n * Use subclasses to use it in an ol or mapbox map.\n */\nclass CopyrightControlCommon extends ControlCommon {\n render() {\n if (!this.element) {\n return;\n }\n this.element.innerHTML = this.active\n ? this.getCopyrights().join(' | ')\n : '';\n }\n createDefaultElement() {\n this.element = document.createElement('div');\n this.element.id = 'mbt-copyright';\n Object.assign(this.element.style, {\n position: 'absolute',\n bottom: 0,\n right: 0,\n fontSize: '.8rem',\n padding: '0 10px',\n });\n }\n getCopyrights() {\n // eslint-disable-next-line no-console\n console.error('The getCopyrights() function must be implemented in subclasses.', this);\n return [];\n }\n}\nexport default CopyrightControlCommon;\n", @@ -4158,7 +4217,7 @@ "lineNumber": 1 }, { - "__docId__": 180, + "__docId__": 183, "kind": "class", "name": "CopyrightControlCommon", "memberof": "build/common/controls/CopyrightControlCommon.js", @@ -4176,7 +4235,7 @@ ] }, { - "__docId__": 181, + "__docId__": 184, "kind": "method", "name": "render", "memberof": "build/common/controls/CopyrightControlCommon.js~CopyrightControlCommon", @@ -4192,7 +4251,7 @@ "return": null }, { - "__docId__": 182, + "__docId__": 185, "kind": "method", "name": "createDefaultElement", "memberof": "build/common/controls/CopyrightControlCommon.js~CopyrightControlCommon", @@ -4208,7 +4267,7 @@ "return": null }, { - "__docId__": 183, + "__docId__": 186, "kind": "member", "name": "element", "memberof": "build/common/controls/CopyrightControlCommon.js~CopyrightControlCommon", @@ -4225,7 +4284,7 @@ } }, { - "__docId__": 184, + "__docId__": 187, "kind": "method", "name": "getCopyrights", "memberof": "build/common/controls/CopyrightControlCommon.js~CopyrightControlCommon", @@ -4245,10 +4304,10 @@ } }, { - "__docId__": 185, + "__docId__": 188, "kind": "file", "name": "build/common/controls/StopFinderControlCommon.js", - "content": "import { StopsAPI } from '../../api';\nimport ControlCommon from './ControlCommon';\n/**\n * A class representing a stop finder control to display on map.\n * This class only draw the html elements.\n * The geographic logic must be implemented by subclasses.\n */\nclass StopFinderControlCommon extends ControlCommon {\n /**\n * Constructor.\n *\n * @param {Object} options Map options\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/). See StopsAPI.\n * @param {string} [options.url='https://api.geops.io/tracker/v1'] Stops service url. See StopsAPI.\n * @param {string} [options.placeholder='Search for a stop...'] Input field placeholder.\n * @param {StopsSearchParams} [options.apiParams={ limit: 20 }] Request parameters. See [Stops service documentation](https://developer.geops.io/apis/5dcbd702a256d90001cf1361/).\n */\n constructor(options) {\n super(options);\n const { apiParams, apiKey, url, placeholder } = options || {};\n this.apiParams = Object.assign({ limit: 20 }, (apiParams || {}));\n this.placeholder = placeholder || 'Search for a stop...';\n const apiOptions = { apiKey };\n if (url) {\n apiOptions.url = url;\n }\n this.api = new StopsAPI(apiOptions);\n this.abortController = new AbortController();\n }\n deactivate() { }\n render(featureCollection) {\n const suggestions = (featureCollection === null || featureCollection === void 0 ? void 0 : featureCollection.features) || [];\n if (!this.suggestionsElt) {\n return;\n }\n this.suggestionsElt.style.display = suggestions.length ? 'block' : 'none';\n this.suggestionsElt.innerHTML = '';\n suggestions.forEach((suggestion) => {\n var _a, _b;\n const suggElt = document.createElement('div');\n suggElt.innerHTML = (_a = suggestion === null || suggestion === void 0 ? void 0 : suggestion.properties) === null || _a === void 0 ? void 0 : _a.name;\n suggElt.onclick = () => {\n // @ts-ignore\n this.onSuggestionClick(suggestion);\n };\n Object.assign(suggElt.style, {\n padding: '5px 12px',\n });\n (_b = this.suggestionsElt) === null || _b === void 0 ? void 0 : _b.appendChild(suggElt);\n });\n }\n createDefaultElement() {\n /**\n * Define a default element.\n */\n this.element = document.createElement('div');\n this.element.id = 'mbt-search';\n Object.assign(this.element.style, {\n position: 'absolute',\n top: 0,\n left: '50px',\n margin: '10px',\n display: 'flex',\n flexDirection: 'column',\n width: '320px',\n });\n // Create input element\n this.inputElt = document.createElement('input');\n this.inputElt.type = 'text';\n this.inputElt.placeholder = this.placeholder;\n this.inputElt.autocomplete = 'off';\n this.inputElt.onkeyup = (evt) => {\n var _a;\n (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();\n this.abortController = new AbortController();\n // @ts-ignore\n this.search(evt.target.value, this.abortController);\n };\n Object.assign(this.inputElt.style, {\n padding: '10px 30px 10px 10px',\n });\n this.element.appendChild(this.inputElt);\n // Create suggestions list element\n this.suggestionsElt = document.createElement('div');\n Object.assign(this.suggestionsElt.style, {\n backgroundColor: 'white',\n overflowY: 'auto',\n cursor: 'pointer',\n });\n this.element.appendChild(this.suggestionsElt);\n this.clearElt = document.createElement('div');\n Object.assign(this.clearElt.style, {\n display: 'none',\n position: 'absolute',\n right: '0',\n padding: '0 10px',\n fontSize: '200%',\n cursor: 'pointer',\n });\n this.clearElt.innerHTML = '×';\n this.clearElt.onclick = () => this.clear();\n this.element.appendChild(this.clearElt);\n }\n /**\n * Launch a search.\n *\n * @param {String} query The query to search for.\n * @param {AbortController} abortController Abort controller used to cancel the request.\n * @return {Promise>} An array of GeoJSON features with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n search(q, abortController) {\n if (q !== undefined || q !== null) {\n this.apiParams.q = q;\n }\n if (this.clearElt) {\n this.clearElt.style.display = 'block';\n }\n return this.api\n .search(this.apiParams, abortController && { signal: abortController.signal })\n .then((data) => {\n this.render(data);\n })\n .catch(() => {\n this.render();\n });\n }\n /**\n * To be defined in inherited class\n */\n // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n onSuggestionClick(suggestion) { }\n /**\n * Clear the search field and close the control.\n */\n clear() {\n if (!this.suggestionsElt || !this.inputElt || !this.clearElt) {\n return;\n }\n this.inputElt.value = '';\n this.suggestionsElt.innerHTML = '';\n this.clearElt.style.display = 'none';\n }\n}\nexport default StopFinderControlCommon;\n", + "content": "import { StopsAPI } from '../../api';\nimport ControlCommon from './ControlCommon';\n/**\n * A class representing a stop finder control to display on map.\n * This class only draw the html elements.\n * The geographic logic must be implemented by subclasses.\n */\nclass StopFinderControlCommon extends ControlCommon {\n /**\n * Constructor.\n *\n * @param {Object} options Map options\n * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/). See StopsAPI.\n * @param {string} [options.url='https://api.geops.io/tracker/v1'] Stops service url. See StopsAPI.\n * @param {string} [options.placeholder='Search for a stop...'] Input field placeholder.\n * @param {StopsSearchParams} [options.apiParams={ limit: 20 }] Request parameters. See [Stops service documentation](https://developer.geops.io/apis/5dcbd702a256d90001cf1361/).\n */\n constructor(options) {\n super(options);\n const { apiParams, apiKey, url, placeholder } = options || {};\n this.apiParams = { limit: 20, ...(apiParams || {}) };\n this.placeholder = placeholder || 'Search for a stop...';\n const apiOptions = { apiKey };\n if (url) {\n apiOptions.url = url;\n }\n this.api = new StopsAPI(apiOptions);\n this.abortController = new AbortController();\n }\n deactivate() { }\n render(featureCollection) {\n const suggestions = featureCollection?.features || [];\n if (!this.suggestionsElt) {\n return;\n }\n this.suggestionsElt.style.display = suggestions.length ? 'block' : 'none';\n this.suggestionsElt.innerHTML = '';\n suggestions.forEach((suggestion) => {\n const suggElt = document.createElement('div');\n suggElt.innerHTML = suggestion?.properties?.name;\n suggElt.onclick = () => {\n // @ts-ignore\n this.onSuggestionClick(suggestion);\n };\n Object.assign(suggElt.style, {\n padding: '5px 12px',\n });\n this.suggestionsElt?.appendChild(suggElt);\n });\n }\n createDefaultElement() {\n /**\n * Define a default element.\n */\n this.element = document.createElement('div');\n this.element.id = 'mbt-search';\n Object.assign(this.element.style, {\n position: 'absolute',\n top: 0,\n left: '50px',\n margin: '10px',\n display: 'flex',\n flexDirection: 'column',\n width: '320px',\n });\n // Create input element\n this.inputElt = document.createElement('input');\n this.inputElt.type = 'text';\n this.inputElt.placeholder = this.placeholder;\n this.inputElt.autocomplete = 'off';\n this.inputElt.onkeyup = (evt) => {\n this.abortController?.abort();\n this.abortController = new AbortController();\n // @ts-ignore\n this.search(evt.target.value, this.abortController);\n };\n Object.assign(this.inputElt.style, {\n padding: '10px 30px 10px 10px',\n });\n this.element.appendChild(this.inputElt);\n // Create suggestions list element\n this.suggestionsElt = document.createElement('div');\n Object.assign(this.suggestionsElt.style, {\n backgroundColor: 'white',\n overflowY: 'auto',\n cursor: 'pointer',\n });\n this.element.appendChild(this.suggestionsElt);\n this.clearElt = document.createElement('div');\n Object.assign(this.clearElt.style, {\n display: 'none',\n position: 'absolute',\n right: '0',\n padding: '0 10px',\n fontSize: '200%',\n cursor: 'pointer',\n });\n this.clearElt.innerHTML = '×';\n this.clearElt.onclick = () => this.clear();\n this.element.appendChild(this.clearElt);\n }\n /**\n * Launch a search.\n *\n * @param {String} query The query to search for.\n * @param {AbortController} abortController Abort controller used to cancel the request.\n * @return {Promise>} An array of GeoJSON features with coordinates in [EPSG:4326](http://epsg.io/4326).\n */\n search(q, abortController) {\n if (q !== undefined || q !== null) {\n this.apiParams.q = q;\n }\n if (this.clearElt) {\n this.clearElt.style.display = 'block';\n }\n return this.api\n .search(this.apiParams, abortController && { signal: abortController.signal })\n .then((data) => {\n this.render(data);\n })\n .catch(() => {\n this.render();\n });\n }\n /**\n * To be defined in inherited class\n */\n // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n onSuggestionClick(suggestion) { }\n /**\n * Clear the search field and close the control.\n */\n clear() {\n if (!this.suggestionsElt || !this.inputElt || !this.clearElt) {\n return;\n }\n this.inputElt.value = '';\n this.suggestionsElt.innerHTML = '';\n this.clearElt.style.display = 'none';\n }\n}\nexport default StopFinderControlCommon;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/controls/StopFinderControlCommon.js", "access": "public", @@ -4256,7 +4315,7 @@ "lineNumber": 1 }, { - "__docId__": 186, + "__docId__": 189, "kind": "class", "name": "StopFinderControlCommon", "memberof": "build/common/controls/StopFinderControlCommon.js", @@ -4274,7 +4333,7 @@ ] }, { - "__docId__": 187, + "__docId__": 190, "kind": "constructor", "name": "constructor", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4345,7 +4404,7 @@ ] }, { - "__docId__": 188, + "__docId__": 191, "kind": "member", "name": "apiParams", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4357,12 +4416,12 @@ "undocument": true, "type": { "types": [ - "*" + "{\"limit\": number, ...undefined: Object}" ] } }, { - "__docId__": 189, + "__docId__": 192, "kind": "member", "name": "placeholder", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4379,7 +4438,7 @@ } }, { - "__docId__": 190, + "__docId__": 193, "kind": "member", "name": "api", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4396,7 +4455,7 @@ } }, { - "__docId__": 191, + "__docId__": 194, "kind": "member", "name": "abortController", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4413,7 +4472,7 @@ } }, { - "__docId__": 192, + "__docId__": 195, "kind": "method", "name": "deactivate", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4429,7 +4488,7 @@ "return": null }, { - "__docId__": 193, + "__docId__": 196, "kind": "method", "name": "render", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4452,7 +4511,7 @@ "return": null }, { - "__docId__": 194, + "__docId__": 197, "kind": "method", "name": "createDefaultElement", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4462,13 +4521,13 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#createDefaultElement", "access": "public", "description": null, - "lineNumber": 52, + "lineNumber": 51, "undocument": true, "params": [], "return": null }, { - "__docId__": 195, + "__docId__": 198, "kind": "member", "name": "element", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4476,7 +4535,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#element", "access": "public", "description": "Define a default element.", - "lineNumber": 56, + "lineNumber": 55, "type": { "types": [ "*" @@ -4484,7 +4543,7 @@ } }, { - "__docId__": 196, + "__docId__": 199, "kind": "member", "name": "inputElt", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4492,7 +4551,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#inputElt", "access": "public", "description": null, - "lineNumber": 68, + "lineNumber": 67, "undocument": true, "type": { "types": [ @@ -4501,7 +4560,7 @@ } }, { - "__docId__": 198, + "__docId__": 201, "kind": "member", "name": "suggestionsElt", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4509,7 +4568,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#suggestionsElt", "access": "public", "description": null, - "lineNumber": 84, + "lineNumber": 82, "undocument": true, "type": { "types": [ @@ -4518,7 +4577,7 @@ } }, { - "__docId__": 199, + "__docId__": 202, "kind": "member", "name": "clearElt", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4526,7 +4585,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#clearElt", "access": "public", "description": null, - "lineNumber": 91, + "lineNumber": 89, "undocument": true, "type": { "types": [ @@ -4535,7 +4594,7 @@ } }, { - "__docId__": 200, + "__docId__": 203, "kind": "method", "name": "search", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4545,7 +4604,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#search", "access": "public", "description": "Launch a search.", - "lineNumber": 111, + "lineNumber": 109, "params": [ { "nullable": null, @@ -4578,7 +4637,7 @@ } }, { - "__docId__": 201, + "__docId__": 204, "kind": "method", "name": "onSuggestionClick", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4588,7 +4647,7 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#onSuggestionClick", "access": "public", "description": "To be defined in inherited class", - "lineNumber": 131, + "lineNumber": 129, "params": [ { "name": "suggestion", @@ -4600,7 +4659,7 @@ "return": null }, { - "__docId__": 202, + "__docId__": 205, "kind": "method", "name": "clear", "memberof": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon", @@ -4610,12 +4669,12 @@ "longname": "build/common/controls/StopFinderControlCommon.js~StopFinderControlCommon#clear", "access": "public", "description": "Clear the search field and close the control.", - "lineNumber": 135, + "lineNumber": 133, "params": [], "return": null }, { - "__docId__": 203, + "__docId__": 206, "kind": "file", "name": "build/common/index.js", "content": "export * from './utils';\nexport * from './styles';\n", @@ -4626,10 +4685,10 @@ "lineNumber": 1 }, { - "__docId__": 204, + "__docId__": 207, "kind": "file", "name": "build/common/layers/LayerCommon.js", - "content": "import BaseObject from 'ol/Object';\nimport { v4 as uuid } from 'uuid';\nimport BaseEvent from 'ol/events/Event';\nimport getLayersAsFlatArray from '../utils/getLayersAsFlatArray';\n/**\n * A class representing a layer to display on map.\n *\n * @example\n * const layer = new Layer({\n * name: 'My Layer',\n * });\n *\n * @classproperty {string} key - Identifier of the layer. Must be unique.\n * @classproperty {string} name - Name of the layer\n * @classproperty {string[]} copyrights - Array of copyrights.\n * @classproperty {Layer[]} children - List of children layers.\n * @classproperty {boolean} visible - Define if the layer is currently display on the map.\n * @classproperty {boolean} disabled - Define if the layer is currently display on the map but can't be seen (extent, zoom ,data restrictions).\n * @classproperty {number} hitTolerance - Hit-detection tolerance in css pixels. Pixels inside the radius around the given position will be checked for features.\n * @classproperty {Object} properties - Custom properties.\n * @classproperty {ol/Map~Map|mapboxgl.Map} map - The map where the layer is displayed.\n */\nexport default class Layer extends BaseObject {\n /**\n * Constructor\n *\n * @param {Object} options\n * @param {string} [options.key=uuid()] Identifier of the layer. Muste be unique. Default use a generated uuid.\n * @param {string} [options.name] Name of the layer.\n * @param {string[]} [options.copyrights] Array of copyrights.\n * @param {Array} [options.children=[]] Sublayers, all child layers will have a parent property associated to this layer.\n * @param {boolean} [options.visible=true] Define if the layer is currently display on the map.\n * @param {boolean} [options.disabled=false] Define if the layer is currently display on the map but can't be seen (extent, zoom ,data restrictions).\n * @param {number} [options.hitTolerance=5] Hit-detection tolerance in css pixels. Pixels inside the radius around the given position will be checked for features.\n * @param {Object} [options.properties={}] Application-specific layer properties.\n */\n constructor(options = {}) {\n super();\n this.properties = {};\n this.options = {};\n this.defineProperties(options);\n if (options.properties) {\n this.setProperties(options.properties);\n }\n this.options = options;\n this.visible = options.visible === undefined ? true : !!options.visible;\n this.group = options.group;\n this.copyrights = options.copyrights;\n this.children = options.children;\n // Listen for group visiblity change\n // if a layer from a group is newly visible we hide the others.\n /* @ts-ignore */\n this.on(`change:visible:group`, (evt) => {\n // We hide layer of the same group\n if (this.group === evt.target.group &&\n this !== evt.target &&\n this.visible) {\n this.visible = false;\n // Propagate event to parent\n }\n else if (this.children) {\n this.children.forEach((child) => child.dispatchEvent(evt));\n }\n });\n }\n /**\n * Define layer's properties that needs custom get and set.\n *\n * @ignore\n */\n defineProperties(options = {}) {\n const { name, key, properties, hitTolerance } = Object.assign({}, options);\n const uid = uuid();\n Object.defineProperties(this, {\n /* Layer's information properties */\n name: {\n value: name,\n },\n key: {\n value: key || name || uid,\n },\n group: {\n get: () => this.get('group'),\n set: (newGroup) => {\n this.set('group', newGroup);\n },\n },\n copyrights: {\n get: () => this.get('copyrights'),\n set: (newCopyrights) => {\n const arrValue = newCopyrights && !Array.isArray(newCopyrights)\n ? [newCopyrights]\n : newCopyrights;\n this.set('copyrights', arrValue || []);\n },\n },\n // options is used for clone function.\n options: {\n value: options,\n },\n map: {\n writable: true,\n },\n /* Layer's state properties */\n visible: {\n get: () => this.get('visible'),\n set: (newVisible) => {\n if (newVisible === this.visible) {\n return;\n }\n this.set('visible', newVisible);\n if (this.visible) {\n // We make the parent visible\n if (this.parent) {\n this.parent.visible = true;\n }\n // If children doesn't contain any visible layers, we display all children.\n if (this.children &&\n !this.children.some((child) => child.visible)) {\n this.children.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.visible = true;\n });\n }\n // Warn the same group that a new layer is visible\n if (this.parent && this.group) {\n // We search for the higher parent then it will dispatch to all the tree.\n let higherParent = this.parent;\n while (higherParent.parent) {\n higherParent = higherParent.parent;\n }\n const evt = new BaseEvent(`change:visible:group`);\n evt.target = this;\n higherParent.dispatchEvent(evt);\n }\n }\n else if (!this.visible) {\n // We hide all the children\n if (this.children) {\n this.children.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.visible = false;\n });\n }\n // If the parent has no more visible child we also hide it.\n if (this.parent &&\n this.parent.visible &&\n this.parent.children &&\n !this.parent.children.find((child) => child.visible)) {\n this.parent.visible = false;\n }\n }\n },\n },\n disabled: {\n get: () => this.get('disabled'),\n set: (newValue) => {\n this.set('disabled', newValue);\n },\n },\n /* Layer's hierarchy properties */\n parent: {\n value: null,\n writable: true,\n },\n children: {\n get: () => this.get('children') || [],\n set: (newValue) => {\n (this.children || []).forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.parent = undefined;\n });\n if (Array.isArray(newValue)) {\n newValue.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.parent = this;\n });\n }\n this.set('children', newValue || []);\n },\n },\n /* Layer's query properties */\n hitTolerance: {\n value: hitTolerance || 5,\n writable: true,\n },\n /* Custom app specific properties */\n properties: {\n value: Object.assign({}, (properties || {})),\n },\n });\n }\n /**\n * Initialize the layer with the map passed in parameters.\n *\n * @param {ol/Map~Map|mapboxgl.Map} map A map.\n */\n attachToMap(map) {\n this.detachFromMap();\n /** @ignore */\n this.map = map;\n if (this.children) {\n this.children.forEach((child) => {\n child.attachToMap(map);\n });\n }\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n // eslint-disable-next-line class-methods-use-this\n detachFromMap() {\n /** @ignore */\n this.map = undefined;\n }\n /**\n * Request feature information for a given coordinate.\n * This function must be implemented by inheriting layers.\n *\n * @param {ol/coordinate~Coordinate} coordinate Coordinate.\n * @param {Object} options Some options. See child classes to see which are supported.\n * @return {Promise} An empty response.\n */\n // eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars\n getFeatureInfoAtCoordinate(coordinate, \n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n options) {\n // eslint-disable-next-line no-console\n console.error('getFeatureInfoAtCoordinate must be implemented by inheriting layers', this.key);\n // This layer returns no feature info.\n // The function is implemented by inheriting layers.\n return Promise.resolve({\n layer: this,\n features: [],\n coordinate,\n });\n }\n /**\n * Return the an array containing all the descendants of the layer in a flat array. Including the current layer.\n */\n flat() {\n return getLayersAsFlatArray(this);\n }\n}\n", + "content": "import BaseObject from 'ol/Object';\nimport { v4 as uuid } from 'uuid';\nimport BaseEvent from 'ol/events/Event';\nimport getLayersAsFlatArray from '../utils/getLayersAsFlatArray';\n/**\n * A class representing a layer to display on map.\n *\n * @example\n * const layer = new Layer({\n * name: 'My Layer',\n * });\n *\n * @classproperty {string} key - Identifier of the layer. Must be unique.\n * @classproperty {string} name - Name of the layer\n * @classproperty {string[]} copyrights - Array of copyrights.\n * @classproperty {Layer[]} children - List of children layers.\n * @classproperty {boolean} visible - Define if the layer is currently display on the map.\n * @classproperty {boolean} disabled - Define if the layer is currently display on the map but can't be seen (extent, zoom ,data restrictions).\n * @classproperty {number} hitTolerance - Hit-detection tolerance in css pixels. Pixels inside the radius around the given position will be checked for features.\n * @classproperty {Object} properties - Custom properties.\n * @classproperty {ol/Map~Map|mapboxgl.Map} map - The map where the layer is displayed.\n */\nexport default class Layer extends BaseObject {\n /**\n * Constructor\n *\n * @param {Object} options\n * @param {string} [options.key=uuid()] Identifier of the layer. Muste be unique. Default use a generated uuid.\n * @param {string} [options.name] Name of the layer.\n * @param {string[]} [options.copyrights] Array of copyrights.\n * @param {Array} [options.children=[]] Sublayers, all child layers will have a parent property associated to this layer.\n * @param {boolean} [options.visible=true] Define if the layer is currently display on the map.\n * @param {boolean} [options.disabled=false] Define if the layer is currently display on the map but can't be seen (extent, zoom ,data restrictions).\n * @param {number} [options.hitTolerance=5] Hit-detection tolerance in css pixels. Pixels inside the radius around the given position will be checked for features.\n * @param {Object} [options.properties={}] Application-specific layer properties.\n */\n constructor(options = {}) {\n super();\n this.properties = {};\n this.options = {};\n this.defineProperties(options);\n if (options.properties) {\n this.setProperties(options.properties);\n }\n this.options = options;\n this.visible = options.visible === undefined ? true : !!options.visible;\n this.group = options.group;\n this.copyrights = options.copyrights;\n this.children = options.children;\n // Listen for group visiblity change\n // if a layer from a group is newly visible we hide the others.\n /* @ts-ignore */\n this.on(`change:visible:group`, (evt) => {\n // We hide layer of the same group\n if (this.group === evt.target.group &&\n this !== evt.target &&\n this.visible) {\n this.visible = false;\n // Propagate event to parent\n }\n else if (this.children) {\n this.children.forEach((child) => child.dispatchEvent(evt));\n }\n });\n }\n /**\n * Define layer's properties that needs custom get and set.\n *\n * @ignore\n */\n defineProperties(options = {}) {\n const { name, key, properties, hitTolerance } = {\n ...options,\n };\n const uid = uuid();\n Object.defineProperties(this, {\n /* Layer's information properties */\n name: {\n value: name,\n },\n key: {\n value: key || name || uid,\n },\n group: {\n get: () => this.get('group'),\n set: (newGroup) => {\n this.set('group', newGroup);\n },\n },\n copyrights: {\n get: () => this.get('copyrights'),\n set: (newCopyrights) => {\n const arrValue = newCopyrights && !Array.isArray(newCopyrights)\n ? [newCopyrights]\n : newCopyrights;\n this.set('copyrights', arrValue || []);\n },\n },\n // options is used for clone function.\n options: {\n value: options,\n },\n map: {\n writable: true,\n },\n /* Layer's state properties */\n visible: {\n get: () => this.get('visible'),\n set: (newVisible) => {\n if (newVisible === this.visible) {\n return;\n }\n this.set('visible', newVisible);\n if (this.visible) {\n // We make the parent visible\n if (this.parent) {\n this.parent.visible = true;\n }\n // If children doesn't contain any visible layers, we display all children.\n if (this.children &&\n !this.children.some((child) => child.visible)) {\n this.children.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.visible = true;\n });\n }\n // Warn the same group that a new layer is visible\n if (this.parent && this.group) {\n // We search for the higher parent then it will dispatch to all the tree.\n let higherParent = this.parent;\n while (higherParent.parent) {\n higherParent = higherParent.parent;\n }\n const evt = new BaseEvent(`change:visible:group`);\n evt.target = this;\n higherParent.dispatchEvent(evt);\n }\n }\n else if (!this.visible) {\n // We hide all the children\n if (this.children) {\n this.children.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.visible = false;\n });\n }\n // If the parent has no more visible child we also hide it.\n if (this.parent &&\n this.parent.visible &&\n this.parent.children &&\n !this.parent.children.find((child) => child.visible)) {\n this.parent.visible = false;\n }\n }\n },\n },\n disabled: {\n get: () => this.get('disabled'),\n set: (newValue) => {\n this.set('disabled', newValue);\n },\n },\n /* Layer's hierarchy properties */\n parent: {\n value: null,\n writable: true,\n },\n children: {\n get: () => this.get('children') || [],\n set: (newValue) => {\n (this.children || []).forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.parent = undefined;\n });\n if (Array.isArray(newValue)) {\n newValue.forEach((child) => {\n // eslint-disable-next-line no-param-reassign\n child.parent = this;\n });\n }\n this.set('children', newValue || []);\n },\n },\n /* Layer's query properties */\n hitTolerance: {\n value: hitTolerance || 5,\n writable: true,\n },\n /* Custom app specific properties */\n properties: {\n value: { ...(properties || {}) },\n },\n });\n }\n /**\n * Initialize the layer with the map passed in parameters.\n *\n * @param {ol/Map~Map|mapboxgl.Map} map A map.\n */\n attachToMap(map) {\n this.detachFromMap();\n /** @ignore */\n this.map = map;\n if (this.children) {\n this.children.forEach((child) => {\n child.attachToMap(map);\n });\n }\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n // eslint-disable-next-line class-methods-use-this\n detachFromMap() {\n /** @ignore */\n this.map = undefined;\n }\n /**\n * Request feature information for a given coordinate.\n * This function must be implemented by inheriting layers.\n *\n * @param {ol/coordinate~Coordinate} coordinate Coordinate.\n * @param {Object} options Some options. See child classes to see which are supported.\n * @return {Promise} An empty response.\n */\n // eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars\n getFeatureInfoAtCoordinate(coordinate, \n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n options) {\n // eslint-disable-next-line no-console\n console.error('getFeatureInfoAtCoordinate must be implemented by inheriting layers', this.key);\n // This layer returns no feature info.\n // The function is implemented by inheriting layers.\n return Promise.resolve({\n layer: this,\n features: [],\n coordinate,\n });\n }\n /**\n * Return the an array containing all the descendants of the layer in a flat array. Including the current layer.\n */\n flat() {\n return getLayersAsFlatArray(this);\n }\n}\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/layers/LayerCommon.js", "access": "public", @@ -4637,7 +4696,7 @@ "lineNumber": 1 }, { - "__docId__": 205, + "__docId__": 208, "kind": "class", "name": "Layer", "memberof": "build/common/layers/LayerCommon.js", @@ -4696,7 +4755,7 @@ ] }, { - "__docId__": 206, + "__docId__": 209, "kind": "constructor", "name": "constructor", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4813,7 +4872,7 @@ ] }, { - "__docId__": 207, + "__docId__": 210, "kind": "member", "name": "properties", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4830,7 +4889,7 @@ } }, { - "__docId__": 208, + "__docId__": 211, "kind": "member", "name": "options", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4847,7 +4906,7 @@ } }, { - "__docId__": 210, + "__docId__": 213, "kind": "member", "name": "visible", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4864,7 +4923,7 @@ } }, { - "__docId__": 211, + "__docId__": 214, "kind": "member", "name": "group", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4881,7 +4940,7 @@ } }, { - "__docId__": 212, + "__docId__": 215, "kind": "member", "name": "copyrights", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4898,7 +4957,7 @@ } }, { - "__docId__": 213, + "__docId__": 216, "kind": "member", "name": "children", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4915,7 +4974,7 @@ } }, { - "__docId__": 215, + "__docId__": 218, "kind": "method", "name": "defineProperties", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4941,7 +5000,7 @@ "return": null }, { - "__docId__": 216, + "__docId__": 219, "kind": "method", "name": "attachToMap", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4951,7 +5010,7 @@ "longname": "build/common/layers/LayerCommon.js~Layer#attachToMap", "access": "public", "description": "Initialize the layer with the map passed in parameters.", - "lineNumber": 198, + "lineNumber": 200, "params": [ { "nullable": null, @@ -4968,7 +5027,7 @@ "return": null }, { - "__docId__": 217, + "__docId__": 220, "kind": "member", "name": "map", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4976,7 +5035,7 @@ "longname": "build/common/layers/LayerCommon.js~Layer#map", "access": "public", "description": null, - "lineNumber": 201, + "lineNumber": 203, "ignore": true, "type": { "types": [ @@ -4985,7 +5044,7 @@ } }, { - "__docId__": 218, + "__docId__": 221, "kind": "method", "name": "detachFromMap", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -4995,12 +5054,12 @@ "longname": "build/common/layers/LayerCommon.js~Layer#detachFromMap", "access": "public", "description": "Terminate what was initialized in init function. Remove layer, events...", - "lineNumber": 212, + "lineNumber": 214, "params": [], "return": null }, { - "__docId__": 220, + "__docId__": 223, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -5010,7 +5069,7 @@ "longname": "build/common/layers/LayerCommon.js~Layer#getFeatureInfoAtCoordinate", "access": "public", "description": "Request feature information for a given coordinate.\nThis function must be implemented by inheriting layers.", - "lineNumber": 225, + "lineNumber": 227, "params": [ { "nullable": null, @@ -5043,7 +5102,7 @@ } }, { - "__docId__": 221, + "__docId__": 224, "kind": "method", "name": "flat", "memberof": "build/common/layers/LayerCommon.js~Layer", @@ -5053,7 +5112,7 @@ "longname": "build/common/layers/LayerCommon.js~Layer#flat", "access": "public", "description": "Return the an array containing all the descendants of the layer in a flat array. Including the current layer.", - "lineNumber": 241, + "lineNumber": 243, "params": [], "return": { "types": [ @@ -5062,10 +5121,10 @@ } }, { - "__docId__": 222, + "__docId__": 225, "kind": "file", "name": "build/common/mixins/RealtimeLayerMixin.js", - "content": "/* eslint-disable no-empty-function,@typescript-eslint/no-empty-function */\n/* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */\n/* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */\n/* eslint-disable class-methods-use-this */\n/* eslint-disable max-classes-per-file */\nimport { buffer, containsCoordinate, intersects } from 'ol/extent';\nimport { unByKey } from 'ol/Observable';\nimport GeoJSON from 'ol/format/GeoJSON';\nimport debounce from 'lodash.debounce';\nimport throttle from 'lodash.throttle';\nimport { fromLonLat } from 'ol/proj';\nimport realtimeDefaultStyle from '../styles/realtimeDefaultStyle';\nimport { RealtimeAPI, RealtimeModes } from '../../api';\nimport renderTrajectories from '../utils/renderTrajectories';\nimport * as realtimeConfig from '../utils/realtimeConfig';\n/**\n * RealtimeLayerInterface.\n */\nexport class RealtimeLayerInterface {\n /**\n * Start the clock.\n */\n start() { }\n /**\n * Stop the clock.\n */\n stop() { }\n /**\n * Set the Realtime api's bbox.\n *\n * @param {Array} extent Extent to request, [minX, minY, maxX, maxY, zoom].\n * @param {number} zoom Zoom level to request. Must be an integer.\n */\n setBbox(extent, zoom) { }\n /**\n * Render the trajectories\n */\n renderTrajectories() { }\n /**\n * Request the stopSequence and the fullTrajectory informations for a vehicle.\n *\n * @param {string} id The vehicle identifier (the train_id property).\n * @param {RealtimeMode} mode The mode to request. If not defined, the layer´s mode propetrty will be used.\n * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.\n */\n getTrajectoryInfos(id, mode) { }\n}\n/**\n * Mixin for RealtimeLayerInterface.\n *\n * @param {Class} Base A class to extend with {RealtimeLayerInterface} functionnalities.\n * @return {Class} A class that implements {RealtimeLayerInterface} class and extends Base;\n * @private\n */\nfunction RealtimeLayerMixin(Base) {\n // @ts-ignore\n return class Mixin extends Base {\n constructor(options) {\n super(Object.assign({ hitTolerance: 10 }, options));\n this.debug = options.debug || false;\n this.mode = options.mode || RealtimeModes.TOPOGRAPHIC;\n this.api = options.api || new RealtimeAPI(options);\n this.tenant = options.tenant || ''; // sbb,sbh or sbm\n this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.\n this.format = new GeoJSON();\n // MOTs by zoom\n const allMots = [\n 'tram',\n 'subway',\n 'rail',\n 'bus',\n 'ferry',\n 'cablecar',\n 'gondola',\n 'funicular',\n 'coach',\n ];\n const onlyRail = ['rail'];\n const withoutCable = ['tram', 'subway', 'rail', 'bus'];\n // Server will block non train before zoom 9\n this.motsByZoom = options.motsByZoom || [\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n withoutCable,\n withoutCable,\n allMots,\n allMots,\n allMots,\n allMots,\n ];\n this.getMotsByZoom = (zoom) => {\n return ((options.getMotsByZoom &&\n options.getMotsByZoom(zoom, this.motsByZoom)) ||\n this.motsByZoom[zoom] ||\n this.motsByZoom[this.motsByZoom.length - 1]);\n };\n // Generalization levels by zoom\n this.generalizationLevelByZoom = options.generalizationLevelByZoom || [\n 5, 5, 5, 5, 5, 5, 5, 5, 10, 30, 30, 100, 100, 100,\n ];\n this.getGeneralizationLevelByZoom = (zoom) => {\n return ((options.getGeneralizationLevelByZoom &&\n options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom)) ||\n this.generalizationLevelByZoom[zoom]);\n };\n // Render time interval by zoom\n this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [\n 100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,\n 300, 250, 180, 90, 60, 50, 50, 50, 50, 50,\n ];\n this.getRenderTimeIntervalByZoom = (zoom) => {\n return ((options.getRenderTimeIntervalByZoom &&\n options.getRenderTimeIntervalByZoom(zoom, this.renderTimeIntervalByZoom)) ||\n this.renderTimeIntervalByZoom[zoom]);\n };\n // This property will call api.setBbox on each movend event\n this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;\n // Define throttling and debounce render function\n this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });\n this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, trailing: true, maxWait: 5000 });\n // Bind callbacks\n this.onFeatureHover = this.onFeatureHover.bind(this);\n this.onFeatureClick = this.onFeatureClick.bind(this);\n this.renderTrajectoriesInternal =\n this.renderTrajectoriesInternal.bind(this);\n this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);\n this.onDeleteTrajectoryMessage =\n this.onDeleteTrajectoryMessage.bind(this);\n this.onDocumentVisibilityChange =\n this.onDocumentVisibilityChange.bind(this);\n }\n /**\n * Define layer's properties.\n *\n * @ignore\n */\n defineProperties(options) {\n const { style, speed, pixelRatio, hoverVehicleId, selectedVehicleId, filter, sort, time, live, canvas, styleOptions, mode, } = options;\n let currCanvas = canvas;\n let currSpeed = speed || 1;\n let currTime = time || new Date();\n let currMode = mode || RealtimeModes.TOPOGRAPHIC;\n let currStyle = style || realtimeDefaultStyle;\n super.defineProperties(options);\n Object.defineProperties(this, {\n isTrackerLayer: { value: true },\n canvas: {\n get: () => {\n if (!currCanvas) {\n currCanvas = document.createElement('canvas');\n }\n return currCanvas;\n },\n set: (cnvas) => {\n currCanvas = cnvas;\n },\n },\n /**\n * Style function used to render a vehicle.\n */\n mode: {\n get: () => currMode,\n set: (newMode) => {\n var _a, _b;\n if (newMode === currMode) {\n return;\n }\n currMode = newMode;\n if ((_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.wsApi) === null || _b === void 0 ? void 0 : _b.open) {\n this.stop();\n this.start();\n }\n },\n },\n /**\n * Style function used to render a vehicle.\n */\n style: {\n get: () => currStyle,\n set: (newStyle) => {\n currStyle = newStyle;\n // @ts-ignore function without parameters is defined in subclasses\n this.renderTrajectories();\n },\n },\n /**\n * Custom options to pass as last parameter of the style function.\n */\n styleOptions: {\n value: Object.assign(Object.assign({}, realtimeConfig), (styleOptions || {})),\n },\n /**\n * Speed of the wheel of time.\n * If live property is true. The speed is ignored.\n */\n speed: {\n get: () => currSpeed,\n set: (newSpeed) => {\n currSpeed = newSpeed;\n this.start();\n },\n },\n /**\n * Function to filter which vehicles to display.\n */\n filter: {\n value: filter,\n writable: true,\n },\n /**\n * Function to sort the vehicles to display.\n */\n sort: {\n value: sort,\n writable: true,\n },\n /**\n * If true. The layer will always use Date.now() on the next tick to render the trajectories.\n * When true, setting the time property has no effect.\n */\n live: {\n value: live === false ? live : true,\n writable: true,\n },\n /**\n * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.\n * If live property is true. The setter does nothing.\n */\n time: {\n get: () => currTime,\n set: (newTime) => {\n currTime = newTime && newTime.getTime ? newTime : new Date(newTime);\n // @ts-ignore function without parameters is defined in subclasses\n this.renderTrajectories();\n },\n },\n /**\n * Keep track of which trajectories are stored.\n */\n trajectories: {\n value: {},\n writable: true,\n },\n /**\n * Id of the hovered vehicle.\n */\n hoverVehicleId: {\n value: hoverVehicleId,\n writable: true,\n },\n /**\n * Id of the selected vehicle.\n */\n selectedVehicleId: {\n value: selectedVehicleId,\n writable: true,\n },\n /**\n * Id of the selected vehicle.\n */\n pixelRatio: {\n value: pixelRatio ||\n (typeof window !== 'undefined' ? window.devicePixelRatio : 1),\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.\n */\n useRequestAnimationFrame: {\n value: options.useRequestAnimationFrame || false,\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.\n */\n useThrottle: {\n value: options.useThrottle !== false,\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a debounce function.\n */\n useDebounce: {\n value: options.useDebounce || false,\n writable: true,\n },\n /**\n * Debug properties.\n */\n // Not used anymore, but could be useful for debugging.\n // showVehicleTraj: {\n // value:\n // options.showVehicleTraj !== undefined\n // ? options.showVehicleTraj\n // : true,\n // writable: true,\n // },\n });\n }\n attachToMap(map) {\n super.attachToMap(map);\n // If the layer is visible we start the rendering clock\n if (this.visible) {\n this.start();\n }\n // On change of visibility we start/stop the rendering clock\n this.visibilityRef = this.on('change:visible', (evt) => {\n if (evt.target.visible) {\n this.start();\n }\n else {\n this.stop();\n }\n });\n // To avoid browser hanging when the tab is not visible for a certain amount of time,\n // We stop the rendering and the websocket when hide and start again when show.\n document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);\n }\n detachFromMap() {\n document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);\n this.stop();\n unByKey(this.visibilityRef);\n if (this.canvas) {\n const context = this.canvas.getContext('2d');\n if (context) {\n context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n super.detachFromMap();\n }\n }\n start() {\n this.stop();\n // Before starting to update trajectories, we remove trajectories that have\n // a time_intervals in the past, it will\n // avoid phantom train that are at the end of their route because we never\n // received the deleted_vehicle event because we have changed the browser tab.\n this.purgeOutOfDateTrajectories();\n // @ts-ignore function without parameters must be define in subclasses\n this.renderTrajectories();\n this.startUpdateTime();\n this.api.open();\n this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);\n this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);\n if (this.isUpdateBboxOnMoveEnd) {\n // Update the bbox on each move end\n // @ts-ignore function without parameters defined by subclasses\n this.setBbox();\n }\n }\n /**\n * Start the clock.\n * @private\n */\n startUpdateTime() {\n this.stopUpdateTime();\n this.updateTimeDelay = this.getRefreshTimeInMs() || 0;\n this.updateTimeInterval = window.setInterval(() => {\n // When live=true, we update the time with new Date();\n if (this.live) {\n this.time = new Date();\n }\n else if (this.time && this.updateTimeDelay && this.speed) {\n this.time = new Date(this.time.getTime() + this.updateTimeDelay * this.speed);\n }\n }, this.updateTimeDelay);\n }\n stop() {\n this.api.unsubscribeTrajectory(this.onTrajectoryMessage);\n this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);\n this.api.close();\n }\n /**\n * Stop the clock.\n * @private\n */\n stopUpdateTime() {\n if (this.updateTimeInterval) {\n clearInterval(this.updateTimeInterval);\n this.updateTimeInterval = undefined;\n }\n }\n /**\n * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.\n *\n * @param {object} viewState The view state of the map.\n * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.\n * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.\n * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.\n * @param {number} [viewState.rotation = 0] Rotation of the map to render.\n * @param {number} viewState.resolution Resolution of the map to render.\n * @param {boolean} noInterpolate If true trajectories are not interpolated but\n * drawn at the last known coordinate. Use this for performance optimization\n * during map navigation.\n * @private\n */\n renderTrajectoriesInternal(viewState, noInterpolate = false) {\n var _a;\n if (!this.map || !this.trajectories) {\n return false;\n }\n const time = this.live ? Date.now() : (_a = this.time) === null || _a === void 0 ? void 0 : _a.getTime();\n const trajectories = Object.values(this.trajectories);\n // console.time('sort');\n if (this.sort) {\n // @ts-ignore\n trajectories.sort(this.sort);\n }\n // console.timeEnd('sort');\n if (!this.canvas || !this.style) {\n return true;\n }\n // console.time('render');\n this.renderState = renderTrajectories(this.canvas, trajectories, this.style, Object.assign(Object.assign({}, viewState), { pixelRatio: this.pixelRatio || 1, time }), Object.assign({ filter: this.filter, noInterpolate: (viewState.zoom || 0) < this.minZoomInterpolation\n ? true\n : noInterpolate, hoverVehicleId: this.hoverVehicleId, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));\n // console.timeEnd('render');\n return true;\n }\n /**\n * Render the trajectories requesting an animation frame and cancelling the previous one.\n * This function must be overrided by children to provide the correct parameters.\n *\n * @param {object} viewState The view state of the map.\n * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.\n * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.\n * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.\n * @param {number} [viewState.rotation = 0] Rotation of the map to render.\n * @param {number} viewState.resolution Resolution of the map to render.\n * @param {boolean} noInterpolate If true trajectories are not interpolated but\n * drawn at the last known coordinate. Use this for performance optimization\n * during map navigation.\n * @private\n */\n renderTrajectories(viewState, noInterpolate) {\n if (this.requestId) {\n cancelAnimationFrame(this.requestId);\n this.requestId = undefined;\n }\n if (!viewState) {\n return;\n }\n if (!noInterpolate && this.useRequestAnimationFrame) {\n this.requestId = requestAnimationFrame(() => {\n this.renderTrajectoriesInternal(viewState, noInterpolate);\n });\n }\n else if (!noInterpolate && this.useDebounce) {\n this.debounceRenderTrajectories(viewState, noInterpolate);\n }\n else if (!noInterpolate && this.useThrottle) {\n this.throttleRenderTrajectories(viewState, noInterpolate);\n }\n else {\n this.renderTrajectoriesInternal(viewState, noInterpolate);\n }\n }\n setBbox(extent, zoom) {\n // Clean trajectories before sending the new bbox\n // Purge trajectories:\n // - which are outside the extent\n // - when it's bus and zoom level is too low for them\n if (this.trajectories && extent && zoom) {\n const keys = Object.keys(this.trajectories);\n for (let i = keys.length - 1; i >= 0; i -= 1) {\n this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);\n }\n }\n if (!extent) {\n return;\n }\n const bbox = [...extent];\n if (this.isUpdateBboxOnMoveEnd && zoom) {\n bbox.push(zoom);\n if (this.tenant) {\n bbox.push(`tenant=${this.tenant}`);\n }\n /* @ignore */\n this.generalizationLevel = this.getGeneralizationLevelByZoom(zoom);\n if (this.generalizationLevel) {\n bbox.push(`gen=${this.generalizationLevel}`);\n }\n /* @ignore */\n this.mots = this.getMotsByZoom(zoom);\n if (this.mots) {\n bbox.push(`mots=${this.mots}`);\n }\n }\n this.api.bbox = bbox;\n }\n /**\n * Get the duration before the next update depending on zoom level.\n *\n * @private\n * @param {number} zoom\n */\n getRefreshTimeInMs(zoom = 0) {\n var _a;\n const roundedZoom = zoom !== undefined ? Math.round(zoom) : -1;\n const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;\n const nextTick = Math.max(25, timeStep / (this.speed || 1));\n const nextThrottleTick = Math.min(nextTick, 500);\n // TODO: see if this should go elsewhere.\n if (this.useThrottle) {\n this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });\n }\n else if (this.useDebounce) {\n this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true, maxWait: 5000 });\n }\n if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {\n const [, size] = this.api.buffer;\n this.api.buffer = [nextThrottleTick, size];\n }\n return nextTick;\n }\n /**\n * Get vehicle.\n * @param {function} filterFc A function use to filter results.\n * @return {Array} Array of vehicle.\n */\n getVehicle(filterFc) {\n return ((this.trajectories &&\n // @ts-ignore\n Object.values(this.trajectories).filter(filterFc)) ||\n []);\n }\n /**\n * Request feature information for a given coordinate.\n *\n * @param {ol/coordinate~Coordinate} coordinate Coordinate.\n * @param {Object} options Options See child classes to see which options are supported.\n * @param {number} [options.resolution=1] The resolution of the map.\n * @param {number} [options.nb=Infinity] The max number of vehicles to return.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate, options) {\n const { resolution, nb } = options;\n const ext = buffer([...coordinate, ...coordinate], this.hitTolerance * resolution);\n let trajectories = Object.values(this.trajectories || {});\n if (this.sort) {\n // @ts-ignore\n trajectories = trajectories.sort(this.sort);\n }\n const vehicles = [];\n for (let i = 0; i < trajectories.length; i += 1) {\n if (trajectories[i].properties.coordinate &&\n containsCoordinate(ext, trajectories[i].properties.coordinate)) {\n vehicles.push(trajectories[i]);\n }\n if (vehicles.length === nb) {\n break;\n }\n }\n return Promise.resolve({\n layer: this,\n features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),\n coordinate,\n });\n }\n /**\n * Request the stopSequence and the fullTrajectory informations for a vehicle.\n *\n * @param {string} id The vehicle identifier (the train_id property).\n * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.\n */\n getTrajectoryInfos(id) {\n // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.\n // Then we combine them in one response and send them to inherited layers.\n const promises = [\n this.api.getStopSequence(id),\n this.api.getFullTrajectory(id, this.mode, this.generalizationLevel),\n ];\n return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {\n const response = {\n stopSequence,\n fullTrajectory,\n };\n return response;\n });\n }\n /**\n * Remove all trajectories that are in the past.\n */\n purgeOutOfDateTrajectories() {\n Object.entries(this.trajectories || {}).forEach(([key, trajectory]) => {\n var _a;\n const timeIntervals = (_a = trajectory === null || trajectory === void 0 ? void 0 : trajectory.properties) === null || _a === void 0 ? void 0 : _a.time_intervals;\n if (this.time && timeIntervals.length) {\n const lastTimeInterval = timeIntervals[timeIntervals.length - 1][0];\n if (lastTimeInterval < this.time) {\n this.removeTrajectory(key);\n }\n }\n });\n }\n /**\n * Determine if the trajectory is useless and should be removed from the list or not.\n * By default, this function exclude vehicles:\n * - that have their trajectory outside the current extent and\n * - that aren't in the MOT list.\n *\n * @param {RealtimeTrajectory} trajectory\n * @param {Array} extent\n * @param {number} zoom\n * @return {boolean} if the trajectory must be displayed or not.\n * @ignore\n */\n purgeTrajectory(trajectory, extent, zoom) {\n const { type, bounds } = trajectory.properties;\n if (!intersects(extent, bounds) ||\n (this.mots && !this.mots.includes(type))) {\n this.removeTrajectory(trajectory);\n return true;\n }\n return false;\n }\n /**\n * Add a trajectory.\n * @param {RealtimeTrajectory} trajectory The trajectory to add.\n * @private\n */\n addTrajectory(trajectory) {\n if (this.filter && !this.filter(trajectory)) {\n return;\n }\n if (!this.trajectories) {\n this.trajectories = {};\n }\n this.trajectories[trajectory.properties.train_id] = trajectory;\n // @ts-ignore the parameter are set by subclasses\n this.renderTrajectories();\n }\n removeTrajectory(trajectoryOrId) {\n var _a;\n let id;\n if (typeof trajectoryOrId !== 'string') {\n id = (_a = trajectoryOrId === null || trajectoryOrId === void 0 ? void 0 : trajectoryOrId.properties) === null || _a === void 0 ? void 0 : _a.train_id;\n }\n else {\n id = trajectoryOrId;\n }\n if (this.trajectories) {\n delete this.trajectories[id];\n }\n }\n /**\n * On zoomend we adjust the time interval of the update of vehicles positions.\n *\n * @param evt Event that triggered the function.\n * @private\n */\n onZoomEnd() {\n this.startUpdateTime();\n }\n onDocumentVisibilityChange() {\n if (!this.visible) {\n return;\n }\n if (document.hidden) {\n this.stop();\n // Since we don't receive deleted_vehicles event when docuement\n // is hidden. We have to clean all the trajectories for a fresh\n // start when the document is visible again.\n this.trajectories = {};\n }\n else {\n this.start();\n }\n }\n /**\n * Callback on websocket's trajectory channel events.\n * It adds a trajectory to the list.\n *\n * @private\n */\n onTrajectoryMessage(data) {\n if (!data.content) {\n return;\n }\n const trajectory = data.content;\n const { geometry, properties: { train_id: id, time_since_update: timeSinceUpdate, raw_coordinates: rawCoordinates, }, } = trajectory;\n // ignore old events [SBAHNM-97]\n if (timeSinceUpdate < 0) {\n return;\n }\n // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);\n // @ts-ignore default value for extentand zoom are provided by subclasses\n if (this.purgeTrajectory(trajectory)) {\n return;\n }\n if (this.debug &&\n this.mode === RealtimeModes.TOPOGRAPHIC &&\n rawCoordinates) {\n trajectory.properties.olGeometry = {\n type: 'Point',\n coordinates: fromLonLat(rawCoordinates, this.map.getView().getProjection()),\n };\n }\n else {\n trajectory.properties.olGeometry = this.format.readGeometry(geometry);\n }\n // TODO Make sure the timeOffset is useful. May be we can remove it.\n trajectory.properties.timeOffset = Date.now() - data.timestamp;\n this.addTrajectory(trajectory);\n }\n /**\n * Callback on websocket's deleted_vehicles channel events.\n * It removes the trajectory from the list.\n *\n * @private\n * @override\n */\n onDeleteTrajectoryMessage(data) {\n if (!data.content) {\n return;\n }\n this.removeTrajectory(data.content);\n }\n /**\n * Callback when user moves the mouse/pointer over the map.\n * It sets the layer's hoverVehicleId property with the current hovered vehicle's id.\n *\n * @private\n * @override\n */\n onFeatureHover(features, layer, coordinate) {\n const [feature] = features;\n let id = null;\n if (feature) {\n id = feature.get('train_id');\n }\n if (this.hoverVehicleId !== id) {\n /** @ignore */\n this.hoverVehicleId = id;\n // @ts-ignore\n this.renderTrajectories(true);\n }\n }\n /**\n * Callback when user clicks on the map.\n * It sets the layer's selectedVehicleId property with the current selected vehicle's id.\n *\n * @private\n * @override\n */\n onFeatureClick(features, layer, coordinate) {\n const [feature] = features;\n let id = null;\n if (feature) {\n id = feature.get('train_id');\n }\n if (this.selectedVehicleId !== id) {\n /** @ignore */\n this.selectedVehicleId = id;\n this.selectedVehicle = feature;\n // @ts-ignore parameters are provided by subclasses\n this.renderTrajectories(true);\n }\n }\n };\n}\nexport default RealtimeLayerMixin;\n", + "content": "/* eslint-disable no-empty-function,@typescript-eslint/no-empty-function */\n/* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */\n/* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */\n/* eslint-disable class-methods-use-this */\n/* eslint-disable max-classes-per-file */\nimport { buffer, containsCoordinate, intersects } from 'ol/extent';\nimport { unByKey } from 'ol/Observable';\nimport GeoJSON from 'ol/format/GeoJSON';\nimport debounce from 'lodash.debounce';\nimport throttle from 'lodash.throttle';\nimport { fromLonLat } from 'ol/proj';\nimport realtimeDefaultStyle from '../styles/realtimeDefaultStyle';\nimport { RealtimeAPI, RealtimeModes } from '../../api';\nimport renderTrajectories from '../utils/renderTrajectories';\nimport * as realtimeConfig from '../utils/realtimeConfig';\n/**\n * RealtimeLayerInterface.\n */\nexport class RealtimeLayerInterface {\n /**\n * Start the clock.\n */\n start() { }\n /**\n * Stop the clock.\n */\n stop() { }\n /**\n * Set the Realtime api's bbox.\n *\n * @param {Array} extent Extent to request, [minX, minY, maxX, maxY, zoom].\n * @param {number} zoom Zoom level to request. Must be an integer.\n */\n setBbox(extent, zoom) { }\n /**\n * Render the trajectories\n */\n renderTrajectories() { }\n /**\n * Request the stopSequence and the fullTrajectory informations for a vehicle.\n *\n * @param {string} id The vehicle identifier (the train_id property).\n * @param {RealtimeMode} mode The mode to request. If not defined, the layer´s mode propetrty will be used.\n * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.\n */\n getTrajectoryInfos(id, mode) { }\n}\n/**\n * Mixin for RealtimeLayerInterface.\n *\n * @param {Class} Base A class to extend with {RealtimeLayerInterface} functionnalities.\n * @return {Class} A class that implements {RealtimeLayerInterface} class and extends Base;\n * @private\n */\nfunction RealtimeLayerMixin(Base) {\n // @ts-ignore\n return class Mixin extends Base {\n constructor(options) {\n super({\n hitTolerance: 10,\n ...options,\n });\n this.debug = options.debug || false;\n this.mode = options.mode || RealtimeModes.TOPOGRAPHIC;\n this.api = options.api || new RealtimeAPI(options);\n this.tenant = options.tenant || ''; // sbb,sbh or sbm\n this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.\n this.format = new GeoJSON();\n // MOTs by zoom\n const allMots = [\n 'tram',\n 'subway',\n 'rail',\n 'bus',\n 'ferry',\n 'cablecar',\n 'gondola',\n 'funicular',\n 'coach',\n ];\n const onlyRail = ['rail'];\n const withoutCable = ['tram', 'subway', 'rail', 'bus'];\n // Server will block non train before zoom 9\n this.motsByZoom = options.motsByZoom || [\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n onlyRail,\n withoutCable,\n withoutCable,\n allMots,\n allMots,\n allMots,\n allMots,\n ];\n this.getMotsByZoom = (zoom) => {\n return ((options.getMotsByZoom &&\n options.getMotsByZoom(zoom, this.motsByZoom)) ||\n this.motsByZoom[zoom] ||\n this.motsByZoom[this.motsByZoom.length - 1]);\n };\n // Generalization levels by zoom\n this.generalizationLevelByZoom = options.generalizationLevelByZoom || [\n 5, 5, 5, 5, 5, 5, 5, 5, 10, 30, 30, 100, 100, 100,\n ];\n this.getGeneralizationLevelByZoom = (zoom) => {\n return ((options.getGeneralizationLevelByZoom &&\n options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom)) ||\n this.generalizationLevelByZoom[zoom]);\n };\n // Render time interval by zoom\n this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [\n 100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,\n 300, 250, 180, 90, 60, 50, 50, 50, 50, 50,\n ];\n this.getRenderTimeIntervalByZoom = (zoom) => {\n return ((options.getRenderTimeIntervalByZoom &&\n options.getRenderTimeIntervalByZoom(zoom, this.renderTimeIntervalByZoom)) ||\n this.renderTimeIntervalByZoom[zoom]);\n };\n // This property will call api.setBbox on each movend event\n this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;\n // Define throttling and debounce render function\n this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });\n this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, trailing: true, maxWait: 5000 });\n // Bind callbacks\n this.onFeatureHover = this.onFeatureHover.bind(this);\n this.onFeatureClick = this.onFeatureClick.bind(this);\n this.renderTrajectoriesInternal =\n this.renderTrajectoriesInternal.bind(this);\n this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);\n this.onDeleteTrajectoryMessage =\n this.onDeleteTrajectoryMessage.bind(this);\n this.onDocumentVisibilityChange =\n this.onDocumentVisibilityChange.bind(this);\n }\n /**\n * Define layer's properties.\n *\n * @ignore\n */\n defineProperties(options) {\n const { style, speed, pixelRatio, hoverVehicleId, selectedVehicleId, filter, sort, time, live, canvas, styleOptions, mode, } = options;\n let currCanvas = canvas;\n let currSpeed = speed || 1;\n let currTime = time || new Date();\n let currMode = mode || RealtimeModes.TOPOGRAPHIC;\n let currStyle = style || realtimeDefaultStyle;\n super.defineProperties(options);\n Object.defineProperties(this, {\n isTrackerLayer: { value: true },\n canvas: {\n get: () => {\n if (!currCanvas) {\n currCanvas = document.createElement('canvas');\n }\n return currCanvas;\n },\n set: (cnvas) => {\n currCanvas = cnvas;\n },\n },\n /**\n * Style function used to render a vehicle.\n */\n mode: {\n get: () => currMode,\n set: (newMode) => {\n if (newMode === currMode) {\n return;\n }\n currMode = newMode;\n if (this.api?.wsApi?.open) {\n this.stop();\n this.start();\n }\n },\n },\n /**\n * Style function used to render a vehicle.\n */\n style: {\n get: () => currStyle,\n set: (newStyle) => {\n currStyle = newStyle;\n // @ts-ignore function without parameters is defined in subclasses\n this.renderTrajectories();\n },\n },\n /**\n * Custom options to pass as last parameter of the style function.\n */\n styleOptions: {\n value: { ...realtimeConfig, ...(styleOptions || {}) },\n },\n /**\n * Speed of the wheel of time.\n * If live property is true. The speed is ignored.\n */\n speed: {\n get: () => currSpeed,\n set: (newSpeed) => {\n currSpeed = newSpeed;\n this.start();\n },\n },\n /**\n * Function to filter which vehicles to display.\n */\n filter: {\n value: filter,\n writable: true,\n },\n /**\n * Function to sort the vehicles to display.\n */\n sort: {\n value: sort,\n writable: true,\n },\n /**\n * If true. The layer will always use Date.now() on the next tick to render the trajectories.\n * When true, setting the time property has no effect.\n */\n live: {\n value: live === false ? live : true,\n writable: true,\n },\n /**\n * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.\n * If live property is true. The setter does nothing.\n */\n time: {\n get: () => currTime,\n set: (newTime) => {\n currTime = newTime && newTime.getTime ? newTime : new Date(newTime);\n // @ts-ignore function without parameters is defined in subclasses\n this.renderTrajectories();\n },\n },\n /**\n * Keep track of which trajectories are stored.\n */\n trajectories: {\n value: {},\n writable: true,\n },\n /**\n * Id of the hovered vehicle.\n */\n hoverVehicleId: {\n value: hoverVehicleId,\n writable: true,\n },\n /**\n * Id of the selected vehicle.\n */\n selectedVehicleId: {\n value: selectedVehicleId,\n writable: true,\n },\n /**\n * Id of the selected vehicle.\n */\n pixelRatio: {\n value: pixelRatio ||\n (typeof window !== 'undefined' ? window.devicePixelRatio : 1),\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.\n */\n useRequestAnimationFrame: {\n value: options.useRequestAnimationFrame || false,\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.\n */\n useThrottle: {\n value: options.useThrottle !== false,\n writable: true,\n },\n /**\n * If true, encapsulates the renderTrajectories calls in a debounce function.\n */\n useDebounce: {\n value: options.useDebounce || false,\n writable: true,\n },\n /**\n * Debug properties.\n */\n // Not used anymore, but could be useful for debugging.\n // showVehicleTraj: {\n // value:\n // options.showVehicleTraj !== undefined\n // ? options.showVehicleTraj\n // : true,\n // writable: true,\n // },\n });\n }\n attachToMap(map) {\n super.attachToMap(map);\n // If the layer is visible we start the rendering clock\n if (this.visible) {\n this.start();\n }\n // On change of visibility we start/stop the rendering clock\n this.visibilityRef = this.on('change:visible', (evt) => {\n if (evt.target.visible) {\n this.start();\n }\n else {\n this.stop();\n }\n });\n // To avoid browser hanging when the tab is not visible for a certain amount of time,\n // We stop the rendering and the websocket when hide and start again when show.\n document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);\n }\n detachFromMap() {\n document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);\n this.stop();\n unByKey(this.visibilityRef);\n if (this.canvas) {\n const context = this.canvas.getContext('2d');\n if (context) {\n context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n super.detachFromMap();\n }\n }\n start() {\n this.stop();\n // Before starting to update trajectories, we remove trajectories that have\n // a time_intervals in the past, it will\n // avoid phantom train that are at the end of their route because we never\n // received the deleted_vehicle event because we have changed the browser tab.\n this.purgeOutOfDateTrajectories();\n // @ts-ignore function without parameters must be define in subclasses\n this.renderTrajectories();\n this.startUpdateTime();\n this.api.open();\n this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);\n this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);\n if (this.isUpdateBboxOnMoveEnd) {\n // Update the bbox on each move end\n // @ts-ignore function without parameters defined by subclasses\n this.setBbox();\n }\n }\n /**\n * Start the clock.\n * @private\n */\n startUpdateTime() {\n this.stopUpdateTime();\n this.updateTimeDelay = this.getRefreshTimeInMs() || 0;\n this.updateTimeInterval = window.setInterval(() => {\n // When live=true, we update the time with new Date();\n if (this.live) {\n this.time = new Date();\n }\n else if (this.time && this.updateTimeDelay && this.speed) {\n this.time = new Date(this.time.getTime() + this.updateTimeDelay * this.speed);\n }\n }, this.updateTimeDelay);\n }\n stop() {\n this.api.unsubscribeTrajectory(this.onTrajectoryMessage);\n this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);\n this.api.close();\n }\n /**\n * Stop the clock.\n * @private\n */\n stopUpdateTime() {\n if (this.updateTimeInterval) {\n clearInterval(this.updateTimeInterval);\n this.updateTimeInterval = undefined;\n }\n }\n /**\n * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.\n *\n * @param {object} viewState The view state of the map.\n * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.\n * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.\n * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.\n * @param {number} [viewState.rotation = 0] Rotation of the map to render.\n * @param {number} viewState.resolution Resolution of the map to render.\n * @param {boolean} noInterpolate If true trajectories are not interpolated but\n * drawn at the last known coordinate. Use this for performance optimization\n * during map navigation.\n * @private\n */\n renderTrajectoriesInternal(viewState, noInterpolate = false) {\n if (!this.map || !this.trajectories) {\n return false;\n }\n const time = this.live ? Date.now() : this.time?.getTime();\n const trajectories = Object.values(this.trajectories);\n // console.time('sort');\n if (this.sort) {\n // @ts-ignore\n trajectories.sort(this.sort);\n }\n // console.timeEnd('sort');\n if (!this.canvas || !this.style) {\n return true;\n }\n // console.time('render');\n this.renderState = renderTrajectories(this.canvas, trajectories, this.style, {\n ...viewState,\n pixelRatio: this.pixelRatio || 1,\n time,\n }, {\n filter: this.filter,\n noInterpolate: (viewState.zoom || 0) < this.minZoomInterpolation\n ? true\n : noInterpolate,\n hoverVehicleId: this.hoverVehicleId,\n selectedVehicleId: this.selectedVehicleId,\n ...this.styleOptions,\n });\n // console.timeEnd('render');\n return true;\n }\n /**\n * Render the trajectories requesting an animation frame and cancelling the previous one.\n * This function must be overrided by children to provide the correct parameters.\n *\n * @param {object} viewState The view state of the map.\n * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.\n * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.\n * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.\n * @param {number} [viewState.rotation = 0] Rotation of the map to render.\n * @param {number} viewState.resolution Resolution of the map to render.\n * @param {boolean} noInterpolate If true trajectories are not interpolated but\n * drawn at the last known coordinate. Use this for performance optimization\n * during map navigation.\n * @private\n */\n renderTrajectories(viewState, noInterpolate) {\n if (this.requestId) {\n cancelAnimationFrame(this.requestId);\n this.requestId = undefined;\n }\n if (!viewState) {\n return;\n }\n if (!noInterpolate && this.useRequestAnimationFrame) {\n this.requestId = requestAnimationFrame(() => {\n this.renderTrajectoriesInternal(viewState, noInterpolate);\n });\n }\n else if (!noInterpolate && this.useDebounce) {\n this.debounceRenderTrajectories(viewState, noInterpolate);\n }\n else if (!noInterpolate && this.useThrottle) {\n this.throttleRenderTrajectories(viewState, noInterpolate);\n }\n else {\n this.renderTrajectoriesInternal(viewState, noInterpolate);\n }\n }\n setBbox(extent, zoom) {\n // Clean trajectories before sending the new bbox\n // Purge trajectories:\n // - which are outside the extent\n // - when it's bus and zoom level is too low for them\n if (this.trajectories && extent && zoom) {\n const keys = Object.keys(this.trajectories);\n for (let i = keys.length - 1; i >= 0; i -= 1) {\n this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);\n }\n }\n if (!extent) {\n return;\n }\n const bbox = [...extent];\n if (this.isUpdateBboxOnMoveEnd && zoom) {\n bbox.push(zoom);\n if (this.tenant) {\n bbox.push(`tenant=${this.tenant}`);\n }\n /* @ignore */\n this.generalizationLevel = this.getGeneralizationLevelByZoom(zoom);\n if (this.generalizationLevel) {\n bbox.push(`gen=${this.generalizationLevel}`);\n }\n /* @ignore */\n this.mots = this.getMotsByZoom(zoom);\n if (this.mots) {\n bbox.push(`mots=${this.mots}`);\n }\n }\n this.api.bbox = bbox;\n }\n /**\n * Get the duration before the next update depending on zoom level.\n *\n * @private\n * @param {number} zoom\n */\n getRefreshTimeInMs(zoom = 0) {\n const roundedZoom = zoom !== undefined ? Math.round(zoom) : -1;\n const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;\n const nextTick = Math.max(25, timeStep / (this.speed || 1));\n const nextThrottleTick = Math.min(nextTick, 500);\n // TODO: see if this should go elsewhere.\n if (this.useThrottle) {\n this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });\n }\n else if (this.useDebounce) {\n this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true, maxWait: 5000 });\n }\n if (this.api?.buffer) {\n const [, size] = this.api.buffer;\n this.api.buffer = [nextThrottleTick, size];\n }\n return nextTick;\n }\n /**\n * Get vehicle.\n * @param {function} filterFc A function use to filter results.\n * @return {Array} Array of vehicle.\n */\n getVehicle(filterFc) {\n return ((this.trajectories &&\n // @ts-ignore\n Object.values(this.trajectories).filter(filterFc)) ||\n []);\n }\n /**\n * Request feature information for a given coordinate.\n *\n * @param {ol/coordinate~Coordinate} coordinate Coordinate.\n * @param {Object} options Options See child classes to see which options are supported.\n * @param {number} [options.resolution=1] The resolution of the map.\n * @param {number} [options.nb=Infinity] The max number of vehicles to return.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate, options) {\n const { resolution, nb } = options;\n const ext = buffer([...coordinate, ...coordinate], this.hitTolerance * resolution);\n let trajectories = Object.values(this.trajectories || {});\n if (this.sort) {\n // @ts-ignore\n trajectories = trajectories.sort(this.sort);\n }\n const vehicles = [];\n for (let i = 0; i < trajectories.length; i += 1) {\n if (trajectories[i].properties.coordinate &&\n containsCoordinate(ext, trajectories[i].properties.coordinate)) {\n vehicles.push(trajectories[i]);\n }\n if (vehicles.length === nb) {\n break;\n }\n }\n return Promise.resolve({\n layer: this,\n features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),\n coordinate,\n });\n }\n /**\n * Request the stopSequence and the fullTrajectory informations for a vehicle.\n *\n * @param {string} id The vehicle identifier (the train_id property).\n * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.\n */\n getTrajectoryInfos(id) {\n // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.\n // Then we combine them in one response and send them to inherited layers.\n const promises = [\n this.api.getStopSequence(id),\n this.api.getFullTrajectory(id, this.mode, this.generalizationLevel),\n ];\n return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {\n const response = {\n stopSequence,\n fullTrajectory,\n };\n return response;\n });\n }\n /**\n * Remove all trajectories that are in the past.\n */\n purgeOutOfDateTrajectories() {\n Object.entries(this.trajectories || {}).forEach(([key, trajectory]) => {\n const timeIntervals = trajectory?.properties?.time_intervals;\n if (this.time && timeIntervals.length) {\n const lastTimeInterval = timeIntervals[timeIntervals.length - 1][0];\n if (lastTimeInterval < this.time) {\n this.removeTrajectory(key);\n }\n }\n });\n }\n /**\n * Determine if the trajectory is useless and should be removed from the list or not.\n * By default, this function exclude vehicles:\n * - that have their trajectory outside the current extent and\n * - that aren't in the MOT list.\n *\n * @param {RealtimeTrajectory} trajectory\n * @param {Array} extent\n * @param {number} zoom\n * @return {boolean} if the trajectory must be displayed or not.\n * @ignore\n */\n purgeTrajectory(trajectory, extent, zoom) {\n const { type, bounds } = trajectory.properties;\n if (!intersects(extent, bounds) ||\n (this.mots && !this.mots.includes(type))) {\n this.removeTrajectory(trajectory);\n return true;\n }\n return false;\n }\n /**\n * Add a trajectory.\n * @param {RealtimeTrajectory} trajectory The trajectory to add.\n * @private\n */\n addTrajectory(trajectory) {\n if (this.filter && !this.filter(trajectory)) {\n return;\n }\n if (!this.trajectories) {\n this.trajectories = {};\n }\n this.trajectories[trajectory.properties.train_id] = trajectory;\n this.worker?.postMessage({\n action: 'addTrajectory',\n trajectory,\n });\n // @ts-ignore the parameter are set by subclasses\n this.renderTrajectories();\n }\n removeTrajectory(trajectoryOrId) {\n let id;\n if (typeof trajectoryOrId !== 'string') {\n id = trajectoryOrId?.properties?.train_id;\n }\n else {\n id = trajectoryOrId;\n }\n if (this.trajectories) {\n delete this.trajectories[id];\n }\n this.worker?.postMessage({\n action: 'removeTrajectory',\n trajectoryId: id,\n });\n }\n /**\n * On zoomend we adjust the time interval of the update of vehicles positions.\n *\n * @param evt Event that triggered the function.\n * @private\n */\n onZoomEnd() {\n this.startUpdateTime();\n }\n onDocumentVisibilityChange() {\n if (!this.visible) {\n return;\n }\n if (document.hidden) {\n this.stop();\n // Since we don't receive deleted_vehicles event when docuement\n // is hidden. We have to clean all the trajectories for a fresh\n // start when the document is visible again.\n this.trajectories = {};\n }\n else {\n this.start();\n }\n }\n /**\n * Callback on websocket's trajectory channel events.\n * It adds a trajectory to the list.\n *\n * @private\n */\n onTrajectoryMessage(data) {\n if (!data.content) {\n return;\n }\n const trajectory = data.content;\n const { geometry, properties: { train_id: id, time_since_update: timeSinceUpdate, raw_coordinates: rawCoordinates, }, } = trajectory;\n // ignore old events [SBAHNM-97]\n if (timeSinceUpdate < 0) {\n return;\n }\n // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);\n // @ts-ignore default value for extentand zoom are provided by subclasses\n if (this.purgeTrajectory(trajectory)) {\n return;\n }\n if (this.debug &&\n this.mode === RealtimeModes.TOPOGRAPHIC &&\n rawCoordinates) {\n trajectory.properties.olGeometry = {\n type: 'Point',\n coordinates: fromLonLat(rawCoordinates, this.map.getView().getProjection()),\n };\n }\n else if (!this.worker) {\n trajectory.properties.olGeometry = this.format.readGeometry(geometry);\n }\n // TODO Make sure the timeOffset is useful. May be we can remove it.\n trajectory.properties.timeOffset = Date.now() - data.timestamp;\n this.addTrajectory(trajectory);\n }\n /**\n * Callback on websocket's deleted_vehicles channel events.\n * It removes the trajectory from the list.\n *\n * @private\n * @override\n */\n onDeleteTrajectoryMessage(data) {\n if (!data.content) {\n return;\n }\n this.removeTrajectory(data.content);\n }\n /**\n * Callback when user moves the mouse/pointer over the map.\n * It sets the layer's hoverVehicleId property with the current hovered vehicle's id.\n *\n * @private\n * @override\n */\n onFeatureHover(features, layer, coordinate) {\n const [feature] = features;\n let id = null;\n if (feature) {\n id = feature.get('train_id');\n }\n if (this.hoverVehicleId !== id) {\n /** @ignore */\n this.hoverVehicleId = id;\n // @ts-ignore\n this.renderTrajectories(true);\n }\n }\n /**\n * Callback when user clicks on the map.\n * It sets the layer's selectedVehicleId property with the current selected vehicle's id.\n *\n * @private\n * @override\n */\n onFeatureClick(features, layer, coordinate) {\n const [feature] = features;\n let id = null;\n if (feature) {\n id = feature.get('train_id');\n }\n if (this.selectedVehicleId !== id) {\n /** @ignore */\n this.selectedVehicleId = id;\n this.selectedVehicle = feature;\n // @ts-ignore parameters are provided by subclasses\n this.renderTrajectories(true);\n }\n }\n };\n}\nexport default RealtimeLayerMixin;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/mixins/RealtimeLayerMixin.js", "access": "public", @@ -5073,7 +5132,7 @@ "lineNumber": 1 }, { - "__docId__": 223, + "__docId__": 226, "kind": "class", "name": "RealtimeLayerInterface", "memberof": "build/common/mixins/RealtimeLayerMixin.js", @@ -5088,7 +5147,7 @@ "interface": false }, { - "__docId__": 224, + "__docId__": 227, "kind": "method", "name": "start", "memberof": "build/common/mixins/RealtimeLayerMixin.js~RealtimeLayerInterface", @@ -5103,7 +5162,7 @@ "return": null }, { - "__docId__": 225, + "__docId__": 228, "kind": "method", "name": "stop", "memberof": "build/common/mixins/RealtimeLayerMixin.js~RealtimeLayerInterface", @@ -5118,7 +5177,7 @@ "return": null }, { - "__docId__": 226, + "__docId__": 229, "kind": "method", "name": "setBbox", "memberof": "build/common/mixins/RealtimeLayerMixin.js~RealtimeLayerInterface", @@ -5154,7 +5213,7 @@ "return": null }, { - "__docId__": 227, + "__docId__": 230, "kind": "method", "name": "renderTrajectories", "memberof": "build/common/mixins/RealtimeLayerMixin.js~RealtimeLayerInterface", @@ -5169,7 +5228,7 @@ "return": null }, { - "__docId__": 228, + "__docId__": 231, "kind": "method", "name": "getTrajectoryInfos", "memberof": "build/common/mixins/RealtimeLayerMixin.js~RealtimeLayerInterface", @@ -5212,7 +5271,7 @@ } }, { - "__docId__": 229, + "__docId__": 232, "kind": "function", "name": "RealtimeLayerMixin", "memberof": "build/common/mixins/RealtimeLayerMixin.js", @@ -5248,7 +5307,7 @@ } }, { - "__docId__": 230, + "__docId__": 233, "kind": "file", "name": "build/common/mixins/UserInteractionsLayerMixin.js", "content": "/* eslint-disable no-empty-function,@typescript-eslint/no-empty-function */\n/* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */\n/* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */\n/* eslint-disable class-methods-use-this */\n/* eslint-disable max-classes-per-file */\nimport { fromLonLat } from 'ol/proj';\nimport { unByKey } from 'ol/Observable';\nimport BaseEvent from 'ol/events/Event';\n/**\n * UserInteractionsLayerInterface.\n */\nexport class UserInteractionsLayerInterface {\n /*\n * Constructor\n \n * @param {Object} options Layer options.\n * @param {string} options.userInteractions If true, it listens for user mouse hover and click event.\n * @param {string} options.userClickInteractions If true, it listens for user click event.\n * @param {string} options.userHoverInteractions If true, it listens for user mouse over event.\n * @param {string} options.defaultUserInteractions If true, it adds default listeners for user mouse hover and click event.\n */\n constructor(options = {}) { }\n /**\n * Initialize the layer adding user interactions.\n *\n * @param {ol/Map~Map} map\n */\n attachToMap(map) { }\n /**\n * Terminate the layer unsubscribing user interactions.\n */\n detachFromMap() { }\n /**\n * Activate map listeners events.\n */\n activateUserInteractions() { }\n /**\n * Deactivate map listeners events.\n */\n deactivateUserInteractions() { }\n /**\n * Subscribe on user:click event.\n */\n onClick(callback) { }\n /**\n * Subscribe on user:hover event.\n */\n onHover(callback) { }\n /**\n * Unsubscribe on user:click event.\n */\n unClick(callback) { }\n /**\n * Unsubscribe on user:hover event.\n */\n unHover(callback) { }\n}\n/**\n * Mixin for UserInteractionsLayerInterface. It provide onClick and onHover functions.\n *\n * @param {Class} Base A class to extend with {UserInteractionsLayerInterface} functionnalities.\n * @return {Class} A class that implements {UserInteractionsLayerInterface} class and extends Base;\n * @private\n */\nfunction UserInteractionsLayerMixin(Base) {\n // @ts-ignore\n return class extends Base {\n constructor(options = {}) {\n super(options);\n const { userInteractions = true, userClickInteractions = true, userHoverInteractions = true, defaultUserInteractions = true, } = options;\n this.userInteractions = userInteractions;\n this.userClickInteractions = userClickInteractions;\n this.userHoverInteractions = userHoverInteractions;\n this.defaultUserInteractions = defaultUserInteractions;\n this.userClickCallbacks = [];\n this.userHoverCallbacks = [];\n this.userClickEventsKeys = [];\n this.userHoverEventsKeys = [];\n this.onUserClickCallback = this.onUserClickCallback.bind(this);\n this.onUserMoveCallback = this.onUserMoveCallback.bind(this);\n // Add mouse event callbacks\n const { onClick, onHover } = options;\n if (this.userInteractions && this.userClickInteractions && onClick) {\n this.onClick(onClick);\n }\n if (this.userInteractions && this.userHoverInteractions && onHover) {\n this.onHover(onHover);\n }\n }\n attachToMap(map) {\n super.attachToMap(map);\n if (this.userInteractions &&\n this.defaultUserInteractions &&\n this.userClickInteractions &&\n this.onFeatureClick) {\n this.onClick(this.onFeatureClick);\n }\n if (this.userInteractions &&\n this.defaultUserInteractions &&\n this.userHoverInteractions &&\n this.onFeatureHover) {\n this.onHover(this.onFeatureHover);\n }\n this.listenEvents();\n }\n detachFromMap() {\n this.unlistenEvents();\n super.detachFromMap();\n }\n listenEvents() {\n this.unlistenEvents();\n this.userClickCallbacks.forEach((callback) => {\n this.userClickEventsKeys.push(this.on(\n // @ts-ignore\n 'user:click', ({ target: { features, layer, coordinate, event }, }) => {\n callback(features, layer, coordinate, event);\n }));\n });\n this.userHoverCallbacks.forEach((callback) => {\n this.userHoverEventsKeys.push(this.on(\n // @ts-ignore\n 'user:hover', ({ target: { features, layer, coordinate, event }, }) => {\n callback(features, layer, coordinate, event);\n }));\n });\n }\n unlistenEvents() {\n unByKey(this.userClickEventsKeys);\n unByKey(this.userHoverEventsKeys);\n this.userClickEventsKeys = [];\n this.userHoverEventsKeys = [];\n }\n /**\n * Listens to click events on the layer.\n * @param {function} callback Callback function, called with the clicked\n * features,\n * the layer instance and the click event.\n */\n onClick(callback) {\n this.userClickCallbacks.push(callback);\n this.activateUserInteractions();\n if (this.map) {\n // If the layer is already attached to the map we reload the events\n this.listenEvents();\n }\n }\n /**\n * Listens to hover events on the layer.\n * @param {function} callback Callback function, called with the clicked\n * features, the layer instance and the click event.\n */\n onHover(callback) {\n this.userHoverCallbacks.push(callback);\n this.activateUserInteractions();\n if (this.map) {\n // If the layer is already attached to the map we reload the events\n this.listenEvents();\n }\n }\n /**\n * Unlistens to click events on the layer.\n * @param {function} callback Callback function, called with the clicked\n * features,\n * the layer instance and the click event.\n */\n unClick(callback) {\n const index = this.userClickCallbacks.indexOf(callback);\n if (index !== -1) {\n return;\n }\n this.userClickCallbacks = this.userClickCallbacks.slice(index, 1);\n if (this.map) {\n // If the layer is already attached to the map we reload the events\n this.listenEvents();\n }\n }\n /**\n * Unlistens to hover events on the layer.\n * @param {function} callback Callback function, called with the clicked\n * features, the layer instance and the click event.\n */\n unHover(callback) {\n const index = this.userHoverCallbacks.indexOf(callback);\n if (index !== -1) {\n return;\n }\n this.userHoverCallbacks = this.userHoverCallbacks.slice(index, 1);\n if (this.map) {\n // If the layer is already attached to the map we reload the events\n this.listenEvents();\n }\n }\n /**\n * Function triggered when the user click the map.\n * @private\n */\n onUserClickCallback(evt) {\n const coordinate = evt.coordinate ||\n fromLonLat(evt.lngLat.toArray());\n const emptyFeatureInfo = {\n features: [],\n layer: this,\n coordinate,\n event: evt,\n };\n return this.getFeatureInfoAtCoordinate(coordinate)\n .then((featureInfo) => {\n const event = new BaseEvent('user:click');\n event.target = featureInfo;\n this.dispatchEvent(event);\n return featureInfo;\n })\n .catch(() => emptyFeatureInfo);\n }\n /**\n * Function triggered when the user move the cursor.\n * @private\n */\n onUserMoveCallback(evt) {\n const coordinate = evt.coordinate ||\n fromLonLat(evt.lngLat.toArray());\n const emptyFeatureInfo = {\n features: [],\n layer: this,\n coordinate,\n event: evt,\n };\n return this.getFeatureInfoAtCoordinate(coordinate)\n .then((featureInfo) => {\n const event = new BaseEvent('user:hover');\n event.target = featureInfo;\n this.dispatchEvent(event);\n return featureInfo;\n })\n .catch(() => emptyFeatureInfo);\n }\n activateUserInteractions() { }\n deactivateUserInteractions() { }\n };\n}\nexport default UserInteractionsLayerMixin;\n", @@ -5259,7 +5318,7 @@ "lineNumber": 1 }, { - "__docId__": 231, + "__docId__": 234, "kind": "class", "name": "UserInteractionsLayerInterface", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js", @@ -5274,7 +5333,7 @@ "interface": false }, { - "__docId__": 232, + "__docId__": 235, "kind": "constructor", "name": "constructor", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5288,7 +5347,7 @@ "undocument": true }, { - "__docId__": 233, + "__docId__": 236, "kind": "method", "name": "attachToMap", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5314,7 +5373,7 @@ "return": null }, { - "__docId__": 234, + "__docId__": 237, "kind": "method", "name": "detachFromMap", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5329,7 +5388,7 @@ "return": null }, { - "__docId__": 235, + "__docId__": 238, "kind": "method", "name": "activateUserInteractions", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5344,7 +5403,7 @@ "return": null }, { - "__docId__": 236, + "__docId__": 239, "kind": "method", "name": "deactivateUserInteractions", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5359,7 +5418,7 @@ "return": null }, { - "__docId__": 237, + "__docId__": 240, "kind": "method", "name": "onClick", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5381,7 +5440,7 @@ "return": null }, { - "__docId__": 238, + "__docId__": 241, "kind": "method", "name": "onHover", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5403,7 +5462,7 @@ "return": null }, { - "__docId__": 239, + "__docId__": 242, "kind": "method", "name": "unClick", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5425,7 +5484,7 @@ "return": null }, { - "__docId__": 240, + "__docId__": 243, "kind": "method", "name": "unHover", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js~UserInteractionsLayerInterface", @@ -5447,7 +5506,7 @@ "return": null }, { - "__docId__": 241, + "__docId__": 244, "kind": "function", "name": "UserInteractionsLayerMixin", "memberof": "build/common/mixins/UserInteractionsLayerMixin.js", @@ -5483,7 +5542,7 @@ } }, { - "__docId__": 242, + "__docId__": 245, "kind": "file", "name": "build/common/styles/index.js", "content": "export { default as realtimeDefaultStyle } from './realtimeDefaultStyle';\nexport { default as realtimeDelayStyle } from './realtimeDelayStyle';\nexport { default as realtimeSimpleStyle } from './realtimeSimpleStyle';\nexport * from './realtimeDefaultStyle';\n", @@ -5494,10 +5553,10 @@ "lineNumber": 1 }, { - "__docId__": 243, + "__docId__": 246, "kind": "file", "name": "build/common/styles/realtimeDefaultStyle.js", - "content": "import createCanvas from '../utils/createCanvas';\n/** @private */\nconst cacheDelayBg = {};\n/**\n * Draw circle delay background\n *\n * @private\n */\nexport const getDelayBgCanvas = (origin, radius, color) => {\n const key = `${origin}, ${radius}, ${color}`;\n if (!cacheDelayBg[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.beginPath();\n ctx.arc(origin, origin, radius, 0, 2 * Math.PI, false);\n ctx.fillStyle = color;\n ctx.filter = 'blur(1px)';\n ctx.fill();\n cacheDelayBg[key] = canvas;\n }\n }\n return cacheDelayBg[key];\n};\n/** @private */\nconst cacheDelayText = {};\n/**\n * Draw delay text\n *\n * @private\n */\nexport const getDelayTextCanvas = (text, fontSize, font, delayColor, delayOutlineColor = '#000', pixelRatio = 1) => {\n const key = `${text}, ${font}, ${delayColor}, ${delayOutlineColor}, ${pixelRatio}`;\n if (!cacheDelayText[key]) {\n const canvas = createCanvas(Math.ceil(text.length * fontSize), Math.ceil(fontSize + 8 * pixelRatio));\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.font = font;\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n ctx.font = font;\n ctx.fillStyle = delayColor;\n ctx.strokeStyle = delayOutlineColor;\n ctx.lineWidth = 1.5 * pixelRatio;\n ctx.strokeText(text, 0, fontSize);\n ctx.fillText(text, 0, fontSize);\n cacheDelayText[key] = canvas;\n }\n }\n return cacheDelayText[key];\n};\n/** @private */\nconst cacheCircle = {};\n/**\n * Draw colored circle with black border\n *\n * @private\n */\nexport const getCircleCanvas = (origin, radius, color, hasStroke, hasDash, pixelRatio) => {\n const key = `${origin}, ${radius}, ${color}, ${hasStroke}, ${hasDash}, ${pixelRatio}`;\n if (!cacheCircle[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.fillStyle = color;\n if (hasStroke) {\n ctx.lineWidth = 1 * pixelRatio;\n ctx.strokeStyle = '#000000';\n }\n ctx.beginPath();\n ctx.arc(origin, origin, radius, 0, 2 * Math.PI, false);\n ctx.fill();\n if (hasDash) {\n ctx.setLineDash([5, 3]);\n }\n if (hasStroke) {\n ctx.stroke();\n }\n cacheCircle[key] = canvas;\n }\n }\n return cacheCircle[key];\n};\n/** @private */\nconst cacheText = {};\n/**\n * Draw text in the circle\n *\n * @private\n */\nexport const getTextCanvas = (text, origin, textSize, fillColor, strokeColor, hasStroke, pixelRatio) => {\n const key = `${text}, ${origin}, ${textSize}, ${fillColor},${strokeColor}, ${hasStroke}, ${pixelRatio}`;\n if (!cacheText[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n // Draw a stroke to the text only if a provider provides realtime but we don't use it.\n if (hasStroke) {\n ctx.save();\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'center';\n ctx.font = `bold ${textSize + 2}px Arial`;\n ctx.strokeStyle = strokeColor;\n ctx.strokeText(text, origin, origin);\n ctx.restore();\n }\n // Draw a text\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'center';\n ctx.fillStyle = fillColor;\n ctx.font = `bold ${textSize}px Arial`;\n ctx.strokeStyle = strokeColor;\n ctx.strokeText(text, origin, origin);\n ctx.fillText(text, origin, origin);\n cacheText[key] = canvas;\n }\n }\n return cacheText[key];\n};\n/** @private */\nconst cache = {};\n/**\n * A tracker style that take in account the delay.\n *\n * @param {RealtimeTrajectory} trajectory The trajectory to render.\n * @param {ViewState} viewState The view state of the map.\n * @param {RealtimeStyleOptions} options Some options to change the rendering\n * @return a canvas\n */\nconst realtimeDefaultStyle = (trajectory, viewState, options) => {\n const { hoverVehicleId, selectedVehicleId, useDelayStyle, delayOutlineColor = '#000', delayDisplay = 300000, getRadius = () => 0, getBgColor = () => '#000', getDelayColor = () => '#000', getDelayText = () => null, getTextColor = () => '#000', getTextSize = () => 0, getMaxRadiusForText = () => 10, getMaxRadiusForStrokeAndDelay = () => 7, } = options;\n const { zoom, pixelRatio = 1 } = viewState;\n let { type } = trajectory.properties;\n const { train_id: id, line, delay, state, operator_provides_realtime_journey: operatorProvidesRealtime, } = trajectory.properties;\n let { name, text_color: textColor, color } = line || {};\n const cancelled = state === 'JOURNEY_CANCELLED';\n if (!type) {\n type = 'Rail';\n }\n if (!name) {\n name = 'I';\n }\n if (!textColor) {\n textColor = '#000000';\n }\n if (color && color[0] !== '#') {\n color = `#${color}`;\n }\n if (textColor[0] !== '#') {\n textColor = `#${textColor}`;\n }\n const z = Math.min(Math.floor(zoom || 1), 16);\n const hover = !!(hoverVehicleId && hoverVehicleId === id);\n const selected = !!(selectedVehicleId && selectedVehicleId === id);\n // Calcul the radius of the circle\n let radius = getRadius(type, z) * pixelRatio;\n const isDisplayStrokeAndDelay = radius >= getMaxRadiusForStrokeAndDelay() * pixelRatio;\n if (hover || selected) {\n radius = isDisplayStrokeAndDelay\n ? radius + 5 * pixelRatio\n : 14 * pixelRatio;\n }\n const isDisplayText = radius > getMaxRadiusForText() * pixelRatio;\n // Optimize the cache key, very important in high zoom level\n let key = `${radius}${hover || selected}`;\n if (useDelayStyle) {\n key += `${operatorProvidesRealtime}${delay}`;\n if (isDisplayStrokeAndDelay) {\n key += `${cancelled}`;\n }\n }\n else {\n key += `${color || type}`;\n if (isDisplayStrokeAndDelay) {\n key += `${cancelled}${delay}`;\n }\n }\n if (isDisplayText) {\n key += `${name}${textColor}`;\n }\n if (!cache[key]) {\n if (radius === 0) {\n return null;\n }\n const margin = 1 * pixelRatio;\n const radiusDelay = radius + 2;\n const markerSize = radius * 2;\n const size = radiusDelay * 2 + margin * 2;\n const origin = size / 2;\n // Draw circle delay background\n let delayBg = null;\n if (isDisplayStrokeAndDelay && delay !== null) {\n delayBg = getDelayBgCanvas(origin, radiusDelay, getDelayColor(delay, cancelled));\n }\n // Show delay if feature is hovered or if delay is above 5mins.\n let delayText = null;\n let fontSize = 0;\n if (isDisplayStrokeAndDelay &&\n (hover || (delay || 0) >= delayDisplay || cancelled)) {\n // Draw delay text\n fontSize =\n Math.max(cancelled ? 19 : 14, Math.min(cancelled ? 19 : 17, radius * 1.2)) * pixelRatio;\n const text = getDelayText(delay, cancelled);\n if (text) {\n delayText = getDelayTextCanvas(text, fontSize, `bold ${fontSize}px arial, sans-serif`, getDelayColor(delay, cancelled, true), delayOutlineColor, pixelRatio);\n }\n }\n // Draw colored circle with black border\n let circleFillColor;\n if (useDelayStyle) {\n circleFillColor = getDelayColor(delay, cancelled);\n }\n else {\n circleFillColor = color || getBgColor(type);\n }\n const hasStroke = isDisplayStrokeAndDelay || hover || selected;\n const hasDash = !!isDisplayStrokeAndDelay &&\n !!useDelayStyle &&\n delay === null &&\n operatorProvidesRealtime === 'yes';\n const circle = getCircleCanvas(origin, radius, circleFillColor, hasStroke, hasDash, pixelRatio);\n // Create the canvas\n const width = size + ((delayText === null || delayText === void 0 ? void 0 : delayText.width) || 0) * 2;\n const height = size;\n const canvas = createCanvas(width, height);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n // The renderTrajectories will center the image on the vehicle positions.\n const originX = (delayText === null || delayText === void 0 ? void 0 : delayText.width) || 0;\n if (delayBg) {\n ctx.drawImage(delayBg, originX, 0);\n }\n if (circle) {\n ctx.drawImage(circle, originX, 0);\n }\n // Draw text in the circle\n let circleText = null;\n if (isDisplayText) {\n const fontSize2 = Math.max(radius, 10);\n const textSize = getTextSize(ctx, markerSize, name, fontSize2);\n const textColor2 = !useDelayStyle\n ? textColor || getTextColor(type)\n : '#000000';\n const hasStroke2 = !!useDelayStyle &&\n delay === null &&\n operatorProvidesRealtime === 'yes';\n circleText = getTextCanvas(name, origin, textSize, textColor2, circleFillColor, hasStroke2, pixelRatio);\n }\n if (circleText) {\n ctx.drawImage(circleText, originX, 0);\n }\n if (delayText) {\n ctx.drawImage(delayText, originX + Math.ceil(origin + radiusDelay) + margin, Math.ceil(origin - fontSize));\n }\n cache[key] = canvas;\n }\n }\n return cache[key];\n};\nexport default realtimeDefaultStyle;\n", + "content": "import createCanvas from '../utils/createCanvas';\n/** @private */\nconst cacheDelayBg = {};\n/**\n * Draw circle delay background\n *\n * @private\n */\nexport const getDelayBgCanvas = (origin, radius, color) => {\n const key = `${origin}, ${radius}, ${color}`;\n if (!cacheDelayBg[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.beginPath();\n ctx.arc(origin, origin, radius, 0, 2 * Math.PI, false);\n ctx.fillStyle = color;\n ctx.filter = 'blur(1px)';\n ctx.fill();\n cacheDelayBg[key] = canvas;\n }\n }\n return cacheDelayBg[key];\n};\n/** @private */\nconst cacheDelayText = {};\n/**\n * Draw delay text\n *\n * @private\n */\nexport const getDelayTextCanvas = (text, fontSize, font, delayColor, delayOutlineColor = '#000', pixelRatio = 1) => {\n const key = `${text}, ${font}, ${delayColor}, ${delayOutlineColor}, ${pixelRatio}`;\n if (!cacheDelayText[key]) {\n const canvas = createCanvas(Math.ceil(text.length * fontSize), Math.ceil(fontSize + 8 * pixelRatio));\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.font = font;\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n ctx.font = font;\n ctx.fillStyle = delayColor;\n ctx.strokeStyle = delayOutlineColor;\n ctx.lineWidth = 1.5 * pixelRatio;\n ctx.strokeText(text, 0, fontSize);\n ctx.fillText(text, 0, fontSize);\n cacheDelayText[key] = canvas;\n }\n }\n return cacheDelayText[key];\n};\n/** @private */\nconst cacheCircle = {};\n/**\n * Draw colored circle with black border\n *\n * @private\n */\nexport const getCircleCanvas = (origin, radius, color, hasStroke, hasDash, pixelRatio) => {\n const key = `${origin}, ${radius}, ${color}, ${hasStroke}, ${hasDash}, ${pixelRatio}`;\n if (!cacheCircle[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n ctx.fillStyle = color;\n if (hasStroke) {\n ctx.lineWidth = 1 * pixelRatio;\n ctx.strokeStyle = '#000000';\n }\n ctx.beginPath();\n ctx.arc(origin, origin, radius, 0, 2 * Math.PI, false);\n ctx.fill();\n if (hasDash) {\n ctx.setLineDash([5, 3]);\n }\n if (hasStroke) {\n ctx.stroke();\n }\n cacheCircle[key] = canvas;\n }\n }\n return cacheCircle[key];\n};\n/** @private */\nconst cacheText = {};\n/**\n * Draw text in the circle\n *\n * @private\n */\nexport const getTextCanvas = (text, origin, textSize, fillColor, strokeColor, hasStroke, pixelRatio) => {\n const key = `${text}, ${origin}, ${textSize}, ${fillColor},${strokeColor}, ${hasStroke}, ${pixelRatio}`;\n if (!cacheText[key]) {\n const canvas = createCanvas(origin * 2, origin * 2);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n // Draw a stroke to the text only if a provider provides realtime but we don't use it.\n if (hasStroke) {\n ctx.save();\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'center';\n ctx.font = `bold ${textSize + 2}px Arial`;\n ctx.strokeStyle = strokeColor;\n ctx.strokeText(text, origin, origin);\n ctx.restore();\n }\n // Draw a text\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'center';\n ctx.fillStyle = fillColor;\n ctx.font = `bold ${textSize}px Arial`;\n ctx.strokeStyle = strokeColor;\n ctx.strokeText(text, origin, origin);\n ctx.fillText(text, origin, origin);\n cacheText[key] = canvas;\n }\n }\n return cacheText[key];\n};\n/** @private */\nconst cache = {};\n/**\n * A tracker style that take in account the delay.\n *\n * @param {RealtimeTrajectory} trajectory The trajectory to render.\n * @param {ViewState} viewState The view state of the map.\n * @param {RealtimeStyleOptions} options Some options to change the rendering\n * @return a canvas\n */\nconst realtimeDefaultStyle = (trajectory, viewState, options) => {\n const { hoverVehicleId, selectedVehicleId, useDelayStyle, delayOutlineColor = '#000', delayDisplay = 300000, getRadius = () => 0, getBgColor = () => '#000', getDelayColor = () => '#000', getDelayText = () => null, getTextColor = () => '#000', getTextSize = () => 0, getMaxRadiusForText = () => 10, getMaxRadiusForStrokeAndDelay = () => 7, } = options;\n const { zoom, pixelRatio = 1 } = viewState;\n let { type } = trajectory.properties;\n const { train_id: id, line, delay, state, operator_provides_realtime_journey: operatorProvidesRealtime, } = trajectory.properties;\n let { name, text_color: textColor, color } = line || {};\n const cancelled = state === 'JOURNEY_CANCELLED';\n if (!type) {\n type = 'Rail';\n }\n if (!name) {\n name = 'I';\n }\n if (!textColor) {\n textColor = '#000000';\n }\n if (color && color[0] !== '#') {\n color = `#${color}`;\n }\n if (textColor[0] !== '#') {\n textColor = `#${textColor}`;\n }\n const z = Math.min(Math.floor(zoom || 1), 16);\n const hover = !!(hoverVehicleId && hoverVehicleId === id);\n const selected = !!(selectedVehicleId && selectedVehicleId === id);\n // Calcul the radius of the circle\n let radius = getRadius(type, z) * pixelRatio;\n const isDisplayStrokeAndDelay = radius >= getMaxRadiusForStrokeAndDelay() * pixelRatio;\n if (hover || selected) {\n radius = isDisplayStrokeAndDelay\n ? radius + 5 * pixelRatio\n : 14 * pixelRatio;\n }\n const isDisplayText = radius > getMaxRadiusForText() * pixelRatio;\n // Optimize the cache key, very important in high zoom level\n let key = `${radius}${hover || selected}`;\n if (useDelayStyle) {\n key += `${operatorProvidesRealtime}${delay}`;\n if (isDisplayStrokeAndDelay) {\n key += `${cancelled}`;\n }\n }\n else {\n key += `${color || type}`;\n if (isDisplayStrokeAndDelay) {\n key += `${cancelled}${delay}`;\n }\n }\n if (isDisplayText) {\n key += `${name}${textColor}`;\n }\n if (!cache[key]) {\n if (radius === 0) {\n return null;\n }\n const margin = 1 * pixelRatio;\n const radiusDelay = radius + 2;\n const markerSize = radius * 2;\n const size = radiusDelay * 2 + margin * 2;\n const origin = size / 2;\n // Draw circle delay background\n let delayBg = null;\n if (isDisplayStrokeAndDelay && delay !== null) {\n delayBg = getDelayBgCanvas(origin, radiusDelay, getDelayColor(delay, cancelled));\n }\n // Show delay if feature is hovered or if delay is above 5mins.\n let delayText = null;\n let fontSize = 0;\n if (isDisplayStrokeAndDelay &&\n (hover || (delay || 0) >= delayDisplay || cancelled)) {\n // Draw delay text\n fontSize =\n Math.max(cancelled ? 19 : 14, Math.min(cancelled ? 19 : 17, radius * 1.2)) * pixelRatio;\n const text = getDelayText(delay, cancelled);\n if (text) {\n delayText = getDelayTextCanvas(text, fontSize, `bold ${fontSize}px arial, sans-serif`, getDelayColor(delay, cancelled, true), delayOutlineColor, pixelRatio);\n }\n }\n // Draw colored circle with black border\n let circleFillColor;\n if (useDelayStyle) {\n circleFillColor = getDelayColor(delay, cancelled);\n }\n else {\n circleFillColor = color || getBgColor(type);\n }\n const hasStroke = isDisplayStrokeAndDelay || hover || selected;\n const hasDash = !!isDisplayStrokeAndDelay &&\n !!useDelayStyle &&\n delay === null &&\n operatorProvidesRealtime === 'yes';\n const circle = getCircleCanvas(origin, radius, circleFillColor, hasStroke, hasDash, pixelRatio);\n // Create the canvas\n const width = size + (delayText?.width || 0) * 2;\n const height = size;\n const canvas = createCanvas(width, height);\n if (canvas) {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n return null;\n }\n // The renderTrajectories will center the image on the vehicle positions.\n const originX = delayText?.width || 0;\n if (delayBg) {\n ctx.drawImage(delayBg, originX, 0);\n }\n if (circle) {\n ctx.drawImage(circle, originX, 0);\n }\n // Draw text in the circle\n let circleText = null;\n if (isDisplayText) {\n const fontSize2 = Math.max(radius, 10);\n const textSize = getTextSize(ctx, markerSize, name, fontSize2);\n const textColor2 = !useDelayStyle\n ? textColor || getTextColor(type)\n : '#000000';\n const hasStroke2 = !!useDelayStyle &&\n delay === null &&\n operatorProvidesRealtime === 'yes';\n circleText = getTextCanvas(name, origin, textSize, textColor2, circleFillColor, hasStroke2, pixelRatio);\n }\n if (circleText) {\n ctx.drawImage(circleText, originX, 0);\n }\n if (delayText) {\n ctx.drawImage(delayText, originX + Math.ceil(origin + radiusDelay) + margin, Math.ceil(origin - fontSize));\n }\n cache[key] = canvas;\n }\n }\n return cache[key];\n};\nexport default realtimeDefaultStyle;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/styles/realtimeDefaultStyle.js", "access": "public", @@ -5505,7 +5564,7 @@ "lineNumber": 1 }, { - "__docId__": 244, + "__docId__": 247, "kind": "variable", "name": "cacheDelayBg", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5525,7 +5584,7 @@ "ignore": true }, { - "__docId__": 245, + "__docId__": 248, "kind": "function", "name": "getDelayBgCanvas", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5566,7 +5625,7 @@ } }, { - "__docId__": 246, + "__docId__": 249, "kind": "variable", "name": "cacheDelayText", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5586,7 +5645,7 @@ "ignore": true }, { - "__docId__": 247, + "__docId__": 250, "kind": "function", "name": "getDelayTextCanvas", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5651,7 +5710,7 @@ } }, { - "__docId__": 248, + "__docId__": 251, "kind": "variable", "name": "cacheCircle", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5671,7 +5730,7 @@ "ignore": true }, { - "__docId__": 249, + "__docId__": 252, "kind": "function", "name": "getCircleCanvas", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5730,7 +5789,7 @@ } }, { - "__docId__": 250, + "__docId__": 253, "kind": "variable", "name": "cacheText", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5750,7 +5809,7 @@ "ignore": true }, { - "__docId__": 251, + "__docId__": 254, "kind": "function", "name": "getTextCanvas", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5815,7 +5874,7 @@ } }, { - "__docId__": 252, + "__docId__": 255, "kind": "variable", "name": "cache", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5835,7 +5894,7 @@ "ignore": true }, { - "__docId__": 253, + "__docId__": 256, "kind": "function", "name": "realtimeDefaultStyle", "memberof": "build/common/styles/realtimeDefaultStyle.js", @@ -5891,10 +5950,10 @@ } }, { - "__docId__": 254, + "__docId__": 257, "kind": "file", "name": "build/common/styles/realtimeDelayStyle.js", - "content": "import realtimeDefaultStyle from './realtimeDefaultStyle';\n/**\n * A tracker style that display the delay as backgroundColor.\n *\n * @param {*} trajectory The trajectory to render.\n * @param {*} viewState The view state of the map.\n * @param {*} options Some options to change the rendering\n * @return a canvas\n */\nconst realtimeDelayStyle = (trajectory, viewState, options) => {\n return realtimeDefaultStyle(trajectory, viewState, Object.assign(Object.assign({}, options), { useDelayStyle: true }));\n};\nexport default realtimeDelayStyle;\n", + "content": "import realtimeDefaultStyle from './realtimeDefaultStyle';\n/**\n * A tracker style that display the delay as backgroundColor.\n *\n * @param {*} trajectory The trajectory to render.\n * @param {*} viewState The view state of the map.\n * @param {*} options Some options to change the rendering\n * @return a canvas\n */\nconst realtimeDelayStyle = (trajectory, viewState, options) => {\n return realtimeDefaultStyle(trajectory, viewState, {\n ...options,\n useDelayStyle: true,\n });\n};\nexport default realtimeDelayStyle;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/styles/realtimeDelayStyle.js", "access": "public", @@ -5902,7 +5961,7 @@ "lineNumber": 1 }, { - "__docId__": 255, + "__docId__": 258, "kind": "function", "name": "realtimeDelayStyle", "memberof": "build/common/styles/realtimeDelayStyle.js", @@ -5958,10 +6017,10 @@ } }, { - "__docId__": 256, + "__docId__": 259, "kind": "file", "name": "build/common/styles/realtimeSimpleStyle.js", - "content": "import createCanvas from '../utils/createCanvas';\n/**\n * A very simple tracker style.\n * Display blue point for each train.\n */\nlet canvas;\nconst realtimeSimpleStyle = () => {\n if (!canvas) {\n canvas = createCanvas(15, 15);\n const ctx = canvas === null || canvas === void 0 ? void 0 : canvas.getContext('2d');\n if (ctx) {\n ctx.arc(8, 8, 5, 0, 2 * Math.PI, false);\n ctx.fillStyle = '#8ED6FF';\n ctx.fill();\n ctx.lineWidth = 3;\n ctx.strokeStyle = 'black';\n ctx.stroke();\n ctx.lineWidth = 3;\n }\n }\n return canvas;\n};\nexport default realtimeSimpleStyle;\n", + "content": "import createCanvas from '../utils/createCanvas';\n/**\n * A very simple tracker style.\n * Display blue point for each train.\n */\nlet canvas;\nconst realtimeSimpleStyle = () => {\n if (!canvas) {\n canvas = createCanvas(15, 15);\n const ctx = canvas?.getContext('2d');\n if (ctx) {\n ctx.arc(8, 8, 5, 0, 2 * Math.PI, false);\n ctx.fillStyle = '#8ED6FF';\n ctx.fill();\n ctx.lineWidth = 3;\n ctx.strokeStyle = 'black';\n ctx.stroke();\n ctx.lineWidth = 3;\n }\n }\n return canvas;\n};\nexport default realtimeSimpleStyle;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/styles/realtimeSimpleStyle.js", "access": "public", @@ -5969,7 +6028,7 @@ "lineNumber": 1 }, { - "__docId__": 257, + "__docId__": 260, "kind": "function", "name": "realtimeSimpleStyle", "memberof": "build/common/styles/realtimeSimpleStyle.js", @@ -5992,7 +6051,150 @@ } }, { - "__docId__": 258, + "__docId__": 261, + "kind": "file", + "name": "build/common/tracker.worker.js", + "content": "/// \n/// \n/// \n/// \nimport GeoJSON from 'ol/format/GeoJSON';\nimport { realtimeDefaultStyle } from './styles';\nimport { renderTrajectories, realtimeConfig } from './utils';\nconst debug = false;\nconst trajectories = {};\nlet renderTimeout;\nlet count = 0;\nconst format = new GeoJSON();\nconst canvas = new OffscreenCanvas(1, 1);\nconst render = (evt) => {\n // eslint-disable-next-line no-console\n if (debug)\n console.time('render');\n // eslint-disable-next-line no-console\n if (debug)\n console.log('render', evt.data.viewState);\n count = 0;\n const { viewState, options } = evt.data;\n const { renderedTrajectories } = renderTrajectories(canvas, Object.values(trajectories), realtimeDefaultStyle, viewState, {\n ...options,\n getRadius: realtimeConfig.getRadius,\n getTextColor: realtimeConfig.getTextColor,\n getBgColor: realtimeConfig.getBgColor,\n getDelayColor: realtimeConfig.getDelayColor,\n getTextSize: realtimeConfig.getTextSize,\n });\n if (debug)\n console.timeEnd('render');\n if (debug)\n console.log('NUMBER OF STYLES CREATED', count);\n const imageData = canvas.transferToImageBitmap();\n // eslint-disable-next-line no-restricted-globals\n self.postMessage({\n action: 'rendered',\n imageData,\n viewState,\n nbRenderedTRajectories: renderedTrajectories?.length || 0,\n }, [imageData]);\n renderTimeout = null;\n};\n// eslint-disable-next-line no-restricted-globals\nself.onmessage = (evt) => {\n // debugger;\n if (evt.data.action === 'addTrajectory') {\n const { trajectory } = evt.data;\n const id = trajectory.properties.train_id;\n trajectories[id] = trajectory;\n trajectories[id].properties.olGeometry = format.readGeometry(trajectory.geometry);\n return;\n }\n if (evt.data.action === 'removeTrajectory') {\n delete trajectories[evt.data.trajectoryId];\n return;\n }\n // if (evt.data.action === 'sendData') {\n // // eslint-disable-next-line no-console\n // if (debug) console.log('sendData', evt.data);\n // if (debug) console.time('sendData');\n // trajectories = evt.data.trajectories;\n // if (debug) console.timeEnd('sendData');\n // return;\n // }\n if (evt.data.action !== 'render') {\n return;\n }\n if (renderTimeout) {\n clearTimeout(renderTimeout);\n }\n // eslint-disable-next-line no-restricted-globals\n renderTimeout = self.setTimeout(() => {\n render(evt);\n }, 0);\n};\n// We need an export to force this file to act like a module, so TS will let us re-type `self`\n// export default null;\n", + "static": true, + "longname": "/home/olivier/GIT/mobility-toolbox-js/build/common/tracker.worker.js", + "access": "public", + "description": null, + "lineNumber": 1 + }, + { + "__docId__": 262, + "kind": "variable", + "name": "debug", + "memberof": "build/common/tracker.worker.js", + "static": true, + "longname": "build/common/tracker.worker.js~debug", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 8, + "undocument": true, + "type": { + "types": [ + "boolean" + ] + }, + "ignore": true + }, + { + "__docId__": 263, + "kind": "variable", + "name": "trajectories", + "memberof": "build/common/tracker.worker.js", + "static": true, + "longname": "build/common/tracker.worker.js~trajectories", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 9, + "undocument": true, + "type": { + "types": [ + "{}" + ] + }, + "ignore": true + }, + { + "__docId__": 264, + "kind": "variable", + "name": "count", + "memberof": "build/common/tracker.worker.js", + "static": true, + "longname": "build/common/tracker.worker.js~count", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 11, + "undocument": true, + "type": { + "types": [ + "number" + ] + }, + "ignore": true + }, + { + "__docId__": 265, + "kind": "variable", + "name": "format", + "memberof": "build/common/tracker.worker.js", + "static": true, + "longname": "build/common/tracker.worker.js~format", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 12, + "undocument": true, + "type": { + "types": [ + "*" + ] + }, + "ignore": true + }, + { + "__docId__": 266, + "kind": "variable", + "name": "canvas", + "memberof": "build/common/tracker.worker.js", + "static": true, + "longname": "build/common/tracker.worker.js~canvas", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 13, + "undocument": true, + "type": { + "types": [ + "*" + ] + }, + "ignore": true + }, + { + "__docId__": 267, + "kind": "function", + "name": "render", + "memberof": "build/common/tracker.worker.js", + "generator": false, + "async": false, + "static": true, + "longname": "build/common/tracker.worker.js~render", + "access": "public", + "export": false, + "importPath": "mobility-toolbox-js/build/common/tracker.worker.js", + "importStyle": null, + "description": null, + "lineNumber": 14, + "undocument": true, + "params": [ + { + "name": "evt", + "types": [ + "*" + ] + } + ], + "return": null, + "ignore": true + }, + { + "__docId__": 268, "kind": "file", "name": "build/common/typedefs.js", "content": "\"use strict\";\n/**\n * @typedef {function} FilterFunction\n * @param {Vehicle} vehicle Vehicle to filter.\n * @returns boolean\n */\n/**\n * @typedef {function} SortFunction\n * @param {any} a Object a to compare.\n * @param {any} b Object b to compare.\n * @returns number\n */\n/**\n * @typedef {Object} ViewState\n * @property {number|undefined} time A time in ms.\n * @property {number[2]|undefined} center A center in mercator coordinate.\n * @property {number[4]} extent An Extent in mercator coordinates.\n * @property {number[2]} size A size ([width, height]).\n * @property {number} rotation A rotation in radians.\n * @property {number} resolution A resolution.\n * @property {number} zoom A zoom level.\n * @property {number|undefined} pixelRatio A pixel ratio.\n */\n/**\n * @typedef {Object} FeatureInfo\n * @property {Layer} layer A layer.\n * @property {ol/Feature~Feature[]} features An array of features.\n * @property {number[2]} coordinate The coordinate where to find the featue.\n */\n/**\n * @typedef {ol/Map~Map|mapboxgl.Map|maplibregl.Map} AnyMap\n */\n/**\n * @typedef {Object} ControlCommonOptions\n * @property {boolean} [active = true] Whether the control is active or not.\n * @property {HTMLElement} element The HTML element used to render the control.\n * @property {HTMLElement} target The HTML element where to render the element property. Default is the map's element.\n * @property {function} render Render function called whenever the control needs to be rerendered.\n */\n/**\n * @typedef {Object} LayerCommonOptions\n * @property {string!} key Identifier of the layer. Must be unique.\n * @property {string!} name Name of the layer.\n * @property {string!} group Group of the layer.\n * @property {string[]!} copyrights List of copyrights.\n * @property {Layer[]!} children List of children layers.\n * @property {boolean!} visible Define if the layer is currently display on the map.\n * @property {boolean!} disabled Define if the layer is currently display on the map but can't be seen (extent, zoom ,data restrictions).\n * @property {number!} hittolerance Hit-detection tolerance in css pixels. Pixels inside the radius around the given position will be checked for features.\n * @property {Object!} properties - Custom properties.\n * @property {AnyMap!} map - The map used to display the layer.\n */\n", @@ -6003,10 +6205,10 @@ "lineNumber": 1 }, { - "__docId__": 259, + "__docId__": 269, "kind": "file", "name": "build/index.js", - "content": "import * as ol from './ol';\nimport * as mapbox from './mapbox';\nimport * as ol_1 from './ol';\nexport { ol_1 as ol };\nimport * as mapbox_1 from './mapbox';\nexport { mapbox_1 as mapbox };\nexport default {\n ol,\n mapbox,\n};\n", + "content": "import * as ol from './ol';\nimport * as mapbox from './mapbox';\nexport * as ol from './ol';\nexport * as mapbox from './mapbox';\nexport default {\n ol,\n mapbox,\n};\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/index.js", "access": "public", @@ -6014,7 +6216,7 @@ "lineNumber": 1 }, { - "__docId__": 260, + "__docId__": 270, "kind": "file", "name": "build/mapbox/controls/CopyrightControl.js", "content": "import CopyrightControlCommon from '../../common/controls/CopyrightControlCommon';\nimport { getMapboxMapCopyrights } from '../../common/utils';\n/**\n * Display layer's copyrights.\n *\n * @example\n * import { Map } from 'mapbox-gl';\n * import { CopyrightControl } from 'mobility-toolbox-js/mapbox';\n *\n * const map = new Map({\n * container: 'map',\n * style: `https://maps.geops.io/styles/travic_v2/style.json?key=${window.apiKey}`,\n * });\n *\n * const control = new CopyrightControl();\n * control.attachToMap(map);\n *\n *\n * @see Mapbox copyright example\n *\n * @extends {CopyrightControlCommon}\n */\nclass CopyrightControl extends CopyrightControlCommon {\n constructor(options) {\n super(options);\n this.render = this.render.bind(this);\n }\n activate() {\n super.activate();\n if (this.map) {\n this.map.on('sourcedata', this.render);\n this.map.on('styledata', this.render);\n this.map.on('idle', this.render);\n }\n }\n deactivate() {\n if (this.map) {\n this.map.off('sourcedata', this.render);\n this.map.off('styledata', this.render);\n this.map.off('idle', this.render);\n }\n super.deactivate();\n }\n getCopyrights() {\n return getMapboxMapCopyrights(this.map);\n }\n}\nexport default CopyrightControl;\n", @@ -6025,7 +6227,7 @@ "lineNumber": 1 }, { - "__docId__": 261, + "__docId__": 271, "kind": "class", "name": "CopyrightControl", "memberof": "build/mapbox/controls/CopyrightControl.js", @@ -6049,7 +6251,7 @@ ] }, { - "__docId__": 262, + "__docId__": 272, "kind": "constructor", "name": "constructor", "memberof": "build/mapbox/controls/CopyrightControl.js~CopyrightControl", @@ -6063,7 +6265,7 @@ "undocument": true }, { - "__docId__": 263, + "__docId__": 273, "kind": "member", "name": "render", "memberof": "build/mapbox/controls/CopyrightControl.js~CopyrightControl", @@ -6080,7 +6282,7 @@ } }, { - "__docId__": 264, + "__docId__": 274, "kind": "method", "name": "activate", "memberof": "build/mapbox/controls/CopyrightControl.js~CopyrightControl", @@ -6096,7 +6298,7 @@ "return": null }, { - "__docId__": 265, + "__docId__": 275, "kind": "method", "name": "deactivate", "memberof": "build/mapbox/controls/CopyrightControl.js~CopyrightControl", @@ -6112,7 +6314,7 @@ "return": null }, { - "__docId__": 266, + "__docId__": 276, "kind": "method", "name": "getCopyrights", "memberof": "build/mapbox/controls/CopyrightControl.js~CopyrightControl", @@ -6132,7 +6334,7 @@ } }, { - "__docId__": 267, + "__docId__": 277, "kind": "file", "name": "build/mapbox/controls/index.js", "content": "// eslint-disable-next-line import/prefer-default-export\nexport { default as CopyrightControl } from './CopyrightControl';\n", @@ -6143,7 +6345,7 @@ "lineNumber": 1 }, { - "__docId__": 268, + "__docId__": 278, "kind": "file", "name": "build/mapbox/index.js", "content": "export * from '../api';\nexport * from '../common';\nexport * from './controls';\nexport * from './layers';\nexport * from './utils';\n", @@ -6154,10 +6356,10 @@ "lineNumber": 1 }, { - "__docId__": 269, + "__docId__": 279, "kind": "file", "name": "build/mapbox/layers/Layer.js", - "content": "import { unByKey } from 'ol/Observable';\nimport { transformExtent } from 'ol/proj';\nimport LayerCommon from '../../common/layers/LayerCommon';\nimport userInteractionsMixin from '../../common/mixins/UserInteractionsLayerMixin';\n/**\n * A class representing a layer to display on an OpenLayers map.\n *\n * @example\n * import { Layer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new Layer({\n * olLayer: ...,\n * });\n *\n * @see Map example\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass Layer extends userInteractionsMixin(LayerCommon) {\n /**\n * Initialize the layer and listen to user events.\n * @param {mapboxgl.Map|maplibregl.Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n if (this.userInteractions) {\n this.toggleVisibleListeners();\n this.onChangeVisibleKey = this.on(\n // @ts-ignore\n 'change:visible', this.toggleVisibleListeners);\n }\n }\n detachFromMap() {\n if (this.map) {\n this.deactivateUserInteractions();\n // @ts-ignore\n unByKey(this.onChangeVisibleKey);\n }\n super.detachFromMap();\n }\n activateUserInteractions() {\n var _a, _b;\n this.deactivateUserInteractions();\n if (this.map &&\n this.userInteractions &&\n this.userClickInteractions &&\n ((_a = this.userClickCallbacks) === null || _a === void 0 ? void 0 : _a.length)) {\n this.map.on('click', this.onUserClickCallback);\n }\n if (this.map &&\n this.userInteractions &&\n this.userHoverInteractions &&\n ((_b = this.userHoverCallbacks) === null || _b === void 0 ? void 0 : _b.length)) {\n this.map.on('mousemove', this.onUserMoveCallback);\n }\n }\n deactivateUserInteractions() {\n if (this.map) {\n this.map.off('mousemove', this.onUserMoveCallback);\n this.map.off('click', this.onUserClickCallback);\n }\n }\n /**\n * Toggle listeners needed when a layer is avisible or not.\n * @private\n */\n toggleVisibleListeners() {\n if (this.visible) {\n this.activateUserInteractions();\n }\n else {\n this.deactivateUserInteractions();\n }\n }\n /**\n * Returns the current extent in mercator coordinates.\n */\n getMercatorExtent() {\n const bounds = this.map.getBounds().toArray();\n return transformExtent([...bounds[0], ...bounds[1]], 'EPSG:4326', 'EPSG:3857');\n }\n /**\n * Returns the equivalent zoom in Openlayers.\n */\n getOlZoom() {\n return this.map.getZoom() + 1;\n }\n /**\n * Create a copy of the Layer.\n * @param {Object} newOptions Options to override\n * @return {Layer} A Layer\n */\n clone(newOptions) {\n return new Layer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default Layer;\n", + "content": "import { unByKey } from 'ol/Observable';\nimport { transformExtent } from 'ol/proj';\nimport LayerCommon from '../../common/layers/LayerCommon';\nimport userInteractionsMixin from '../../common/mixins/UserInteractionsLayerMixin';\n/**\n * A class representing a layer to display on an OpenLayers map.\n *\n * @example\n * import { Layer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new Layer({\n * olLayer: ...,\n * });\n *\n * @see Map example\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass Layer extends userInteractionsMixin(LayerCommon) {\n /**\n * Initialize the layer and listen to user events.\n * @param {mapboxgl.Map|maplibregl.Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n if (this.userInteractions) {\n this.toggleVisibleListeners();\n this.onChangeVisibleKey = this.on(\n // @ts-ignore\n 'change:visible', this.toggleVisibleListeners);\n }\n }\n detachFromMap() {\n if (this.map) {\n this.deactivateUserInteractions();\n // @ts-ignore\n unByKey(this.onChangeVisibleKey);\n }\n super.detachFromMap();\n }\n activateUserInteractions() {\n this.deactivateUserInteractions();\n if (this.map &&\n this.userInteractions &&\n this.userClickInteractions &&\n this.userClickCallbacks?.length) {\n this.map.on('click', this.onUserClickCallback);\n }\n if (this.map &&\n this.userInteractions &&\n this.userHoverInteractions &&\n this.userHoverCallbacks?.length) {\n this.map.on('mousemove', this.onUserMoveCallback);\n }\n }\n deactivateUserInteractions() {\n if (this.map) {\n this.map.off('mousemove', this.onUserMoveCallback);\n this.map.off('click', this.onUserClickCallback);\n }\n }\n /**\n * Toggle listeners needed when a layer is avisible or not.\n * @private\n */\n toggleVisibleListeners() {\n if (this.visible) {\n this.activateUserInteractions();\n }\n else {\n this.deactivateUserInteractions();\n }\n }\n /**\n * Returns the current extent in mercator coordinates.\n */\n getMercatorExtent() {\n const bounds = this.map.getBounds().toArray();\n return transformExtent([...bounds[0], ...bounds[1]], 'EPSG:4326', 'EPSG:3857');\n }\n /**\n * Returns the equivalent zoom in Openlayers.\n */\n getOlZoom() {\n return this.map.getZoom() + 1;\n }\n /**\n * Create a copy of the Layer.\n * @param {Object} newOptions Options to override\n * @return {Layer} A Layer\n */\n clone(newOptions) {\n return new Layer({ ...this.options, ...newOptions });\n }\n}\nexport default Layer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/mapbox/layers/Layer.js", "access": "public", @@ -6165,7 +6367,7 @@ "lineNumber": 1 }, { - "__docId__": 270, + "__docId__": 280, "kind": "class", "name": "Layer", "memberof": "build/mapbox/layers/Layer.js", @@ -6195,7 +6397,7 @@ ] }, { - "__docId__": 271, + "__docId__": 281, "kind": "method", "name": "attachToMap", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6222,7 +6424,7 @@ "return": null }, { - "__docId__": 272, + "__docId__": 282, "kind": "member", "name": "onChangeVisibleKey", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6239,7 +6441,7 @@ } }, { - "__docId__": 273, + "__docId__": 283, "kind": "method", "name": "detachFromMap", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6255,7 +6457,7 @@ "return": null }, { - "__docId__": 274, + "__docId__": 284, "kind": "method", "name": "activateUserInteractions", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6271,7 +6473,7 @@ "return": null }, { - "__docId__": 275, + "__docId__": 285, "kind": "method", "name": "deactivateUserInteractions", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6281,13 +6483,13 @@ "longname": "build/mapbox/layers/Layer.js~Layer#deactivateUserInteractions", "access": "public", "description": null, - "lineNumber": 61, + "lineNumber": 60, "undocument": true, "params": [], "return": null }, { - "__docId__": 276, + "__docId__": 286, "kind": "method", "name": "toggleVisibleListeners", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6297,12 +6499,12 @@ "longname": "build/mapbox/layers/Layer.js~Layer#toggleVisibleListeners", "access": "private", "description": "Toggle listeners needed when a layer is avisible or not.", - "lineNumber": 71, + "lineNumber": 70, "params": [], "return": null }, { - "__docId__": 277, + "__docId__": 287, "kind": "method", "name": "getMercatorExtent", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6312,7 +6514,7 @@ "longname": "build/mapbox/layers/Layer.js~Layer#getMercatorExtent", "access": "public", "description": "Returns the current extent in mercator coordinates.", - "lineNumber": 82, + "lineNumber": 81, "params": [], "return": { "types": [ @@ -6321,7 +6523,7 @@ } }, { - "__docId__": 278, + "__docId__": 288, "kind": "method", "name": "getOlZoom", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6331,7 +6533,7 @@ "longname": "build/mapbox/layers/Layer.js~Layer#getOlZoom", "access": "public", "description": "Returns the equivalent zoom in Openlayers.", - "lineNumber": 89, + "lineNumber": 88, "params": [], "return": { "types": [ @@ -6340,7 +6542,7 @@ } }, { - "__docId__": 279, + "__docId__": 289, "kind": "method", "name": "clone", "memberof": "build/mapbox/layers/Layer.js~Layer", @@ -6350,7 +6552,7 @@ "longname": "build/mapbox/layers/Layer.js~Layer#clone", "access": "public", "description": "Create a copy of the Layer.", - "lineNumber": 97, + "lineNumber": 96, "params": [ { "nullable": null, @@ -6373,10 +6575,10 @@ } }, { - "__docId__": 280, + "__docId__": 290, "kind": "file", "name": "build/mapbox/layers/RealtimeLayer.js", - "content": "import { fromLonLat } from 'ol/proj';\nimport { unByKey } from 'ol/Observable';\nimport { getWidth, getHeight } from 'ol/extent';\nimport transformRotate from '@turf/transform-rotate';\nimport { point } from '@turf/helpers';\nimport mixin from '../../common/mixins/RealtimeLayerMixin';\nimport Layer from './Layer';\nimport { getSourceCoordinates, getMercatorResolution } from '../utils';\n/**\n * Responsible for loading and display data from a Realtime service.\n *\n * @example\n * import { RealtimeLayer } from 'mobility-toolbox-js/mapbox';\n *\n * const layer = new RealtimeLayer({\n * url: [yourUrl],\n * apiKey: [yourApiKey],\n * });\n *\n *\n * @see RealtimeAPI\n *\n * @extends {Layer}\n * @implements {RealtimeLayerInterface}\n */\n// @ts-ignore\nclass RealtimeLayer extends mixin(Layer) {\n constructor(options = {}) {\n super(Object.assign({}, options));\n /** @ignore */\n this.onLoad = this.onLoad.bind(this);\n /** @ignore */\n this.onMove = this.onMove.bind(this);\n /** @ignore */\n this.onMoveEnd = this.onMoveEnd.bind(this);\n /** @ignore */\n this.onZoomEnd = this.onZoomEnd.bind(this);\n /** @ignore */\n this.onVisibilityChange = this.onVisibilityChange.bind(this);\n }\n /**\n * Initialize the layer.\n *\n * @param {mapboxgl.Map} map A [mapbox Map](https://docs.mapbox.com/mapbox-gl-js/api/map/).\n * @param {string} beforeId Layer's id before which we want to add the new layer.\n * @override\n */\n // @ts-ignore\n attachToMap(map, beforeId) {\n if (!map) {\n return;\n }\n super.attachToMap(map);\n this.source = {\n type: 'canvas',\n canvas: this.canvas,\n coordinates: getSourceCoordinates(map, this.pixelRatio),\n // Set to true if the canvas source is animated. If the canvas is static, animate should be set to false to improve performance.\n animate: true,\n attribution: this.copyrights && this.copyrights.join(', '),\n };\n this.beforeId = beforeId;\n this.layer = {\n id: this.key,\n type: 'raster',\n source: this.key,\n layout: {\n visibility: this.visible ? 'visible' : 'none',\n },\n paint: {\n 'raster-opacity': 1,\n 'raster-fade-duration': 0,\n 'raster-resampling': 'nearest', // important otherwise it looks blurry\n },\n };\n if (map.isStyleLoaded()) {\n this.onLoad();\n }\n this.map.on('load', this.onLoad);\n this.listeners = [this.on('change:visible', this.onVisibilityChange)];\n }\n /**\n * Remove listeners from the Mapbox Map.\n */\n detachFromMap() {\n if (this.map) {\n this.map.off('load', this.onLoad);\n // @ts-ignore\n this.listeners.forEach((listener) => {\n unByKey(listener);\n });\n if (this.map.style && this.map.getLayer(this.key)) {\n this.map.removeLayer(this.key);\n }\n if (this.map.style && this.map.getSource(this.key)) {\n this.map.removeSource(this.key);\n }\n }\n super.detachFromMap();\n }\n /**\n * Start updating vehicles position.\n *\n * @listens {mapboxgl.map.event:zoomend} Listen to zoom end event.\n * @listens {mapboxgl.map.event:mousemove} Listen to mousemove end.\n * @override\n */\n start() {\n super.start();\n this.map.on('move', this.onMove);\n this.map.on('moveend', this.onMoveEnd);\n this.map.on('zoomend', this.onZoomEnd);\n }\n /**\n * Stop updating vehicles position, and unlisten events.\n *\n * @override\n */\n stop() {\n super.stop();\n if (this.map) {\n this.map.off('move', this.onMove);\n this.map.off('moveend', this.onMoveEnd);\n this.map.off('zoomend', this.onZoomEnd);\n }\n }\n onLoad() {\n if (!this.map.getSource(this.key)) {\n this.map.addSource(this.key, this.source);\n }\n if (!this.map.getLayer(this.key)) {\n this.map.addLayer(this.layer, this.beforeId);\n }\n }\n /**\n * Function triggered when the user moves the cursor over the map.\n * @override\n */\n onUserMoveCallback(evt) {\n super.onUserMoveCallback(Object.assign({ coordinate: fromLonLat(evt.lngLat.toArray()) }, evt));\n }\n /**\n * Render the trajectories using current map's size, resolution and rotation.\n * @param {boolean} noInterpolate if true, renders the vehicles without interpolating theirs positions.\n * @overrides\n */\n // @ts-ignore\n renderTrajectories(noInterpolate = false) {\n if (!this.map) {\n return;\n }\n if (!this.pixelRatio) {\n this.pixelRatio = 1;\n }\n const { width, height } = this.map.getCanvas();\n const center = this.map.getCenter();\n // We use turf here to have good transform.\n const leftBottom = this.map.unproject({\n x: 0,\n y: height / this.pixelRatio,\n }); // southWest\n const rightTop = this.map.unproject({\n x: width / this.pixelRatio,\n y: 0,\n }); // north east\n const coord0 = transformRotate(point([leftBottom.lng, leftBottom.lat]), -this.map.getBearing(), {\n pivot: [center.lng, center.lat],\n }).geometry.coordinates;\n const coord1 = transformRotate(point([rightTop.lng, rightTop.lat]), -this.map.getBearing(), {\n pivot: [center.lng, center.lat],\n }).geometry.coordinates;\n const bounds = [...fromLonLat(coord0), ...fromLonLat(coord1)];\n const xResolution = getWidth(bounds) / (width / this.pixelRatio);\n const yResolution = getHeight(bounds) / (height / this.pixelRatio);\n const res = Math.max(xResolution, yResolution);\n // Coordinate of trajectories are in mercator so we have to pass the proper resolution and center in mercator.\n const viewState = {\n size: [width / this.pixelRatio, height / this.pixelRatio],\n center: fromLonLat([center.lng, center.lat]),\n extent: bounds,\n resolution: res,\n zoom: this.getOlZoom(),\n rotation: -(this.map.getBearing() * Math.PI) / 180,\n pixelRatio: this.pixelRatio,\n };\n super.renderTrajectories(viewState, noInterpolate);\n }\n /**\n * Return the delay in ms before the next rendering.\n */\n getRefreshTimeInMs() {\n return super.getRefreshTimeInMs(this.map.getZoom());\n }\n getFeatureInfoAtCoordinate(coordinate, options = {}) {\n const resolution = getMercatorResolution(this.map);\n return super.getFeatureInfoAtCoordinate(coordinate, Object.assign({ resolution }, options));\n }\n onVisibilityChange() {\n if (this.visible && !this.map.getLayer(this.key)) {\n this.map.addLayer(this.layer, this.beforeId);\n }\n else if (this.map.getLayer(this.key)) {\n this.map.removeLayer(this.key);\n }\n // We can't use setLayoutProperty it triggers an error probably a bug in mapbox\n // this.map.setLayoutProperty(\n // this.key,\n // 'visibilty',\n // this.visible ? 'visible' : 'none',\n // );\n }\n /**\n * Remove the trajectory form the list if necessary.\n *\n * @private\n */\n purgeTrajectory(trajectory, extent, zoom) {\n return super.purgeTrajectory(trajectory, extent || this.getMercatorExtent(), zoom || Math.floor(this.getOlZoom()));\n }\n /**\n * Send the current bbox to the websocket\n */\n setBbox(extent, zoom) {\n let newExtent = extent;\n let newZoom = zoom;\n if (!newExtent && this.isUpdateBboxOnMoveEnd) {\n newExtent = extent || this.getMercatorExtent();\n newZoom = Math.floor(this.getOlZoom());\n }\n super.setBbox(newExtent, newZoom);\n }\n /**\n * Callback on 'move' event.\n *\n * @private\n */\n onMove() {\n this.renderTrajectories();\n }\n renderTrajectoriesInternal(viewState, noInterpolate = false) {\n const render = super.renderTrajectoriesInternal(viewState, noInterpolate);\n if (render && this.map.style) {\n const extent = getSourceCoordinates(this.map, this.pixelRatio);\n const source = this.map.getSource(this.key);\n if (source) {\n source.setCoordinates(extent);\n }\n }\n return render;\n }\n /**\n * Send the new BBOX to the websocket.\n *\n * @private\n * @override\n */\n onMoveEnd() {\n this.renderTrajectories();\n if (this.visible && this.isUpdateBboxOnMoveEnd) {\n this.setBbox();\n }\n }\n /**\n * Update the cursor style when hovering a vehicle.\n *\n * @private\n * @override\n */\n onFeatureHover(features, layer, coordinate) {\n super.onFeatureHover(features, layer, coordinate);\n this.map.getCanvasContainer().style.cursor = features.length\n ? 'pointer'\n : 'auto';\n }\n}\nexport default RealtimeLayer;\n", + "content": "import { fromLonLat } from 'ol/proj';\nimport { unByKey } from 'ol/Observable';\nimport { getWidth, getHeight } from 'ol/extent';\nimport transformRotate from '@turf/transform-rotate';\nimport { point } from '@turf/helpers';\nimport mixin from '../../common/mixins/RealtimeLayerMixin';\nimport Layer from './Layer';\nimport { getSourceCoordinates, getMercatorResolution } from '../utils';\n/**\n * Responsible for loading and display data from a Realtime service.\n *\n * @example\n * import { RealtimeLayer } from 'mobility-toolbox-js/mapbox';\n *\n * const layer = new RealtimeLayer({\n * url: [yourUrl],\n * apiKey: [yourApiKey],\n * });\n *\n *\n * @see RealtimeAPI\n *\n * @extends {Layer}\n * @implements {RealtimeLayerInterface}\n */\n// @ts-ignore\nclass RealtimeLayer extends mixin(Layer) {\n constructor(options = {}) {\n super({\n ...options,\n });\n /** @ignore */\n this.onLoad = this.onLoad.bind(this);\n /** @ignore */\n this.onMove = this.onMove.bind(this);\n /** @ignore */\n this.onMoveEnd = this.onMoveEnd.bind(this);\n /** @ignore */\n this.onZoomEnd = this.onZoomEnd.bind(this);\n /** @ignore */\n this.onVisibilityChange = this.onVisibilityChange.bind(this);\n }\n /**\n * Initialize the layer.\n *\n * @param {mapboxgl.Map} map A [mapbox Map](https://docs.mapbox.com/mapbox-gl-js/api/map/).\n * @param {string} beforeId Layer's id before which we want to add the new layer.\n * @override\n */\n // @ts-ignore\n attachToMap(map, beforeId) {\n if (!map) {\n return;\n }\n super.attachToMap(map);\n this.source = {\n type: 'canvas',\n canvas: this.canvas,\n coordinates: getSourceCoordinates(map, this.pixelRatio),\n // Set to true if the canvas source is animated. If the canvas is static, animate should be set to false to improve performance.\n animate: true,\n attribution: this.copyrights && this.copyrights.join(', '),\n };\n this.beforeId = beforeId;\n this.layer = {\n id: this.key,\n type: 'raster',\n source: this.key,\n layout: {\n visibility: this.visible ? 'visible' : 'none',\n },\n paint: {\n 'raster-opacity': 1,\n 'raster-fade-duration': 0,\n 'raster-resampling': 'nearest', // important otherwise it looks blurry\n },\n };\n if (map.isStyleLoaded()) {\n this.onLoad();\n }\n this.map.on('load', this.onLoad);\n this.listeners = [this.on('change:visible', this.onVisibilityChange)];\n }\n /**\n * Remove listeners from the Mapbox Map.\n */\n detachFromMap() {\n if (this.map) {\n this.map.off('load', this.onLoad);\n // @ts-ignore\n this.listeners.forEach((listener) => {\n unByKey(listener);\n });\n if (this.map.style && this.map.getLayer(this.key)) {\n this.map.removeLayer(this.key);\n }\n if (this.map.style && this.map.getSource(this.key)) {\n this.map.removeSource(this.key);\n }\n }\n super.detachFromMap();\n }\n /**\n * Start updating vehicles position.\n *\n * @listens {mapboxgl.map.event:zoomend} Listen to zoom end event.\n * @listens {mapboxgl.map.event:mousemove} Listen to mousemove end.\n * @override\n */\n start() {\n super.start();\n this.map.on('move', this.onMove);\n this.map.on('moveend', this.onMoveEnd);\n this.map.on('zoomend', this.onZoomEnd);\n }\n /**\n * Stop updating vehicles position, and unlisten events.\n *\n * @override\n */\n stop() {\n super.stop();\n if (this.map) {\n this.map.off('move', this.onMove);\n this.map.off('moveend', this.onMoveEnd);\n this.map.off('zoomend', this.onZoomEnd);\n }\n }\n onLoad() {\n if (!this.map.getSource(this.key)) {\n this.map.addSource(this.key, this.source);\n }\n if (!this.map.getLayer(this.key)) {\n this.map.addLayer(this.layer, this.beforeId);\n }\n }\n /**\n * Function triggered when the user moves the cursor over the map.\n * @override\n */\n onUserMoveCallback(evt) {\n super.onUserMoveCallback({\n coordinate: fromLonLat(evt.lngLat.toArray()),\n ...evt,\n });\n }\n /**\n * Render the trajectories using current map's size, resolution and rotation.\n * @param {boolean} noInterpolate if true, renders the vehicles without interpolating theirs positions.\n * @overrides\n */\n // @ts-ignore\n renderTrajectories(noInterpolate = false) {\n if (!this.map) {\n return;\n }\n if (!this.pixelRatio) {\n this.pixelRatio = 1;\n }\n const { width, height } = this.map.getCanvas();\n const center = this.map.getCenter();\n // We use turf here to have good transform.\n const leftBottom = this.map.unproject({\n x: 0,\n y: height / this.pixelRatio,\n }); // southWest\n const rightTop = this.map.unproject({\n x: width / this.pixelRatio,\n y: 0,\n }); // north east\n const coord0 = transformRotate(point([leftBottom.lng, leftBottom.lat]), -this.map.getBearing(), {\n pivot: [center.lng, center.lat],\n }).geometry.coordinates;\n const coord1 = transformRotate(point([rightTop.lng, rightTop.lat]), -this.map.getBearing(), {\n pivot: [center.lng, center.lat],\n }).geometry.coordinates;\n const bounds = [...fromLonLat(coord0), ...fromLonLat(coord1)];\n const xResolution = getWidth(bounds) / (width / this.pixelRatio);\n const yResolution = getHeight(bounds) / (height / this.pixelRatio);\n const res = Math.max(xResolution, yResolution);\n // Coordinate of trajectories are in mercator so we have to pass the proper resolution and center in mercator.\n const viewState = {\n size: [width / this.pixelRatio, height / this.pixelRatio],\n center: fromLonLat([center.lng, center.lat]),\n extent: bounds,\n resolution: res,\n zoom: this.getOlZoom(),\n rotation: -(this.map.getBearing() * Math.PI) / 180,\n pixelRatio: this.pixelRatio,\n };\n super.renderTrajectories(viewState, noInterpolate);\n }\n /**\n * Return the delay in ms before the next rendering.\n */\n getRefreshTimeInMs() {\n return super.getRefreshTimeInMs(this.map.getZoom());\n }\n getFeatureInfoAtCoordinate(coordinate, options = {}) {\n const resolution = getMercatorResolution(this.map);\n return super.getFeatureInfoAtCoordinate(coordinate, {\n resolution,\n ...options,\n });\n }\n onVisibilityChange() {\n if (this.visible && !this.map.getLayer(this.key)) {\n this.map.addLayer(this.layer, this.beforeId);\n }\n else if (this.map.getLayer(this.key)) {\n this.map.removeLayer(this.key);\n }\n // We can't use setLayoutProperty it triggers an error probably a bug in mapbox\n // this.map.setLayoutProperty(\n // this.key,\n // 'visibilty',\n // this.visible ? 'visible' : 'none',\n // );\n }\n /**\n * Remove the trajectory form the list if necessary.\n *\n * @private\n */\n purgeTrajectory(trajectory, extent, zoom) {\n return super.purgeTrajectory(trajectory, extent || this.getMercatorExtent(), zoom || Math.floor(this.getOlZoom()));\n }\n /**\n * Send the current bbox to the websocket\n */\n setBbox(extent, zoom) {\n let newExtent = extent;\n let newZoom = zoom;\n if (!newExtent && this.isUpdateBboxOnMoveEnd) {\n newExtent = extent || this.getMercatorExtent();\n newZoom = Math.floor(this.getOlZoom());\n }\n super.setBbox(newExtent, newZoom);\n }\n /**\n * Callback on 'move' event.\n *\n * @private\n */\n onMove() {\n this.renderTrajectories();\n }\n renderTrajectoriesInternal(viewState, noInterpolate = false) {\n const render = super.renderTrajectoriesInternal(viewState, noInterpolate);\n if (render && this.map.style) {\n const extent = getSourceCoordinates(this.map, this.pixelRatio);\n const source = this.map.getSource(this.key);\n if (source) {\n source.setCoordinates(extent);\n }\n }\n return render;\n }\n /**\n * Send the new BBOX to the websocket.\n *\n * @private\n * @override\n */\n onMoveEnd() {\n this.renderTrajectories();\n if (this.visible && this.isUpdateBboxOnMoveEnd) {\n this.setBbox();\n }\n }\n /**\n * Update the cursor style when hovering a vehicle.\n *\n * @private\n * @override\n */\n onFeatureHover(features, layer, coordinate) {\n super.onFeatureHover(features, layer, coordinate);\n this.map.getCanvasContainer().style.cursor = features.length\n ? 'pointer'\n : 'auto';\n }\n}\nexport default RealtimeLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/mapbox/layers/RealtimeLayer.js", "access": "public", @@ -6384,7 +6586,7 @@ "lineNumber": 1 }, { - "__docId__": 281, + "__docId__": 291, "kind": "class", "name": "RealtimeLayer", "memberof": "build/mapbox/layers/RealtimeLayer.js", @@ -6411,7 +6613,7 @@ ] }, { - "__docId__": 282, + "__docId__": 292, "kind": "constructor", "name": "constructor", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6425,7 +6627,7 @@ "undocument": true }, { - "__docId__": 286, + "__docId__": 296, "kind": "member", "name": "onZoomEnd", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6433,7 +6635,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onZoomEnd", "access": "public", "description": null, - "lineNumber": 37, + "lineNumber": 39, "ignore": true, "type": { "types": [ @@ -6442,7 +6644,7 @@ } }, { - "__docId__": 288, + "__docId__": 298, "kind": "method", "name": "attachToMap", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6452,7 +6654,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#attachToMap", "access": "public", "description": "Initialize the layer.", - "lineNumber": 49, + "lineNumber": 51, "params": [ { "nullable": null, @@ -6479,7 +6681,7 @@ "return": null }, { - "__docId__": 289, + "__docId__": 299, "kind": "member", "name": "source", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6487,7 +6689,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#source", "access": "public", "description": null, - "lineNumber": 54, + "lineNumber": 56, "undocument": true, "type": { "types": [ @@ -6496,7 +6698,7 @@ } }, { - "__docId__": 290, + "__docId__": 300, "kind": "member", "name": "beforeId", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6504,7 +6706,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#beforeId", "access": "public", "description": null, - "lineNumber": 62, + "lineNumber": 64, "undocument": true, "type": { "types": [ @@ -6513,7 +6715,7 @@ } }, { - "__docId__": 291, + "__docId__": 301, "kind": "member", "name": "layer", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6521,7 +6723,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#layer", "access": "public", "description": null, - "lineNumber": 63, + "lineNumber": 65, "undocument": true, "type": { "types": [ @@ -6530,7 +6732,7 @@ } }, { - "__docId__": 292, + "__docId__": 302, "kind": "member", "name": "listeners", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6538,7 +6740,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#listeners", "access": "public", "description": null, - "lineNumber": 80, + "lineNumber": 82, "undocument": true, "type": { "types": [ @@ -6547,7 +6749,7 @@ } }, { - "__docId__": 293, + "__docId__": 303, "kind": "method", "name": "detachFromMap", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6557,12 +6759,12 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#detachFromMap", "access": "public", "description": "Remove listeners from the Mapbox Map.", - "lineNumber": 85, + "lineNumber": 87, "params": [], "return": null }, { - "__docId__": 294, + "__docId__": 304, "kind": "method", "name": "start", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6572,7 +6774,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#start", "access": "public", "description": "Start updating vehicles position.", - "lineNumber": 108, + "lineNumber": 110, "override": true, "listens": [ { @@ -6592,7 +6794,7 @@ "return": null }, { - "__docId__": 295, + "__docId__": 305, "kind": "method", "name": "stop", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6602,13 +6804,13 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#stop", "access": "public", "description": "Stop updating vehicles position, and unlisten events.", - "lineNumber": 119, + "lineNumber": 121, "override": true, "params": [], "return": null }, { - "__docId__": 296, + "__docId__": 306, "kind": "method", "name": "onLoad", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6618,13 +6820,13 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onLoad", "access": "public", "description": null, - "lineNumber": 127, + "lineNumber": 129, "undocument": true, "params": [], "return": null }, { - "__docId__": 297, + "__docId__": 307, "kind": "method", "name": "onUserMoveCallback", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6634,7 +6836,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onUserMoveCallback", "access": "public", "description": "Function triggered when the user moves the cursor over the map.", - "lineNumber": 139, + "lineNumber": 141, "override": true, "params": [ { @@ -6647,7 +6849,7 @@ "return": null }, { - "__docId__": 298, + "__docId__": 308, "kind": "method", "name": "renderTrajectories", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6657,7 +6859,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#renderTrajectories", "access": "public", "description": "Render the trajectories using current map's size, resolution and rotation.", - "lineNumber": 148, + "lineNumber": 153, "unknown": [ { "tagName": "@overrides", @@ -6679,7 +6881,7 @@ "return": null }, { - "__docId__": 299, + "__docId__": 309, "kind": "member", "name": "pixelRatio", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6687,7 +6889,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#pixelRatio", "access": "public", "description": null, - "lineNumber": 153, + "lineNumber": 158, "undocument": true, "type": { "types": [ @@ -6696,7 +6898,7 @@ } }, { - "__docId__": 300, + "__docId__": 310, "kind": "method", "name": "getRefreshTimeInMs", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6706,7 +6908,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#getRefreshTimeInMs", "access": "public", "description": "Return the delay in ms before the next rendering.", - "lineNumber": 191, + "lineNumber": 196, "params": [], "return": { "types": [ @@ -6715,7 +6917,7 @@ } }, { - "__docId__": 301, + "__docId__": 311, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6725,7 +6927,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#getFeatureInfoAtCoordinate", "access": "public", "description": null, - "lineNumber": 194, + "lineNumber": 199, "undocument": true, "params": [ { @@ -6751,7 +6953,7 @@ } }, { - "__docId__": 302, + "__docId__": 312, "kind": "method", "name": "onVisibilityChange", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6761,13 +6963,13 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onVisibilityChange", "access": "public", "description": null, - "lineNumber": 198, + "lineNumber": 206, "undocument": true, "params": [], "return": null }, { - "__docId__": 303, + "__docId__": 313, "kind": "method", "name": "purgeTrajectory", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6777,7 +6979,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#purgeTrajectory", "access": "private", "description": "Remove the trajectory form the list if necessary.", - "lineNumber": 217, + "lineNumber": 225, "params": [ { "name": "trajectory", @@ -6805,7 +7007,7 @@ } }, { - "__docId__": 304, + "__docId__": 314, "kind": "method", "name": "setBbox", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6815,7 +7017,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#setBbox", "access": "public", "description": "Send the current bbox to the websocket", - "lineNumber": 223, + "lineNumber": 231, "params": [ { "name": "extent", @@ -6833,7 +7035,7 @@ "return": null }, { - "__docId__": 305, + "__docId__": 315, "kind": "method", "name": "onMove", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6843,12 +7045,12 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onMove", "access": "private", "description": "Callback on 'move' event.", - "lineNumber": 237, + "lineNumber": 245, "params": [], "return": null }, { - "__docId__": 306, + "__docId__": 316, "kind": "method", "name": "renderTrajectoriesInternal", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6858,7 +7060,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#renderTrajectoriesInternal", "access": "public", "description": null, - "lineNumber": 240, + "lineNumber": 248, "undocument": true, "params": [ { @@ -6884,7 +7086,7 @@ } }, { - "__docId__": 307, + "__docId__": 317, "kind": "method", "name": "onMoveEnd", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6894,13 +7096,13 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onMoveEnd", "access": "private", "description": "Send the new BBOX to the websocket.", - "lineNumber": 257, + "lineNumber": 265, "override": true, "params": [], "return": null }, { - "__docId__": 308, + "__docId__": 318, "kind": "method", "name": "onFeatureHover", "memberof": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer", @@ -6910,7 +7112,7 @@ "longname": "build/mapbox/layers/RealtimeLayer.js~RealtimeLayer#onFeatureHover", "access": "private", "description": "Update the cursor style when hovering a vehicle.", - "lineNumber": 269, + "lineNumber": 277, "override": true, "params": [ { @@ -6935,7 +7137,7 @@ "return": null }, { - "__docId__": 309, + "__docId__": 319, "kind": "file", "name": "build/mapbox/layers/index.js", "content": "export { default as Layer } from './Layer';\nexport { default as RealtimeLayer } from './RealtimeLayer';\n", @@ -6946,10 +7148,10 @@ "lineNumber": 1 }, { - "__docId__": 310, + "__docId__": 320, "kind": "file", "name": "build/ol/controls/CopyrightControl.js", - "content": "import { inView } from 'ol/layer/Layer';\nimport CopyrightControlCommon from '../../common/controls/CopyrightControlCommon';\nimport removeDuplicate from '../../common/utils/removeDuplicate';\n/**\n * Display layer's copyrights.\n *\n * @example\n * import { Map } from 'ol';\n * import { CopyrightControl } from 'mobility-toolbox-js/ol';\n *\n * const map = new Map({\n * target: 'map',\n * });\n * const control = new CopyrightControl();\n * control.attachToMap(map)\n *\n *\n * @see Openlayers copyright example\n *\n * @extends {CopyrightControlCommon}\n */\nclass CopyrightControl extends CopyrightControlCommon {\n constructor(options) {\n super(options);\n this.onPostRender = this.onPostRender.bind(this);\n }\n getCopyrights() {\n var _a;\n if (!this.frameState) {\n return [];\n }\n let copyrights = [];\n // This code loop comes mainly from ol.\n (_a = this.frameState) === null || _a === void 0 ? void 0 : _a.layerStatesArray.forEach((layerState) => {\n const { layer } = layerState;\n if (this.frameState &&\n inView(layerState, this.frameState.viewState) &&\n layer &&\n layer.getSource &&\n layer.getSource() &&\n layer.getSource().getAttributions()) {\n copyrights = copyrights.concat(layer.getSource().getAttributions()(this.frameState));\n }\n });\n return removeDuplicate(copyrights);\n }\n activate() {\n super.activate();\n if (this.map) {\n this.map.on('postrender', this.onPostRender);\n }\n }\n deactivate() {\n if (this.map) {\n this.map.un('postrender', this.onPostRender);\n }\n }\n onPostRender(evt) {\n if (this.map && this.element) {\n /**\n * @ignore\n */\n this.frameState = evt.frameState || undefined;\n this.render();\n }\n }\n}\nexport default CopyrightControl;\n", + "content": "import { inView } from 'ol/layer/Layer';\nimport CopyrightControlCommon from '../../common/controls/CopyrightControlCommon';\nimport removeDuplicate from '../../common/utils/removeDuplicate';\n/**\n * Display layer's copyrights.\n *\n * @example\n * import { Map } from 'ol';\n * import { CopyrightControl } from 'mobility-toolbox-js/ol';\n *\n * const map = new Map({\n * target: 'map',\n * });\n * const control = new CopyrightControl();\n * control.attachToMap(map)\n *\n *\n * @see Openlayers copyright example\n *\n * @extends {CopyrightControlCommon}\n */\nclass CopyrightControl extends CopyrightControlCommon {\n constructor(options) {\n super(options);\n this.onPostRender = this.onPostRender.bind(this);\n }\n getCopyrights() {\n if (!this.frameState) {\n return [];\n }\n let copyrights = [];\n // This code loop comes mainly from ol.\n this.frameState?.layerStatesArray.forEach((layerState) => {\n const { layer } = layerState;\n if (this.frameState &&\n inView(layerState, this.frameState.viewState) &&\n layer &&\n layer.getSource &&\n layer.getSource() &&\n layer.getSource().getAttributions()) {\n copyrights = copyrights.concat(layer.getSource().getAttributions()(this.frameState));\n }\n });\n return removeDuplicate(copyrights);\n }\n activate() {\n super.activate();\n if (this.map) {\n this.map.on('postrender', this.onPostRender);\n }\n }\n deactivate() {\n if (this.map) {\n this.map.un('postrender', this.onPostRender);\n }\n }\n onPostRender(evt) {\n if (this.map && this.element) {\n /**\n * @ignore\n */\n this.frameState = evt.frameState || undefined;\n this.render();\n }\n }\n}\nexport default CopyrightControl;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/controls/CopyrightControl.js", "access": "public", @@ -6957,7 +7159,7 @@ "lineNumber": 1 }, { - "__docId__": 311, + "__docId__": 321, "kind": "class", "name": "CopyrightControl", "memberof": "build/ol/controls/CopyrightControl.js", @@ -6981,7 +7183,7 @@ ] }, { - "__docId__": 312, + "__docId__": 322, "kind": "constructor", "name": "constructor", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -6995,7 +7197,7 @@ "undocument": true }, { - "__docId__": 314, + "__docId__": 324, "kind": "method", "name": "getCopyrights", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -7015,7 +7217,7 @@ } }, { - "__docId__": 315, + "__docId__": 325, "kind": "method", "name": "activate", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -7025,13 +7227,13 @@ "longname": "build/ol/controls/CopyrightControl.js~CopyrightControl#activate", "access": "public", "description": null, - "lineNumber": 47, + "lineNumber": 46, "undocument": true, "params": [], "return": null }, { - "__docId__": 316, + "__docId__": 326, "kind": "method", "name": "deactivate", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -7041,13 +7243,13 @@ "longname": "build/ol/controls/CopyrightControl.js~CopyrightControl#deactivate", "access": "public", "description": null, - "lineNumber": 53, + "lineNumber": 52, "undocument": true, "params": [], "return": null }, { - "__docId__": 317, + "__docId__": 327, "kind": "method", "name": "onPostRender", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -7057,7 +7259,7 @@ "longname": "build/ol/controls/CopyrightControl.js~CopyrightControl#onPostRender", "access": "public", "description": null, - "lineNumber": 58, + "lineNumber": 57, "undocument": true, "params": [ { @@ -7070,7 +7272,7 @@ "return": null }, { - "__docId__": 318, + "__docId__": 328, "kind": "member", "name": "frameState", "memberof": "build/ol/controls/CopyrightControl.js~CopyrightControl", @@ -7078,7 +7280,7 @@ "longname": "build/ol/controls/CopyrightControl.js~CopyrightControl#frameState", "access": "public", "description": "", - "lineNumber": 63, + "lineNumber": 62, "ignore": true, "type": { "types": [ @@ -7087,10 +7289,10 @@ } }, { - "__docId__": 319, + "__docId__": 329, "kind": "file", "name": "build/ol/controls/RoutingControl.js", - "content": "import { Feature } from 'ol';\nimport { LineString, Point } from 'ol/geom';\nimport { Modify } from 'ol/interaction';\nimport { unByKey } from 'ol/Observable';\nimport { click } from 'ol/events/condition';\nimport { GeoJSON } from 'ol/format';\nimport { buffer } from 'ol/extent';\nimport { fromLonLat, toLonLat } from 'ol/proj';\nimport BaseEvent from 'ol/events/Event';\nimport { RoutingAPI } from '../../api';\nimport ControlCommon from '../../common/controls/ControlCommon';\nimport RoutingLayer from '../layers/RoutingLayer';\n// Examples for a single hop:\n// basel sbb a station named \"basel sbb\"\n// ZUE, station \"Zürich HB\" by its common abbreviation\n// Zürich Hauptbahnhof or HBF Zürich are all valid synonyms für \"Zürich HB\"\n// @47.37811,8.53935 a station at position 47.37811, 8.53935\n// @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935\n// zürich hb@47.37811,8.53935$8 track 8 in station \"Zürich HB\" at position 47.37811, 8.53935\n/** @private */\nconst REGEX_VIA_POINT = /^([^@$!\\n]*)(@?([\\d.]+),([\\d.]+))?(\\$?([a-zA-Z0-9]{0,2}))$/;\n// Examples for a single hop:\n//\n// 47.37811,8.53935 a position 47.37811, 8.53935\n/** @private */\nconst REGEX_VIA_POINT_COORD = /^([\\d.]+),([\\d.]+)$/;\n// Examples for a single hop:\n//\n// !8596126 a station with id 8596126\n// !8596126$4 a station with id 8596126\n/** @private */\nconst REGEX_VIA_POINT_STATION_ID = /^!([^$]*)(\\$?([a-zA-Z0-9]{0,2}))$/;\n/** @private */\nconst STOP_FETCH_ABORT_CONTROLLER_KEY = 'stop-fetch';\n/** @private */\nconst getFlatCoordinatesFromSegments = (segmentArray) => {\n const coords = [];\n segmentArray.forEach((seg) => {\n var _a;\n // @ts-ignore\n const coordArr = (_a = seg.getGeometry()) === null || _a === void 0 ? void 0 : _a.getCoordinates();\n if (coordArr === null || coordArr === void 0 ? void 0 : coordArr.length) {\n coords.push(...coordArr);\n }\n });\n return coords;\n};\n/**\n * Display a route of a specified mean of transport.\n *\n * @example\n * import { Map } from 'ol';\n * import { RoutingControl } from 'mobility-toolbox-js/ol';\n *\n * const map = new Map({\n * target: 'map'\n * });\n *\n * const control = new RoutingControl();\n *\n * control.attachToMap(map)\n *\n * @classproperty {string} apiKey - Key used for RoutingApi requests.\n * @classproperty {string} stopsApiKey - Key used for Stop lookup requests (defaults to apiKey).\n * @classproperty {string} stopsApiUrl - Url used for Stop lookup requests (defaults to https://api.geops.io/stops/v1/lookup/).\n * @classproperty {Array.>} graphs - 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]]\"\n * @classproperty {string} mot - Mean of transport to be used for routing.\n * @classproperty {object} routingApiParams - object of additional parameters to pass to the routing api request.\n * @classproperty {object} snapToClosestStation - If true, the routing will snap the coordinate to the closest station. Default to false.\n * @classproperty {boolean} useRawViaPoints - Experimental property. Wen 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.\n * @classproperty {RoutingLayer|Layer} routingLayer - Layer for adding route features.\n * @classproperty {function} onRouteError - Callback on error.\n * @classproperty {boolean} loading - True if the control is requesting the backend.\n * @see Openlayers routing example\n *\n * @extends {Control}\n * @implements {RoutingInterface}\n */\nclass RoutingControl extends ControlCommon {\n constructor(options = {}) {\n super(options);\n this.viaPoints = [];\n this.loading = false;\n this.graphs = [];\n this.modify = true;\n this.useRawViaPoints = false;\n this.snapToClosestStation = false;\n this.cacheStationData = {};\n this.abortControllers = {};\n this.segments = [];\n this.format = new GeoJSON({ featureProjection: 'EPSG:3857' });\n this.initialRouteDrag = {};\n Object.defineProperties(this, {\n mot: {\n get: () => this.get('mot'),\n set: (newMot) => {\n if (newMot) {\n this.set('mot', newMot);\n if (this.viaPoints) {\n this.drawRoute();\n }\n }\n },\n },\n loading: {\n get: () => this.get('loading'),\n set: (newLoading) => {\n this.set('loading', newLoading);\n },\n },\n modify: {\n get: () => this.get('modify'),\n set: (modify) => {\n this.set('modify', modify);\n },\n },\n });\n /** True if the control is requesting the backend. */\n this.loading = false;\n /** @ignore */\n this.graphs = options.graphs || [['osm', 0, 99]];\n /** @ignore */\n this.mot = options.mot || 'bus';\n /** @ignore */\n this.modify = options.modify !== false;\n /** @ignore */\n this.routingApiParams = options.routingApiParams;\n /** @ignore */\n this.useRawViaPoints = options.useRawViaPoints || false;\n /** @ignore */\n this.snapToClosestStation = options.snapToClosestStation || false;\n /** @ignore */\n this.apiKey = options.apiKey;\n /** @ignore */\n this.stopsApiKey = options.stopsApiKey || this.apiKey;\n /** @ignore */\n this.stopsApiUrl = options.stopsApiUrl || 'https://api.geops.io/stops/v1/';\n /** @ignore */\n this.api = new RoutingAPI(Object.assign({}, options));\n /** @ignore */\n this.routingLayer =\n options.routingLayer ||\n new RoutingLayer({\n name: 'routing-layer',\n style: options.style,\n });\n /** @ignore */\n this.onRouteError =\n options.onRouteError ||\n ((error) => {\n this.dispatchEvent(new BaseEvent('change:route'));\n this.reset();\n // eslint-disable-next-line no-console\n console.error(error);\n });\n /** @ignore */\n this.onMapClick = this.onMapClick.bind(this);\n /** @ignore */\n this.onModifyEnd = this.onModifyEnd.bind(this);\n /** @ignore */\n this.onModifyStart = this.onModifyStart.bind(this);\n /** @ignore */\n this.createModifyInteraction();\n }\n /**\n * Calculate at which resolutions corresponds each generalizations.\n *\n * @private\n */\n static getGraphsResolutions(graphs, map) {\n const view = map.getView();\n return graphs.map(([, minZoom, maxZoom]) => [\n view.getResolutionForZoom(minZoom),\n view.getResolutionForZoom(maxZoom || minZoom + 1),\n ]);\n }\n /**\n * Adds/Replaces a viaPoint to the viaPoints array and redraws route:\n * Adds a viaPoint at end of array by default.\n * If an index is passed a viaPoint is added at the specified index.\n * If an index is passed and overwrite x is > 0, x viaPoints at the specified\n * index are replaced with a single new viaPoint.\n * @param {number[]|string} coordinates Array of coordinates\n * @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.\n * @param {number} [overwrite=0] Marks the number of viaPoints that are removed at the specified index on add.\n */\n addViaPoint(coordinatesOrString, index = -1, overwrite = 0) {\n /* Add/Insert/Overwrite viapoint and redraw route */\n this.viaPoints.splice(index === -1 ? this.viaPoints.length : index, overwrite, coordinatesOrString);\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Removes a viaPoint at the passed array index and redraws route\n * By default the last viaPoint is removed.\n * @param {number} index Integer representing the index of the viaPoint to delete.\n */\n removeViaPoint(index = (this.viaPoints || []).length - 1) {\n /* Remove viapoint and redraw route */\n if (this.viaPoints.length && this.viaPoints[index]) {\n this.viaPoints.splice(index, 1);\n }\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Replaces the current viaPoints with a new coordinate array.\n * @param {Array>} coordinateArray Array of nested coordinates\n */\n setViaPoints(coordinateArray) {\n this.viaPoints = [...coordinateArray];\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Removes all viaPoints, clears the source and triggers a change event\n */\n reset() {\n var _a, _b, _c;\n // Clear viaPoints and source\n this.abortRequests();\n this.viaPoints = [];\n (_c = (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.clear();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Aborts viapoint and route requests\n * @private\n */\n abortRequests() {\n var _a;\n // Abort Routing API requests\n this.graphs.forEach((graph) => {\n const graphName = graph[0];\n if (this.abortControllers[graphName]) {\n this.abortControllers[graphName].abort();\n }\n this.abortControllers[graphName] = new AbortController();\n });\n // Abort Stops API requests\n (_a = this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]) === null || _a === void 0 ? void 0 : _a.abort();\n this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY] =\n new AbortController();\n this.loading = false;\n }\n /**\n * Draws route on map using an array of coordinates:\n * If a single coordinate is passed a single point feature is added to map.\n * If two or more coordinates are passed a request to the RoutingAPI fetches\n * the route using the passed coordinates and the current mot.\n * @private\n */\n drawRoute() {\n var _a, _b, _c, _d, _e, _f;\n /* Calls RoutingAPI to draw a route using the viaPoints array */\n this.abortRequests();\n (_c = (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.clear();\n if (!this.viaPoints.length) {\n return null;\n }\n if (this.viaPoints.length === 1) {\n // Add point for first node\n return this.drawViaPoint(this.viaPoints[0], 0, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]);\n }\n const formattedViaPoints = this.viaPoints.map((viaPoint) => {\n var _a;\n if (Array.isArray(viaPoint)) {\n const projection = (_a = this.map) === null || _a === void 0 ? void 0 : _a.getView().getProjection();\n // viaPoint is a coordinate\n // Coordinates need to be reversed as required by the backend RoutingAPI\n const [lon, lat] = toLonLat(viaPoint, projection);\n return this.snapToClosestStation ? [`@${lat}`, lon] : [lat, lon];\n }\n // viaPoint is a string to use as it is\n return this.useRawViaPoints ? viaPoint : `!${viaPoint}`;\n });\n this.loading = true;\n // Clear source\n (_f = (_e = (_d = this.routingLayer) === null || _d === void 0 ? void 0 : _d.olLayer) === null || _e === void 0 ? void 0 : _e.getSource()) === null || _f === void 0 ? void 0 : _f.clear();\n // Create point features for the viaPoints\n this.viaPoints.forEach((viaPoint, idx) => this.drawViaPoint(viaPoint, idx, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]));\n return Promise.all(this.graphs.map(([graph], index) => {\n const { signal } = this.abortControllers[graph];\n if (!this.api) {\n return Promise.resolve([]);\n }\n return this.api\n .route(Object.assign({ graph, via: `${formattedViaPoints.join('|')}`, mot: this.mot, \n // @ts-ignore missing property in swagger\n 'resolve-hops': false, elevation: false, 'coord-radius': 100.0, 'coord-punish': 1000.0 }, (this.routingApiParams || {})), { signal })\n .then((featureCollection) => {\n var _a, _b, _c, _d;\n this.segments = this.format.readFeatures(featureCollection);\n if (this.mot === 'foot') {\n // Extract unique values from viaPoint target value\n const uniqueVias = this.segments.reduce((resultVias, currentFeat) => {\n const segTrg = currentFeat.get('trg');\n return resultVias.find((via) => via[0] === segTrg[0] && via[1] === segTrg[1])\n ? resultVias\n : [...resultVias, segTrg];\n }, []);\n // Create LineString features from segments with same unique value\n this.segments = uniqueVias.map((via) => {\n const viaSegments = this.segments.filter((seg) => {\n const segTrg = seg.get('trg');\n return segTrg[0] === via[0] && segTrg[1] === via[1];\n });\n const coords = getFlatCoordinatesFromSegments(viaSegments);\n return new Feature({\n geometry: new LineString(coords),\n });\n });\n }\n // Create the new route. This route will be modifiable by the Modifiy interaction.\n const coords = getFlatCoordinatesFromSegments(this.segments);\n const routeFeature = new Feature({\n geometry: new LineString(coords),\n });\n routeFeature.set('graph', graph);\n routeFeature.set('mot', this.mot);\n if (this.graphsResolutions &&\n ((_a = this.graphsResolutions[index]) === null || _a === void 0 ? void 0 : _a.length) >= 2) {\n routeFeature.set('minResolution', this.graphsResolutions[index][0]);\n routeFeature.set('maxResolution', this.graphsResolutions[index][1]);\n }\n (_d = (_c = (_b = this.routingLayer) === null || _b === void 0 ? void 0 : _b.olLayer) === null || _c === void 0 ? void 0 : _c.getSource()) === null || _d === void 0 ? void 0 : _d.addFeature(routeFeature);\n this.loading = false;\n })\n .catch((error) => {\n if (error.name === 'AbortError') {\n // Ignore abort error\n return;\n }\n this.segments = [];\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n });\n }));\n }\n /**\n * Draw a via point. This function can parse all the possibilitiies\n *\n * @private\n */\n drawViaPoint(viaPoint, idx, abortController) {\n var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;\n const pointFeature = new Feature();\n pointFeature.set('viaPointIdx', idx);\n // The via point is a coordinate using the current map's projection\n if (Array.isArray(viaPoint)) {\n pointFeature.setGeometry(new Point(viaPoint));\n (_c = (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n // Possibility to parse:\n //\n // !8596126 a station with id 8596126\n // !8596126$4 a station with id 8596126\n if (!this.useRawViaPoints || REGEX_VIA_POINT_STATION_ID.test(viaPoint)) {\n let stationId;\n let track;\n if (this.useRawViaPoints) {\n [, stationId, , track] =\n REGEX_VIA_POINT_STATION_ID.exec(viaPoint) || [];\n }\n else {\n [stationId, track] = viaPoint.split('$');\n }\n return fetch(`${this.stopsApiUrl}lookup/${stationId}?key=${this.stopsApiKey}`, { signal: abortController.signal })\n .then((res) => res.json())\n .then((stationData) => {\n var _a, _b, _c;\n const { coordinates } = stationData.features[0].geometry;\n this.cacheStationData[viaPoint] = fromLonLat(coordinates);\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(fromLonLat(coordinates)));\n (_c = (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.addFeature(pointFeature);\n return pointFeature;\n })\n .catch((error) => {\n if (error.name === 'AbortError') {\n // Ignore abort error\n return;\n }\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n });\n }\n // Only when this.useRawViaPoints is true.\n // Possibility to parse:\n //\n // 47.37811,8.53935 a position 47.37811, 8.53935\n if (this.useRawViaPoints && REGEX_VIA_POINT_COORD.test(viaPoint)) {\n const [lat, lon] = REGEX_VIA_POINT_COORD.exec(viaPoint) || [];\n const coordinates = fromLonLat([parseFloat(lon), parseFloat(lat)], (_d = this.map) === null || _d === void 0 ? void 0 : _d.getView().getProjection());\n pointFeature.setGeometry(new Point(coordinates));\n (_g = (_f = (_e = this.routingLayer) === null || _e === void 0 ? void 0 : _e.olLayer) === null || _f === void 0 ? void 0 : _f.getSource()) === null || _g === void 0 ? void 0 : _g.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n // Only when this.useRawViaPoints is true.\n // It will parse the via point to find some name, id, track coordinates.\n //\n // Possibility to parse:\n //\n // @47.37811,8.53935 a station at position 47.37811, 8.53935\n // @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935\n // zürich hb@47.37811,8.53935$8 track 8 in station \"Zürich HB\" at position 47.37811, 8.53935\n const [, stationName, , lat, lon, , track] = REGEX_VIA_POINT.exec(viaPoint) || [];\n if (lon && lat) {\n const coordinates = fromLonLat([parseFloat(lon), parseFloat(lat)], (_h = this.map) === null || _h === void 0 ? void 0 : _h.getView().getProjection());\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(coordinates));\n (_l = (_k = (_j = this.routingLayer) === null || _j === void 0 ? void 0 : _j.olLayer) === null || _k === void 0 ? void 0 : _k.getSource()) === null || _l === void 0 ? void 0 : _l.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n if (stationName) {\n return fetch(`${this.stopsApiUrl}?key=${this.stopsApiKey}&q=${stationName}&limit=1`, { signal: abortController.signal })\n .then((res) => res.json())\n .then((stationData) => {\n var _a, _b, _c;\n const { coordinates } = stationData.features[0].geometry;\n this.cacheStationData[viaPoint] = fromLonLat(coordinates);\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(fromLonLat(coordinates)));\n (_c = (_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.addFeature(pointFeature);\n return pointFeature;\n })\n .catch((error) => {\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n return null;\n });\n }\n return Promise.resolve(null);\n }\n /**\n * Used on click on map while control is active:\n * By default adds a viaPoint to the end of array.\n * If an existing viaPoint is clicked removes the clicked viaPoint.\n * @private\n */\n onMapClick(evt) {\n const feats = evt.target.getFeaturesAtPixel(evt.pixel);\n const viaPoint = feats.find((feat) => {\n var _a;\n return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point' &&\n feat.get('viaPointIdx') !== undefined;\n });\n if (viaPoint) {\n // Remove existing viaPoint on click and abort viaPoint add\n this.removeViaPoint(viaPoint.get('viaPointIdx'));\n return;\n }\n this.addViaPoint(evt.coordinate);\n }\n /**\n * Used on start of the modify interaction. Stores relevant data\n * in this.initialRouteDrag object\n * @private\n */\n onModifyStart(evt) {\n var _a;\n // When modify start, we search the index of the segment that is modifying.\n let segmentIndex = -1;\n const route = evt.features\n .getArray()\n .find((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'LineString'; });\n // Find the segment index that is being modified\n if (route && route.getGeometry() && evt.mapBrowserEvent.coordinate) {\n // We use a buff extent to fix floating issues , see https://github.com/openlayers/openlayers/issues/7130#issuecomment-535856422\n const closestExtent = buffer(new Point(\n // @ts-ignore\n (_a = route.getGeometry()) === null || _a === void 0 ? void 0 : _a.getClosestPoint(evt.mapBrowserEvent.coordinate)).getExtent(), 0.001);\n segmentIndex = this.segments.findIndex((segment) => { var _a; return (_a = segment.getGeometry()) === null || _a === void 0 ? void 0 : _a.intersectsExtent(closestExtent); });\n }\n // Find the viaPoint that is being modified\n const viaPoint = (evt.features\n .getArray()\n .filter((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point'; }) ||\n [])[0];\n // Write object with modify info\n /** @ignore */\n this.initialRouteDrag = {\n viaPoint,\n oldRoute: route && route.clone(),\n segmentIndex,\n };\n }\n /**\n * Used on end of the modify interaction. Resolves feature modification:\n * Line drag creates new viaPoint at the final coordinate of drag.\n * Point drag replaces old viaPoint.\n * @private\n */\n onModifyEnd(evt) {\n const coord = evt.mapBrowserEvent.coordinate;\n const { oldRoute, viaPoint, segmentIndex } = this.initialRouteDrag || {};\n // If viaPoint is being relocated overwrite the old viaPoint\n if (viaPoint) {\n return this.addViaPoint(coord, viaPoint.get('viaPointIdx'), 1);\n }\n // In case there is no route overwrite first coordinate\n if (!oldRoute) {\n return this.addViaPoint(coord, 0, 1);\n }\n // We can't add a via point because we haven't found which segment has been modified.\n if (segmentIndex === -1) {\n return Promise.reject(new Error('No segment found'));\n }\n // Insert new viaPoint at the modified segment index + 1\n return this.addViaPoint(coord, (segmentIndex || 0) + 1);\n }\n /**\n * Define a default element.\n *\n * @private\n */\n createDefaultElement() {\n /** @ignore */\n this.element = document.createElement('button');\n this.element.id = 'ol-toggle-routing';\n this.element.innerHTML = 'Toggle Route Control';\n this.element.onclick = () => this.active ? this.deactivate() : this.activate();\n Object.assign(this.element.style, {\n position: 'absolute',\n right: '10px',\n top: '10px',\n });\n }\n /**\n * Create the interaction used to modify vertexes of features.\n * @private\n */\n createModifyInteraction() {\n var _a, _b, _c;\n /**\n * @type {ol.interaction.Modify}\n * @private\n */\n // Define and add modify interaction\n this.modifyInteraction = new Modify({\n source: ((_b = (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) || undefined,\n pixelTolerance: 4,\n hitDetection: (_c = this.routingLayer) === null || _c === void 0 ? void 0 : _c.olLayer,\n deleteCondition: (e) => {\n const feats = e.target.getFeaturesAtPixel(e.pixel, {\n hitTolerance: 5,\n });\n const viaPoint = feats.find((feat) => { var _a; return ((_a = feat.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType()) === 'Point' && feat.get('index'); });\n if (click(e) && viaPoint) {\n // Remove node & viaPoint if an existing viaPoint was clicked\n this.removeViaPoint(viaPoint.get('index'));\n return true;\n }\n return false;\n },\n });\n this.modifyInteraction.on('modifystart', this.onModifyStart);\n this.modifyInteraction.on('modifyend', this.onModifyEnd);\n this.modifyInteraction.setActive(false);\n }\n /**\n * Add click listener to map.\n * @private\n */\n addListeners() {\n var _a;\n if (!this.modify) {\n return;\n }\n this.removeListeners();\n /** @ignore */\n this.onMapClickKey = (_a = this.map) === null || _a === void 0 ? void 0 : _a.on('singleclick', this.onMapClick);\n }\n /**\n * Remove click listener from map.\n * @private\n */\n removeListeners() {\n if (this.onMapClickKey) {\n unByKey(this.onMapClickKey);\n }\n }\n activate() {\n var _a, _b;\n super.activate();\n if (this.map) {\n /** @ignore */\n this.format = new GeoJSON({\n featureProjection: this.map.getView().getProjection(),\n });\n /** @ignore */\n this.graphsResolutions = RoutingControl.getGraphsResolutions(this.graphs, this.map);\n // Clean the modifyInteraction if present\n if (this.modifyInteraction) {\n this.map.removeInteraction(this.modifyInteraction);\n }\n // Add modify interaction, RoutingLayer and listeners\n (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.attachToMap(this.map);\n if (this.modifyInteraction) {\n this.map.addInteraction(this.modifyInteraction);\n }\n (_b = this.modifyInteraction) === null || _b === void 0 ? void 0 : _b.setActive(this.modify);\n this.addListeners();\n }\n }\n deactivate() {\n var _a;\n if (this.map) {\n // Remove modify interaction, RoutingLayer, listeners and viaPoints\n (_a = this.routingLayer) === null || _a === void 0 ? void 0 : _a.detachFromMap();\n if (this.modifyInteraction) {\n this.map.removeInteraction(this.modifyInteraction);\n }\n this.removeListeners();\n this.reset();\n }\n }\n render() { }\n}\nexport default RoutingControl;\n", + "content": "import { Feature } from 'ol';\nimport { LineString, Point } from 'ol/geom';\nimport { Modify } from 'ol/interaction';\nimport { unByKey } from 'ol/Observable';\nimport { click } from 'ol/events/condition';\nimport { GeoJSON } from 'ol/format';\nimport { buffer } from 'ol/extent';\nimport { fromLonLat, toLonLat } from 'ol/proj';\nimport BaseEvent from 'ol/events/Event';\nimport { RoutingAPI } from '../../api';\nimport ControlCommon from '../../common/controls/ControlCommon';\nimport RoutingLayer from '../layers/RoutingLayer';\n// Examples for a single hop:\n// basel sbb a station named \"basel sbb\"\n// ZUE, station \"Zürich HB\" by its common abbreviation\n// Zürich Hauptbahnhof or HBF Zürich are all valid synonyms für \"Zürich HB\"\n// @47.37811,8.53935 a station at position 47.37811, 8.53935\n// @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935\n// zürich hb@47.37811,8.53935$8 track 8 in station \"Zürich HB\" at position 47.37811, 8.53935\n/** @private */\nconst REGEX_VIA_POINT = /^([^@$!\\n]*)(@?([\\d.]+),([\\d.]+))?(\\$?([a-zA-Z0-9]{0,2}))$/;\n// Examples for a single hop:\n//\n// 47.37811,8.53935 a position 47.37811, 8.53935\n/** @private */\nconst REGEX_VIA_POINT_COORD = /^([\\d.]+),([\\d.]+)$/;\n// Examples for a single hop:\n//\n// !8596126 a station with id 8596126\n// !8596126$4 a station with id 8596126\n/** @private */\nconst REGEX_VIA_POINT_STATION_ID = /^!([^$]*)(\\$?([a-zA-Z0-9]{0,2}))$/;\n/** @private */\nconst STOP_FETCH_ABORT_CONTROLLER_KEY = 'stop-fetch';\n/** @private */\nconst getFlatCoordinatesFromSegments = (segmentArray) => {\n const coords = [];\n segmentArray.forEach((seg) => {\n // @ts-ignore\n const coordArr = seg.getGeometry()?.getCoordinates();\n if (coordArr?.length) {\n coords.push(...coordArr);\n }\n });\n return coords;\n};\n/**\n * Display a route of a specified mean of transport.\n *\n * @example\n * import { Map } from 'ol';\n * import { RoutingControl } from 'mobility-toolbox-js/ol';\n *\n * const map = new Map({\n * target: 'map'\n * });\n *\n * const control = new RoutingControl();\n *\n * control.attachToMap(map)\n *\n * @classproperty {string} apiKey - Key used for RoutingApi requests.\n * @classproperty {string} stopsApiKey - Key used for Stop lookup requests (defaults to apiKey).\n * @classproperty {string} stopsApiUrl - Url used for Stop lookup requests (defaults to https://api.geops.io/stops/v1/lookup/).\n * @classproperty {Array.>} graphs - 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]]\"\n * @classproperty {string} mot - Mean of transport to be used for routing.\n * @classproperty {object} routingApiParams - object of additional parameters to pass to the routing api request.\n * @classproperty {object} snapToClosestStation - If true, the routing will snap the coordinate to the closest station. Default to false.\n * @classproperty {boolean} useRawViaPoints - Experimental property. Wen 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.\n * @classproperty {RoutingLayer|Layer} routingLayer - Layer for adding route features.\n * @classproperty {function} onRouteError - Callback on error.\n * @classproperty {boolean} loading - True if the control is requesting the backend.\n * @see Openlayers routing example\n *\n * @extends {Control}\n * @implements {RoutingInterface}\n */\nclass RoutingControl extends ControlCommon {\n constructor(options = {}) {\n super(options);\n this.viaPoints = [];\n this.loading = false;\n this.graphs = [];\n this.modify = true;\n this.useRawViaPoints = false;\n this.snapToClosestStation = false;\n this.cacheStationData = {};\n this.abortControllers = {};\n this.segments = [];\n this.format = new GeoJSON({ featureProjection: 'EPSG:3857' });\n this.initialRouteDrag = {};\n Object.defineProperties(this, {\n mot: {\n get: () => this.get('mot'),\n set: (newMot) => {\n if (newMot) {\n this.set('mot', newMot);\n if (this.viaPoints) {\n this.drawRoute();\n }\n }\n },\n },\n loading: {\n get: () => this.get('loading'),\n set: (newLoading) => {\n this.set('loading', newLoading);\n },\n },\n modify: {\n get: () => this.get('modify'),\n set: (modify) => {\n this.set('modify', modify);\n },\n },\n });\n /** True if the control is requesting the backend. */\n this.loading = false;\n /** @ignore */\n this.graphs = options.graphs || [['osm', 0, 99]];\n /** @ignore */\n this.mot = options.mot || 'bus';\n /** @ignore */\n this.modify = options.modify !== false;\n /** @ignore */\n this.routingApiParams = options.routingApiParams;\n /** @ignore */\n this.useRawViaPoints = options.useRawViaPoints || false;\n /** @ignore */\n this.snapToClosestStation = options.snapToClosestStation || false;\n /** @ignore */\n this.apiKey = options.apiKey;\n /** @ignore */\n this.stopsApiKey = options.stopsApiKey || this.apiKey;\n /** @ignore */\n this.stopsApiUrl = options.stopsApiUrl || 'https://api.geops.io/stops/v1/';\n /** @ignore */\n this.api = new RoutingAPI({\n ...options,\n });\n /** @ignore */\n this.routingLayer =\n options.routingLayer ||\n new RoutingLayer({\n name: 'routing-layer',\n style: options.style,\n });\n /** @ignore */\n this.onRouteError =\n options.onRouteError ||\n ((error) => {\n this.dispatchEvent(new BaseEvent('change:route'));\n this.reset();\n // eslint-disable-next-line no-console\n console.error(error);\n });\n /** @ignore */\n this.onMapClick = this.onMapClick.bind(this);\n /** @ignore */\n this.onModifyEnd = this.onModifyEnd.bind(this);\n /** @ignore */\n this.onModifyStart = this.onModifyStart.bind(this);\n /** @ignore */\n this.createModifyInteraction();\n }\n /**\n * Calculate at which resolutions corresponds each generalizations.\n *\n * @private\n */\n static getGraphsResolutions(graphs, map) {\n const view = map.getView();\n return graphs.map(([, minZoom, maxZoom]) => [\n view.getResolutionForZoom(minZoom),\n view.getResolutionForZoom(maxZoom || minZoom + 1),\n ]);\n }\n /**\n * Adds/Replaces a viaPoint to the viaPoints array and redraws route:\n * Adds a viaPoint at end of array by default.\n * If an index is passed a viaPoint is added at the specified index.\n * If an index is passed and overwrite x is > 0, x viaPoints at the specified\n * index are replaced with a single new viaPoint.\n * @param {number[]|string} coordinates Array of coordinates\n * @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.\n * @param {number} [overwrite=0] Marks the number of viaPoints that are removed at the specified index on add.\n */\n addViaPoint(coordinatesOrString, index = -1, overwrite = 0) {\n /* Add/Insert/Overwrite viapoint and redraw route */\n this.viaPoints.splice(index === -1 ? this.viaPoints.length : index, overwrite, coordinatesOrString);\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Removes a viaPoint at the passed array index and redraws route\n * By default the last viaPoint is removed.\n * @param {number} index Integer representing the index of the viaPoint to delete.\n */\n removeViaPoint(index = (this.viaPoints || []).length - 1) {\n /* Remove viapoint and redraw route */\n if (this.viaPoints.length && this.viaPoints[index]) {\n this.viaPoints.splice(index, 1);\n }\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Replaces the current viaPoints with a new coordinate array.\n * @param {Array>} coordinateArray Array of nested coordinates\n */\n setViaPoints(coordinateArray) {\n this.viaPoints = [...coordinateArray];\n this.drawRoute();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Removes all viaPoints, clears the source and triggers a change event\n */\n reset() {\n // Clear viaPoints and source\n this.abortRequests();\n this.viaPoints = [];\n this.routingLayer?.olLayer?.getSource()?.clear();\n this.dispatchEvent(new BaseEvent('change:route'));\n }\n /**\n * Aborts viapoint and route requests\n * @private\n */\n abortRequests() {\n // Abort Routing API requests\n this.graphs.forEach((graph) => {\n const graphName = graph[0];\n if (this.abortControllers[graphName]) {\n this.abortControllers[graphName].abort();\n }\n this.abortControllers[graphName] = new AbortController();\n });\n // Abort Stops API requests\n this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]?.abort();\n this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY] =\n new AbortController();\n this.loading = false;\n }\n /**\n * Draws route on map using an array of coordinates:\n * If a single coordinate is passed a single point feature is added to map.\n * If two or more coordinates are passed a request to the RoutingAPI fetches\n * the route using the passed coordinates and the current mot.\n * @private\n */\n drawRoute() {\n /* Calls RoutingAPI to draw a route using the viaPoints array */\n this.abortRequests();\n this.routingLayer?.olLayer?.getSource()?.clear();\n if (!this.viaPoints.length) {\n return null;\n }\n if (this.viaPoints.length === 1) {\n // Add point for first node\n return this.drawViaPoint(this.viaPoints[0], 0, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]);\n }\n const formattedViaPoints = this.viaPoints.map((viaPoint) => {\n if (Array.isArray(viaPoint)) {\n const projection = this.map?.getView().getProjection();\n // viaPoint is a coordinate\n // Coordinates need to be reversed as required by the backend RoutingAPI\n const [lon, lat] = toLonLat(viaPoint, projection);\n return this.snapToClosestStation ? [`@${lat}`, lon] : [lat, lon];\n }\n // viaPoint is a string to use as it is\n return this.useRawViaPoints ? viaPoint : `!${viaPoint}`;\n });\n this.loading = true;\n // Clear source\n this.routingLayer?.olLayer?.getSource()?.clear();\n // Create point features for the viaPoints\n this.viaPoints.forEach((viaPoint, idx) => this.drawViaPoint(viaPoint, idx, this.abortControllers[STOP_FETCH_ABORT_CONTROLLER_KEY]));\n return Promise.all(this.graphs.map(([graph], index) => {\n const { signal } = this.abortControllers[graph];\n if (!this.api) {\n return Promise.resolve([]);\n }\n return this.api\n .route({\n graph,\n via: `${formattedViaPoints.join('|')}`,\n mot: this.mot,\n // @ts-ignore missing property in swagger\n 'resolve-hops': false,\n elevation: false,\n 'coord-radius': 100.0,\n 'coord-punish': 1000.0,\n ...(this.routingApiParams || {}),\n }, { signal })\n .then((featureCollection) => {\n this.segments = this.format.readFeatures(featureCollection);\n if (this.mot === 'foot') {\n // Extract unique values from viaPoint target value\n const uniqueVias = this.segments.reduce((resultVias, currentFeat) => {\n const segTrg = currentFeat.get('trg');\n return resultVias.find((via) => via[0] === segTrg[0] && via[1] === segTrg[1])\n ? resultVias\n : [...resultVias, segTrg];\n }, []);\n // Create LineString features from segments with same unique value\n this.segments = uniqueVias.map((via) => {\n const viaSegments = this.segments.filter((seg) => {\n const segTrg = seg.get('trg');\n return segTrg[0] === via[0] && segTrg[1] === via[1];\n });\n const coords = getFlatCoordinatesFromSegments(viaSegments);\n return new Feature({\n geometry: new LineString(coords),\n });\n });\n }\n // Create the new route. This route will be modifiable by the Modifiy interaction.\n const coords = getFlatCoordinatesFromSegments(this.segments);\n const routeFeature = new Feature({\n geometry: new LineString(coords),\n });\n routeFeature.set('graph', graph);\n routeFeature.set('mot', this.mot);\n if (this.graphsResolutions &&\n this.graphsResolutions[index]?.length >= 2) {\n routeFeature.set('minResolution', this.graphsResolutions[index][0]);\n routeFeature.set('maxResolution', this.graphsResolutions[index][1]);\n }\n this.routingLayer?.olLayer?.getSource()?.addFeature(routeFeature);\n this.loading = false;\n })\n .catch((error) => {\n if (error.name === 'AbortError') {\n // Ignore abort error\n return;\n }\n this.segments = [];\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n });\n }));\n }\n /**\n * Draw a via point. This function can parse all the possibilitiies\n *\n * @private\n */\n drawViaPoint(viaPoint, idx, abortController) {\n const pointFeature = new Feature();\n pointFeature.set('viaPointIdx', idx);\n // The via point is a coordinate using the current map's projection\n if (Array.isArray(viaPoint)) {\n pointFeature.setGeometry(new Point(viaPoint));\n this.routingLayer?.olLayer?.getSource()?.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n // Possibility to parse:\n //\n // !8596126 a station with id 8596126\n // !8596126$4 a station with id 8596126\n if (!this.useRawViaPoints || REGEX_VIA_POINT_STATION_ID.test(viaPoint)) {\n let stationId;\n let track;\n if (this.useRawViaPoints) {\n [, stationId, , track] =\n REGEX_VIA_POINT_STATION_ID.exec(viaPoint) || [];\n }\n else {\n [stationId, track] = viaPoint.split('$');\n }\n return fetch(`${this.stopsApiUrl}lookup/${stationId}?key=${this.stopsApiKey}`, { signal: abortController.signal })\n .then((res) => res.json())\n .then((stationData) => {\n const { coordinates } = stationData.features[0].geometry;\n this.cacheStationData[viaPoint] = fromLonLat(coordinates);\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(fromLonLat(coordinates)));\n this.routingLayer?.olLayer?.getSource()?.addFeature(pointFeature);\n return pointFeature;\n })\n .catch((error) => {\n if (error.name === 'AbortError') {\n // Ignore abort error\n return;\n }\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n });\n }\n // Only when this.useRawViaPoints is true.\n // Possibility to parse:\n //\n // 47.37811,8.53935 a position 47.37811, 8.53935\n if (this.useRawViaPoints && REGEX_VIA_POINT_COORD.test(viaPoint)) {\n const [lat, lon] = REGEX_VIA_POINT_COORD.exec(viaPoint) || [];\n const coordinates = fromLonLat([parseFloat(lon), parseFloat(lat)], this.map?.getView().getProjection());\n pointFeature.setGeometry(new Point(coordinates));\n this.routingLayer?.olLayer?.getSource()?.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n // Only when this.useRawViaPoints is true.\n // It will parse the via point to find some name, id, track coordinates.\n //\n // Possibility to parse:\n //\n // @47.37811,8.53935 a station at position 47.37811, 8.53935\n // @47.37811,8.53935$4 track 4 in a station at position 47.37811, 8.53935\n // zürich hb@47.37811,8.53935$8 track 8 in station \"Zürich HB\" at position 47.37811, 8.53935\n const [, stationName, , lat, lon, , track] = REGEX_VIA_POINT.exec(viaPoint) || [];\n if (lon && lat) {\n const coordinates = fromLonLat([parseFloat(lon), parseFloat(lat)], this.map?.getView().getProjection());\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(coordinates));\n this.routingLayer?.olLayer?.getSource()?.addFeature(pointFeature);\n return Promise.resolve(pointFeature);\n }\n if (stationName) {\n return fetch(`${this.stopsApiUrl}?key=${this.stopsApiKey}&q=${stationName}&limit=1`, { signal: abortController.signal })\n .then((res) => res.json())\n .then((stationData) => {\n const { coordinates } = stationData.features[0].geometry;\n this.cacheStationData[viaPoint] = fromLonLat(coordinates);\n pointFeature.set('viaPointTrack', track);\n pointFeature.setGeometry(new Point(fromLonLat(coordinates)));\n this.routingLayer?.olLayer?.getSource()?.addFeature(pointFeature);\n return pointFeature;\n })\n .catch((error) => {\n // Dispatch error event and execute error function\n this.dispatchEvent(new BaseEvent('error'));\n this.onRouteError(error, this);\n this.loading = false;\n return null;\n });\n }\n return Promise.resolve(null);\n }\n /**\n * Used on click on map while control is active:\n * By default adds a viaPoint to the end of array.\n * If an existing viaPoint is clicked removes the clicked viaPoint.\n * @private\n */\n onMapClick(evt) {\n const feats = evt.target.getFeaturesAtPixel(evt.pixel);\n const viaPoint = feats.find((feat) => feat.getGeometry()?.getType() === 'Point' &&\n feat.get('viaPointIdx') !== undefined);\n if (viaPoint) {\n // Remove existing viaPoint on click and abort viaPoint add\n this.removeViaPoint(viaPoint.get('viaPointIdx'));\n return;\n }\n this.addViaPoint(evt.coordinate);\n }\n /**\n * Used on start of the modify interaction. Stores relevant data\n * in this.initialRouteDrag object\n * @private\n */\n onModifyStart(evt) {\n // When modify start, we search the index of the segment that is modifying.\n let segmentIndex = -1;\n const route = evt.features\n .getArray()\n .find((feat) => feat.getGeometry()?.getType() === 'LineString');\n // Find the segment index that is being modified\n if (route && route.getGeometry() && evt.mapBrowserEvent.coordinate) {\n // We use a buff extent to fix floating issues , see https://github.com/openlayers/openlayers/issues/7130#issuecomment-535856422\n const closestExtent = buffer(new Point(\n // @ts-ignore\n route.getGeometry()?.getClosestPoint(evt.mapBrowserEvent.coordinate)).getExtent(), 0.001);\n segmentIndex = this.segments.findIndex((segment) => segment.getGeometry()?.intersectsExtent(closestExtent));\n }\n // Find the viaPoint that is being modified\n const viaPoint = (evt.features\n .getArray()\n .filter((feat) => feat.getGeometry()?.getType() === 'Point') ||\n [])[0];\n // Write object with modify info\n /** @ignore */\n this.initialRouteDrag = {\n viaPoint,\n oldRoute: route && route.clone(),\n segmentIndex,\n };\n }\n /**\n * Used on end of the modify interaction. Resolves feature modification:\n * Line drag creates new viaPoint at the final coordinate of drag.\n * Point drag replaces old viaPoint.\n * @private\n */\n onModifyEnd(evt) {\n const coord = evt.mapBrowserEvent.coordinate;\n const { oldRoute, viaPoint, segmentIndex } = this.initialRouteDrag || {};\n // If viaPoint is being relocated overwrite the old viaPoint\n if (viaPoint) {\n return this.addViaPoint(coord, viaPoint.get('viaPointIdx'), 1);\n }\n // In case there is no route overwrite first coordinate\n if (!oldRoute) {\n return this.addViaPoint(coord, 0, 1);\n }\n // We can't add a via point because we haven't found which segment has been modified.\n if (segmentIndex === -1) {\n return Promise.reject(new Error('No segment found'));\n }\n // Insert new viaPoint at the modified segment index + 1\n return this.addViaPoint(coord, (segmentIndex || 0) + 1);\n }\n /**\n * Define a default element.\n *\n * @private\n */\n createDefaultElement() {\n /** @ignore */\n this.element = document.createElement('button');\n this.element.id = 'ol-toggle-routing';\n this.element.innerHTML = 'Toggle Route Control';\n this.element.onclick = () => this.active ? this.deactivate() : this.activate();\n Object.assign(this.element.style, {\n position: 'absolute',\n right: '10px',\n top: '10px',\n });\n }\n /**\n * Create the interaction used to modify vertexes of features.\n * @private\n */\n createModifyInteraction() {\n /**\n * @type {ol.interaction.Modify}\n * @private\n */\n // Define and add modify interaction\n this.modifyInteraction = new Modify({\n source: this.routingLayer?.olLayer?.getSource() || undefined,\n pixelTolerance: 4,\n hitDetection: this.routingLayer?.olLayer,\n deleteCondition: (e) => {\n const feats = e.target.getFeaturesAtPixel(e.pixel, {\n hitTolerance: 5,\n });\n const viaPoint = feats.find((feat) => feat.getGeometry()?.getType() === 'Point' && feat.get('index'));\n if (click(e) && viaPoint) {\n // Remove node & viaPoint if an existing viaPoint was clicked\n this.removeViaPoint(viaPoint.get('index'));\n return true;\n }\n return false;\n },\n });\n this.modifyInteraction.on('modifystart', this.onModifyStart);\n this.modifyInteraction.on('modifyend', this.onModifyEnd);\n this.modifyInteraction.setActive(false);\n }\n /**\n * Add click listener to map.\n * @private\n */\n addListeners() {\n if (!this.modify) {\n return;\n }\n this.removeListeners();\n /** @ignore */\n this.onMapClickKey = this.map?.on('singleclick', this.onMapClick);\n }\n /**\n * Remove click listener from map.\n * @private\n */\n removeListeners() {\n if (this.onMapClickKey) {\n unByKey(this.onMapClickKey);\n }\n }\n activate() {\n super.activate();\n if (this.map) {\n /** @ignore */\n this.format = new GeoJSON({\n featureProjection: this.map.getView().getProjection(),\n });\n /** @ignore */\n this.graphsResolutions = RoutingControl.getGraphsResolutions(this.graphs, this.map);\n // Clean the modifyInteraction if present\n if (this.modifyInteraction) {\n this.map.removeInteraction(this.modifyInteraction);\n }\n // Add modify interaction, RoutingLayer and listeners\n this.routingLayer?.attachToMap(this.map);\n if (this.modifyInteraction) {\n this.map.addInteraction(this.modifyInteraction);\n }\n this.modifyInteraction?.setActive(this.modify);\n this.addListeners();\n }\n }\n deactivate() {\n if (this.map) {\n // Remove modify interaction, RoutingLayer, listeners and viaPoints\n this.routingLayer?.detachFromMap();\n if (this.modifyInteraction) {\n this.map.removeInteraction(this.modifyInteraction);\n }\n this.removeListeners();\n this.reset();\n }\n }\n render() { }\n}\nexport default RoutingControl;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/controls/RoutingControl.js", "access": "public", @@ -7098,7 +7300,7 @@ "lineNumber": 1 }, { - "__docId__": 320, + "__docId__": 330, "kind": "variable", "name": "REGEX_VIA_POINT", "memberof": "build/ol/controls/RoutingControl.js", @@ -7118,7 +7320,7 @@ "ignore": true }, { - "__docId__": 321, + "__docId__": 331, "kind": "variable", "name": "REGEX_VIA_POINT_COORD", "memberof": "build/ol/controls/RoutingControl.js", @@ -7138,7 +7340,7 @@ "ignore": true }, { - "__docId__": 322, + "__docId__": 332, "kind": "variable", "name": "REGEX_VIA_POINT_STATION_ID", "memberof": "build/ol/controls/RoutingControl.js", @@ -7158,7 +7360,7 @@ "ignore": true }, { - "__docId__": 323, + "__docId__": 333, "kind": "variable", "name": "STOP_FETCH_ABORT_CONTROLLER_KEY", "memberof": "build/ol/controls/RoutingControl.js", @@ -7178,7 +7380,7 @@ "ignore": true }, { - "__docId__": 324, + "__docId__": 334, "kind": "function", "name": "getFlatCoordinatesFromSegments", "memberof": "build/ol/controls/RoutingControl.js", @@ -7208,7 +7410,7 @@ "ignore": true }, { - "__docId__": 325, + "__docId__": 335, "kind": "class", "name": "RoutingControl", "memberof": "build/ol/controls/RoutingControl.js", @@ -7225,7 +7427,7 @@ "see": [ "Openlayers routing example" ], - "lineNumber": 79, + "lineNumber": 78, "unknown": [ { "tagName": "@classproperty", @@ -7281,7 +7483,7 @@ ] }, { - "__docId__": 326, + "__docId__": 336, "kind": "constructor", "name": "constructor", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7291,11 +7493,11 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#constructor", "access": "public", "description": null, - "lineNumber": 80, + "lineNumber": 79, "undocument": true }, { - "__docId__": 327, + "__docId__": 337, "kind": "member", "name": "viaPoints", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7303,7 +7505,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#viaPoints", "access": "public", "description": null, - "lineNumber": 82, + "lineNumber": 81, "undocument": true, "type": { "types": [ @@ -7312,7 +7514,7 @@ } }, { - "__docId__": 328, + "__docId__": 338, "kind": "member", "name": "loading", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7320,7 +7522,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#loading", "access": "public", "description": null, - "lineNumber": 83, + "lineNumber": 82, "undocument": true, "type": { "types": [ @@ -7329,7 +7531,7 @@ } }, { - "__docId__": 329, + "__docId__": 339, "kind": "member", "name": "graphs", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7337,7 +7539,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#graphs", "access": "public", "description": null, - "lineNumber": 84, + "lineNumber": 83, "undocument": true, "type": { "types": [ @@ -7346,7 +7548,7 @@ } }, { - "__docId__": 330, + "__docId__": 340, "kind": "member", "name": "modify", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7354,7 +7556,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#modify", "access": "public", "description": null, - "lineNumber": 85, + "lineNumber": 84, "undocument": true, "type": { "types": [ @@ -7363,7 +7565,7 @@ } }, { - "__docId__": 331, + "__docId__": 341, "kind": "member", "name": "useRawViaPoints", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7371,7 +7573,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#useRawViaPoints", "access": "public", "description": null, - "lineNumber": 86, + "lineNumber": 85, "undocument": true, "type": { "types": [ @@ -7380,7 +7582,7 @@ } }, { - "__docId__": 332, + "__docId__": 342, "kind": "member", "name": "snapToClosestStation", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7388,7 +7590,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#snapToClosestStation", "access": "public", "description": null, - "lineNumber": 87, + "lineNumber": 86, "undocument": true, "type": { "types": [ @@ -7397,7 +7599,7 @@ } }, { - "__docId__": 333, + "__docId__": 343, "kind": "member", "name": "cacheStationData", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7405,7 +7607,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#cacheStationData", "access": "public", "description": null, - "lineNumber": 88, + "lineNumber": 87, "undocument": true, "type": { "types": [ @@ -7414,7 +7616,7 @@ } }, { - "__docId__": 334, + "__docId__": 344, "kind": "member", "name": "abortControllers", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7422,7 +7624,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#abortControllers", "access": "public", "description": null, - "lineNumber": 89, + "lineNumber": 88, "undocument": true, "type": { "types": [ @@ -7431,7 +7633,7 @@ } }, { - "__docId__": 335, + "__docId__": 345, "kind": "member", "name": "segments", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7439,7 +7641,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#segments", "access": "public", "description": null, - "lineNumber": 90, + "lineNumber": 89, "undocument": true, "type": { "types": [ @@ -7448,7 +7650,7 @@ } }, { - "__docId__": 336, + "__docId__": 346, "kind": "member", "name": "format", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7456,7 +7658,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#format", "access": "public", "description": null, - "lineNumber": 91, + "lineNumber": 90, "undocument": true, "type": { "types": [ @@ -7465,7 +7667,7 @@ } }, { - "__docId__": 337, + "__docId__": 347, "kind": "member", "name": "initialRouteDrag", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7473,7 +7675,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#initialRouteDrag", "access": "public", "description": null, - "lineNumber": 92, + "lineNumber": 91, "undocument": true, "type": { "types": [ @@ -7482,7 +7684,7 @@ } }, { - "__docId__": 340, + "__docId__": 350, "kind": "member", "name": "mot", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7490,7 +7692,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#mot", "access": "public", "description": null, - "lineNumber": 123, + "lineNumber": 122, "ignore": true, "type": { "types": [ @@ -7499,7 +7701,7 @@ } }, { - "__docId__": 342, + "__docId__": 352, "kind": "member", "name": "routingApiParams", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7507,7 +7709,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#routingApiParams", "access": "public", "description": null, - "lineNumber": 127, + "lineNumber": 126, "ignore": true, "type": { "types": [ @@ -7516,7 +7718,7 @@ } }, { - "__docId__": 345, + "__docId__": 355, "kind": "member", "name": "apiKey", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7524,7 +7726,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#apiKey", "access": "public", "description": null, - "lineNumber": 133, + "lineNumber": 132, "ignore": true, "type": { "types": [ @@ -7533,7 +7735,7 @@ } }, { - "__docId__": 346, + "__docId__": 356, "kind": "member", "name": "stopsApiKey", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7541,7 +7743,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#stopsApiKey", "access": "public", "description": null, - "lineNumber": 135, + "lineNumber": 134, "ignore": true, "type": { "types": [ @@ -7550,7 +7752,7 @@ } }, { - "__docId__": 347, + "__docId__": 357, "kind": "member", "name": "stopsApiUrl", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7558,7 +7760,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#stopsApiUrl", "access": "public", "description": null, - "lineNumber": 137, + "lineNumber": 136, "ignore": true, "type": { "types": [ @@ -7567,7 +7769,7 @@ } }, { - "__docId__": 348, + "__docId__": 358, "kind": "member", "name": "api", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7575,7 +7777,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#api", "access": "public", "description": null, - "lineNumber": 139, + "lineNumber": 138, "ignore": true, "type": { "types": [ @@ -7584,7 +7786,7 @@ } }, { - "__docId__": 349, + "__docId__": 359, "kind": "member", "name": "routingLayer", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7592,7 +7794,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#routingLayer", "access": "public", "description": null, - "lineNumber": 141, + "lineNumber": 142, "ignore": true, "type": { "types": [ @@ -7601,7 +7803,7 @@ } }, { - "__docId__": 350, + "__docId__": 360, "kind": "member", "name": "onRouteError", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7609,7 +7811,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onRouteError", "access": "public", "description": null, - "lineNumber": 148, + "lineNumber": 149, "ignore": true, "type": { "types": [ @@ -7618,7 +7820,7 @@ } }, { - "__docId__": 354, + "__docId__": 364, "kind": "method", "name": "getGraphsResolutions", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7628,7 +7830,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl.getGraphsResolutions", "access": "private", "description": "Calculate at which resolutions corresponds each generalizations.", - "lineNumber": 170, + "lineNumber": 171, "params": [ { "name": "graphs", @@ -7650,7 +7852,7 @@ } }, { - "__docId__": 355, + "__docId__": 365, "kind": "method", "name": "addViaPoint", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7660,7 +7862,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#addViaPoint", "access": "public", "description": "Adds/Replaces a viaPoint to the viaPoints array and redraws route:\n Adds a viaPoint at end of array by default.\n If an index is passed a viaPoint is added at the specified index.\n If an index is passed and overwrite x is > 0, x viaPoints at the specified\n index are replaced with a single new viaPoint.", - "lineNumber": 187, + "lineNumber": 188, "params": [ { "nullable": null, @@ -7701,7 +7903,7 @@ "return": null }, { - "__docId__": 356, + "__docId__": 366, "kind": "method", "name": "removeViaPoint", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7711,7 +7913,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#removeViaPoint", "access": "public", "description": "Removes a viaPoint at the passed array index and redraws route\nBy default the last viaPoint is removed.", - "lineNumber": 198, + "lineNumber": 199, "params": [ { "nullable": null, @@ -7727,7 +7929,7 @@ "return": null }, { - "__docId__": 357, + "__docId__": 367, "kind": "method", "name": "setViaPoints", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7737,7 +7939,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#setViaPoints", "access": "public", "description": "Replaces the current viaPoints with a new coordinate array.", - "lineNumber": 210, + "lineNumber": 211, "params": [ { "nullable": null, @@ -7753,7 +7955,7 @@ "return": null }, { - "__docId__": 359, + "__docId__": 369, "kind": "method", "name": "reset", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7763,12 +7965,12 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#reset", "access": "public", "description": "Removes all viaPoints, clears the source and triggers a change event", - "lineNumber": 218, + "lineNumber": 219, "params": [], "return": null }, { - "__docId__": 361, + "__docId__": 371, "kind": "method", "name": "abortRequests", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7783,7 +7985,7 @@ "return": null }, { - "__docId__": 363, + "__docId__": 373, "kind": "method", "name": "drawRoute", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7793,7 +7995,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#drawRoute", "access": "private", "description": "Draws route on map using an array of coordinates:\n If a single coordinate is passed a single point feature is added to map.\n If two or more coordinates are passed a request to the RoutingAPI fetches\n the route using the passed coordinates and the current mot.", - "lineNumber": 253, + "lineNumber": 252, "params": [], "return": { "types": [ @@ -7802,7 +8004,7 @@ } }, { - "__docId__": 370, + "__docId__": 380, "kind": "method", "name": "drawViaPoint", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7812,7 +8014,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#drawViaPoint", "access": "private", "description": "Draw a via point. This function can parse all the possibilitiies", - "lineNumber": 347, + "lineNumber": 351, "params": [ { "name": "viaPoint", @@ -7840,7 +8042,7 @@ } }, { - "__docId__": 373, + "__docId__": 383, "kind": "method", "name": "onMapClick", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7850,7 +8052,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onMapClick", "access": "private", "description": "Used on click on map while control is active:\n By default adds a viaPoint to the end of array.\n If an existing viaPoint is clicked removes the clicked viaPoint.", - "lineNumber": 448, + "lineNumber": 449, "params": [ { "name": "evt", @@ -7862,7 +8064,7 @@ "return": null }, { - "__docId__": 374, + "__docId__": 384, "kind": "method", "name": "onModifyStart", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7872,7 +8074,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onModifyStart", "access": "private", "description": "Used on start of the modify interaction. Stores relevant data\nin this.initialRouteDrag object", - "lineNumber": 467, + "lineNumber": 465, "params": [ { "name": "evt", @@ -7884,7 +8086,7 @@ "return": null }, { - "__docId__": 376, + "__docId__": 386, "kind": "method", "name": "onModifyEnd", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7894,7 +8096,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onModifyEnd", "access": "private", "description": "Used on end of the modify interaction. Resolves feature modification:\n Line drag creates new viaPoint at the final coordinate of drag.\n Point drag replaces old viaPoint.", - "lineNumber": 501, + "lineNumber": 498, "params": [ { "name": "evt", @@ -7910,7 +8112,7 @@ } }, { - "__docId__": 377, + "__docId__": 387, "kind": "method", "name": "createDefaultElement", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7920,12 +8122,12 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#createDefaultElement", "access": "private", "description": "Define a default element.", - "lineNumber": 524, + "lineNumber": 521, "params": [], "return": null }, { - "__docId__": 378, + "__docId__": 388, "kind": "member", "name": "element", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7933,7 +8135,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#element", "access": "public", "description": null, - "lineNumber": 526, + "lineNumber": 523, "ignore": true, "type": { "types": [ @@ -7942,7 +8144,7 @@ } }, { - "__docId__": 379, + "__docId__": 389, "kind": "method", "name": "createModifyInteraction", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7952,12 +8154,12 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#createModifyInteraction", "access": "private", "description": "Create the interaction used to modify vertexes of features.", - "lineNumber": 540, + "lineNumber": 537, "params": [], "return": null }, { - "__docId__": 380, + "__docId__": 390, "kind": "member", "name": "modifyInteraction", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7965,7 +8167,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#modifyInteraction", "access": "private", "description": "", - "lineNumber": 547, + "lineNumber": 543, "type": { "nullable": null, "types": [ @@ -7976,7 +8178,7 @@ } }, { - "__docId__": 381, + "__docId__": 391, "kind": "method", "name": "addListeners", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7986,12 +8188,12 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#addListeners", "access": "private", "description": "Add click listener to map.", - "lineNumber": 572, + "lineNumber": 568, "params": [], "return": null }, { - "__docId__": 382, + "__docId__": 392, "kind": "member", "name": "onMapClickKey", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -7999,7 +8201,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onMapClickKey", "access": "public", "description": null, - "lineNumber": 579, + "lineNumber": 574, "ignore": true, "type": { "types": [ @@ -8008,7 +8210,7 @@ } }, { - "__docId__": 383, + "__docId__": 393, "kind": "method", "name": "removeListeners", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -8018,12 +8220,12 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#removeListeners", "access": "private", "description": "Remove click listener from map.", - "lineNumber": 585, + "lineNumber": 580, "params": [], "return": null }, { - "__docId__": 384, + "__docId__": 394, "kind": "method", "name": "activate", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -8033,13 +8235,13 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#activate", "access": "public", "description": null, - "lineNumber": 590, + "lineNumber": 585, "undocument": true, "params": [], "return": null }, { - "__docId__": 386, + "__docId__": 396, "kind": "member", "name": "graphsResolutions", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -8047,7 +8249,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#graphsResolutions", "access": "public", "description": null, - "lineNumber": 599, + "lineNumber": 593, "ignore": true, "type": { "types": [ @@ -8056,7 +8258,7 @@ } }, { - "__docId__": 387, + "__docId__": 397, "kind": "method", "name": "deactivate", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -8066,13 +8268,13 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#deactivate", "access": "public", "description": null, - "lineNumber": 613, + "lineNumber": 607, "undocument": true, "params": [], "return": null }, { - "__docId__": 388, + "__docId__": 398, "kind": "method", "name": "render", "memberof": "build/ol/controls/RoutingControl.js~RoutingControl", @@ -8082,13 +8284,13 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#render", "access": "public", "description": null, - "lineNumber": 625, + "lineNumber": 618, "undocument": true, "params": [], "return": null }, { - "__docId__": 389, + "__docId__": 399, "kind": "file", "name": "build/ol/controls/StopFinderControl.js", "content": "import { fromLonLat } from 'ol/proj';\nimport StopFinderControlCommon from '../../common/controls/StopFinderControlCommon';\n/**\n * Search stations.\n *\n * @example\n * import { Map } from 'ol';\n * import { StopFinderControl } from 'mobility-toolbox-js/ol';\n *\n * const map = new Map({\n * target: 'map',\n * });\n *\n * const control = new StopFinderControl({\n * apiKey: [yourApiKey]\n * });\n *\n * control.attachToMap(map);\n *\n *\n * @see Openlayers search example\n */\nclass StopFinderControl extends StopFinderControlCommon {\n /**\n * @private\n */\n onSuggestionClick(suggestion) {\n const coord = fromLonLat(suggestion.geometry.coordinates);\n this.map.getView().setCenter(coord);\n }\n}\nexport default StopFinderControl;\n", @@ -8099,7 +8301,7 @@ "lineNumber": 1 }, { - "__docId__": 390, + "__docId__": 400, "kind": "class", "name": "StopFinderControl", "memberof": "build/ol/controls/StopFinderControl.js", @@ -8123,7 +8325,7 @@ ] }, { - "__docId__": 391, + "__docId__": 401, "kind": "method", "name": "onSuggestionClick", "memberof": "build/ol/controls/StopFinderControl.js~StopFinderControl", @@ -8145,7 +8347,7 @@ "return": null }, { - "__docId__": 392, + "__docId__": 402, "kind": "file", "name": "build/ol/controls/index.js", "content": "export { default as CopyrightControl } from './CopyrightControl';\nexport { default as RoutingControl } from './RoutingControl';\nexport { default as StopFinderControl } from './StopFinderControl';\n", @@ -8156,7 +8358,7 @@ "lineNumber": 1 }, { - "__docId__": 393, + "__docId__": 403, "kind": "file", "name": "build/ol/index.js", "content": "export * from '../api';\nexport * from '../common';\nexport * from './controls';\nexport * from './layers';\nexport * from './styles';\n", @@ -8167,10 +8369,10 @@ "lineNumber": 1 }, { - "__docId__": 394, + "__docId__": 404, "kind": "file", "name": "build/ol/layers/Layer.js", - "content": "import { unByKey } from 'ol/Observable';\nimport LayerCommon from '../../common/layers/LayerCommon';\nimport userInteractionsMixin from '../../common/mixins/UserInteractionsLayerMixin';\n/**\n * A class representing a layer to display on an OpenLayers map.\n *\n * @example\n * import { Layer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new Layer({\n * olLayer: ...,\n * });\n *\n * @see Map example\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {LayerCommon}\n */\nclass Layer extends userInteractionsMixin(LayerCommon) {\n /**\n * Constructor.\n *\n * @param {LayerCommonOptions} options\n * @param {ol/layer/Layer~Layer} options.olLayer The layer (required).\n * @param {string} [options.name=uuid()] Layer name. Default use a generated uuid.\n * @param {string} [options.key=uuid().toLowerCase()] Layer key, will use options.name.toLowerCase() if not specified.\n * @param {string} [options.copyright=undefined] Copyright-Statement.\n * @param {Array} [options.children=[]] Sublayers.\n * @param {Object} [options.properties={}] Application-specific layer properties.\n * @param {boolean} [options.visible=true] If true this layer is the currently visible layer on the map.\n */\n constructor(options) {\n var _a;\n super(options);\n (_a = this.olLayer) === null || _a === void 0 ? void 0 : _a.setVisible(this.visible);\n }\n /**\n * Define layer's properties.\n *\n * @ignore\n */\n defineProperties(options) {\n super.defineProperties(options);\n Object.defineProperties(this, {\n olLayer: { value: options.olLayer, writable: true },\n olListenersKeys: {\n value: [],\n },\n });\n }\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n var _a, _b, _c, _d, _e;\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n // Make sure the visiblity is correct\n (_a = this.olLayer) === null || _a === void 0 ? void 0 : _a.setVisible(this.visible);\n if (this.olLayer &&\n !((_d = (_c = (_b = this.map) === null || _b === void 0 ? void 0 : _b.getLayers()) === null || _c === void 0 ? void 0 : _c.getArray()) === null || _d === void 0 ? void 0 : _d.includes(this.olLayer))) {\n this.map.addLayer(this.olLayer);\n }\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', () => {\n if (this.olLayer) {\n this.olLayer.setVisible(this.visible);\n }\n }));\n this.olListenersKeys.push(this.map.getLayers().on('remove', (evt) => {\n if (evt.element === this.olLayer) {\n this.detachFromMap();\n }\n }));\n this.toggleVisibleListeners();\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', this.toggleVisibleListeners));\n // We set the copyright to the source used by the layer.\n if (this.copyrights && this.olLayer) {\n const attributions = this.copyrights || [];\n if (this.olLayer.getLayers) {\n this.olLayer\n .getLayers()\n .getArray()\n .forEach((layer) => {\n var _a;\n // @ts-ignore\n if (layer.getSource) {\n // @ts-ignore\n (_a = layer.getSource()) === null || _a === void 0 ? void 0 : _a.setAttributions(attributions);\n }\n });\n // @ts-ignore\n }\n else if (this.olLayer.getSource) {\n // @ts-ignore\n (_e = this.olLayer.getSource()) === null || _e === void 0 ? void 0 : _e.setAttributions(attributions);\n }\n }\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n detachFromMap() {\n var _a, _b, _c;\n this.deactivateUserInteractions();\n unByKey(this.olListenersKeys);\n if (this.olLayer &&\n ((_c = (_b = (_a = this.map) === null || _a === void 0 ? void 0 : _a.getLayers()) === null || _b === void 0 ? void 0 : _b.getArray()) === null || _c === void 0 ? void 0 : _c.includes(this.olLayer))) {\n this.map.removeLayer(this.olLayer);\n }\n super.detachFromMap();\n }\n activateUserInteractions() {\n var _a, _b;\n this.deactivateUserInteractions();\n if (this.map &&\n this.userInteractions &&\n this.userClickInteractions &&\n ((_a = this.userClickCallbacks) === null || _a === void 0 ? void 0 : _a.length)) {\n this.singleClickListenerKey = this.map.on('singleclick', this.onUserClickCallback);\n this.olListenersKeys.push(this.singleClickListenerKey);\n }\n if (this.map &&\n this.userInteractions &&\n this.userHoverInteractions &&\n ((_b = this.userHoverCallbacks) === null || _b === void 0 ? void 0 : _b.length)) {\n this.pointerMoveListenerKey = this.map.on('pointermove', this.onUserMoveCallback);\n }\n }\n deactivateUserInteractions() {\n unByKey([this.pointerMoveListenerKey, this.singleClickListenerKey]);\n }\n /**\n * Toggle listeners needed when a layer is avisible or not.\n * @private\n */\n toggleVisibleListeners() {\n if (this.visible) {\n this.activateUserInteractions();\n }\n else {\n this.deactivateUserInteractions();\n }\n }\n /**\n * Create a copy of the Layer.\n * @param {Object} newOptions Options to override\n * @return {Layer} A Layer\n */\n clone(newOptions) {\n return new Layer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default Layer;\n", + "content": "import { unByKey } from 'ol/Observable';\nimport LayerCommon from '../../common/layers/LayerCommon';\nimport userInteractionsMixin from '../../common/mixins/UserInteractionsLayerMixin';\n/**\n * A class representing a layer to display on an OpenLayers map.\n *\n * @example\n * import { Layer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new Layer({\n * olLayer: ...,\n * });\n *\n * @see Map example\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {LayerCommon}\n */\nclass Layer extends userInteractionsMixin(LayerCommon) {\n /**\n * Constructor.\n *\n * @param {LayerCommonOptions} options\n * @param {ol/layer/Layer~Layer} options.olLayer The layer (required).\n * @param {string} [options.name=uuid()] Layer name. Default use a generated uuid.\n * @param {string} [options.key=uuid().toLowerCase()] Layer key, will use options.name.toLowerCase() if not specified.\n * @param {string} [options.copyright=undefined] Copyright-Statement.\n * @param {Array} [options.children=[]] Sublayers.\n * @param {Object} [options.properties={}] Application-specific layer properties.\n * @param {boolean} [options.visible=true] If true this layer is the currently visible layer on the map.\n */\n constructor(options) {\n super(options);\n this.olLayer?.setVisible(this.visible);\n }\n /**\n * Define layer's properties.\n *\n * @ignore\n */\n defineProperties(options) {\n super.defineProperties(options);\n Object.defineProperties(this, {\n olLayer: { value: options.olLayer, writable: true },\n olListenersKeys: {\n value: [],\n },\n });\n }\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n // Make sure the visiblity is correct\n this.olLayer?.setVisible(this.visible);\n if (this.olLayer &&\n !this.map?.getLayers()?.getArray()?.includes(this.olLayer)) {\n this.map.addLayer(this.olLayer);\n }\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', () => {\n if (this.olLayer) {\n this.olLayer.setVisible(this.visible);\n }\n }));\n this.olListenersKeys.push(this.map.getLayers().on('remove', (evt) => {\n if (evt.element === this.olLayer) {\n this.detachFromMap();\n }\n }));\n this.toggleVisibleListeners();\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', this.toggleVisibleListeners));\n // We set the copyright to the source used by the layer.\n if (this.copyrights && this.olLayer) {\n const attributions = this.copyrights || [];\n if (this.olLayer.getLayers) {\n this.olLayer\n .getLayers()\n .getArray()\n .forEach((layer) => {\n // @ts-ignore\n if (layer.getSource) {\n // @ts-ignore\n layer.getSource()?.setAttributions(attributions);\n }\n });\n // @ts-ignore\n }\n else if (this.olLayer.getSource) {\n // @ts-ignore\n this.olLayer.getSource()?.setAttributions(attributions);\n }\n }\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n detachFromMap() {\n this.deactivateUserInteractions();\n unByKey(this.olListenersKeys);\n if (this.olLayer &&\n this.map?.getLayers()?.getArray()?.includes(this.olLayer)) {\n this.map.removeLayer(this.olLayer);\n }\n super.detachFromMap();\n }\n activateUserInteractions() {\n this.deactivateUserInteractions();\n if (this.map &&\n this.userInteractions &&\n this.userClickInteractions &&\n this.userClickCallbacks?.length) {\n this.singleClickListenerKey = this.map.on('singleclick', this.onUserClickCallback);\n this.olListenersKeys.push(this.singleClickListenerKey);\n }\n if (this.map &&\n this.userInteractions &&\n this.userHoverInteractions &&\n this.userHoverCallbacks?.length) {\n this.pointerMoveListenerKey = this.map.on('pointermove', this.onUserMoveCallback);\n }\n }\n deactivateUserInteractions() {\n unByKey([this.pointerMoveListenerKey, this.singleClickListenerKey]);\n }\n /**\n * Toggle listeners needed when a layer is avisible or not.\n * @private\n */\n toggleVisibleListeners() {\n if (this.visible) {\n this.activateUserInteractions();\n }\n else {\n this.deactivateUserInteractions();\n }\n }\n /**\n * Create a copy of the Layer.\n * @param {Object} newOptions Options to override\n * @return {Layer} A Layer\n */\n clone(newOptions) {\n return new Layer({ ...this.options, ...newOptions });\n }\n}\nexport default Layer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/Layer.js", "access": "public", @@ -8178,7 +8380,7 @@ "lineNumber": 1 }, { - "__docId__": 395, + "__docId__": 405, "kind": "class", "name": "Layer", "memberof": "build/ol/layers/Layer.js", @@ -8208,7 +8410,7 @@ ] }, { - "__docId__": 396, + "__docId__": 406, "kind": "constructor", "name": "constructor", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8315,7 +8517,7 @@ ] }, { - "__docId__": 397, + "__docId__": 407, "kind": "method", "name": "defineProperties", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8325,7 +8527,7 @@ "longname": "build/ol/layers/Layer.js~Layer#defineProperties", "access": "public", "description": "Define layer's properties.", - "lineNumber": 42, + "lineNumber": 41, "ignore": true, "params": [ { @@ -8338,7 +8540,7 @@ "return": null }, { - "__docId__": 398, + "__docId__": 408, "kind": "method", "name": "attachToMap", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8348,7 +8550,7 @@ "longname": "build/ol/layers/Layer.js~Layer#attachToMap", "access": "public", "description": "Initialize the layer and listen to feature clicks.", - "lineNumber": 55, + "lineNumber": 54, "params": [ { "nullable": null, @@ -8364,7 +8566,7 @@ "return": null }, { - "__docId__": 399, + "__docId__": 409, "kind": "method", "name": "detachFromMap", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8374,12 +8576,12 @@ "longname": "build/ol/layers/Layer.js~Layer#detachFromMap", "access": "public", "description": "Terminate what was initialized in init function. Remove layer, events...", - "lineNumber": 109, + "lineNumber": 106, "params": [], "return": null }, { - "__docId__": 400, + "__docId__": 410, "kind": "method", "name": "activateUserInteractions", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8389,13 +8591,13 @@ "longname": "build/ol/layers/Layer.js~Layer#activateUserInteractions", "access": "public", "description": null, - "lineNumber": 119, + "lineNumber": 115, "undocument": true, "params": [], "return": null }, { - "__docId__": 401, + "__docId__": 411, "kind": "member", "name": "singleClickListenerKey", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8403,7 +8605,7 @@ "longname": "build/ol/layers/Layer.js~Layer#singleClickListenerKey", "access": "public", "description": null, - "lineNumber": 126, + "lineNumber": 121, "undocument": true, "type": { "types": [ @@ -8412,7 +8614,7 @@ } }, { - "__docId__": 402, + "__docId__": 412, "kind": "member", "name": "pointerMoveListenerKey", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8420,7 +8622,7 @@ "longname": "build/ol/layers/Layer.js~Layer#pointerMoveListenerKey", "access": "public", "description": null, - "lineNumber": 133, + "lineNumber": 128, "undocument": true, "type": { "types": [ @@ -8429,7 +8631,7 @@ } }, { - "__docId__": 403, + "__docId__": 413, "kind": "method", "name": "deactivateUserInteractions", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8439,13 +8641,13 @@ "longname": "build/ol/layers/Layer.js~Layer#deactivateUserInteractions", "access": "public", "description": null, - "lineNumber": 136, + "lineNumber": 131, "undocument": true, "params": [], "return": null }, { - "__docId__": 404, + "__docId__": 414, "kind": "method", "name": "toggleVisibleListeners", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8455,12 +8657,12 @@ "longname": "build/ol/layers/Layer.js~Layer#toggleVisibleListeners", "access": "private", "description": "Toggle listeners needed when a layer is avisible or not.", - "lineNumber": 143, + "lineNumber": 138, "params": [], "return": null }, { - "__docId__": 405, + "__docId__": 415, "kind": "method", "name": "clone", "memberof": "build/ol/layers/Layer.js~Layer", @@ -8470,7 +8672,7 @@ "longname": "build/ol/layers/Layer.js~Layer#clone", "access": "public", "description": "Create a copy of the Layer.", - "lineNumber": 156, + "lineNumber": 151, "params": [ { "nullable": null, @@ -8493,10 +8695,10 @@ } }, { - "__docId__": 406, + "__docId__": 416, "kind": "file", "name": "build/ol/layers/MapGlLayer.js", - "content": "/* eslint-disable max-classes-per-file */\nimport { toLonLat } from 'ol/proj';\nimport OlLayer from 'ol/layer/Layer';\nimport Source from 'ol/source/Source';\nimport GeoJSON from 'ol/format/GeoJSON';\nimport BaseEvent from 'ol/events/Event';\nimport { getUrlWithParams, getMapboxMapCopyrights } from '../../common/utils';\nimport Layer from './Layer';\n/**\n * Common class for Mapbox and Maplibre and potential other fork from Mapbox.\n * It's used to share code between Mapbox and Maplibre layers without importing both libs.\n */\nclass MapGlLayer extends Layer {\n constructor(options) {\n super(options);\n this.olLayer = new OlLayer({\n source: new Source({}),\n render: this.getOlLayerRender(),\n });\n /**\n * Url of the mapbox style.\n * @type {string}\n * @private\n */\n this.styleUrl = options.url;\n /**\n * Api key for the url of the mapbox style.\n * If set to false, the apiKey is not required.\n * @type {string}\n * @private\n */\n this.apiKey = options.apiKey;\n /**\n * Name of the apiKey to set in the url request.\n * Default is 'key'.\n * @type {string}\n * @private\n */\n this.apiKeyName = options.apiKeyName || 'key';\n /** @ignore */\n this.updateAttribution = this.updateAttribution.bind(this);\n }\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n /**\n * The feature format.\n * @type {ol/format/GeoJSON}\n */\n this.format = new GeoJSON({\n featureProjection: this.map.getView().getProjection(),\n });\n this.loadMbMap();\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n detachFromMap() {\n if (this.mbMap) {\n this.mbMap.off('idle', this.updateAttribution);\n // Some asynchrone repaints are triggered even if the mbMap has been removed,\n // to avoid display of errors we set an empty function.\n this.mbMap.triggerRepaint = () => { };\n this.mbMap.remove();\n this.mbMap = undefined;\n }\n this.loaded = false;\n super.detachFromMap();\n }\n /**\n * Create the mapbox map.\n * @private\n */\n loadMbMap() {\n var _a, _b, _c;\n this.olListenersKeys.push(\n // @ts-ignore\n (_a = this.map) === null || _a === void 0 ? void 0 : _a.on('change:target', () => {\n this.loadMbMap();\n }));\n if (!((_b = this.map) === null || _b === void 0 ? void 0 : _b.getTargetElement())) {\n return;\n }\n if (!this.visible) {\n // On next change of visibility we load the map\n this.olListenersKeys.push(\n // @ts-ignore\n this.once('change:visible', () => {\n this.loadMbMap();\n }));\n return;\n }\n const container = document.createElement('div');\n container.style.position = 'absolute';\n container.style.width = '100%';\n container.style.height = '100%';\n if (!this.styleUrl) {\n // eslint-disable-next-line no-console\n console.error(`No styleUrl defined for mapbox layer: ${this.styleUrl}`);\n return;\n }\n if (!this.apiKey && !((_c = this.styleUrl) === null || _c === void 0 ? void 0 : _c.includes(this.apiKeyName))) {\n // eslint-disable-next-line no-console\n console.error(`No apiKey defined for mapbox layer with style url to ${this.styleUrl}`);\n }\n const Map = this.getMapboxMapClass();\n /**\n * A mapbox map\n * @type {mapboxgl.Map}\n */\n this.mbMap = new Map(Object.assign({ style: getUrlWithParams(this.styleUrl, {\n [this.apiKeyName]: this.apiKey,\n }).toString(), container, interactive: false, trackResize: false, attributionControl: false }, (this.options.mapOptions || {})));\n this.mbMap.once('load', () => {\n /**\n * Is the map loaded.\n * @type {boolean}\n */\n this.loaded = true;\n this.dispatchEvent(new BaseEvent('load'));\n });\n this.mbMap.on('idle', this.updateAttribution);\n }\n /**\n * Update attributions of the source.\n * @private\n */\n updateAttribution(evt) {\n var _a, _b, _c;\n const newAttributions = getMapboxMapCopyrights(evt.target) || [];\n if (((_a = this.copyrights) === null || _a === void 0 ? void 0 : _a.toString()) !== newAttributions.toString()) {\n this.copyrights = newAttributions;\n // @ts-ignore\n (_c = (_b = this.olLayer) === null || _b === void 0 ? void 0 : _b.getSource()) === null || _c === void 0 ? void 0 : _c.setAttributions(newAttributions);\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate Coordinate to request the information at.\n * @param {Object} options A [mapboxgl.Map#queryrenderedfeatures](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#queryrenderedfeatures) options parameter.\n * @return {Promise} Promise with features, layer and coordinate. The original Mapbox feature is available as a property named 'mapboxFeature'.\n */\n getFeatureInfoAtCoordinate(coordinate, options) {\n // Ignore the getFeatureInfo until the mapbox map is loaded\n if (!options ||\n !this.format ||\n !this.mbMap ||\n !this.mbMap.isStyleLoaded()) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n const pixel = coordinate &&\n this.mbMap.project(toLonLat(coordinate));\n let pixels;\n if (this.hitTolerance) {\n const { x, y } = pixel;\n pixels = [\n {\n x: x - this.hitTolerance,\n y: y - this.hitTolerance,\n },\n {\n x: x + this.hitTolerance,\n y: y + this.hitTolerance,\n },\n ];\n }\n // At this point we get GeoJSON Mapbox feature, we transform it to an OpenLayers\n // feature to be consistent with other layers.\n const features = this.mbMap\n // @ts-ignore\n .queryRenderedFeatures(pixels || pixel, options)\n .map((feature) => {\n const olFeature = this.format.readFeature(feature);\n if (olFeature) {\n // We save the original mapbox feature to avoid losing informations\n // potentially needed for other functionnality like highlighting\n // (id, layer id, source, sourceLayer ...)\n olFeature.set('mapboxFeature', feature);\n }\n return olFeature;\n });\n return Promise.resolve({\n layer: this,\n features,\n coordinate,\n });\n }\n /**\n * Return the render function function for the ol layer.\n *\n */\n // eslint-disable-next-line class-methods-use-this\n getOlLayerRender() {\n // eslint-disable-next-line no-console\n console.error('This function must be implemented in subclasses');\n const div = document.createElement('div');\n return () => div;\n }\n /**\n * Return the Class to instanciate for the mapbox map.\n *\n * @return {mapboxgl.Map|maplibregl.Map} map\n */\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n // eslint-disable-next-line no-console\n console.error('This function must be implemented in subclasses');\n // @ts-ignore\n return null;\n }\n}\nexport default MapGlLayer;\n", + "content": "/* eslint-disable max-classes-per-file */\nimport { toLonLat } from 'ol/proj';\nimport OlLayer from 'ol/layer/Layer';\nimport Source from 'ol/source/Source';\nimport GeoJSON from 'ol/format/GeoJSON';\nimport BaseEvent from 'ol/events/Event';\nimport { getUrlWithParams, getMapboxMapCopyrights } from '../../common/utils';\nimport Layer from './Layer';\n/**\n * Common class for Mapbox and Maplibre and potential other fork from Mapbox.\n * It's used to share code between Mapbox and Maplibre layers without importing both libs.\n */\nclass MapGlLayer extends Layer {\n constructor(options) {\n super(options);\n this.olLayer = new OlLayer({\n source: new Source({}),\n render: this.getOlLayerRender(),\n });\n /**\n * Url of the mapbox style.\n * @type {string}\n * @private\n */\n this.styleUrl = options.url;\n /**\n * Api key for the url of the mapbox style.\n * If set to false, the apiKey is not required.\n * @type {string}\n * @private\n */\n this.apiKey = options.apiKey;\n /**\n * Name of the apiKey to set in the url request.\n * Default is 'key'.\n * @type {string}\n * @private\n */\n this.apiKeyName = options.apiKeyName || 'key';\n /** @ignore */\n this.updateAttribution = this.updateAttribution.bind(this);\n }\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n /**\n * The feature format.\n * @type {ol/format/GeoJSON}\n */\n this.format = new GeoJSON({\n featureProjection: this.map.getView().getProjection(),\n });\n this.loadMbMap();\n }\n /**\n * Terminate what was initialized in init function. Remove layer, events...\n */\n detachFromMap() {\n if (this.mbMap) {\n this.mbMap.off('idle', this.updateAttribution);\n // Some asynchrone repaints are triggered even if the mbMap has been removed,\n // to avoid display of errors we set an empty function.\n this.mbMap.triggerRepaint = () => { };\n this.mbMap.remove();\n this.mbMap = undefined;\n }\n this.loaded = false;\n super.detachFromMap();\n }\n /**\n * Create the mapbox map.\n * @private\n */\n loadMbMap() {\n this.olListenersKeys.push(\n // @ts-ignore\n this.map?.on('change:target', () => {\n this.loadMbMap();\n }));\n if (!this.map?.getTargetElement()) {\n return;\n }\n if (!this.visible) {\n // On next change of visibility we load the map\n this.olListenersKeys.push(\n // @ts-ignore\n this.once('change:visible', () => {\n this.loadMbMap();\n }));\n return;\n }\n const container = document.createElement('div');\n container.style.position = 'absolute';\n container.style.width = '100%';\n container.style.height = '100%';\n if (!this.styleUrl) {\n // eslint-disable-next-line no-console\n console.error(`No styleUrl defined for mapbox layer: ${this.styleUrl}`);\n return;\n }\n if (!this.apiKey && !this.styleUrl?.includes(this.apiKeyName)) {\n // eslint-disable-next-line no-console\n console.error(`No apiKey defined for mapbox layer with style url to ${this.styleUrl}`);\n }\n const Map = this.getMapboxMapClass();\n /**\n * A mapbox map\n * @type {mapboxgl.Map}\n */\n this.mbMap = new Map({\n style: getUrlWithParams(this.styleUrl, {\n [this.apiKeyName]: this.apiKey,\n }).toString(),\n container,\n interactive: false,\n trackResize: false,\n attributionControl: false,\n ...(this.options.mapOptions || {}),\n });\n this.mbMap.once('load', () => {\n /**\n * Is the map loaded.\n * @type {boolean}\n */\n this.loaded = true;\n this.dispatchEvent(new BaseEvent('load'));\n });\n this.mbMap.on('idle', this.updateAttribution);\n }\n /**\n * Update attributions of the source.\n * @private\n */\n updateAttribution(evt) {\n const newAttributions = getMapboxMapCopyrights(evt.target) || [];\n if (this.copyrights?.toString() !== newAttributions.toString()) {\n this.copyrights = newAttributions;\n // @ts-ignore\n this.olLayer?.getSource()?.setAttributions(newAttributions);\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate Coordinate to request the information at.\n * @param {Object} options A [mapboxgl.Map#queryrenderedfeatures](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#queryrenderedfeatures) options parameter.\n * @return {Promise} Promise with features, layer and coordinate. The original Mapbox feature is available as a property named 'mapboxFeature'.\n */\n getFeatureInfoAtCoordinate(coordinate, options) {\n // Ignore the getFeatureInfo until the mapbox map is loaded\n if (!options ||\n !this.format ||\n !this.mbMap ||\n !this.mbMap.isStyleLoaded()) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n const pixel = coordinate &&\n this.mbMap.project(toLonLat(coordinate));\n let pixels;\n if (this.hitTolerance) {\n const { x, y } = pixel;\n pixels = [\n {\n x: x - this.hitTolerance,\n y: y - this.hitTolerance,\n },\n {\n x: x + this.hitTolerance,\n y: y + this.hitTolerance,\n },\n ];\n }\n // At this point we get GeoJSON Mapbox feature, we transform it to an OpenLayers\n // feature to be consistent with other layers.\n const features = this.mbMap\n // @ts-ignore\n .queryRenderedFeatures(pixels || pixel, options)\n .map((feature) => {\n const olFeature = this.format.readFeature(feature);\n if (olFeature) {\n // We save the original mapbox feature to avoid losing informations\n // potentially needed for other functionnality like highlighting\n // (id, layer id, source, sourceLayer ...)\n olFeature.set('mapboxFeature', feature);\n }\n return olFeature;\n });\n return Promise.resolve({\n layer: this,\n features,\n coordinate,\n });\n }\n /**\n * Return the render function function for the ol layer.\n *\n */\n // eslint-disable-next-line class-methods-use-this\n getOlLayerRender() {\n // eslint-disable-next-line no-console\n console.error('This function must be implemented in subclasses');\n const div = document.createElement('div');\n return () => div;\n }\n /**\n * Return the Class to instanciate for the mapbox map.\n *\n * @return {mapboxgl.Map|maplibregl.Map} map\n */\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n // eslint-disable-next-line no-console\n console.error('This function must be implemented in subclasses');\n // @ts-ignore\n return null;\n }\n}\nexport default MapGlLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/MapGlLayer.js", "access": "public", @@ -8504,7 +8706,7 @@ "lineNumber": 1 }, { - "__docId__": 407, + "__docId__": 417, "kind": "class", "name": "MapGlLayer", "memberof": "build/ol/layers/MapGlLayer.js", @@ -8522,7 +8724,7 @@ ] }, { - "__docId__": 408, + "__docId__": 418, "kind": "constructor", "name": "constructor", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8536,7 +8738,7 @@ "undocument": true }, { - "__docId__": 409, + "__docId__": 419, "kind": "member", "name": "olLayer", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8553,7 +8755,7 @@ } }, { - "__docId__": 410, + "__docId__": 420, "kind": "member", "name": "styleUrl", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8572,7 +8774,7 @@ } }, { - "__docId__": 411, + "__docId__": 421, "kind": "member", "name": "apiKey", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8591,7 +8793,7 @@ } }, { - "__docId__": 412, + "__docId__": 422, "kind": "member", "name": "apiKeyName", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8610,7 +8812,7 @@ } }, { - "__docId__": 414, + "__docId__": 424, "kind": "method", "name": "attachToMap", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8636,7 +8838,7 @@ "return": null }, { - "__docId__": 415, + "__docId__": 425, "kind": "member", "name": "format", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8655,7 +8857,7 @@ } }, { - "__docId__": 416, + "__docId__": 426, "kind": "method", "name": "detachFromMap", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8670,7 +8872,7 @@ "return": null }, { - "__docId__": 417, + "__docId__": 427, "kind": "member", "name": "mbMap", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8687,7 +8889,7 @@ } }, { - "__docId__": 418, + "__docId__": 428, "kind": "member", "name": "loaded", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8704,7 +8906,7 @@ } }, { - "__docId__": 419, + "__docId__": 429, "kind": "method", "name": "loadMbMap", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8719,7 +8921,7 @@ "return": null }, { - "__docId__": 422, + "__docId__": 432, "kind": "method", "name": "updateAttribution", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8729,7 +8931,7 @@ "longname": "build/ol/layers/MapGlLayer.js~MapGlLayer#updateAttribution", "access": "private", "description": "Update attributions of the source.", - "lineNumber": 134, + "lineNumber": 140, "params": [ { "name": "evt", @@ -8741,7 +8943,7 @@ "return": null }, { - "__docId__": 423, + "__docId__": 433, "kind": "member", "name": "copyrights", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8749,7 +8951,7 @@ "longname": "build/ol/layers/MapGlLayer.js~MapGlLayer#copyrights", "access": "public", "description": null, - "lineNumber": 138, + "lineNumber": 143, "undocument": true, "type": { "types": [ @@ -8758,7 +8960,7 @@ } }, { - "__docId__": 424, + "__docId__": 434, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8768,7 +8970,7 @@ "longname": "build/ol/layers/MapGlLayer.js~MapGlLayer#getFeatureInfoAtCoordinate", "access": "public", "description": "Request feature information for a given coordinate.", - "lineNumber": 149, + "lineNumber": 154, "params": [ { "nullable": null, @@ -8801,7 +9003,7 @@ } }, { - "__docId__": 425, + "__docId__": 435, "kind": "method", "name": "getOlLayerRender", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8811,7 +9013,7 @@ "longname": "build/ol/layers/MapGlLayer.js~MapGlLayer#getOlLayerRender", "access": "public", "description": "Return the render function function for the ol layer.", - "lineNumber": 199, + "lineNumber": 204, "params": [], "return": { "types": [ @@ -8820,7 +9022,7 @@ } }, { - "__docId__": 426, + "__docId__": 436, "kind": "method", "name": "getMapboxMapClass", "memberof": "build/ol/layers/MapGlLayer.js~MapGlLayer", @@ -8830,7 +9032,7 @@ "longname": "build/ol/layers/MapGlLayer.js~MapGlLayer#getMapboxMapClass", "access": "public", "description": "Return the Class to instanciate for the mapbox map.", - "lineNumber": 211, + "lineNumber": 216, "return": { "nullable": null, "types": [ @@ -8843,10 +9045,10 @@ "params": [] }, { - "__docId__": 427, + "__docId__": 437, "kind": "file", "name": "build/ol/layers/MapboxLayer.js", - "content": "/* eslint-disable no-underscore-dangle */\nimport { Map } from 'mapbox-gl';\nimport { getMapboxMapCopyrights, getMapboxRender } from '../../common/utils';\nimport MapGlLayer from './MapGlLayer';\n/**\n * A class representing Mapboxlayer to display on BasicMap\n *\n * @example\n * import { MapboxLayer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new MapboxLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json',\n * apikey: 'yourApiKey',\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nexport default class MapboxLayer extends MapGlLayer {\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n this.olListenersKeys.push(this.map.on('change:size', () => {\n try {\n if (this.mbMap) {\n this.mbMap.resize();\n }\n }\n catch (err) {\n // ignore render errors\n // eslint-disable-next-line no-console\n console.warn(err);\n }\n }));\n }\n /**\n * Create the mapbox map.\n * @private\n */\n loadMbMap() {\n var _a;\n // If the map hasn't been resized, the center could be [NaN,NaN].\n // We set default good value for the mapbox map, to avoid the app crashes.\n let [x, y] = ((_a = this.map) === null || _a === void 0 ? void 0 : _a.getView().getCenter()) || [];\n if (!x || !y) {\n x = 0;\n y = 0;\n }\n // Options the last render run did happen. If something changes\n // we have to render again\n /** @ignore */\n this.renderState = {\n center: [x, y],\n zoom: undefined,\n rotation: undefined,\n visible: undefined,\n opacity: undefined,\n size: [0, 0],\n };\n super.loadMbMap();\n if (!this.mbMap) {\n // mbMap could ne bull if the map is not in the dom yet.\n return;\n }\n this.mbMap.once('load', () => {\n var _a, _b;\n if (!this.mbMap) {\n return;\n }\n this.mbMap.resize();\n /** @ignore */\n this.copyrights = getMapboxMapCopyrights(this.mbMap) || [];\n // @ts-ignore\n (_b = (_a = this.olLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.setAttributions(this.copyrights);\n });\n const mapboxCanvas = this.mbMap.getCanvas();\n if (mapboxCanvas) {\n if (this.options.tabIndex) {\n mapboxCanvas.setAttribute('tabindex', `${this.options.tabIndex}`);\n }\n else {\n // With a tabIndex='-1' the mouse events works but the map is not focused when we click on it\n // so we remove completely the tabIndex attribute.\n mapboxCanvas.removeAttribute('tabindex');\n }\n }\n }\n getOlLayerRender() {\n return getMapboxRender(this);\n }\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n return Map;\n }\n /**\n * Create a copy of the MapboxLayer.\n * @param {Object} newOptions Options to override\n * @return {MapboxLayer} A MapboxLayer\n */\n clone(newOptions) {\n return new MapboxLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\n", + "content": "/* eslint-disable no-underscore-dangle */\nimport { Map } from 'mapbox-gl';\nimport { getMapboxMapCopyrights, getMapboxRender } from '../../common/utils';\nimport MapGlLayer from './MapGlLayer';\n/**\n * A class representing Mapboxlayer to display on BasicMap\n *\n * @example\n * import { MapboxLayer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new MapboxLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json',\n * apikey: 'yourApiKey',\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nexport default class MapboxLayer extends MapGlLayer {\n /**\n * Initialize the layer and listen to feature clicks.\n * @param {ol/Map~Map} map\n */\n attachToMap(map) {\n super.attachToMap(map);\n if (!this.map) {\n return;\n }\n this.olListenersKeys.push(this.map.on('change:size', () => {\n try {\n if (this.mbMap) {\n this.mbMap.resize();\n }\n }\n catch (err) {\n // ignore render errors\n // eslint-disable-next-line no-console\n console.warn(err);\n }\n }));\n }\n /**\n * Create the mapbox map.\n * @private\n */\n loadMbMap() {\n // If the map hasn't been resized, the center could be [NaN,NaN].\n // We set default good value for the mapbox map, to avoid the app crashes.\n let [x, y] = this.map?.getView().getCenter() || [];\n if (!x || !y) {\n x = 0;\n y = 0;\n }\n // Options the last render run did happen. If something changes\n // we have to render again\n /** @ignore */\n this.renderState = {\n center: [x, y],\n zoom: undefined,\n rotation: undefined,\n visible: undefined,\n opacity: undefined,\n size: [0, 0],\n };\n super.loadMbMap();\n if (!this.mbMap) {\n // mbMap could ne bull if the map is not in the dom yet.\n return;\n }\n this.mbMap.once('load', () => {\n if (!this.mbMap) {\n return;\n }\n this.mbMap.resize();\n /** @ignore */\n this.copyrights = getMapboxMapCopyrights(this.mbMap) || [];\n // @ts-ignore\n this.olLayer?.getSource()?.setAttributions(this.copyrights);\n });\n const mapboxCanvas = this.mbMap.getCanvas();\n if (mapboxCanvas) {\n if (this.options.tabIndex) {\n mapboxCanvas.setAttribute('tabindex', `${this.options.tabIndex}`);\n }\n else {\n // With a tabIndex='-1' the mouse events works but the map is not focused when we click on it\n // so we remove completely the tabIndex attribute.\n mapboxCanvas.removeAttribute('tabindex');\n }\n }\n }\n getOlLayerRender() {\n return getMapboxRender(this);\n }\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n return Map;\n }\n /**\n * Create a copy of the MapboxLayer.\n * @param {Object} newOptions Options to override\n * @return {MapboxLayer} A MapboxLayer\n */\n clone(newOptions) {\n return new MapboxLayer({ ...this.options, ...newOptions });\n }\n}\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/MapboxLayer.js", "access": "public", @@ -8854,7 +9056,7 @@ "lineNumber": 1 }, { - "__docId__": 428, + "__docId__": 438, "kind": "class", "name": "MapboxLayer", "memberof": "build/ol/layers/MapboxLayer.js", @@ -8881,7 +9083,7 @@ ] }, { - "__docId__": 429, + "__docId__": 439, "kind": "method", "name": "attachToMap", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8907,7 +9109,7 @@ "return": null }, { - "__docId__": 430, + "__docId__": 440, "kind": "method", "name": "loadMbMap", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8922,7 +9124,7 @@ "return": null }, { - "__docId__": 431, + "__docId__": 441, "kind": "member", "name": "renderState", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8930,7 +9132,7 @@ "longname": "build/ol/layers/MapboxLayer.js~MapboxLayer#renderState", "access": "public", "description": null, - "lineNumber": 58, + "lineNumber": 57, "ignore": true, "type": { "types": [ @@ -8939,7 +9141,7 @@ } }, { - "__docId__": 432, + "__docId__": 442, "kind": "member", "name": "copyrights", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8947,7 +9149,7 @@ "longname": "build/ol/layers/MapboxLayer.js~MapboxLayer#copyrights", "access": "public", "description": null, - "lineNumber": 78, + "lineNumber": 76, "ignore": true, "type": { "types": [ @@ -8956,7 +9158,7 @@ } }, { - "__docId__": 433, + "__docId__": 443, "kind": "method", "name": "getOlLayerRender", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8966,7 +9168,7 @@ "longname": "build/ol/layers/MapboxLayer.js~MapboxLayer#getOlLayerRender", "access": "public", "description": null, - "lineNumber": 94, + "lineNumber": 92, "undocument": true, "params": [], "return": { @@ -8976,7 +9178,7 @@ } }, { - "__docId__": 434, + "__docId__": 444, "kind": "method", "name": "getMapboxMapClass", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -8986,7 +9188,7 @@ "longname": "build/ol/layers/MapboxLayer.js~MapboxLayer#getMapboxMapClass", "access": "public", "description": null, - "lineNumber": 98, + "lineNumber": 96, "undocument": true, "params": [], "return": { @@ -8996,7 +9198,7 @@ } }, { - "__docId__": 435, + "__docId__": 445, "kind": "method", "name": "clone", "memberof": "build/ol/layers/MapboxLayer.js~MapboxLayer", @@ -9006,7 +9208,7 @@ "longname": "build/ol/layers/MapboxLayer.js~MapboxLayer#clone", "access": "public", "description": "Create a copy of the MapboxLayer.", - "lineNumber": 106, + "lineNumber": 104, "params": [ { "nullable": null, @@ -9029,10 +9231,10 @@ } }, { - "__docId__": 436, + "__docId__": 446, "kind": "file", "name": "build/ol/layers/MapboxStyleLayer.js", - "content": "import Layer from './Layer';\n/**\n * Layer for visualizing a specific set of layer from a MapboxLayer.\n *\n * @example\n * import { MapboxLayer, MapboxStyleLayer } from 'mobility-toolbox-js/ol';\n *\n * const mapboxLayer = new MapboxLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json?key=[yourApiKey]',\n * });\n *\n * const layer = new MapboxStyleLayer({\n * mapboxLayer: mapboxLayer,\n * styleLayersFilter: () => {},\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass MapboxStyleLayer extends Layer {\n /**\n * Constructor.\n *\n * @param {Object} options\n * @param {MapboxLayer} [options.mapboxLayer] The MapboxLayer to use.\n * @param {Function} [options.styleLayersFilter] Filter function to decide which style layer to display.\n */\n constructor(options) {\n super(options);\n /**\n * MapboxLayer provided for the style Layer.\n * @type {MapboxLayer}\n * @private\n */\n this.mapboxLayer = options.mapboxLayer;\n /**\n * Define if the layer has data to display in the current mapbox layer.\n */\n this.disabled = false;\n /**\n * Function to filter features to be displayed.\n * @type {function}\n * @private\n */\n this.styleLayersFilter = options.styleLayersFilter;\n /**\n * Mapbox style layer id where to add the style layers.\n * See [mapbox.map.addLayer](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#addlayer) documentation.\n * @type {String}\n * @private\n */\n this.beforeId = options.beforeId;\n /**\n * Function to filter features for getFeatureInfoAtCoordinate method.\n * @type {function}\n * @private\n */\n this.featureInfoFilter = options.featureInfoFilter || ((obj) => obj);\n /**\n * Function to query the rendered features.\n * @type {function}\n * @private\n */\n this.queryRenderedLayersFilter = options.queryRenderedLayersFilter;\n /**\n * Array of features to highlight.\n * @type {Array
    }\n * @private\n */\n this.highlightedFeatures = [];\n /**\n * Array of selected features.\n * @type {Array
      }\n * @private\n */\n this.selectedFeatures = [];\n /**\n * Array of mapbox style layers to add.\n * @type {Array}\n * @private\n */\n this.styleLayers =\n (options.styleLayer ? [options.styleLayer] : options.styleLayers) || [];\n /**\n * @private\n */\n this.addStyleLayers = this.addStyleLayers.bind(this);\n /**\n * @private\n */\n this.onLoad = this.onLoad.bind(this);\n if (options.filters) {\n /** @private */\n this.addDynamicFilters = () => {\n this.setFilter(typeof options.filters === 'function'\n ? options.filters(this)\n : options.filters);\n };\n }\n if (!this.styleLayersFilter && this.styleLayers) {\n this.styleLayersFilter = (styleLayer) => {\n var _a;\n return !!((_a = this.styleLayers) === null || _a === void 0 ? void 0 : _a.find((sl) => styleLayer.id === sl.id));\n };\n }\n }\n /**\n * Initialize the layer.\n * @param {ol/Map~Map} map the mapbox map.\n * @override\n */\n attachToMap(map) {\n var _a;\n if (this.mapboxLayer && !this.mapboxLayer.map) {\n (_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.attachToMap(map);\n }\n super.attachToMap(map);\n if (!this.map || !this.mapboxLayer) {\n return;\n }\n // Apply the initial visibiltity.\n const { mbMap } = this.mapboxLayer;\n if (!mbMap) {\n // If the mbMap is not yet created because the map has no target yet, we\n // relaunch the initialisation when it's the case.\n this.olListenersKeys.push(this.map.on('change:target', () => {\n this.attachToMap(map);\n }));\n return;\n }\n // mbMap.loaded() and mbMap.isStyleLoaded() are reliable only on the first call of init.\n // On the next call (when a topic change for example), these functions returns false because\n // the style is being modified.\n // That's why we rely on a property instead for the next calls.\n if (this.mapboxLayer.loaded || mbMap.isStyleLoaded() || mbMap.loaded()) {\n this.onLoad();\n }\n else {\n mbMap.once('load', this.onLoad);\n }\n // Apply the visibiltity when layer's visibility change.\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', (evt) => {\n // Once the map is loaded we can apply vsiiblity without waiting\n // the style. Mapbox take care of the application of style changes.\n this.applyLayoutVisibility(evt);\n }));\n this.olListenersKeys.push(\n // @ts-ignore\n this.mapboxLayer.on('load', () => {\n this.onLoad();\n }));\n }\n /**\n * Terminate the layer.\n * @override\n */\n detachFromMap() {\n var _a;\n if ((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap) {\n const { mbMap } = this.mapboxLayer;\n mbMap.off('load', this.onLoad);\n this.removeStyleLayers();\n }\n super.detachFromMap();\n }\n /** @ignore */\n addStyleLayers() {\n var _a;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach((styleLayer) => {\n const { id, source } = styleLayer;\n if (mbMap.getSource(source) && id && !mbMap.getLayer(id)) {\n // @ts-ignore\n mbMap.addLayer(styleLayer, this.beforeId);\n }\n });\n this.applyLayoutVisibility();\n }\n /** @ignore */\n removeStyleLayers() {\n var _a;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach((styleLayer) => {\n const { id } = styleLayer;\n if (id && mbMap.getLayer(id)) {\n mbMap.removeLayer(id);\n }\n });\n }\n /**\n * On Mapbox map load callback function. Add style layers and dynaimc filters.\n * @ignore\n */\n onLoad() {\n var _a;\n this.addStyleLayers();\n if (this.addDynamicFilters) {\n this.addDynamicFilters();\n }\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n const style = mbMap.getStyle();\n if (style && this.styleLayersFilter) {\n // @ts-ignore\n const styles = style.layers.filter(this.styleLayersFilter);\n this.disabled = !styles.length;\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate Coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n var _a;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n const { mbMap } = this.mapboxLayer;\n // Ignore the getFeatureInfo until the mapbox map is loaded\n if (!mbMap.isStyleLoaded()) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n // We query features only on style layers used by this layer.\n let layers = this.styleLayers || [];\n if (this.styleLayersFilter) {\n // @ts-ignore\n layers = mbMap.getStyle().layers.filter(this.styleLayersFilter);\n }\n if (this.queryRenderedLayersFilter) {\n // @ts-ignore\n layers = mbMap.getStyle().layers.filter(this.queryRenderedLayersFilter);\n }\n return this.mapboxLayer\n .getFeatureInfoAtCoordinate(coordinate, {\n layers: layers.map((layer) => layer && layer.id),\n validate: false,\n })\n .then((featureInfo) => {\n const features = featureInfo.features.filter((feature) => {\n var _a;\n // @ts-ignore\n return this.featureInfoFilter(feature, (_a = this.map) === null || _a === void 0 ? void 0 : _a.getView().getResolution());\n });\n this.highlight(features);\n return Object.assign(Object.assign({}, featureInfo), { features, layer: this });\n });\n }\n /**\n * Set filter that determines which features should be rendered in a style layer.\n * @param {mapboxgl.filter} filter Determines which features should be rendered in a style layer.\n */\n setFilter(filter) {\n var _a;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach(({ id }) => {\n if (id && filter && mbMap.getLayer(id)) {\n // @ts-ignore\n mbMap.setFilter(id, filter);\n }\n });\n }\n /**\n * Set if features are hovered or not.\n * @param {Array
        } features\n * @param {boolean} state Is the feature hovered\n * @private\n */\n setHoverState(features, state) {\n var _a;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n if (!features || !mbMap) {\n return;\n }\n features.forEach((feature) => {\n const { source, sourceLayer } = feature.get('mapboxFeature') || {};\n if ((!source && !sourceLayer) || !feature.getId()) {\n if (!feature.getId()) {\n // eslint-disable-next-line no-console\n console.warn(\"No feature's id found. To use the feature state functionnality, tiles must be generated with --generate-ids. See https://github.com/mapbox/tippecanoe#adding-calculated-attributes.\", feature.getId(), feature.getProperties());\n }\n return;\n }\n mbMap.setFeatureState({\n id: feature.getId(),\n source,\n sourceLayer,\n }, { hover: state });\n });\n }\n /**\n * Select a list of features.\n * @param {Array
          } [features=[]] Features to select.\n * @private\n */\n select(features = []) {\n this.setHoverState(this.selectedFeatures || [], false);\n this.selectedFeatures = features;\n this.setHoverState(this.selectedFeatures || [], true);\n }\n /**\n * Highlight a list of features.\n * @param {Array
            } [features=[]] Features to highlight.\n * @private\n */\n highlight(features = []) {\n var _a;\n // Filter out selected features\n const filtered = ((_a = this.highlightedFeatures) === null || _a === void 0 ? void 0 : _a.filter((feature) => !(this.selectedFeatures || [])\n .map((feat) => feat.getId())\n .includes(feature.getId()))) || [];\n // Remove previous highlight\n this.setHoverState(filtered, false);\n this.highlightedFeatures = features;\n // Add highlight\n this.setHoverState(this.highlightedFeatures, true);\n }\n /**\n * Apply visibility to style layers that fits the styleLayersFilter function.\n * @param {Event} evt Layer's event that has called the function.\n * @private\n */\n // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n applyLayoutVisibility(evt) {\n var _a;\n const { visible } = this;\n const filterFunc = this.styleLayersFilter;\n if (!((_a = this.mapboxLayer) === null || _a === void 0 ? void 0 : _a.mbMap)) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n const style = mbMap.getStyle();\n if (!style) {\n return;\n }\n if (filterFunc) {\n const visibilityValue = visible ? 'visible' : 'none';\n const layers = style.layers || [];\n for (let i = 0; i < layers.length; i += 1) {\n const styleLayer = layers[i];\n if (filterFunc(styleLayer)) {\n if (mbMap.getLayer(styleLayer.id)) {\n mbMap.setLayoutProperty(styleLayer.id, 'visibility', visibilityValue);\n }\n }\n }\n }\n }\n /**\n * Create a copy of the MapboxStyleLayer.\n * @param {Object} newOptions Options to override.\n * @return {MapboxStyleLayer} A MapboxStyleLayer.\n */\n clone(newOptions) {\n return new MapboxStyleLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default MapboxStyleLayer;\n", + "content": "import Layer from './Layer';\n/**\n * Layer for visualizing a specific set of layer from a MapboxLayer.\n *\n * @example\n * import { MapboxLayer, MapboxStyleLayer } from 'mobility-toolbox-js/ol';\n *\n * const mapboxLayer = new MapboxLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json?key=[yourApiKey]',\n * });\n *\n * const layer = new MapboxStyleLayer({\n * mapboxLayer: mapboxLayer,\n * styleLayersFilter: () => {},\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass MapboxStyleLayer extends Layer {\n /**\n * Constructor.\n *\n * @param {Object} options\n * @param {MapboxLayer} [options.mapboxLayer] The MapboxLayer to use.\n * @param {Function} [options.styleLayersFilter] Filter function to decide which style layer to display.\n */\n constructor(options) {\n super(options);\n /**\n * MapboxLayer provided for the style Layer.\n * @type {MapboxLayer}\n * @private\n */\n this.mapboxLayer = options.mapboxLayer;\n /**\n * Define if the layer has data to display in the current mapbox layer.\n */\n this.disabled = false;\n /**\n * Function to filter features to be displayed.\n * @type {function}\n * @private\n */\n this.styleLayersFilter = options.styleLayersFilter;\n /**\n * Mapbox style layer id where to add the style layers.\n * See [mapbox.map.addLayer](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#addlayer) documentation.\n * @type {String}\n * @private\n */\n this.beforeId = options.beforeId;\n /**\n * Function to filter features for getFeatureInfoAtCoordinate method.\n * @type {function}\n * @private\n */\n this.featureInfoFilter = options.featureInfoFilter || ((obj) => obj);\n /**\n * Function to query the rendered features.\n * @type {function}\n * @private\n */\n this.queryRenderedLayersFilter = options.queryRenderedLayersFilter;\n /**\n * Array of features to highlight.\n * @type {Array
              }\n * @private\n */\n this.highlightedFeatures = [];\n /**\n * Array of selected features.\n * @type {Array
                }\n * @private\n */\n this.selectedFeatures = [];\n /**\n * Array of mapbox style layers to add.\n * @type {Array}\n * @private\n */\n this.styleLayers =\n (options.styleLayer ? [options.styleLayer] : options.styleLayers) || [];\n /**\n * @private\n */\n this.addStyleLayers = this.addStyleLayers.bind(this);\n /**\n * @private\n */\n this.onLoad = this.onLoad.bind(this);\n if (options.filters) {\n /** @private */\n this.addDynamicFilters = () => {\n this.setFilter(typeof options.filters === 'function'\n ? options.filters(this)\n : options.filters);\n };\n }\n if (!this.styleLayersFilter && this.styleLayers) {\n this.styleLayersFilter = (styleLayer) => {\n return !!this.styleLayers?.find((sl) => styleLayer.id === sl.id);\n };\n }\n }\n /**\n * Initialize the layer.\n * @param {ol/Map~Map} map the mapbox map.\n * @override\n */\n attachToMap(map) {\n if (this.mapboxLayer && !this.mapboxLayer.map) {\n this.mapboxLayer?.attachToMap(map);\n }\n super.attachToMap(map);\n if (!this.map || !this.mapboxLayer) {\n return;\n }\n // Apply the initial visibiltity.\n const { mbMap } = this.mapboxLayer;\n if (!mbMap) {\n // If the mbMap is not yet created because the map has no target yet, we\n // relaunch the initialisation when it's the case.\n this.olListenersKeys.push(this.map.on('change:target', () => {\n this.attachToMap(map);\n }));\n return;\n }\n // mbMap.loaded() and mbMap.isStyleLoaded() are reliable only on the first call of init.\n // On the next call (when a topic change for example), these functions returns false because\n // the style is being modified.\n // That's why we rely on a property instead for the next calls.\n if (this.mapboxLayer.loaded || mbMap.isStyleLoaded() || mbMap.loaded()) {\n this.onLoad();\n }\n else {\n mbMap.once('load', this.onLoad);\n }\n // Apply the visibiltity when layer's visibility change.\n this.olListenersKeys.push(\n // @ts-ignore\n this.on('change:visible', (evt) => {\n // Once the map is loaded we can apply vsiiblity without waiting\n // the style. Mapbox take care of the application of style changes.\n this.applyLayoutVisibility(evt);\n }));\n this.olListenersKeys.push(\n // @ts-ignore\n this.mapboxLayer.on('load', () => {\n this.onLoad();\n }));\n }\n /**\n * Terminate the layer.\n * @override\n */\n detachFromMap() {\n if (this.mapboxLayer?.mbMap) {\n const { mbMap } = this.mapboxLayer;\n mbMap.off('load', this.onLoad);\n this.removeStyleLayers();\n }\n super.detachFromMap();\n }\n /** @ignore */\n addStyleLayers() {\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach((styleLayer) => {\n const { id, source } = styleLayer;\n if (mbMap.getSource(source) && id && !mbMap.getLayer(id)) {\n // @ts-ignore\n mbMap.addLayer(styleLayer, this.beforeId);\n }\n });\n this.applyLayoutVisibility();\n }\n /** @ignore */\n removeStyleLayers() {\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach((styleLayer) => {\n const { id } = styleLayer;\n if (id && mbMap.getLayer(id)) {\n mbMap.removeLayer(id);\n }\n });\n }\n /**\n * On Mapbox map load callback function. Add style layers and dynaimc filters.\n * @ignore\n */\n onLoad() {\n this.addStyleLayers();\n if (this.addDynamicFilters) {\n this.addDynamicFilters();\n }\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n const style = mbMap.getStyle();\n if (style && this.styleLayersFilter) {\n // @ts-ignore\n const styles = style.layers.filter(this.styleLayersFilter);\n this.disabled = !styles.length;\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate Coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n if (!this.mapboxLayer?.mbMap) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n const { mbMap } = this.mapboxLayer;\n // Ignore the getFeatureInfo until the mapbox map is loaded\n if (!mbMap.isStyleLoaded()) {\n return Promise.resolve({ coordinate, features: [], layer: this });\n }\n // We query features only on style layers used by this layer.\n let layers = this.styleLayers || [];\n if (this.styleLayersFilter) {\n // @ts-ignore\n layers = mbMap.getStyle().layers.filter(this.styleLayersFilter);\n }\n if (this.queryRenderedLayersFilter) {\n // @ts-ignore\n layers = mbMap.getStyle().layers.filter(this.queryRenderedLayersFilter);\n }\n return this.mapboxLayer\n .getFeatureInfoAtCoordinate(coordinate, {\n layers: layers.map((layer) => layer && layer.id),\n validate: false,\n })\n .then((featureInfo) => {\n const features = featureInfo.features.filter((feature) => {\n // @ts-ignore\n return this.featureInfoFilter(feature, this.map?.getView().getResolution());\n });\n this.highlight(features);\n return { ...featureInfo, features, layer: this };\n });\n }\n /**\n * Set filter that determines which features should be rendered in a style layer.\n * @param {mapboxgl.filter} filter Determines which features should be rendered in a style layer.\n */\n setFilter(filter) {\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n this.styleLayers.forEach(({ id }) => {\n if (id && filter && mbMap.getLayer(id)) {\n // @ts-ignore\n mbMap.setFilter(id, filter);\n }\n });\n }\n /**\n * Set if features are hovered or not.\n * @param {Array
                  } features\n * @param {boolean} state Is the feature hovered\n * @private\n */\n setHoverState(features, state) {\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n if (!features || !mbMap) {\n return;\n }\n features.forEach((feature) => {\n const { source, sourceLayer } = feature.get('mapboxFeature') || {};\n if ((!source && !sourceLayer) || !feature.getId()) {\n if (!feature.getId()) {\n // eslint-disable-next-line no-console\n console.warn(\"No feature's id found. To use the feature state functionnality, tiles must be generated with --generate-ids. See https://github.com/mapbox/tippecanoe#adding-calculated-attributes.\", feature.getId(), feature.getProperties());\n }\n return;\n }\n mbMap.setFeatureState({\n id: feature.getId(),\n source,\n sourceLayer,\n }, { hover: state });\n });\n }\n /**\n * Select a list of features.\n * @param {Array
                    } [features=[]] Features to select.\n * @private\n */\n select(features = []) {\n this.setHoverState(this.selectedFeatures || [], false);\n this.selectedFeatures = features;\n this.setHoverState(this.selectedFeatures || [], true);\n }\n /**\n * Highlight a list of features.\n * @param {Array
                      } [features=[]] Features to highlight.\n * @private\n */\n highlight(features = []) {\n // Filter out selected features\n const filtered = this.highlightedFeatures?.filter((feature) => !(this.selectedFeatures || [])\n .map((feat) => feat.getId())\n .includes(feature.getId())) || [];\n // Remove previous highlight\n this.setHoverState(filtered, false);\n this.highlightedFeatures = features;\n // Add highlight\n this.setHoverState(this.highlightedFeatures, true);\n }\n /**\n * Apply visibility to style layers that fits the styleLayersFilter function.\n * @param {Event} evt Layer's event that has called the function.\n * @private\n */\n // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n applyLayoutVisibility(evt) {\n const { visible } = this;\n const filterFunc = this.styleLayersFilter;\n if (!this.mapboxLayer?.mbMap) {\n return;\n }\n const { mbMap } = this.mapboxLayer;\n const style = mbMap.getStyle();\n if (!style) {\n return;\n }\n if (filterFunc) {\n const visibilityValue = visible ? 'visible' : 'none';\n const layers = style.layers || [];\n for (let i = 0; i < layers.length; i += 1) {\n const styleLayer = layers[i];\n if (filterFunc(styleLayer)) {\n if (mbMap.getLayer(styleLayer.id)) {\n mbMap.setLayoutProperty(styleLayer.id, 'visibility', visibilityValue);\n }\n }\n }\n }\n }\n /**\n * Create a copy of the MapboxStyleLayer.\n * @param {Object} newOptions Options to override.\n * @return {MapboxStyleLayer} A MapboxStyleLayer.\n */\n clone(newOptions) {\n return new MapboxStyleLayer({ ...this.options, ...newOptions });\n }\n}\nexport default MapboxStyleLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/MapboxStyleLayer.js", "access": "public", @@ -9040,7 +9242,7 @@ "lineNumber": 1 }, { - "__docId__": 437, + "__docId__": 447, "kind": "class", "name": "MapboxStyleLayer", "memberof": "build/ol/layers/MapboxStyleLayer.js", @@ -9067,7 +9269,7 @@ ] }, { - "__docId__": 438, + "__docId__": 448, "kind": "constructor", "name": "constructor", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9112,7 +9314,7 @@ ] }, { - "__docId__": 439, + "__docId__": 449, "kind": "member", "name": "mapboxLayer", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9131,7 +9333,7 @@ } }, { - "__docId__": 440, + "__docId__": 450, "kind": "member", "name": "disabled", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9147,7 +9349,7 @@ } }, { - "__docId__": 441, + "__docId__": 451, "kind": "member", "name": "styleLayersFilter", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9166,7 +9368,7 @@ } }, { - "__docId__": 442, + "__docId__": 452, "kind": "member", "name": "beforeId", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9185,7 +9387,7 @@ } }, { - "__docId__": 443, + "__docId__": 453, "kind": "member", "name": "featureInfoFilter", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9204,7 +9406,7 @@ } }, { - "__docId__": 444, + "__docId__": 454, "kind": "member", "name": "queryRenderedLayersFilter", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9223,7 +9425,7 @@ } }, { - "__docId__": 445, + "__docId__": 455, "kind": "member", "name": "highlightedFeatures", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9242,7 +9444,7 @@ } }, { - "__docId__": 446, + "__docId__": 456, "kind": "member", "name": "selectedFeatures", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9261,7 +9463,7 @@ } }, { - "__docId__": 447, + "__docId__": 457, "kind": "member", "name": "styleLayers", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9280,7 +9482,7 @@ } }, { - "__docId__": 450, + "__docId__": 460, "kind": "member", "name": "addDynamicFilters", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9296,7 +9498,7 @@ } }, { - "__docId__": 452, + "__docId__": 462, "kind": "method", "name": "attachToMap", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9306,7 +9508,7 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#attachToMap", "access": "public", "description": "Initialize the layer.", - "lineNumber": 112, + "lineNumber": 111, "params": [ { "nullable": null, @@ -9323,7 +9525,7 @@ "return": null }, { - "__docId__": 453, + "__docId__": 463, "kind": "method", "name": "detachFromMap", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9333,13 +9535,13 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#detachFromMap", "access": "public", "description": "Terminate the layer.", - "lineNumber": 159, + "lineNumber": 157, "override": true, "params": [], "return": null }, { - "__docId__": 454, + "__docId__": 464, "kind": "method", "name": "addStyleLayers", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9349,13 +9551,13 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#addStyleLayers", "access": "public", "description": null, - "lineNumber": 169, + "lineNumber": 166, "ignore": true, "params": [], "return": null }, { - "__docId__": 455, + "__docId__": 465, "kind": "method", "name": "removeStyleLayers", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9365,13 +9567,13 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#removeStyleLayers", "access": "public", "description": null, - "lineNumber": 185, + "lineNumber": 181, "ignore": true, "params": [], "return": null }, { - "__docId__": 456, + "__docId__": 466, "kind": "method", "name": "onLoad", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9381,13 +9583,13 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#onLoad", "access": "public", "description": "On Mapbox map load callback function. Add style layers and dynaimc filters.", - "lineNumber": 202, + "lineNumber": 197, "ignore": true, "params": [], "return": null }, { - "__docId__": 458, + "__docId__": 468, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9397,7 +9599,7 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#getFeatureInfoAtCoordinate", "access": "public", "description": "Request feature information for a given coordinate.", - "lineNumber": 224, + "lineNumber": 218, "params": [ { "nullable": null, @@ -9420,7 +9622,7 @@ } }, { - "__docId__": 459, + "__docId__": 469, "kind": "method", "name": "setFilter", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9430,7 +9632,7 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#setFilter", "access": "public", "description": "Set filter that determines which features should be rendered in a style layer.", - "lineNumber": 263, + "lineNumber": 255, "params": [ { "nullable": null, @@ -9446,7 +9648,7 @@ "return": null }, { - "__docId__": 460, + "__docId__": 470, "kind": "method", "name": "setHoverState", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9456,7 +9658,7 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#setHoverState", "access": "private", "description": "Set if features are hovered or not.", - "lineNumber": 282, + "lineNumber": 273, "params": [ { "nullable": null, @@ -9482,7 +9684,7 @@ "return": null }, { - "__docId__": 461, + "__docId__": 471, "kind": "method", "name": "select", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", @@ -9492,7 +9694,7 @@ "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#select", "access": "private", "description": "Select a list of features.", - "lineNumber": 312, + "lineNumber": 302, "params": [ { "nullable": null, @@ -9510,837 +9712,182 @@ "return": null }, { - "__docId__": 463, + "__docId__": 473, "kind": "method", "name": "highlight", "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#highlight", - "access": "private", - "description": "Highlight a list of features.", - "lineNumber": 322, - "params": [ - { - "nullable": null, - "types": [ - "Array
                        " - ], - "spread": false, - "optional": true, - "defaultValue": "[]", - "defaultRaw": [], - "name": "features", - "description": "Features to highlight." - } - ], - "return": null - }, - { - "__docId__": 465, - "kind": "method", - "name": "applyLayoutVisibility", - "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#applyLayoutVisibility", - "access": "private", - "description": "Apply visibility to style layers that fits the styleLayersFilter function.", - "lineNumber": 340, - "params": [ - { - "nullable": null, - "types": [ - "Event" - ], - "spread": false, - "optional": false, - "name": "evt", - "description": "Layer's event that has called the function." - } - ], - "return": null - }, - { - "__docId__": 466, - "kind": "method", - "name": "clone", - "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#clone", - "access": "public", - "description": "Create a copy of the MapboxStyleLayer.", - "lineNumber": 370, - "params": [ - { - "nullable": null, - "types": [ - "Object" - ], - "spread": false, - "optional": false, - "name": "newOptions", - "description": "Options to override." - } - ], - "return": { - "nullable": null, - "types": [ - "MapboxStyleLayer" - ], - "spread": false, - "description": "A MapboxStyleLayer." - } - }, - { - "__docId__": 467, - "kind": "file", - "name": "build/ol/layers/MaplibreLayer.js", - "content": "import { Map } from 'maplibre-gl';\nimport { getMaplibreRender } from '../../common/utils';\nimport MapGlLayer from './MapGlLayer';\n/**\n * A class representing MaplibreLayer to display on BasicMap\n *\n * @example\n * import { MaplibreLayer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new MaplibreLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json',\n * apikey: 'yourApiKey',\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nexport default class MaplibreLayer extends MapGlLayer {\n getOlLayerRender() {\n return getMaplibreRender(this);\n }\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n return Map;\n }\n /**\n * Create a copy of the MapboxLayer.\n * @param {Object} newOptions Options to override\n * @return {MapboxLayer} A MapboxLayer\n */\n clone(newOptions) {\n return new MaplibreLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\n", - "static": true, - "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/MaplibreLayer.js", - "access": "public", - "description": null, - "lineNumber": 1 - }, - { - "__docId__": 468, - "kind": "class", - "name": "MaplibreLayer", - "memberof": "build/ol/layers/MaplibreLayer.js", - "static": true, - "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", - "access": "public", - "export": true, - "importPath": "mobility-toolbox-js/build/ol/layers/MaplibreLayer.js", - "importStyle": "MaplibreLayer", - "description": "A class representing MaplibreLayer to display on BasicMap", - "examples": [ - "import { MaplibreLayer } from 'mobility-toolbox-js/ol';\n\nconst layer = new MaplibreLayer({\n url: 'https://maps.geops.io/styles/travic_v2/style.json',\n apikey: 'yourApiKey',\n});" - ], - "lineNumber": 18, - "unknown": [ - { - "tagName": "@classproperty", - "tagValue": "{ol/Map~Map} map - The map where the layer is displayed." - } - ], - "interface": false, - "extends": [ - "Layer" - ] - }, - { - "__docId__": 469, - "kind": "method", - "name": "getOlLayerRender", - "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#getOlLayerRender", - "access": "public", - "description": null, - "lineNumber": 19, - "undocument": true, - "params": [], - "return": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 470, - "kind": "method", - "name": "getMapboxMapClass", - "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#getMapboxMapClass", - "access": "public", - "description": null, - "lineNumber": 23, - "undocument": true, - "params": [], - "return": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 471, - "kind": "method", - "name": "clone", - "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#clone", - "access": "public", - "description": "Create a copy of the MapboxLayer.", - "lineNumber": 31, - "params": [ - { - "nullable": null, - "types": [ - "Object" - ], - "spread": false, - "optional": false, - "name": "newOptions", - "description": "Options to override" - } - ], - "return": { - "nullable": null, - "types": [ - "MapboxLayer" - ], - "spread": false, - "description": "A MapboxLayer" - } - }, - { - "__docId__": 472, - "kind": "file", - "name": "build/ol/layers/RealtimeLayer.js", - "content": "import GeoJSON from 'ol/format/GeoJSON';\nimport { Layer as OLLayer, Group, Vector as VectorLayer } from 'ol/layer';\nimport Source from 'ol/source/Source';\nimport { composeCssTransform } from 'ol/transform';\nimport { Vector as VectorSource } from 'ol/source';\nimport Layer from './Layer';\nimport mixin from '../../common/mixins/RealtimeLayerMixin';\nimport { fullTrajectoryStyle } from '../styles';\n/** @private */\nconst format = new GeoJSON();\n/**\n * Responsible for loading and display data from a Realtime service.\n *\n * @example\n * import { RealtimeLayer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new RealtimeLayer({\n * url: [yourUrl],\n * apiKey: [yourApiKey],\n * });\n *\n *\n * @see RealtimeAPI\n *\n * @extends {Layer}\n * @implements {UserInteractionsLayerInterface}\n * @implements {RealtimeLayerInterface}\n */\n// @ts-ignore\nclass RealtimeLayer extends mixin(Layer) {\n /**\n * Constructor.\n *\n * @param {Object} options\n * @private\n */\n constructor(options) {\n // We use a group to be able to add custom vector layer in extended class.\n // For example TrajservLayer use a vectorLayer to display the complete trajectory.\n super(Object.assign({}, options));\n this.allowRenderWhenAnimating = false;\n this.allowRenderWhenAnimating = !!options.allowRenderWhenAnimating;\n /** @ignore */\n this.olLayer =\n options.olLayer ||\n new Group({\n layers: [\n new VectorLayer({\n source: new VectorSource({ features: [] }),\n style: (feature, resolution) => {\n return (options.fullTrajectoryStyle || fullTrajectoryStyle)(feature, resolution, this.styleOptions);\n },\n }),\n new OLLayer({\n source: new Source({}),\n render: (frameState) => {\n var _a, _b, _c;\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.style.position = 'absolute';\n this.container.style.width = '100%';\n this.container.style.height = '100%';\n this.transformContainer = document.createElement('div');\n this.transformContainer.style.position = 'absolute';\n this.transformContainer.style.width = '100%';\n this.transformContainer.style.height = '100%';\n this.container.appendChild(this.transformContainer);\n if (this.canvas) {\n this.canvas.style.position =\n 'absolute';\n this.canvas.style.top = '0';\n this.canvas.style.left = '0';\n this.canvas.style.transformOrigin =\n 'top left';\n this.transformContainer.appendChild(this.canvas);\n }\n }\n if (this.renderedViewState) {\n const { center, resolution, rotation } = frameState.viewState;\n const { center: renderedCenter, resolution: renderedResolution, rotation: renderedRotation, } = this.renderedViewState;\n if (renderedResolution / resolution >= 3) {\n // Avoid having really big points when zooming fast.\n const context = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getContext('2d');\n context === null || context === void 0 ? void 0 : context.clearRect(0, 0, (_b = this.canvas) === null || _b === void 0 ? void 0 : _b.width, (_c = this.canvas) === null || _c === void 0 ? void 0 : _c.height);\n }\n else {\n const pixelCenterRendered = this.map.getPixelFromCoordinate(renderedCenter);\n const pixelCenter = this.map.getPixelFromCoordinate(center);\n this.transformContainer.style.transform = composeCssTransform(pixelCenterRendered[0] - pixelCenter[0], pixelCenterRendered[1] - pixelCenter[1], renderedResolution / resolution, renderedResolution / resolution, rotation - renderedRotation, 0, 0);\n }\n }\n return this.container;\n },\n }),\n ],\n });\n // We store the layer used to highlight the full Trajectory\n this.vectorLayer = this.olLayer.getLayers().item(0);\n // Options the last render run did happen. If something changes\n // we have to render again\n /** @ignore */\n this.renderState = {\n center: [0, 0],\n zoom: undefined,\n rotation: 0,\n };\n }\n attachToMap(map) {\n super.attachToMap(map);\n if (this.map) {\n this.olListenersKeys.push(...this.map.on(['moveend', 'change:target'], (evt) => {\n const view = (evt.map || evt.target).getView();\n if (view.getAnimating() || view.getInteracting()) {\n return;\n }\n const zoom = view.getZoom();\n // Update the interval between render updates\n if (this.currentZoom !== zoom) {\n this.onZoomEnd();\n }\n this.currentZoom = zoom;\n this.onMoveEnd(evt);\n }));\n }\n }\n /**\n * Destroy the container of the tracker.\n */\n detachFromMap() {\n super.detachFromMap();\n this.container = null;\n }\n /**\n * Detect in the canvas if there is data to query at a specific coordinate.\n * @param {ol/coordinate~Coordinate} coordinate The coordinate to test\n * @returns\n */\n hasFeatureInfoAtCoordinate(coordinate) {\n if (this.map && this.canvas) {\n const context = this.canvas.getContext('2d', {\n willReadFrequently: true,\n });\n const pixel = this.map.getPixelFromCoordinate(coordinate);\n return !!(context === null || context === void 0 ? void 0 : context.getImageData(pixel[0] * (this.pixelRatio || 1), pixel[1] * (this.pixelRatio || 1), 1, 1).data[3]);\n }\n return false;\n }\n /**\n * Render the trajectories using current map's size, resolution and rotation.\n * @param {boolean} noInterpolate if true, renders the vehicles without interpolating theirs positions.\n * @overrides\n */\n // @ts-ignore\n renderTrajectories(noInterpolate) {\n if (!this.map) {\n return;\n }\n const view = this.map.getView();\n super.renderTrajectories({\n size: this.map.getSize(),\n center: this.map.getView().getCenter(),\n extent: view.calculateExtent(),\n resolution: view.getResolution(),\n rotation: view.getRotation(),\n zoom: view.getZoom(),\n pixelRatio: this.pixelRatio,\n }, noInterpolate);\n }\n /**\n * Launch renderTrajectories. it avoids duplicating code in renderTrajectories methhod.\n * @private\n * @override\n */\n renderTrajectoriesInternal(viewState, noInterpolate) {\n if (!this.map) {\n return false;\n }\n let isRendered = false;\n const blockRendering = this.allowRenderWhenAnimating\n ? false\n : this.map.getView().getAnimating() ||\n this.map.getView().getInteracting();\n // Don't render the map when the map is animating or interacting.\n isRendered = blockRendering\n ? false\n : super.renderTrajectoriesInternal(viewState, noInterpolate);\n // We update the current render state.\n if (isRendered) {\n this.renderedViewState = Object.assign({}, viewState);\n if (this.transformContainer) {\n this.transformContainer.style.transform = '';\n }\n }\n return isRendered;\n }\n /**\n * Return the delay in ms before the next rendering.\n */\n getRefreshTimeInMs() {\n return super.getRefreshTimeInMs(this.map.getView().getZoom());\n }\n getFeatureInfoAtCoordinate(coordinate, options = {}) {\n if (!this.map || !this.map.getView()) {\n return Promise.resolve({\n layer: this,\n features: [],\n coordinate,\n });\n }\n const resolution = this.map.getView().getResolution();\n return super.getFeatureInfoAtCoordinate(coordinate, Object.assign({ resolution }, options));\n }\n /**\n * On move end we update the websocket with the new bbox.\n *\n * @private\n * @override\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onMoveEnd(evt) {\n if (this.visible && this.isUpdateBboxOnMoveEnd) {\n this.setBbox();\n }\n if (this.visible &&\n this.isUpdateBboxOnMoveEnd &&\n this.userClickInteractions &&\n this.selectedVehicleId) {\n this.highlightTrajectory(this.selectedVehicleId);\n }\n }\n /**\n * Function called on moveend event only when the zoom has changed.\n *\n * @param {ol/MapEvent~MapEvent} evt Moveend event.\n * @private\n * @override\n */\n // eslint-disable-next-line no-unused-vars\n onZoomEnd() {\n super.onZoomEnd();\n if (this.visible && this.isUpdateBboxOnMoveEnd) {\n this.setBbox();\n }\n if (this.visible &&\n this.isUpdateBboxOnMoveEnd &&\n this.userClickInteractions &&\n this.selectedVehicleId) {\n this.highlightTrajectory(this.selectedVehicleId);\n }\n }\n /**\n * Update the cursor style when hovering a vehicle.\n *\n * @private\n * @override\n */\n onFeatureHover(features, layer, coordinate) {\n super.onFeatureHover(features, layer, coordinate);\n this.map.getTargetElement().style.cursor = features.length\n ? 'pointer'\n : 'auto';\n }\n /**\n * Display the complete trajectory of the vehicle.\n *\n * @private\n * @override\n */\n onFeatureClick(features, layer, coordinate) {\n super.onFeatureClick(features, layer, coordinate);\n if (!features.length && this.vectorLayer) {\n this.vectorLayer.getSource().clear();\n }\n if (this.selectedVehicleId) {\n this.highlightTrajectory(this.selectedVehicleId);\n }\n }\n /**\n * Remove the trajectory form the list if necessary.\n *\n * @private\n */\n purgeTrajectory(trajectory, extent, zoom) {\n return super.purgeTrajectory(trajectory, extent || this.map.getView().calculateExtent(), zoom || this.map.getView().getZoom());\n }\n /**\n * Send the current bbox to the websocket\n *\n * @private\n */\n setBbox(extent, zoom) {\n let newExtent = extent;\n let newZoom = zoom;\n if (!newExtent && this.isUpdateBboxOnMoveEnd) {\n newExtent = extent || this.map.getView().calculateExtent();\n newZoom = Math.floor(this.map.getView().getZoom());\n }\n super.setBbox(newExtent, newZoom);\n }\n /**\n * Highlight the trajectory of journey.\n * @private\n */\n highlightTrajectory(id) {\n this.api\n .getFullTrajectory(id, this.mode, this.generalizationLevel)\n .then((data) => {\n const fullTrajectory = data.content;\n this.vectorLayer.getSource().clear();\n if (!fullTrajectory ||\n !fullTrajectory.features ||\n !fullTrajectory.features.length) {\n return;\n }\n const features = format.readFeatures(fullTrajectory);\n this.vectorLayer.getSource().addFeatures(features);\n });\n }\n /**\n * Create a copy of the RealtimeLayer.\n * @param {Object} newOptions Options to override\n * @return {RealtimeLayer} A RealtimeLayer\n */\n clone(newOptions) {\n return new RealtimeLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default RealtimeLayer;\n", - "static": true, - "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/RealtimeLayer.js", - "access": "public", - "description": null, - "lineNumber": 1 - }, - { - "__docId__": 473, - "kind": "variable", - "name": "format", - "memberof": "build/ol/layers/RealtimeLayer.js", - "static": true, - "longname": "build/ol/layers/RealtimeLayer.js~format", - "access": "private", - "export": false, - "importPath": "mobility-toolbox-js/build/ol/layers/RealtimeLayer.js", - "importStyle": null, - "description": null, - "lineNumber": 10, - "type": { - "types": [ - "*" - ] - }, - "ignore": true - }, - { - "__docId__": 474, - "kind": "class", - "name": "RealtimeLayer", - "memberof": "build/ol/layers/RealtimeLayer.js", - "static": true, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "access": "public", - "export": true, - "importPath": "mobility-toolbox-js/build/ol/layers/RealtimeLayer.js", - "importStyle": "RealtimeLayer", - "description": "Responsible for loading and display data from a Realtime service.", - "examples": [ - "import { RealtimeLayer } from 'mobility-toolbox-js/ol';\n\nconst layer = new RealtimeLayer({\n url: [yourUrl],\n apiKey: [yourApiKey],\n});" - ], - "see": [ - "RealtimeAPI" - ], - "lineNumber": 30, - "interface": false, - "extends": [ - "Layer" - ], - "implements": [ - "UserInteractionsLayerInterface", - "RealtimeLayerInterface" - ] - }, - { - "__docId__": 475, - "kind": "constructor", - "name": "constructor", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#constructor", - "access": "private", - "description": "Constructor.", - "lineNumber": 37, - "params": [ - { - "nullable": null, - "types": [ - "Object" - ], - "spread": false, - "optional": false, - "name": "options", - "description": "" - } - ] - }, - { - "__docId__": 476, - "kind": "member", - "name": "allowRenderWhenAnimating", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#allowRenderWhenAnimating", - "access": "public", - "description": null, - "lineNumber": 41, - "undocument": true, - "type": { - "types": [ - "boolean" - ] - } - }, - { - "__docId__": 478, - "kind": "member", - "name": "olLayer", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#olLayer", - "access": "public", - "description": null, - "lineNumber": 44, - "ignore": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 479, - "kind": "member", - "name": "container", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#container", - "access": "public", - "description": null, - "lineNumber": 59, - "undocument": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 480, - "kind": "member", - "name": "transformContainer", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#transformContainer", - "access": "public", - "description": null, - "lineNumber": 63, - "undocument": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 481, - "kind": "member", - "name": "vectorLayer", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#vectorLayer", - "access": "public", - "description": null, - "lineNumber": 98, - "undocument": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 482, - "kind": "member", - "name": "renderState", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#renderState", - "access": "public", - "description": null, - "lineNumber": 102, - "ignore": true, - "type": { - "types": [ - "{\"center\": *, \"zoom\": *, \"rotation\": *}" - ] - } - }, - { - "__docId__": 483, - "kind": "method", - "name": "attachToMap", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#attachToMap", - "access": "public", - "description": null, - "lineNumber": 108, - "undocument": true, - "params": [ - { - "name": "map", - "types": [ - "*" - ] - } - ], - "return": null - }, - { - "__docId__": 484, - "kind": "member", - "name": "currentZoom", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#currentZoom", - "access": "public", - "description": null, - "lineNumber": 121, - "undocument": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 485, - "kind": "method", - "name": "detachFromMap", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#detachFromMap", - "access": "public", - "description": "Destroy the container of the tracker.", - "lineNumber": 129, - "params": [], - "return": null - }, - { - "__docId__": 487, - "kind": "method", - "name": "hasFeatureInfoAtCoordinate", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#hasFeatureInfoAtCoordinate", - "access": "public", - "description": "Detect in the canvas if there is data to query at a specific coordinate.", - "lineNumber": 138, - "unknown": [ - { - "tagName": "@returns", - "tagValue": "" - } - ], - "params": [ - { - "nullable": null, - "types": [ - "ol/coordinate~Coordinate" - ], - "spread": false, - "optional": false, - "name": "coordinate", - "description": "The coordinate to test" - } - ], - "return": { - "types": [ - "boolean" - ] - } - }, - { - "__docId__": 488, - "kind": "method", - "name": "renderTrajectories", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#renderTrajectories", - "access": "public", - "description": "Render the trajectories using current map's size, resolution and rotation.", - "lineNumber": 154, - "unknown": [ - { - "tagName": "@overrides", - "tagValue": "" - } - ], - "params": [ - { - "nullable": null, - "types": [ - "boolean" - ], - "spread": false, - "optional": false, - "name": "noInterpolate", - "description": "if true, renders the vehicles without interpolating theirs positions." - } - ], - "return": null - }, - { - "__docId__": 489, - "kind": "method", - "name": "renderTrajectoriesInternal", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#renderTrajectoriesInternal", - "access": "private", - "description": "Launch renderTrajectories. it avoids duplicating code in renderTrajectories methhod.", - "lineNumber": 174, - "override": true, - "params": [ - { - "name": "viewState", - "types": [ - "*" - ] - }, - { - "name": "noInterpolate", - "types": [ - "*" - ] - } - ], - "return": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 490, - "kind": "member", - "name": "renderedViewState", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#renderedViewState", - "access": "public", - "description": null, - "lineNumber": 189, - "undocument": true, - "type": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 491, - "kind": "method", - "name": "getRefreshTimeInMs", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#getRefreshTimeInMs", - "access": "public", - "description": "Return the delay in ms before the next rendering.", - "lineNumber": 199, - "params": [], - "return": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 492, - "kind": "method", - "name": "getFeatureInfoAtCoordinate", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#getFeatureInfoAtCoordinate", - "access": "public", - "description": null, - "lineNumber": 202, - "undocument": true, - "params": [ - { - "name": "coordinate", - "types": [ - "*" - ] - }, - { - "name": "options", - "optional": true, - "types": [ - "{}" - ], - "defaultRaw": {}, - "defaultValue": "{}" - } - ], - "return": { - "types": [ - "*" - ] - } - }, - { - "__docId__": 493, - "kind": "method", - "name": "onMoveEnd", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#onMoveEnd", - "access": "private", - "description": "On move end we update the websocket with the new bbox.", - "lineNumber": 220, - "override": true, - "params": [ - { - "name": "evt", - "types": [ - "*" - ] - } - ], - "return": null - }, - { - "__docId__": 494, - "kind": "method", - "name": "onZoomEnd", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#onZoomEnd", - "access": "private", - "description": "Function called on moveend event only when the zoom has changed.", - "lineNumber": 239, - "params": [ - { - "nullable": null, - "types": [ - "ol/MapEvent~MapEvent" - ], - "spread": false, - "optional": false, - "name": "evt", - "description": "Moveend event." - } - ], - "override": true, - "return": null - }, - { - "__docId__": 495, - "kind": "method", - "name": "onFeatureHover", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", - "generator": false, - "async": false, - "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#onFeatureHover", - "access": "private", - "description": "Update the cursor style when hovering a vehicle.", - "lineNumber": 257, - "override": true, - "params": [ - { - "name": "features", - "types": [ - "*" - ] - }, - { - "name": "layer", - "types": [ - "*" - ] - }, + "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#highlight", + "access": "private", + "description": "Highlight a list of features.", + "lineNumber": 312, + "params": [ { - "name": "coordinate", + "nullable": null, "types": [ - "*" - ] + "Array
                          " + ], + "spread": false, + "optional": true, + "defaultValue": "[]", + "defaultRaw": [], + "name": "features", + "description": "Features to highlight." } ], "return": null }, { - "__docId__": 496, + "__docId__": 475, "kind": "method", - "name": "onFeatureClick", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", + "name": "applyLayoutVisibility", + "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#onFeatureClick", + "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#applyLayoutVisibility", "access": "private", - "description": "Display the complete trajectory of the vehicle.", - "lineNumber": 269, - "override": true, + "description": "Apply visibility to style layers that fits the styleLayersFilter function.", + "lineNumber": 329, "params": [ { - "name": "features", - "types": [ - "*" - ] - }, - { - "name": "layer", - "types": [ - "*" - ] - }, - { - "name": "coordinate", + "nullable": null, "types": [ - "*" - ] + "Event" + ], + "spread": false, + "optional": false, + "name": "evt", + "description": "Layer's event that has called the function." } ], "return": null }, { - "__docId__": 497, + "__docId__": 476, "kind": "method", - "name": "purgeTrajectory", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", + "name": "clone", + "memberof": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#purgeTrajectory", - "access": "private", - "description": "Remove the trajectory form the list if necessary.", - "lineNumber": 283, + "longname": "build/ol/layers/MapboxStyleLayer.js~MapboxStyleLayer#clone", + "access": "public", + "description": "Create a copy of the MapboxStyleLayer.", + "lineNumber": 358, "params": [ { - "name": "trajectory", - "types": [ - "*" - ] - }, - { - "name": "extent", - "types": [ - "*" - ] - }, - { - "name": "zoom", + "nullable": null, "types": [ - "*" - ] + "Object" + ], + "spread": false, + "optional": false, + "name": "newOptions", + "description": "Options to override." } ], "return": { + "nullable": null, "types": [ - "*" - ] + "MapboxStyleLayer" + ], + "spread": false, + "description": "A MapboxStyleLayer." } }, { - "__docId__": 498, + "__docId__": 477, + "kind": "file", + "name": "build/ol/layers/MaplibreLayer.js", + "content": "import { Map } from 'maplibre-gl';\nimport { getMaplibreRender } from '../../common/utils';\nimport MapGlLayer from './MapGlLayer';\n/**\n * A class representing MaplibreLayer to display on BasicMap\n *\n * @example\n * import { MaplibreLayer } from 'mobility-toolbox-js/ol';\n *\n * const layer = new MaplibreLayer({\n * url: 'https://maps.geops.io/styles/travic_v2/style.json',\n * apikey: 'yourApiKey',\n * });\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nexport default class MaplibreLayer extends MapGlLayer {\n getOlLayerRender() {\n return getMaplibreRender(this);\n }\n // eslint-disable-next-line class-methods-use-this\n getMapboxMapClass() {\n return Map;\n }\n /**\n * Create a copy of the MapboxLayer.\n * @param {Object} newOptions Options to override\n * @return {MapboxLayer} A MapboxLayer\n */\n clone(newOptions) {\n return new MaplibreLayer({ ...this.options, ...newOptions });\n }\n}\n", + "static": true, + "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/MaplibreLayer.js", + "access": "public", + "description": null, + "lineNumber": 1 + }, + { + "__docId__": 478, + "kind": "class", + "name": "MaplibreLayer", + "memberof": "build/ol/layers/MaplibreLayer.js", + "static": true, + "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", + "access": "public", + "export": true, + "importPath": "mobility-toolbox-js/build/ol/layers/MaplibreLayer.js", + "importStyle": "MaplibreLayer", + "description": "A class representing MaplibreLayer to display on BasicMap", + "examples": [ + "import { MaplibreLayer } from 'mobility-toolbox-js/ol';\n\nconst layer = new MaplibreLayer({\n url: 'https://maps.geops.io/styles/travic_v2/style.json',\n apikey: 'yourApiKey',\n});" + ], + "lineNumber": 18, + "unknown": [ + { + "tagName": "@classproperty", + "tagValue": "{ol/Map~Map} map - The map where the layer is displayed." + } + ], + "interface": false, + "extends": [ + "Layer" + ] + }, + { + "__docId__": 479, "kind": "method", - "name": "setBbox", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", + "name": "getOlLayerRender", + "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#setBbox", - "access": "private", - "description": "Send the current bbox to the websocket", - "lineNumber": 291, - "params": [ - { - "name": "extent", - "types": [ - "*" - ] - }, - { - "name": "zoom", - "types": [ - "*" - ] - } - ], - "return": null + "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#getOlLayerRender", + "access": "public", + "description": null, + "lineNumber": 19, + "undocument": true, + "params": [], + "return": { + "types": [ + "*" + ] + } }, { - "__docId__": 499, + "__docId__": 480, "kind": "method", - "name": "highlightTrajectory", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", + "name": "getMapboxMapClass", + "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#highlightTrajectory", - "access": "private", - "description": "Highlight the trajectory of journey.", - "lineNumber": 304, - "params": [ - { - "name": "id", - "types": [ - "*" - ] - } - ], - "return": null + "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#getMapboxMapClass", + "access": "public", + "description": null, + "lineNumber": 23, + "undocument": true, + "params": [], + "return": { + "types": [ + "*" + ] + } }, { - "__docId__": 500, + "__docId__": 481, "kind": "method", "name": "clone", - "memberof": "build/ol/layers/RealtimeLayer.js~RealtimeLayer", + "memberof": "build/ol/layers/MaplibreLayer.js~MaplibreLayer", "generator": false, "async": false, "static": false, - "longname": "build/ol/layers/RealtimeLayer.js~RealtimeLayer#clone", + "longname": "build/ol/layers/MaplibreLayer.js~MaplibreLayer#clone", "access": "public", - "description": "Create a copy of the RealtimeLayer.", - "lineNumber": 324, + "description": "Create a copy of the MapboxLayer.", + "lineNumber": 31, "params": [ { "nullable": null, @@ -10356,17 +9903,17 @@ "return": { "nullable": null, "types": [ - "RealtimeLayer" + "MapboxLayer" ], "spread": false, - "description": "A RealtimeLayer" + "description": "A MapboxLayer" } }, { - "__docId__": 501, + "__docId__": 482, "kind": "file", "name": "build/ol/layers/RoutingLayer.js", - "content": "import { Circle, Fill, Stroke, Style } from 'ol/style';\nimport { Vector as VectorSource } from 'ol/source';\nimport { Vector } from 'ol/layer';\nimport Layer from './Layer';\n/** @private */\nconst circleStyle = new Circle({\n radius: 6,\n fill: new Fill({\n color: [255, 0, 0, 1],\n }),\n stroke: new Stroke({\n color: [0, 0, 0, 1],\n width: 1,\n }),\n});\n/** @private */\nconst blackBorder = new Style({\n stroke: new Stroke({\n color: [0, 0, 0, 1],\n width: 5,\n }),\n});\n/** @private */\nconst redLine = new Style({\n image: circleStyle,\n stroke: new Stroke({\n color: [255, 0, 0, 1],\n width: 3,\n }),\n});\n/** @private */\nconst dashedRedLine = new Style({\n image: circleStyle,\n stroke: new Stroke({\n color: [255, 0, 0, 1],\n width: 3,\n lineDash: [1, 10],\n }),\n});\n/** @private */\nconst defaultStyleFunction = (feature, resolution) => {\n const minResolution = feature.get('minResolution');\n const maxResolution = feature.get('maxResolution');\n const inRange = resolution <= minResolution && resolution > maxResolution;\n if (minResolution && maxResolution && !inRange) {\n return [];\n }\n const mot = feature.get('mot');\n if (mot !== 'foot') {\n return [blackBorder, redLine];\n }\n return [dashedRedLine];\n};\n/**\n * A class use to display vector data.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass RoutingLayer extends Layer {\n /**\n * Constructor.\n * @param {Object} [options]\n * @param {ol/style/Style~StyleLike} [options.style] Style to be used for routes, uses (ol/StyleLike) [https://openlayers.org/en/latest/apidoc/module-ol_style_Style.html#~StyleLike] instances\n */\n constructor(options) {\n super(options);\n this.options = {};\n this.olLayer =\n options.olLayer ||\n new Vector({\n source: new VectorSource(),\n style: options.style || defaultStyleFunction,\n });\n }\n /**\n * Create a copy of the RoutingLayer.\n * @param {Object} newOptions Options to override\n * @return {RoutingLayer} A RoutingLayer\n */\n clone(newOptions) {\n return new RoutingLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default RoutingLayer;\n", + "content": "import { Circle, Fill, Stroke, Style } from 'ol/style';\nimport { Vector as VectorSource } from 'ol/source';\nimport { Vector } from 'ol/layer';\nimport Layer from './Layer';\n/** @private */\nconst circleStyle = new Circle({\n radius: 6,\n fill: new Fill({\n color: [255, 0, 0, 1],\n }),\n stroke: new Stroke({\n color: [0, 0, 0, 1],\n width: 1,\n }),\n});\n/** @private */\nconst blackBorder = new Style({\n stroke: new Stroke({\n color: [0, 0, 0, 1],\n width: 5,\n }),\n});\n/** @private */\nconst redLine = new Style({\n image: circleStyle,\n stroke: new Stroke({\n color: [255, 0, 0, 1],\n width: 3,\n }),\n});\n/** @private */\nconst dashedRedLine = new Style({\n image: circleStyle,\n stroke: new Stroke({\n color: [255, 0, 0, 1],\n width: 3,\n lineDash: [1, 10],\n }),\n});\n/** @private */\nconst defaultStyleFunction = (feature, resolution) => {\n const minResolution = feature.get('minResolution');\n const maxResolution = feature.get('maxResolution');\n const inRange = resolution <= minResolution && resolution > maxResolution;\n if (minResolution && maxResolution && !inRange) {\n return [];\n }\n const mot = feature.get('mot');\n if (mot !== 'foot') {\n return [blackBorder, redLine];\n }\n return [dashedRedLine];\n};\n/**\n * A class use to display vector data.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass RoutingLayer extends Layer {\n /**\n * Constructor.\n * @param {Object} [options]\n * @param {ol/style/Style~StyleLike} [options.style] Style to be used for routes, uses (ol/StyleLike) [https://openlayers.org/en/latest/apidoc/module-ol_style_Style.html#~StyleLike] instances\n */\n constructor(options) {\n super(options);\n this.options = {};\n this.olLayer =\n options.olLayer ||\n new Vector({\n source: new VectorSource(),\n style: options.style || defaultStyleFunction,\n });\n }\n /**\n * Create a copy of the RoutingLayer.\n * @param {Object} newOptions Options to override\n * @return {RoutingLayer} A RoutingLayer\n */\n clone(newOptions) {\n return new RoutingLayer({ ...this.options, ...newOptions });\n }\n}\nexport default RoutingLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/RoutingLayer.js", "access": "public", @@ -10374,7 +9921,7 @@ "lineNumber": 1 }, { - "__docId__": 502, + "__docId__": 483, "kind": "variable", "name": "circleStyle", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10394,7 +9941,7 @@ "ignore": true }, { - "__docId__": 503, + "__docId__": 484, "kind": "variable", "name": "blackBorder", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10414,7 +9961,7 @@ "ignore": true }, { - "__docId__": 504, + "__docId__": 485, "kind": "variable", "name": "redLine", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10434,7 +9981,7 @@ "ignore": true }, { - "__docId__": 505, + "__docId__": 486, "kind": "variable", "name": "dashedRedLine", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10454,7 +10001,7 @@ "ignore": true }, { - "__docId__": 506, + "__docId__": 487, "kind": "function", "name": "defaultStyleFunction", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10490,7 +10037,7 @@ "ignore": true }, { - "__docId__": 507, + "__docId__": 488, "kind": "class", "name": "RoutingLayer", "memberof": "build/ol/layers/RoutingLayer.js", @@ -10514,7 +10061,7 @@ ] }, { - "__docId__": 508, + "__docId__": 489, "kind": "constructor", "name": "constructor", "memberof": "build/ol/layers/RoutingLayer.js~RoutingLayer", @@ -10549,7 +10096,7 @@ ] }, { - "__docId__": 509, + "__docId__": 490, "kind": "member", "name": "options", "memberof": "build/ol/layers/RoutingLayer.js~RoutingLayer", @@ -10566,7 +10113,7 @@ } }, { - "__docId__": 510, + "__docId__": 491, "kind": "member", "name": "olLayer", "memberof": "build/ol/layers/RoutingLayer.js~RoutingLayer", @@ -10583,7 +10130,7 @@ } }, { - "__docId__": 511, + "__docId__": 492, "kind": "method", "name": "clone", "memberof": "build/ol/layers/RoutingLayer.js~RoutingLayer", @@ -10616,10 +10163,10 @@ } }, { - "__docId__": 512, + "__docId__": 493, "kind": "file", "name": "build/ol/layers/VectorLayer.js", - "content": "import Layer from './Layer';\n/**\n * A class use to display vector data.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass VectorLayer extends Layer {\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate the coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n let features = [];\n if (this.map) {\n const pixel = this.map.getPixelFromCoordinate(coordinate);\n features = this.map.getFeaturesAtPixel(pixel, {\n layerFilter: (l) => l === this.olLayer,\n hitTolerance: this.hitTolerance,\n });\n }\n return Promise.resolve({\n features,\n layer: this,\n coordinate,\n });\n }\n /**\n * Create a copy of the VectorLayer.\n * @param {Object} newOptions Options to override\n * @return {VectorLayer} A VectorLayer\n */\n clone(newOptions) {\n return new VectorLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default VectorLayer;\n", + "content": "import Layer from './Layer';\n/**\n * A class use to display vector data.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass VectorLayer extends Layer {\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate the coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n let features = [];\n if (this.map) {\n const pixel = this.map.getPixelFromCoordinate(coordinate);\n features = this.map.getFeaturesAtPixel(pixel, {\n layerFilter: (l) => l === this.olLayer,\n hitTolerance: this.hitTolerance,\n });\n }\n return Promise.resolve({\n features,\n layer: this,\n coordinate,\n });\n }\n /**\n * Create a copy of the VectorLayer.\n * @param {Object} newOptions Options to override\n * @return {VectorLayer} A VectorLayer\n */\n clone(newOptions) {\n return new VectorLayer({ ...this.options, ...newOptions });\n }\n}\nexport default VectorLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/VectorLayer.js", "access": "public", @@ -10627,7 +10174,7 @@ "lineNumber": 1 }, { - "__docId__": 513, + "__docId__": 494, "kind": "class", "name": "VectorLayer", "memberof": "build/ol/layers/VectorLayer.js", @@ -10651,7 +10198,7 @@ ] }, { - "__docId__": 514, + "__docId__": 495, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/ol/layers/VectorLayer.js~VectorLayer", @@ -10684,7 +10231,7 @@ } }, { - "__docId__": 515, + "__docId__": 496, "kind": "method", "name": "clone", "memberof": "build/ol/layers/VectorLayer.js~VectorLayer", @@ -10717,10 +10264,10 @@ } }, { - "__docId__": 516, + "__docId__": 497, "kind": "file", "name": "build/ol/layers/WMSLayer.js", - "content": "import GeoJSON from 'ol/format/GeoJSON';\nimport Layer from './Layer';\n/**\n * Class use to display a WMS layer.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass WMSLayer extends Layer {\n /**\n * @override\n */\n constructor(options) {\n super(options);\n /** @ignore */\n this.abortController = new AbortController();\n /** @ignore */\n this.format = new GeoJSON();\n }\n /**\n * Get features infos' Url.\n * @param {ol/coordinate~Coordinate} coord\n * @return {ol/layer/Layer~Layer}\n */\n getFeatureInfoUrl(coord) {\n var _a, _b, _c, _d, _e, _f;\n if (!this.map) {\n return;\n }\n const projection = this.map.getView().getProjection();\n const resolution = this.map.getView().getResolution();\n if (resolution &&\n projection &&\n ((_b = (_a = this.olLayer) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.getFeatureInfoUrl)) {\n // eslint-disable-next-line consistent-return\n return (_d = (_c = this.olLayer) === null || _c === void 0 ? void 0 : _c.getSource()) === null || _d === void 0 ? void 0 : _d.getFeatureInfoUrl(coord, resolution, projection, {\n info_format: 'application/json',\n query_layers: (_f = (_e = this.olLayer) === null || _e === void 0 ? void 0 : _e.getSource()) === null || _f === void 0 ? void 0 : _f.getParams().layers,\n });\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n var _a;\n (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();\n this.abortController = new AbortController();\n const { signal } = this.abortController;\n const url = this.getFeatureInfoUrl(coordinate);\n if (!url) {\n // eslint-disable-next-line no-console\n console.error('No url for the WMS layer.');\n // resolve an empty feature array something fails\n return Promise.resolve({\n features: [],\n coordinate,\n layer: this,\n });\n }\n return fetch(url, { signal })\n .then((resp) => resp.json())\n .then((r) => r.features)\n .then((data) => ({\n layer: this,\n coordinate,\n features: data.map((d) => this.format.readFeature(d)),\n }))\n .catch(() => \n // resolve an empty feature array something fails\n Promise.resolve({\n features: [],\n coordinate,\n layer: this,\n }));\n }\n /**\n * Create a copy of the WMSLayer.\n * @param {Object} newOptions Options to override\n * @return {WMSLayer} A WMSLayer\n */\n clone(newOptions) {\n return new WMSLayer(Object.assign(Object.assign({}, this.options), newOptions));\n }\n}\nexport default WMSLayer;\n", + "content": "import GeoJSON from 'ol/format/GeoJSON';\nimport Layer from './Layer';\n/**\n * Class use to display a WMS layer.\n *\n * @classproperty {ol/Map~Map} map - The map where the layer is displayed.\n * @extends {Layer}\n */\nclass WMSLayer extends Layer {\n /**\n * @override\n */\n constructor(options) {\n super(options);\n /** @ignore */\n this.abortController = new AbortController();\n /** @ignore */\n this.format = new GeoJSON();\n }\n /**\n * Get features infos' Url.\n * @param {ol/coordinate~Coordinate} coord\n * @return {ol/layer/Layer~Layer}\n */\n getFeatureInfoUrl(coord) {\n if (!this.map) {\n return;\n }\n const projection = this.map.getView().getProjection();\n const resolution = this.map.getView().getResolution();\n if (resolution &&\n projection &&\n this.olLayer?.getSource()?.getFeatureInfoUrl) {\n // eslint-disable-next-line consistent-return\n return this.olLayer\n ?.getSource()\n ?.getFeatureInfoUrl(coord, resolution, projection, {\n info_format: 'application/json',\n query_layers: this.olLayer?.getSource()?.getParams().layers,\n });\n }\n }\n /**\n * Request feature information for a given coordinate.\n * @param {ol/coordinate~Coordinate} coordinate to request the information at.\n * @return {Promise} Promise with features, layer and coordinate.\n */\n getFeatureInfoAtCoordinate(coordinate) {\n this.abortController?.abort();\n this.abortController = new AbortController();\n const { signal } = this.abortController;\n const url = this.getFeatureInfoUrl(coordinate);\n if (!url) {\n // eslint-disable-next-line no-console\n console.error('No url for the WMS layer.');\n // resolve an empty feature array something fails\n return Promise.resolve({\n features: [],\n coordinate,\n layer: this,\n });\n }\n return fetch(url, { signal })\n .then((resp) => resp.json())\n .then((r) => r.features)\n .then((data) => ({\n layer: this,\n coordinate,\n features: data.map((d) => this.format.readFeature(d)),\n }))\n .catch(() => \n // resolve an empty feature array something fails\n Promise.resolve({\n features: [],\n coordinate,\n layer: this,\n }));\n }\n /**\n * Create a copy of the WMSLayer.\n * @param {Object} newOptions Options to override\n * @return {WMSLayer} A WMSLayer\n */\n clone(newOptions) {\n return new WMSLayer({ ...this.options, ...newOptions });\n }\n}\nexport default WMSLayer;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/layers/WMSLayer.js", "access": "public", @@ -10728,7 +10275,7 @@ "lineNumber": 1 }, { - "__docId__": 517, + "__docId__": 498, "kind": "class", "name": "WMSLayer", "memberof": "build/ol/layers/WMSLayer.js", @@ -10752,7 +10299,7 @@ ] }, { - "__docId__": 518, + "__docId__": 499, "kind": "constructor", "name": "constructor", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10766,7 +10313,7 @@ "override": true }, { - "__docId__": 519, + "__docId__": 500, "kind": "member", "name": "abortController", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10783,7 +10330,7 @@ } }, { - "__docId__": 520, + "__docId__": 501, "kind": "member", "name": "format", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10800,7 +10347,7 @@ } }, { - "__docId__": 521, + "__docId__": 502, "kind": "method", "name": "getFeatureInfoUrl", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10833,7 +10380,7 @@ } }, { - "__docId__": 522, + "__docId__": 503, "kind": "method", "name": "getFeatureInfoAtCoordinate", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10843,7 +10390,7 @@ "longname": "build/ol/layers/WMSLayer.js~WMSLayer#getFeatureInfoAtCoordinate", "access": "public", "description": "Request feature information for a given coordinate.", - "lineNumber": 47, + "lineNumber": 48, "params": [ { "nullable": null, @@ -10866,7 +10413,7 @@ } }, { - "__docId__": 524, + "__docId__": 505, "kind": "method", "name": "clone", "memberof": "build/ol/layers/WMSLayer.js~WMSLayer", @@ -10899,7 +10446,7 @@ } }, { - "__docId__": 525, + "__docId__": 506, "kind": "file", "name": "build/ol/layers/index.js", "content": "export { default as Layer } from './Layer';\nexport { default as MapboxLayer } from './MapboxLayer';\nexport { default as MaplibreLayer } from './MaplibreLayer';\nexport { default as MapboxStyleLayer } from './MapboxStyleLayer';\nexport { default as RoutingLayer } from './RoutingLayer';\nexport { default as RealtimeLayer } from './RealtimeLayer';\nexport { default as VectorLayer } from './VectorLayer';\nexport { default as WMSLayer } from './WMSLayer';\n", @@ -10910,7 +10457,7 @@ "lineNumber": 1 }, { - "__docId__": 526, + "__docId__": 507, "kind": "file", "name": "build/ol/styles/fullTrajectoryDelayStyle.js", "content": "import { Style, Fill, Stroke, Circle } from 'ol/style';\n/** @private */\nconst stroke = new Style({\n zIndex: 2,\n image: new Circle({\n radius: 5,\n fill: new Fill({\n color: '#000000',\n }),\n }),\n stroke: new Stroke({\n color: '#000000',\n width: 6,\n }),\n});\n/** @private */\nconst fill = new Style({\n zIndex: 3,\n image: new Circle({\n radius: 4,\n fill: new Fill({\n color: '#a0a0a0',\n }),\n }),\n stroke: new Stroke({\n color: '#a0a0a0',\n width: 4,\n }),\n});\nconst fullTrajectoryDelaystyle = () => {\n return [stroke, fill];\n};\nexport default fullTrajectoryDelaystyle;\n", @@ -10921,7 +10468,7 @@ "lineNumber": 1 }, { - "__docId__": 527, + "__docId__": 508, "kind": "variable", "name": "stroke", "memberof": "build/ol/styles/fullTrajectoryDelayStyle.js", @@ -10941,7 +10488,7 @@ "ignore": true }, { - "__docId__": 528, + "__docId__": 509, "kind": "variable", "name": "fill", "memberof": "build/ol/styles/fullTrajectoryDelayStyle.js", @@ -10961,7 +10508,7 @@ "ignore": true }, { - "__docId__": 529, + "__docId__": 510, "kind": "function", "name": "fullTrajectoryDelaystyle", "memberof": "build/ol/styles/fullTrajectoryDelayStyle.js", @@ -10984,10 +10531,10 @@ } }, { - "__docId__": 530, + "__docId__": 511, "kind": "file", "name": "build/ol/styles/fullTrajectoryStyle.js", - "content": "import { Style, Fill, Stroke, Circle } from 'ol/style';\n/** @private */\nconst borderStyle = new Style({\n zIndex: 2,\n image: new Circle({\n radius: 5,\n fill: new Fill({\n color: '#000000',\n }),\n }),\n stroke: new Stroke({\n color: '#000000',\n width: 6,\n }),\n});\nconst fullTrajectorystyle = (feature, resolution, options) => {\n let lineColor = '#ffffff'; // white\n const type = feature.get('type');\n let stroke = feature.get('stroke');\n if (stroke && stroke[0] !== '#') {\n stroke = `#${stroke}`;\n }\n lineColor = stroke || (options === null || options === void 0 ? void 0 : options.getBgColor(type));\n // Don't allow white lines, use red instead.\n lineColor = /#ffffff/i.test(lineColor) ? '#ff0000' : lineColor;\n const style = [\n borderStyle,\n new Style({\n zIndex: 3,\n image: new Circle({\n radius: 4,\n fill: new Fill({\n color: lineColor,\n }),\n }),\n stroke: new Stroke({\n color: lineColor,\n width: 4,\n }),\n }),\n ];\n return style;\n};\nexport default fullTrajectorystyle;\n", + "content": "import { Style, Fill, Stroke, Circle } from 'ol/style';\n/** @private */\nconst borderStyle = new Style({\n zIndex: 2,\n image: new Circle({\n radius: 5,\n fill: new Fill({\n color: '#000000',\n }),\n }),\n stroke: new Stroke({\n color: '#000000',\n width: 6,\n }),\n});\nconst fullTrajectorystyle = (feature, resolution, options) => {\n let lineColor = '#ffffff'; // white\n const type = feature.get('type');\n let stroke = feature.get('stroke');\n if (stroke && stroke[0] !== '#') {\n stroke = `#${stroke}`;\n }\n lineColor = stroke || options?.getBgColor(type);\n // Don't allow white lines, use red instead.\n lineColor = /#ffffff/i.test(lineColor) ? '#ff0000' : lineColor;\n const style = [\n borderStyle,\n new Style({\n zIndex: 3,\n image: new Circle({\n radius: 4,\n fill: new Fill({\n color: lineColor,\n }),\n }),\n stroke: new Stroke({\n color: lineColor,\n width: 4,\n }),\n }),\n ];\n return style;\n};\nexport default fullTrajectorystyle;\n", "static": true, "longname": "/home/olivier/GIT/mobility-toolbox-js/build/ol/styles/fullTrajectoryStyle.js", "access": "public", @@ -10995,7 +10542,7 @@ "lineNumber": 1 }, { - "__docId__": 531, + "__docId__": 512, "kind": "variable", "name": "borderStyle", "memberof": "build/ol/styles/fullTrajectoryStyle.js", @@ -11015,7 +10562,7 @@ "ignore": true }, { - "__docId__": 532, + "__docId__": 513, "kind": "function", "name": "fullTrajectorystyle", "memberof": "build/ol/styles/fullTrajectoryStyle.js", @@ -11057,7 +10604,7 @@ } }, { - "__docId__": 533, + "__docId__": 514, "kind": "file", "name": "build/ol/styles/index.js", "content": "export { default as fullTrajectoryStyle } from './fullTrajectoryStyle';\nexport { default as fullTrajectoryDelayStyle } from './fullTrajectoryDelayStyle';\n", @@ -11333,7 +10880,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#apiKey", "access": "public", "description": "Key used for RoutingApi requests.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "string" @@ -11348,7 +10895,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#stopsApiKey", "access": "public", "description": "Key used for Stop lookup requests (defaults to apiKey).", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "string" @@ -11363,7 +10910,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#stopsApiUrl", "access": "public", "description": "Url used for Stop lookup requests (defaults to https://api.geops.io/stops/v1/lookup/).", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "string" @@ -11378,7 +10925,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#graphs", "access": "public", "description": "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]]\"", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "Array.>" @@ -11393,7 +10940,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#mot", "access": "public", "description": "Mean of transport to be used for routing.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "string" @@ -11408,7 +10955,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#routingApiParams", "access": "public", "description": "object of additional parameters to pass to the routing api request.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "object" @@ -11423,7 +10970,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#snapToClosestStation", "access": "public", "description": "If true, the routing will snap the coordinate to the closest station. Default to false.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "object" @@ -11438,7 +10985,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#useRawViaPoints", "access": "public", "description": "Experimental property. Wen 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.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "boolean" @@ -11453,7 +11000,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#routingLayer", "access": "public", "description": "Layer for adding route features.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "RoutingLayer", @@ -11469,7 +11016,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#onRouteError", "access": "public", "description": "Callback on error.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "function" @@ -11484,7 +11031,7 @@ "longname": "build/ol/controls/RoutingControl.js~RoutingControl#loading", "access": "public", "description": "True if the control is requesting the backend.", - "lineNumber": 79, + "lineNumber": 78, "type": { "types": [ "boolean" diff --git a/doc/src/components/Example.js b/doc/src/components/Example.js index b2651eb4..92cf56a9 100644 --- a/doc/src/components/Example.js +++ b/doc/src/components/Example.js @@ -9,7 +9,7 @@ const useStyles = makeStyles((theme) => ({ padding: 12, }, htmlContainer: { - height: 500, + height: 900, }, noPointer: { // Remove pointer events for mobile devices on load diff --git a/doc/src/examples.js b/doc/src/examples.js index bf441717..922fac63 100644 --- a/doc/src/examples.js +++ b/doc/src/examples.js @@ -1,5 +1,6 @@ // We load statically the readme to have the content when the page use server side rendering. import olTrackerReadme from '../public/static/examples/ol-tracker.md'; +import olTrackerWorkerReadme from '../public/static/examples/ol-tracker-worker.md'; import mbTrackerReadme from '../public/static/examples/mb-tracker.md'; import olRoutingReadme from '../public/static/examples/ol-routing.md'; import olStopFinderReadme from '../public/static/examples/ol-stop-finder.md'; @@ -22,7 +23,15 @@ export default [ name: 'Live tracker with OpenLayers', key: 'ol-tracker', description: - 'Use the [geOps Realtime API](https://developer.geops.io/apis/realtime/) to show moving trains in a Mapbox map.', + 'Use the [geOps Realtime API](https://developer.geops.io/apis/realtime/) to show moving trains in a Openlayers map.', + img: '/static/img/live_tracker_ol.jpg', + readme: olTrackerReadme, + }, + { + name: 'Live tracker with OpenLayers using web worker', + key: 'ol-tracker-worker', + description: + 'Use the [geOps Realtime API](https://developer.geops.io/apis/realtime/) to show moving trains in a Openlayers map.', img: '/static/img/live_tracker_ol.jpg', readme: olTrackerReadme, }, diff --git a/src/api/trajserv/fetchTrajectories.worker.js b/src/api/trajserv/fetchTrajectories.worker.js new file mode 100644 index 00000000..edbfe6da --- /dev/null +++ b/src/api/trajserv/fetchTrajectories.worker.js @@ -0,0 +1,23 @@ +import { translateTrajCollResponse } from './TrajservAPIUtils'; + +let abortController = new AbortController(); + +// eslint-disable-next-line no-restricted-globals, func-names +self.onmessage = function (evt) { + // console.log('Worker: Message received from main script', evt.data); + abortController.abort(); + abortController = new AbortController(); + fetch(evt.data, { + signal: abortController.signal, + }) + .then((res) => res.json()) + .then((data) => { + const a = translateTrajCollResponse(data.features); + // eslint-disable-next-line no-restricted-globals + self.postMessage(a); + }) + .catch(() => { + // eslint-disable-next-line no-restricted-globals + // self.postMessage(null); + }); +}; diff --git a/src/common/mixins/RealtimeLayerMixin.ts b/src/common/mixins/RealtimeLayerMixin.ts index b73381d7..60f8f6f7 100644 --- a/src/common/mixins/RealtimeLayerMixin.ts +++ b/src/common/mixins/RealtimeLayerMixin.ts @@ -957,6 +957,10 @@ function RealtimeLayerMixin(Base: T) { this.trajectories = {}; } this.trajectories[trajectory.properties.train_id] = trajectory; + this.worker?.postMessage({ + action: 'addTrajectory', + trajectory, + }); // @ts-ignore the parameter are set by subclasses this.renderTrajectories(); } @@ -971,6 +975,10 @@ function RealtimeLayerMixin(Base: T) { if (this.trajectories) { delete this.trajectories[id]; } + this.worker?.postMessage({ + action: 'removeTrajectory', + trajectoryId: id, + }); } /** @@ -1045,7 +1053,7 @@ function RealtimeLayerMixin(Base: T) { this.map.getView().getProjection(), ), }; - } else { + } else if (!this.worker) { trajectory.properties.olGeometry = this.format.readGeometry(geometry); } diff --git a/src/common/tracker.worker.ts b/src/common/tracker.worker.ts new file mode 100644 index 00000000..0f90163e --- /dev/null +++ b/src/common/tracker.worker.ts @@ -0,0 +1,113 @@ +/// +/// +/// +/// + +import GeoJSON from 'ol/format/GeoJSON'; +import type { RealtimeStyleOptions, RealtimeTrajectories } from '../types'; +import { realtimeDefaultStyle } from './styles'; +import { renderTrajectories, realtimeConfig } from './utils'; + +const debug = false; +const trajectories: RealtimeTrajectories = {}; + +// Default type of `self` is `WorkerGlobalScope & typeof globalThis` +// https://github.com/microsoft/TypeScript/issues/14877 +declare let self: DedicatedWorkerGlobalScope; + +export type RealtimeWorkerRenderEvent = { + data: { + viewState: ViewState; + options: RealtimeStyleOptions; + }; +}; + +let renderTimeout: number | null; +let count = 0; +const format = new GeoJSON(); +const canvas = new OffscreenCanvas(1, 1); + +const render = (evt: RealtimeWorkerRenderEvent) => { + // eslint-disable-next-line no-console + if (debug) console.time('render'); + // eslint-disable-next-line no-console + if (debug) console.log('render', evt.data.viewState); + count = 0; + const { viewState, options } = evt.data; + + const { renderedTrajectories } = renderTrajectories( + canvas, + Object.values(trajectories), + realtimeDefaultStyle, + viewState, + { + ...options, + getRadius: realtimeConfig.getRadius, + getTextColor: realtimeConfig.getTextColor, + getBgColor: realtimeConfig.getBgColor, + getDelayColor: realtimeConfig.getDelayColor, + getTextSize: realtimeConfig.getTextSize, + }, + ); + + if (debug) console.timeEnd('render'); + if (debug) console.log('NUMBER OF STYLES CREATED', count); + + const imageData = canvas.transferToImageBitmap(); + + // eslint-disable-next-line no-restricted-globals + self.postMessage( + { + action: 'rendered', + imageData, + viewState, + nbRenderedTRajectories: renderedTrajectories?.length || 0, + }, + [imageData], + ); + renderTimeout = null; +}; + +// eslint-disable-next-line no-restricted-globals +self.onmessage = (evt) => { + // debugger; + if (evt.data.action === 'addTrajectory') { + const { trajectory } = evt.data; + const id = trajectory.properties.train_id; + trajectories[id] = trajectory; + trajectories[id].properties.olGeometry = format.readGeometry( + trajectory.geometry, + ); + return; + } + + if (evt.data.action === 'removeTrajectory') { + delete trajectories[evt.data.trajectoryId]; + return; + } + + // if (evt.data.action === 'sendData') { + // // eslint-disable-next-line no-console + // if (debug) console.log('sendData', evt.data); + // if (debug) console.time('sendData'); + // trajectories = evt.data.trajectories; + // if (debug) console.timeEnd('sendData'); + // return; + // } + + if (evt.data.action !== 'render') { + return; + } + + if (renderTimeout) { + clearTimeout(renderTimeout); + } + + // eslint-disable-next-line no-restricted-globals + renderTimeout = self.setTimeout(() => { + render(evt); + }, 0); +}; + +// We need an export to force this file to act like a module, so TS will let us re-type `self` +// export default null; diff --git a/src/common/utils/index.js b/src/common/utils/index.js index 354c637e..1ee36ec8 100644 --- a/src/common/utils/index.js +++ b/src/common/utils/index.js @@ -4,6 +4,7 @@ export { default as removeDuplicate } from './removeDuplicate'; export { default as createRealtimeFilters } from './createRealtimeFilters'; export { default as getLayersAsFlatArray } from './getLayersAsFlatArray'; export * from './timeUtils'; +export { default as getVehiclePosition } from './getVehiclePosition'; export { default as sortByDelay } from './sortByDelay'; export { default as renderTrajectories } from './renderTrajectories'; export { default as getMaplibreRender } from './getMaplibreRender'; diff --git a/src/ol/layers/RealtimeLayer.ts b/src/ol/layers/RealtimeLayer.ts index 1dffdcd8..faf63e68 100644 --- a/src/ol/layers/RealtimeLayer.ts +++ b/src/ol/layers/RealtimeLayer.ts @@ -7,6 +7,7 @@ import Feature, { FeatureLike } from 'ol/Feature'; import { MapEvent } from 'ol'; import { Coordinate } from 'ol/coordinate'; import { ObjectEvent } from 'ol/Object'; +import type { State } from 'ol/View'; import Layer from './Layer'; import mixin, { RealtimeLayerMixinOptions, @@ -20,6 +21,7 @@ import { } from '../../types'; import { RealtimeTrajectory } from '../../api/typedefs'; import { WebSocketAPIMessageEventData } from '../../common/api/WebSocketAPI'; +// import Worker from '../../common/tracker.worker'; /** @private */ const format = new GeoJSON(); @@ -30,9 +32,38 @@ export type OlRealtimeLayerOptions = RealtimeLayerMixinOptions & { resolution: number, options: any, ) => void; + useWorker?: boolean; allowRenderWhenAnimating?: boolean; }; +const updateContainerTransform = ( + layer: RealtimeLayer, + renderedViewState: State, + mainThreadViewState: State, +) => { + if (renderedViewState) { + const { center, resolution, rotation } = mainThreadViewState; + const { + center: renderedCenter, + resolution: renderedResolution, + rotation: renderedRotation, + } = renderedViewState; + const pixelCenterRendered = + layer.map.getPixelFromCoordinate(renderedCenter); + const pixelCenter = layer.map.getPixelFromCoordinate(center); + // eslint-disable-next-line no-param-reassign + layer.transformContainer.style.transform = composeCssTransform( + pixelCenterRendered[0] - pixelCenter[0], + pixelCenterRendered[1] - pixelCenter[1], + renderedResolution / resolution, + renderedResolution / resolution, + rotation - renderedRotation, + 0, + 0, + ); + } +}; + /** * Responsible for loading and display data from a Realtime service. * @@ -68,6 +99,17 @@ class RealtimeLayer extends mixin(Layer) { ...options, }); + if (options.useWorker) { + const wworker = new Worker( + new URL('../../common/tracker.worker.js', import.meta.url), + ); + // Worker that render trajectories. + this.worker = wworker; + + // Worker messaging and actions + this.worker.onmessage = this.onWorkerMessage.bind(this); + } + this.allowRenderWhenAnimating = !!options.allowRenderWhenAnimating; /** @ignore */ @@ -109,13 +151,12 @@ class RealtimeLayer extends mixin(Layer) { } } + this.mainThreadViewState = frameState.viewState; + if (this.renderedViewState) { - const { center, resolution, rotation } = frameState.viewState; - const { - center: renderedCenter, - resolution: renderedResolution, - rotation: renderedRotation, - } = this.renderedViewState; + const { resolution } = frameState.viewState; + const { resolution: renderedResolution } = + this.renderedViewState; if (renderedResolution / resolution >= 3) { // Avoid having really big points when zooming fast. @@ -127,17 +168,10 @@ class RealtimeLayer extends mixin(Layer) { this.canvas?.height as number, ); } else { - const pixelCenterRendered = - this.map.getPixelFromCoordinate(renderedCenter); - const pixelCenter = this.map.getPixelFromCoordinate(center); - this.transformContainer.style.transform = composeCssTransform( - pixelCenterRendered[0] - pixelCenter[0], - pixelCenterRendered[1] - pixelCenter[1], - renderedResolution / resolution, - renderedResolution / resolution, - rotation - renderedRotation, - 0, - 0, + updateContainerTransform( + this, + this.renderedViewState, + frameState.viewState, ); } } @@ -252,6 +286,7 @@ class RealtimeLayer extends mixin(Layer) { if (!this.map) { return false; } + let isRendered = false; const blockRendering = this.allowRenderWhenAnimating @@ -259,17 +294,39 @@ class RealtimeLayer extends mixin(Layer) { : this.map.getView().getAnimating() || this.map.getView().getInteracting(); - // Don't render the map when the map is animating or interacting. - isRendered = blockRendering - ? false - : super.renderTrajectoriesInternal(viewState, noInterpolate); - - // We update the current render state. - if (isRendered) { - this.renderedViewState = { ...viewState }; - - if (this.transformContainer) { - this.transformContainer.style.transform = ''; + if (!blockRendering && this.worker && this.mainThreadViewState) { + this.worker.postMessage({ + action: 'render', + viewState: { + ...viewState, + // time: this.live ? Date.now() : this.time?.getTime(), + }, + options: { + noInterpolate: + (viewState.zoom || 0) < this.minZoomInterpolation + ? true + : noInterpolate, + hoverVehicleId: this.hoverVehicleId, + selectedVehicleId: this.selectedVehicleId, + iconScale: this.iconScale, + delayDisplay: this.delayDisplay, + delayOutlineColor: this.delayOutlineColor, + useDelayStyle: this.useDelayStyle, + }, + }); + } else if (!this.worker) { + // Don't render the map when the map is animating or interacting. + isRendered = blockRendering + ? false + : super.renderTrajectoriesInternal(viewState, noInterpolate); + + // We update the current render state. + if (isRendered) { + this.renderedViewState = { ...viewState }; + + if (this.transformContainer) { + this.transformContainer.style.transform = ''; + } } } return isRendered; @@ -448,6 +505,48 @@ class RealtimeLayer extends mixin(Layer) { clone(newOptions: OlRealtimeLayerOptions) { return new RealtimeLayer({ ...this.options, ...newOptions }); } + + onWorkerMessage(message: any) { + if (message.data.action === 'requestRender') { + // Worker requested a new render frame + this.map.render(); + } else if (this.canvas && message.data.action === 'rendered') { + if ( + !this.allowRenderWhenAnimating && + (this.map.getView().getInteracting() || + this.map.getView().getAnimating()) + ) { + return; + } + // Worker provides a new render frame + // requestAnimationFrame(() => { + // if ( + // !that.renderWhenInteracting( + // that.mainThreadFrameState.viewState, + // that.renderedViewState, + // ) && + // (that.map.getView().getInteracting() || + // that.map.getView().getAnimating()) + // ) { + // return; + // } + const { imageData, viewState } = message.data; + this.canvas.width = imageData.width; + this.canvas.height = imageData.height; + if ((this.canvas as HTMLCanvasElement)?.style) { + (this.canvas as HTMLCanvasElement).style.transform = ``; + (this.canvas as HTMLCanvasElement).style.width = `${ + this.canvas.width / (this.pixelRatio || 1) + }px`; + (this.canvas as HTMLCanvasElement).style.height = `${ + this.canvas.height / (this.pixelRatio || 1) + }px`; + } + this.renderedViewState = viewState; + updateContainerTransform(this, viewState, this.mainThreadViewState); + this.canvas?.getContext('2d')?.drawImage(imageData, 0, 0); + } + } } export default RealtimeLayer; diff --git a/tsconfig.json b/tsconfig.json index 702ceddc..425faabf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "outDir": "./build", "allowJs": true, - "target": "es6", + "target": "es2020", + "module": "es2020", "strict": true, // Generate d.ts files "declaration": true, @@ -15,7 +16,7 @@ "skipLibCheck": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, - "lib": ["es6", "dom"] + "lib": ["webworker", "es2020", "dom"] }, "allowSyntheticDefaultImports": true, "include": ["./src/**/*"],