@geops/rvf-mobility-web-component 0.1.76 → 0.1.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@geops/rvf-mobility-web-component",
3
3
  "license": "UNLICENSED",
4
4
  "description": "Web components for rvf in the domains of mobility and logistics.",
5
- "version": "0.1.76",
5
+ "version": "0.1.78",
6
6
  "homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
7
7
  "type": "module",
8
8
  "main": "index.js",
@@ -11,8 +11,8 @@
11
11
  "graphql-request": "^7.1.2",
12
12
  "jspdf": "^3.0.3",
13
13
  "lodash.debounce": "^4.0.8",
14
- "maplibre-gl": "^5.9.0",
15
- "mobility-toolbox-js": "3.4.6-beta.1",
14
+ "maplibre-gl": "^5.10.0",
15
+ "mobility-toolbox-js": "3.4.6",
16
16
  "ol": "^10.6.1",
17
17
  "preact": "^10.27.2",
18
18
  "preact-custom-element": "^4.5.1",
@@ -25,9 +25,9 @@
25
25
  "devDependencies": {
26
26
  "@commitlint/cli": "^20.1.0",
27
27
  "@commitlint/config-conventional": "^20.0.0",
28
- "@eslint/js": "^9.37.0",
28
+ "@eslint/js": "^9.38.0",
29
29
  "@geops/eslint-config-react": "^1.6.0-beta.1",
30
- "@tailwindcss/cli": "^4.1.14",
30
+ "@tailwindcss/cli": "^4.1.16",
31
31
  "@tailwindcss/container-queries": "^0.1.1",
32
32
  "@testing-library/preact": "^3.2.4",
33
33
  "@types/geojson": "^7946.0.16",
@@ -35,9 +35,9 @@
35
35
  "@types/lodash": "^4.17.20",
36
36
  "@types/preact-custom-element": "^4.0.4",
37
37
  "concurrently": "^9.2.1",
38
- "esbuild": "^0.25.10",
38
+ "esbuild": "^0.25.11",
39
39
  "esbuild-sass-plugin": "^3.3.1",
40
- "eslint": "^9.37.0",
40
+ "eslint": "^9.38.0",
41
41
  "eslint-plugin-tailwindcss": "^4.0.0-beta.0",
42
42
  "fixpack": "^4.0.0",
43
43
  "generact": "^0.4.0",
@@ -47,15 +47,15 @@
47
47
  "jest-environment-jsdom": "^30.2.0",
48
48
  "jest-preset-preact": "^4.1.1",
49
49
  "next": "15.5.5",
50
- "preact-render-to-string": "^6.6.2",
50
+ "preact-render-to-string": "^6.6.3",
51
51
  "prettier": "^3.6.2",
52
- "prettier-plugin-tailwindcss": "^0.6.14",
52
+ "prettier-plugin-tailwindcss": "^0.7.1",
53
53
  "standard-version": "^9.5.0",
54
54
  "tailwind-merge": "^3.3.1",
55
- "tailwindcss": "^4.1.14",
55
+ "tailwindcss": "^4.1.16",
56
56
  "ts-jest": "^29.4.5",
57
57
  "typescript": "^5.9.3",
58
- "typescript-eslint": "^8.46.1"
58
+ "typescript-eslint": "^8.46.2"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "yarn build:css && yarn build:js && cp index*.js* doc/public/",
@@ -1,6 +1,5 @@
1
1
  import { MapsetLayer as MtbMapsetLayer } from "mobility-toolbox-js/ol";
2
2
  import { unByKey } from "ol/Observable";
3
- import { transformExtent } from "ol/proj";
4
3
  import { memo } from "preact/compat";
5
4
  import { useEffect, useMemo } from "preact/hooks";
6
5
 
@@ -8,8 +7,8 @@ import { LAYER_NAME_MAPSET } from "../utils/constants";
8
7
  import useMapContext from "../utils/hooks/useMapContext";
9
8
 
10
9
  import type { MapsetLayerOptions } from "mobility-toolbox-js/ol";
11
-
12
- let moveEndTimeout: ReturnType<typeof setTimeout>;
10
+ import type { Feature } from "ol";
11
+ import type { EventTypes } from "ol/Observable";
13
12
 
14
13
  export const isFeatureOutsideZoomLimit = (feature, map) => {
15
14
  const zoom = map?.getView()?.getZoom();
@@ -23,7 +22,6 @@ function MapsetLayer(props?: Partial<MapsetLayerOptions>) {
23
22
  apikey,
24
23
  baseLayer,
25
24
  map,
26
- mapsetbbox,
27
25
  mapsetplanid,
28
26
  mapsettags,
29
27
  mapsettenants,
@@ -37,38 +35,8 @@ function MapsetLayer(props?: Partial<MapsetLayerOptions>) {
37
35
  return null;
38
36
  }
39
37
 
40
- let bbox = undefined;
41
- if (mapsetbbox) {
42
- bbox = mapsetbbox?.split(",").map((coord) => {
43
- return Number(coord.trim());
44
- });
45
- if (
46
- bbox.length === 4 &&
47
- !bbox.some((coord) => {
48
- return Number.isNaN(coord);
49
- })
50
- ) {
51
- bbox = transformExtent(bbox, "EPSG:3857", "EPSG:4326");
52
- }
53
- } else {
54
- // we have to wait that the map is well sized to get the extent
55
- // It triggers an erro in FF without this
56
- if (
57
- map.getView()?.getCenter() &&
58
- map.getSize()[0] > 0 &&
59
- map.getSize()[1] > 0
60
- ) {
61
- bbox = transformExtent(
62
- map.getView()?.calculateExtent(),
63
- "EPSG:3857",
64
- "EPSG:4326",
65
- );
66
- }
67
- }
68
38
  return new MtbMapsetLayer({
69
39
  apiKey: apikey,
70
- bbox,
71
- mapseturl: mapseturl || undefined,
72
40
  name: LAYER_NAME_MAPSET,
73
41
  planId: mapsetplanid ?? undefined,
74
42
  tags: mapsettags?.split(",").map((t) => {
@@ -77,14 +45,13 @@ function MapsetLayer(props?: Partial<MapsetLayerOptions>) {
77
45
  tenants: mapsettenants?.split(",").map((t) => {
78
46
  return t.trim();
79
47
  }),
80
- timestamp: mapsettimestamp || new Date().toISOString(), // Load only standard plan
81
- zoom: map.getView().getZoom(),
48
+ timestamp: mapsettimestamp, // Load only standard plan
49
+ url: mapseturl,
82
50
  ...(props || {}),
83
51
  });
84
52
  }, [
85
53
  baseLayer,
86
54
  map,
87
- mapsetbbox,
88
55
  apikey,
89
56
  mapseturl,
90
57
  mapsetplanid,
@@ -109,49 +76,12 @@ function MapsetLayer(props?: Partial<MapsetLayerOptions>) {
109
76
  };
110
77
  }, [map, layer]);
111
78
 
112
- useEffect(() => {
113
- const view = map?.getView();
114
- if (!view || !layer) {
115
- return;
116
- }
117
- const handleMoveEnd = () => {
118
- if (mapsetplanid || !layer.get("visible")) {
119
- return;
120
- }
121
- clearTimeout(moveEndTimeout);
122
- moveEndTimeout = setTimeout(() => {
123
- const currentBbox = transformExtent(
124
- view.calculateExtent(map.getSize()),
125
- "EPSG:3857",
126
- "EPSG:4326",
127
- );
128
- layer.bbox = currentBbox;
129
- layer.zoom = view.getZoom();
130
- }, 100);
131
- };
132
-
133
- const listeners = [
134
- layer.on("change:visible", () => {
135
- if (layer.get("visible")) {
136
- handleMoveEnd();
137
- }
138
- }),
139
- view.on("change:center", handleMoveEnd),
140
- view.on("change:resolution", handleMoveEnd),
141
- ];
142
-
143
- return () => {
144
- clearTimeout(moveEndTimeout);
145
- unByKey(listeners);
146
- };
147
- }, [map, layer, mapsetplanid]);
148
-
149
- // Apply fetaure's minzoom and maxzoom to its style
79
+ // Apply feature's minzoom and maxzoom to its style
150
80
  // TODO should be done by the mapset layer itself
151
81
  useEffect(() => {
152
82
  let key = null;
153
83
  if (layer) {
154
- key = layer.on("updatefeatures", () => {
84
+ key = layer.on("updatefeatures" as EventTypes, () => {
155
85
  const features = layer.getSource()?.getFeatures();
156
86
  if (!features?.length) {
157
87
  return;
@@ -87,6 +87,7 @@ function MobilityMap(props: MobilityMapProps) {
87
87
  const [stationId, setStationId] = useState<RealtimeStationId>();
88
88
  const [trainId, setTrainId] = useState<RealtimeTrainId>();
89
89
  const [linesIds, setLinesIds] = useState<string[]>();
90
+ const [notificationId, setNotificationId] = useState<string>();
90
91
 
91
92
  const [featuresInfos, setFeaturesInfos] = useState<
92
93
  LayerGetFeatureInfoResponse[]
@@ -141,6 +142,7 @@ function MobilityMap(props: MobilityMapProps) {
141
142
  linesNetworkPlanLayer,
142
143
  map,
143
144
  mapsetLayer,
145
+ notificationId,
144
146
  notificationsLayer,
145
147
  permalinkUrlSearchParams,
146
148
  previewNotifications,
@@ -175,6 +177,7 @@ function MobilityMap(props: MobilityMapProps) {
175
177
  setLinesNetworkPlanLayer,
176
178
  setMap,
177
179
  setMapsetLayer,
180
+ setNotificationId,
178
181
  setNotificationsLayer,
179
182
  setPermalinkUrlSearchParams,
180
183
  setPreviewNotifications,
@@ -222,6 +225,7 @@ function MobilityMap(props: MobilityMapProps) {
222
225
  linesNetworkPlanLayer,
223
226
  map,
224
227
  mapsetLayer,
228
+ notificationId,
225
229
  notificationsLayer,
226
230
  permalinkUrlSearchParams,
227
231
  previewNotifications,
@@ -40,7 +40,6 @@ export type MobilityMapAttributeName =
40
40
  | "mainlink"
41
41
  | "mainlinktitle"
42
42
  | "mapset"
43
- | "mapsetbbox"
44
43
  | "mapsetplanid"
45
44
  | "mapsettags"
46
45
  | "mapsettenants"
@@ -191,12 +190,6 @@ where:
191
190
  public: true,
192
191
  type: "boolean",
193
192
  },
194
- mapsetbbox: {
195
- defaultValue: MAX_EXTENT.join(","),
196
- description:
197
- "The BBOX to constrain the boundary of the mapset layer in EPSG:3857 coordinates. Mandatory for mapset layer. <br/>Ex: 831634,5933959,940649,6173660 .",
198
- public: false,
199
- },
200
193
  mapsetplanid: {
201
194
  description:
202
195
  "The id of the mapset plan to display. Mostly for debugging purposes.",
@@ -216,7 +209,7 @@ where:
216
209
  public: false,
217
210
  },
218
211
  mapseturl: {
219
- defaultValue: "https://editor.mapset.io/api/v1",
212
+ defaultValue: "https://editor.mapset.io/api/v1/",
220
213
  description: `The ${geopsMapsetApiLink} url to use.`,
221
214
  public: true,
222
215
  },
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  type AffectedTimeIntervalType,
3
3
  type PublicationType,
4
- type SituationType,
5
4
  type TextualContentType,
6
5
  } from "mobility-toolbox-js/types";
7
6
  import { twMerge } from "tailwind-merge";
@@ -10,6 +9,7 @@ import Warning from "../icons/Warning";
10
9
  import ShadowOverflow from "../ShadowOverflow";
11
10
  import Link from "../ui/Link";
12
11
  import useI18n from "../utils/hooks/useI18n";
12
+ import useMocoSituation from "../utils/hooks/useMocoSituation";
13
13
 
14
14
  import type {
15
15
  MultilingualTextualContentType,
@@ -64,30 +64,8 @@ function NotificationDetails({
64
64
  feature: Feature;
65
65
  }) {
66
66
  const { locale, t } = useI18n();
67
- const { situation } = feature.getProperties();
68
-
69
- // useEffect(() => {
70
- // const abortController = new AbortController();
71
- // if (!notificationtenant) {
72
- // setLines([]);
73
- // }
74
- // fetch(
75
- // `https://tralis-tracker-api.geops.io/api/lines/${notificationtenant}/`,
76
- // )
77
- // .then((res) => {
78
- // return res.json();
79
- // })
80
- // .then((data) => {
81
- // setLines(data);
82
- // })
83
- // .catch((err) => {
84
- // // eslint-disable-next-line no-console
85
- // console.error("Failed to fetch lines", err);
86
- // });
87
- // return () => {
88
- // abortController?.abort();
89
- // };
90
- // }, [notificationtenant]);
67
+ const { situationId } = feature.getProperties();
68
+ const situationParsed = useMocoSituation(situationId);
91
69
 
92
70
  // moco export v2
93
71
  let textualContentMultilingual: Partial<MultilingualTextualContentType> = {};
@@ -96,41 +74,36 @@ function NotificationDetails({
96
74
  let publicationsToDisplay: PublicationType[] = [];
97
75
  let reasonsToDisplay: string[] = [];
98
76
 
99
- try {
100
- const situationParsed: SituationType = JSON.parse(situation) || {};
101
- const publicationsArr: PublicationType[] =
102
- situationParsed?.publications || [];
77
+ // const situationParsed: SituationType = JSON.parse(situation) || {};
78
+ const publicationsArr: PublicationType[] =
79
+ situationParsed?.publications || [];
103
80
 
104
- // Find the current publication(s) at the current date
105
- publicationsToDisplay =
106
- publicationsArr?.filter(({ publicationWindows }) => {
107
- return publicationWindows.find(({ endTime, startTime }) => {
108
- const now = new Date();
109
- const startT = new Date(startTime);
110
- const endT = new Date(endTime);
111
- return startT <= now && now <= endT;
112
- });
113
- }) || [];
81
+ // Find the current publication(s) at the current date
82
+ publicationsToDisplay =
83
+ publicationsArr?.filter(({ publicationWindows }) => {
84
+ return publicationWindows.find(({ endTime, startTime }) => {
85
+ const now = new Date();
86
+ const startT = new Date(startTime);
87
+ const endT = new Date(endTime);
88
+ return startT <= now && now <= endT;
89
+ });
90
+ }) || [];
114
91
 
115
- // Display the current and next affected time intervals not the one in the past
116
- timeIntervalsToDisplay =
117
- (situationParsed?.affectedTimeIntervals || []).filter(
118
- ({ endTime, startTime }) => {
119
- const now = new Date();
120
- const startT = new Date(startTime);
121
- const endT = new Date(endTime);
122
- return (startT <= now && now <= endT) || now < startT;
123
- },
124
- ) || [];
92
+ // Display the current and next affected time intervals not the one in the past
93
+ timeIntervalsToDisplay =
94
+ (situationParsed?.affectedTimeIntervals || []).filter(
95
+ ({ endTime, startTime }) => {
96
+ const now = new Date();
97
+ const startT = new Date(startTime);
98
+ const endT = new Date(endTime);
99
+ return (startT <= now && now <= endT) || now < startT;
100
+ },
101
+ ) || [];
125
102
 
126
- // Display the reasons
127
- reasonsToDisplay = (situationParsed?.reasons || []).map(({ name }) => {
128
- return name;
129
- });
130
- } catch (e) {
131
- // eslint-disable-next-line no-console
132
- console.error("Failed to parse publications", e);
133
- }
103
+ // Display the reasons
104
+ reasonsToDisplay = (situationParsed?.reasons || []).map(({ name }) => {
105
+ return name;
106
+ });
134
107
 
135
108
  return (
136
109
  <ShadowOverflow {...props} className={twMerge("px-4 text-base", className)}>
@@ -1,12 +1,10 @@
1
- import { MocoLayer } from "mobility-toolbox-js/ol";
1
+ import { MocoLayer, type MocoLayerOptions } from "mobility-toolbox-js/ol";
2
2
  import { memo } from "preact/compat";
3
3
  import { useEffect, useMemo } from "preact/hooks";
4
4
 
5
5
  import { LAYER_NAME_NOTIFICATIONS } from "../utils/constants";
6
6
  import useMapContext from "../utils/hooks/useMapContext";
7
7
 
8
- import type { MocoLayerOptions } from "mobility-toolbox-js/ol";
9
-
10
8
  function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
11
9
  const {
12
10
  apikey,
@@ -1,8 +1,8 @@
1
- import { type SituationType } from "mobility-toolbox-js/types";
2
1
  import { twMerge } from "tailwind-merge";
3
2
 
4
3
  import ShadowOverflow from "../../ShadowOverflow";
5
4
  import SituationDetails from "../../SituationDetails";
5
+ import useMocoSituation from "../../utils/hooks/useMocoSituation";
6
6
 
7
7
  import type { Feature } from "ol";
8
8
 
@@ -20,17 +20,8 @@ function NotificationDetails({
20
20
  timeIntervalClassName?: string;
21
21
  toggleable?: boolean;
22
22
  }) {
23
- const { situation } = feature.getProperties();
24
-
25
- // moco export v2
26
- let situationParsed: Partial<SituationType> = {};
27
-
28
- try {
29
- situationParsed = JSON.parse(situation) || {};
30
- } catch (e) {
31
- // eslint-disable-next-line no-console
32
- console.error("Failed to parse publications", e);
33
- }
23
+ const { situationId } = feature.getProperties();
24
+ const situationParsed = useMocoSituation(situationId);
34
25
 
35
26
  return (
36
27
  <ShadowOverflow {...props} className={twMerge("px-4 text-base", className)}>
@@ -0,0 +1,17 @@
1
+
2
+ - creation app/repo/deploy (lint) -> 4h
3
+ - creation map with ArcGIS Maps SDK with SBB base layer(one or more?) -> 4h
4
+ - add Betriebspunkte and Linien layers on the map -> 4h
5
+ - using URL parameter "?betriebspunkte=BN":
6
+ - ask the ArcGIS FeatureServer the Betriebpunkte corresponding to URL paramter -> 4h
7
+ - zoom on it with a buffer if 5km -> 2h
8
+ - ask the ArcGIS FeatureServer the Gemeinde, the Kanton of the Betriebspunkte -> 4h
9
+ - ask the ArcGIS FeatureServer all Linien available in this 5km extent -> 3h
10
+ - show a dialog with Kanton, Gemiende and the list of selectable lines -> 4h
11
+ - define the default "KM von" and "KM bis" for each Linie using the 5km extent -> 4h
12
+ - display "KM von" and "KM bis" in the dialog as input text -> 2h
13
+
14
+ - add a tool to select an area by bbox -> 1d
15
+ - add a tool to select an area by "Frei Skizze" -> 1d
16
+ - use the area drawn to recalculate the "KM von" and "KM bis" of all Linien -> 3h
17
+ -
@@ -0,0 +1,54 @@
1
+ import { useEffect, useMemo, useState } from "preact/hooks";
2
+
3
+ import useMapContext from "./useMapContext";
4
+
5
+ import type { MocoAPI } from "mobility-toolbox-js/maplibre";
6
+ import type {
7
+ MocoExportParameters,
8
+ SituationType,
9
+ } from "mobility-toolbox-js/types";
10
+
11
+ function useMocoSituation(situationId?: string, params?: MocoExportParameters) {
12
+ const { lang, notificationsLayer } = useMapContext();
13
+ const [situation, setSituation] = useState<SituationType>();
14
+
15
+ const api: MocoAPI | undefined = useMemo(() => {
16
+ return notificationsLayer?.api;
17
+ }, [notificationsLayer]);
18
+
19
+ useEffect(() => {
20
+ const abortController = new AbortController();
21
+ api
22
+ ?.exportById(situationId, {
23
+ contentLarge: true,
24
+ contentMedium: true,
25
+ contentSmall: true,
26
+ includeGeoms: false,
27
+ includeLines: true,
28
+ includeStops: true,
29
+ [lang]: true,
30
+ limit: 1,
31
+ ...(params || {}),
32
+ })
33
+ .then((response) => {
34
+ setSituation(response);
35
+ })
36
+ .catch((err) => {
37
+ // 20: AbortError
38
+ if (err.code !== 20) {
39
+ setSituation(undefined);
40
+ // eslint-disable-next-line no-console
41
+ console.error("Failed to fetch situation", err);
42
+ return;
43
+ }
44
+ });
45
+
46
+ return () => {
47
+ abortController.abort();
48
+ };
49
+ }, [api, situationId, params, lang]);
50
+
51
+ return situation;
52
+ }
53
+
54
+ export default useMocoSituation;
@@ -1,130 +0,0 @@
1
- import sbbConstructionBanner from "../icons/sbb_construction_banner.png";
2
- // import sbbAddStop from "../icons/sbb_add_stop.png";
3
- // import sbbAlternative from "../icons/sbb_alternative.png";
4
- // import sbbCancellation from "../icons/sbb_cancellation.png";
5
- import sbbConstruction from "../icons/sbb_construction.png";
6
- // import sbbDelay from "../icons/sbb_delay.png";
7
- import sbbDisruptionBanner from "../icons/sbb_disruption_banner.png";
8
- import sbbDisruption from "../icons/sbb_disruption.png";
9
- // import sbbInfo from "../icons/sbb_info.png";
10
- import sbbMissedConnectionBanner from "../icons/sbb_missed_connection_banner.png";
11
- import sbbMissedConnection from "../icons/sbb_missed_connection.png";
12
- import sbbReplacementBusBanner from "../icons/sbb_replacementbus_banner.png";
13
- // import sbbPlatformChange from "../icons/sbb_platform_change.png";
14
- import sbbReplacementBus from "../icons/sbb_replacementbus.png";
15
- // import sbbReRoute from "../icons/sbb_reroute.png";
16
- // import warningbanner from "../icons/warning-banner.png";
17
- // import warning from "../icons/warning.png";
18
-
19
- // export const AffectedLineStyleCategoryChoices = {
20
- // Construction: "CONSTRUCTION",
21
- // Disruption: "DISRUPTION",
22
- // IndustrialAction: "INDUSTRIAL_ACTION",
23
- // LiftFailure: "LIFT_FAILURE",
24
- // Other: "OTHER",
25
- // RailReplacement: "RAIL_REPLACEMENT",
26
- // SpecialEvent: "SPECIAL_EVENT",
27
- // VehicleFaulure: "VEHICLE_FAULURE",
28
- // Warning: "WARNING",
29
- // } as const;
30
- const defaultIcon = sbbMissedConnection;
31
- const defaultIconBanner = sbbMissedConnectionBanner;
32
- export const icons = {
33
- CONSTRUCTION: sbbConstruction,
34
- CONSTRUCTION_BANNER: sbbConstructionBanner,
35
- DISRUPTION: sbbDisruption,
36
- DISRUPTION_BANNER: sbbDisruptionBanner,
37
- INDUSTRIAL_ACTION: defaultIcon,
38
- INDUSTRIAL_ACTION_BANNER: defaultIconBanner,
39
- LIFT_FAILURE: defaultIcon,
40
- LIFT_FAILURE_BANNER: defaultIconBanner,
41
- OTHER: defaultIcon,
42
- OTHER_BANNER: defaultIconBanner,
43
- RAIL_REPLACEMENT: sbbReplacementBus,
44
-
45
- RAIL_REPLACEMENT_BANNER: sbbReplacementBusBanner,
46
- SPECIAL_EVENT: defaultIcon,
47
- SPECIAL_EVENT_BANNER: defaultIconBanner,
48
- VEHICLE_FAULURE: defaultIcon,
49
- VEHICLE_FAULURE_BANNER: defaultIconBanner,
50
- warning: defaultIcon,
51
- WARNING: defaultIcon,
52
- warningBanner: defaultIconBanner,
53
- };
54
-
55
- // Add a source and styleLayer using the id in parameter.
56
- const addSourceAndLayers = (
57
- mapboxLayer,
58
- sourceId,
59
- sourceData,
60
- styleLayer,
61
- beforeLayerId,
62
- ) => {
63
- if (!mapboxLayer.loaded) {
64
- mapboxLayer.once("load", () => {
65
- addSourceAndLayers(
66
- mapboxLayer,
67
- sourceId,
68
- sourceData,
69
- styleLayer,
70
- beforeLayerId,
71
- );
72
- });
73
- return;
74
- }
75
- const { mbMap } = mapboxLayer;
76
-
77
- Object.keys(icons).forEach((icon) => {
78
- if (!mbMap.getImage(icon)) {
79
- mbMap.loadImage(icons[icon]).then((image) => {
80
- if (!mbMap.getImage(icon)) {
81
- mbMap.addImage(icon, image.data);
82
- }
83
- });
84
- }
85
- });
86
-
87
- // Update source
88
- if (sourceId && sourceData) {
89
- const source = mbMap.getSource(sourceId);
90
- if (source) {
91
- source.setData(sourceData);
92
- } else {
93
- mbMap.addSource(sourceId, {
94
- data: sourceData,
95
- generateId: true,
96
- type: "geojson",
97
- });
98
- }
99
- }
100
-
101
- // Update layer
102
- if (styleLayer) {
103
- let layer = mbMap.getLayer(sourceId);
104
- if (layer) {
105
- mbMap.removeLayer(layer.id);
106
- }
107
-
108
- // styleLayer could be an array of styles to add.
109
- let styleLayers = styleLayer;
110
- if (!Array.isArray(styleLayer)) {
111
- styleLayers = [styleLayer];
112
- }
113
- styleLayers.forEach((style) => {
114
- if (mbMap.getSource(style.source)) {
115
- layer = mbMap.getLayer(style.id);
116
- if (layer) {
117
- mbMap.removeLayer(layer.id);
118
- }
119
- mbMap.addLayer(style, beforeLayerId);
120
- } else {
121
- console.warn(
122
- `The source ${style.source} doesn't exist. This layer can't be added`,
123
- style,
124
- );
125
- }
126
- });
127
- }
128
- };
129
-
130
- export default addSourceAndLayers;