@geops/rvf-mobility-web-component 0.1.42 → 0.1.44

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.42",
5
+ "version": "0.1.44",
6
6
  "homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
7
7
  "type": "module",
8
8
  "main": "index.js",
@@ -12,7 +12,7 @@
12
12
  "jspdf": "^2.5.2",
13
13
  "lodash.debounce": "^4.0.8",
14
14
  "maplibre-gl": "^4.7.1",
15
- "mobility-toolbox-js": "3.1.1-beta.0",
15
+ "mobility-toolbox-js": "3.3.0-beta.32",
16
16
  "ol": "^10.3.1",
17
17
  "preact": "^10.25.4",
18
18
  "preact-custom-element": "^4.3.0",
@@ -51,7 +51,7 @@ export interface MobilityMapProps {
51
51
  notification?: string;
52
52
  notificationat?: string; // 2024-01-25T22:59:00Z
53
53
  notificationbeforelayerid?: string;
54
- notificationurl?: string; // https://moco.geops.io/api/v1/export/notification/?sso_config=sob
54
+ notificationurl?: string; // https://moco.geops.io/api/v1/
55
55
  permalink?: string;
56
56
  realtime?: string;
57
57
  realtimeurl?: string;
@@ -75,7 +75,7 @@ function MobilityMap({
75
75
  notification = "true",
76
76
  notificationat = null,
77
77
  notificationbeforelayerid = null,
78
- notificationurl = null,
78
+ notificationurl = "https://moco.geops.io/api/v1/",
79
79
  permalink = "false",
80
80
  realtime = "true",
81
81
  realtimeurl = "wss://api.geops.io/tracker-ws/v1/ws",
@@ -1,203 +1,23 @@
1
- import { FeatureCollection } from "geojson";
2
- import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
3
- import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
4
- import { useEffect, useMemo, useState } from "preact/hooks";
1
+ import { MocoLayer } from "mobility-toolbox-js/ol";
2
+ import { MocoLayerOptions } from "mobility-toolbox-js/ol/layers/MocoLayer";
3
+ import { memo } from "preact/compat";
4
+ import { useEffect, useMemo } from "preact/hooks";
5
5
 
6
6
  import useMapContext from "../utils/hooks/useMapContext";
7
- import useZoom from "../utils/hooks/useZoom";
8
- import {
9
- addNotificationsLayers,
10
- getCurrentGraph,
11
- getNotificationsWithStatus,
12
- parsePreviewNotification,
13
- } from "./notificationUtils";
14
7
 
15
- type Graphs = Record<string, string>;
16
-
17
- interface Metadata {
18
- graphs?: Graphs;
19
- }
20
-
21
- const useStyleName = (): string => {
22
- const { baselayer } = useMapContext();
23
- return baselayer;
24
- };
25
-
26
- const useStyleMetadata = (): Metadata => {
27
- const { baseLayer } = useMapContext();
28
- const [styleMetadata, setStyleMetadata] = useState<Metadata>();
29
- useEffect(() => {
30
- if (!baseLayer) {
31
- return;
32
- }
33
- if (!baseLayer.loaded) {
34
- // @ts-expect-error bad type definition
35
- baseLayer.once("load", () => {
36
- return setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
37
- });
38
- } else {
39
- setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
40
- }
41
- }, [baseLayer]);
42
- return styleMetadata;
43
- };
44
-
45
- const useGraphMapping = (
46
- defaultMapping: Record<number, string> = { 1: "osm" },
47
- ): Record<number, string> => {
48
- const styleMetadata = useStyleMetadata();
49
- const graphMapping = useMemo(() => {
50
- return styleMetadata?.graphs || defaultMapping;
51
- }, [defaultMapping, styleMetadata?.graphs]);
52
- return graphMapping;
53
- };
54
-
55
- const useNotifications = (): FeatureCollection => {
56
- const { notificationat, notificationurl } = useMapContext();
57
- const [notifications, setNotifications] = useState([]);
58
- const [previewNotification, setPreviewNotification] = useState(null);
59
- const [shouldAddPreviewNotifications, setShouldAddPreviewNotifications] =
60
- useState<boolean>(true);
61
-
62
- const styleName = useStyleName();
63
- const graphMapping = useGraphMapping();
64
-
65
- const now = useMemo(() => {
66
- return notificationat ? new Date(notificationat) : new Date();
67
- }, [notificationat]);
68
-
69
- const graphsString = useMemo(() => {
70
- return [...new Set(Object.values(graphMapping))].join(",");
71
- }, [graphMapping]);
72
-
73
- useEffect(() => {
74
- // Listen for incoming messages through the MOCO iframe
75
- window.addEventListener("message", (event) => {
76
- if (event.data.notification) {
77
- setPreviewNotification(event.data.notification);
78
- setShouldAddPreviewNotifications(true);
79
- }
80
- });
81
- }, []);
82
-
83
- useEffect(() => {
84
- let abortCtrl: AbortController;
85
-
86
- // Fetch the main MOCO notifications
87
- const fetchNotifications = async () => {
88
- const suffix = /\?/.test(notificationurl) ? "&" : "?";
89
- const url = `${notificationurl}${suffix}graph=${graphsString}`;
90
-
91
- abortCtrl?.abort();
92
- abortCtrl = new AbortController();
93
- const response = await fetch(url, { signal: abortCtrl.signal });
94
- const data = await response.json();
95
-
96
- const notifications = getNotificationsWithStatus(data, now);
97
- setNotifications(notifications);
98
- setShouldAddPreviewNotifications(true);
99
- };
100
-
101
- if (notificationurl && graphsString) {
102
- fetchNotifications();
103
- }
104
-
105
- return () => {
106
- abortCtrl?.abort();
107
- };
108
- }, [notificationurl, graphsString, now]);
109
-
110
- useEffect(() => {
111
- // Merge notifications with the previewNotification
112
- const newNotifications = [...notifications];
113
- const notifByStyle = previewNotification?.[styleName];
114
-
115
- if (shouldAddPreviewNotifications && notifByStyle) {
116
- const parsedPreviewNotification = parsePreviewNotification(notifByStyle);
117
- const index = newNotifications.findIndex((n) => {
118
- return n.properties.id === notifByStyle.id;
119
- });
120
-
121
- if (index > -1) {
122
- newNotifications[index] = parsedPreviewNotification;
123
- } else {
124
- newNotifications.push(parsedPreviewNotification);
125
- }
126
-
127
- setNotifications(getNotificationsWithStatus(newNotifications, now));
128
- setShouldAddPreviewNotifications(false);
129
- }
130
- }, [
131
- previewNotification,
132
- notifications,
133
- shouldAddPreviewNotifications,
134
- styleName,
135
- now,
136
- ]);
137
-
138
- if (previewNotification?.id) {
139
- const index = notifications.findIndex((n) => {
140
- return n.properties.id === previewNotification.id;
141
- });
142
- if (index > -1) {
143
- notifications[index].isPreview = true;
144
- notifications[index].features.forEach((feature) => {
145
- feature.properties.isPreview = true;
146
- });
147
- }
148
- }
149
-
150
- const features =
151
- notifications
152
- ?.map((notification) => {
153
- return notification.features;
154
- })
155
- .flat() || [];
156
-
157
- return {
158
- features: features,
159
- type: "FeatureCollection",
160
- };
161
- };
162
-
163
- export default function NotificationLayer(props: MaplibreStyleLayerOptions) {
164
- const notifications = useNotifications();
165
- const { baseLayer, map, notificationbeforelayerid } = useMapContext();
166
- const graphMapping = useGraphMapping();
167
- const zoom = useZoom();
8
+ function NotificationLayer(props: MocoLayerOptions) {
9
+ const { apikey, baseLayer, map } = useMapContext();
168
10
 
169
11
  const layer = useMemo(() => {
170
12
  if (!baseLayer) {
171
13
  return null;
172
14
  }
173
- return new MaplibreStyleLayer({
174
- beforeId: notificationbeforelayerid,
175
- isQueryable: true,
176
- layersFilter: ({ metadata }) => {
177
- return metadata?.["general.filter"] === "notifications";
178
- },
15
+ return new MocoLayer({
16
+ apiKey: apikey,
179
17
  maplibreLayer: baseLayer,
180
18
  ...(props || {}),
181
19
  });
182
- }, [baseLayer, notificationbeforelayerid, props]);
183
-
184
- // Update data and style layers
185
- useEffect(() => {
186
- if (!baseLayer || !graphMapping) {
187
- return null;
188
- }
189
- const graph = getCurrentGraph(graphMapping, zoom);
190
- if (!graph) {
191
- return null;
192
- }
193
- addNotificationsLayers(
194
- baseLayer,
195
- "notifications",
196
- notifications,
197
- notificationbeforelayerid,
198
- graph,
199
- );
200
- }, [baseLayer, graphMapping, notificationbeforelayerid, notifications, zoom]);
20
+ }, [apikey, baseLayer, props]);
201
21
 
202
22
  useEffect(() => {
203
23
  if (!map || !layer) {
@@ -212,3 +32,5 @@ export default function NotificationLayer(props: MaplibreStyleLayerOptions) {
212
32
 
213
33
  return null;
214
34
  }
35
+
36
+ export default memo(NotificationLayer);
@@ -15,14 +15,14 @@ function RouteStopTime(props: RouteStopTimeProps) {
15
15
  return (
16
16
  <div {...props}>
17
17
  <span
18
- className={`${isCancelled ? "text-red-600 line-through" : ""} ${
18
+ className={`${isCancelled ? "text-darkred line-through" : ""} ${
19
19
  isFirst ? "hidden" : ""
20
20
  }`}
21
21
  >
22
22
  {getHoursAndMinutes(aimedArrivalTime)}
23
23
  </span>
24
24
  <span
25
- className={`${status.isCancelled ? "text-red-600 line-through" : ""} ${
25
+ className={`${status.isCancelled ? "text-darkred line-through" : ""} ${
26
26
  isLast ? "hidden" : ""
27
27
  }`}
28
28
  >
@@ -4,7 +4,7 @@ import { memo } from "preact/compat";
4
4
 
5
5
  import useRvfContext from "../utils/hooks/useRvfContext";
6
6
  import RvfLineNetworkDetails from "./RvfLineNetworkDetails/RvfLineNetworkDetails";
7
- import RvfNotificationDetails from "./RvfNotificationDetails";
7
+ // import RvfNotificationDetails from "./RvfNotificationDetails";
8
8
  import RvfSellingPointDetails from "./RvfSellingPointDetails";
9
9
  import RvfSharedMobilityDetails from "./RvfSharedMobilityDetail";
10
10
 
@@ -25,7 +25,7 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
25
25
  const { selectedFeature, selectedFeatures } = useRvfContext();
26
26
  const isSharedMobility = getIsSharedMobility(selectedFeature);
27
27
  const isSellingPoint = !!selectedFeature.get("tickets");
28
- const isNotification = !!selectedFeature.get("disruption_type");
28
+ const isNotification = !!selectedFeature.get("severity");
29
29
  const isLineNetwork = !!selectedFeature.get("original_line_id");
30
30
 
31
31
  const showDefaultData = () => {
@@ -49,7 +49,8 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
49
49
  {isSharedMobility && (
50
50
  <RvfSharedMobilityDetails selectedFeature={selectedFeature} />
51
51
  )}
52
- {isNotification && <RvfNotificationDetails feature={selectedFeature} />}
52
+ {isNotification && null}
53
+ {/* <RvfNotificationDetails feature={selectedFeature} />} */}
53
54
  {isLineNetwork && (
54
55
  <RvfLineNetworkDetails
55
56
  feature={selectedFeature}
@@ -52,6 +52,10 @@ function RvfLineNetworkDetails({
52
52
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
53
53
  const [stopInfosOpenId, setStopInfosOpenId] = useState<string>(null);
54
54
 
55
+ const isRunsDisplay = useMemo(() => {
56
+ return new URLSearchParams(window.location.search).get("runs") === "true";
57
+ }, []);
58
+
55
59
  useEffect(() => {
56
60
  const source = baseLayer?.mapLibreMap?.getSource(
57
61
  LNP_SOURCE_ID,
@@ -117,7 +121,7 @@ function RvfLineNetworkDetails({
117
121
  const byLineId = {};
118
122
  features.forEach((f) => {
119
123
  const lineId = f.get(ORIGINAL_LINE_ID_PROP);
120
- if (lineId && !byLineId[lineId]) {
124
+ if (lineId && !byLineId[lineId] && f.get("stop_ids")) {
121
125
  try {
122
126
  byLineId[lineId] = JSON.parse(f.get("stop_ids"));
123
127
  } catch (e) {
@@ -158,7 +162,7 @@ function RvfLineNetworkDetails({
158
162
  // external_id,
159
163
  long_name,
160
164
  mot,
161
- // runs,
165
+ runs,
162
166
  short_name: shortName,
163
167
  text_color: textColor,
164
168
  } = lineInfo;
@@ -218,7 +222,9 @@ function RvfLineNetworkDetails({
218
222
  {!!longName && (
219
223
  <div className={"flex-1 text-left"}>{longName}</div>
220
224
  )}
221
- {/* <div className={"text-xs"}>{runs}</div> */}
225
+ {isRunsDisplay && (
226
+ <div className={"text-xs"}>{runs}</div>
227
+ )}
222
228
 
223
229
  {!!stops && (
224
230
  <button className={"shrink-0"}>
@@ -23,7 +23,7 @@ const toShortDate = (date, showTime) => {
23
23
  };
24
24
 
25
25
  let rvfLines = null;
26
- fetch("https://tralis-tracker-api.dev.geops.io/api/lines/rvf/")
26
+ fetch("https://tralis-tracker-api.geops.io/api/lines/rvf/")
27
27
  .then((res) => {
28
28
  return res.json();
29
29
  })
@@ -3,7 +3,9 @@ import {
3
3
  MaplibreStyleLayer,
4
4
  RealtimeLayer as MbtRealtimeLayer,
5
5
  } from "mobility-toolbox-js/ol";
6
+ import { MocoLayerOptions } from "mobility-toolbox-js/ol/layers/MocoLayer";
6
7
  import {
8
+ MocoNotification,
7
9
  RealtimeStation,
8
10
  RealtimeStationId,
9
11
  RealtimeStopSequence,
@@ -35,7 +37,7 @@ import RvfExportMenuButton from "../RvfExportMenuButton";
35
37
  import RvfFeatureDetails from "../RvfFeatureDetails";
36
38
  import RvfFloatingMenu from "../RvfFloatingMenu";
37
39
  import RvfLayerTreeButton from "../RvfLayerTreeButton";
38
- // Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%2Fnotification%2F%3Fsso_config%3Dsob&geolocation=false&realtime=false&search=false&notificationat=2024-01-25T22%3A59%3A00Z
40
+ // Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%2Fpublication%2F%3Fsso_config%3Drvf&geolocation=false&realtime=false&search=false&notificationat=2024-01-25T22%3A59%3A00Z
39
41
  import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
40
42
  import Modal from "../RvfModal";
41
43
  import RvfOverlayHeader from "../RvfOverlayHeader";
@@ -108,6 +110,7 @@ const realtimeLayerProps = {
108
110
  bboxParameters: {
109
111
  line_tags: "RVF",
110
112
  },
113
+
111
114
  fullTrajectoryStyle: fullTrajectoryStyle,
112
115
  getMotsByZoom: (z) => {
113
116
  if (z < 9) {
@@ -152,7 +155,7 @@ const realtimeLayerProps = {
152
155
  function RvfMobilityMap({
153
156
  apikey = "5cc87b12d7c5370001c1d655820abcc37dfd4d968d7bab5b2a74a935",
154
157
  // baselayer = "review-geops-tgma-085vyz.de.rvf",
155
- baselayer = "de.rvf",
158
+ baselayer = "de.rvf_moco",
156
159
  center = null,
157
160
  details = "true",
158
161
  embed = "false",
@@ -167,10 +170,10 @@ function RvfMobilityMap({
167
170
  maxzoom = "20",
168
171
  minzoom = null,
169
172
  mots = null,
170
- notification = "true",
171
- notificationat = null,
173
+ notification = "false",
174
+ notificationat = null, //"2025-09-10T00:00:00Z",
172
175
  notificationbeforelayerid = null,
173
- notificationurl = null,
176
+ notificationurl = "https://moco.geops.io/api/v1/",
174
177
  permalink = "false",
175
178
  print = "true",
176
179
  realtime = "true",
@@ -207,6 +210,9 @@ function RvfMobilityMap({
207
210
  const [sharedMobilityLayerGroup, setSharedMobilityLayerGroup] =
208
211
  useState<Group>();
209
212
 
213
+ const [previewNotification, setPreviewNotification] =
214
+ useState<MocoNotification[]>();
215
+
210
216
  // Convert string boolean to boolean
211
217
  const hasToolbar = useMemo(() => {
212
218
  return toolbar === "true";
@@ -221,8 +227,8 @@ function RvfMobilityMap({
221
227
  }, [realtime]);
222
228
 
223
229
  const hasNotification = useMemo(() => {
224
- return notification === "true";
225
- }, [notification]);
230
+ return notification === "true" || !!previewNotification;
231
+ }, [notification, previewNotification]);
226
232
 
227
233
  const hasGeolocation = useMemo(() => {
228
234
  return geolocation === "true";
@@ -278,12 +284,14 @@ function RvfMobilityMap({
278
284
  notificationbeforelayerid,
279
285
  notificationurl,
280
286
  permalink,
287
+ previewNotification,
281
288
  realtimeLayer,
282
289
  realtimeurl,
283
290
  setBaseLayer,
284
291
  setIsFollowing,
285
292
  setIsTracking,
286
293
  setMap,
294
+ setPreviewNotification,
287
295
  setRealtimeLayer,
288
296
  setStation,
289
297
  setStationId,
@@ -308,10 +316,10 @@ function RvfMobilityMap({
308
316
  geolocation,
309
317
  isFollowing,
310
318
  isTracking,
319
+ layers,
311
320
  map,
312
321
  mapsurl,
313
322
  maxextent,
314
- layers,
315
323
  maxzoom,
316
324
  minzoom,
317
325
  mots,
@@ -320,6 +328,7 @@ function RvfMobilityMap({
320
328
  notificationbeforelayerid,
321
329
  notificationurl,
322
330
  permalink,
331
+ previewNotification,
323
332
  realtimeLayer,
324
333
  realtimeurl,
325
334
  station,
@@ -398,6 +407,7 @@ function RvfMobilityMap({
398
407
  layertree,
399
408
  lineNetworkPlanLayer,
400
409
  poisLayer,
410
+ previewNotification,
401
411
  selectedFeature,
402
412
  selectedFeatures,
403
413
  sellingPointsLayer,
@@ -416,11 +426,12 @@ function RvfMobilityMap({
416
426
  };
417
427
  }, [
418
428
  isExportMenuOpen,
419
- layertree,
420
429
  isLayerTreeOpen,
421
430
  isShareMenuOpen,
431
+ layertree,
422
432
  lineNetworkPlanLayer,
423
433
  poisLayer,
434
+ previewNotification,
424
435
  selectedFeature,
425
436
  selectedFeatures,
426
437
  sellingPointsLayer,
@@ -440,11 +451,20 @@ function RvfMobilityMap({
440
451
  return {
441
452
  format: (copyrights) => {
442
453
  const newCopyrights = [];
454
+ let alreadyAGeopsLink = false;
443
455
  copyrights.forEach((copyright) => {
444
- if (!/(sbb|rvf)/i.test(copyright)) {
456
+ if (/geops/i.test(copyright)) {
457
+ // Hack until all geOps tiles are the same
458
+ // Currently there is some geops.com or geops.ch links
459
+ if (!alreadyAGeopsLink) {
460
+ alreadyAGeopsLink = true;
461
+ newCopyrights.push(copyright);
462
+ }
463
+ } else if (!/(sbb|rvf)/i.test(copyright)) {
445
464
  newCopyrights.push(copyright);
446
465
  }
447
466
  });
467
+
448
468
  return newCopyrights.join("&nbsp;|&nbsp;");
449
469
  },
450
470
  };
@@ -458,6 +478,19 @@ function RvfMobilityMap({
458
478
  };
459
479
  }, []);
460
480
 
481
+ const notificationsLayerProps: MocoLayerOptions = useMemo(() => {
482
+ return {
483
+ // beforeId: notificationbeforelayerid,
484
+ apiKey: apikey,
485
+ date: notificationat ? new Date(notificationat) : undefined,
486
+ isQueryable: true,
487
+ notifications: previewNotification,
488
+ tenant: "rvf",
489
+ title: RVF_LAYERS_TITLES.meldungen,
490
+ url: notificationurl,
491
+ };
492
+ }, [apikey, notificationat, notificationurl, previewNotification]);
493
+
461
494
  useEffect(() => {
462
495
  if (hasToolbar && isLayerTreeOpen) {
463
496
  setIsExportMenuOpen(false);
@@ -520,6 +553,15 @@ function RvfMobilityMap({
520
553
  }
521
554
  }, [isShareMenuOpen, hasToolbar]);
522
555
 
556
+ useEffect(() => {
557
+ window.addEventListener("message", (event) => {
558
+ console.log("Received message from parent:", event.data);
559
+ if (event.data.notification) {
560
+ setPreviewNotification([event.data.notification]);
561
+ }
562
+ });
563
+ }, []);
564
+
523
565
  return (
524
566
  <I18nContext.Provider value={i18n}>
525
567
  <style>{tailwind}</style>
@@ -537,6 +579,10 @@ function RvfMobilityMap({
537
579
  <SingleClickListener />
538
580
  {isEmbed && <RvfEmbedNavigation />}
539
581
 
582
+ {hasNotification && (
583
+ <NotificationLayer {...notificationsLayerProps} />
584
+ )}
585
+
540
586
  <RvfSelectedFeatureHighlightLayer />
541
587
 
542
588
  {hasRealtime && (
@@ -551,7 +597,6 @@ function RvfMobilityMap({
551
597
  title={RVF_LAYERS_TITLES.haltestellen}
552
598
  {...stationsLayerProps}
553
599
  />
554
- {hasNotification && <NotificationLayer />}
555
600
  <RvfTarifZonenLayer title={RVF_LAYERS_TITLES.tarifzonen} />
556
601
  <RvfSellingPointsLayer
557
602
  isQueryable={true}
@@ -625,17 +670,19 @@ function RvfMobilityMap({
625
670
  <Station className="relative flex flex-col overflow-y-auto overflow-x-hidden p-2" />
626
671
  </>
627
672
  )}
628
- {hasDetails && selectedFeature && (
629
- <>
630
- <RvfOverlayHeader
631
- onClose={() => {
632
- setSelectedFeature(null);
633
- }}
634
- title={getFeatureInformationTitle(selectedFeature)}
635
- ></RvfOverlayHeader>
636
- <RvfFeatureDetails className="relative flex h-full flex-col gap-4 overflow-y-auto overflow-x-hidden p-4" />
637
- </>
638
- )}
673
+ {hasDetails &&
674
+ selectedFeature &&
675
+ !selectedFeature.get("severity") && (
676
+ <>
677
+ <RvfOverlayHeader
678
+ onClose={() => {
679
+ setSelectedFeature(null);
680
+ }}
681
+ title={getFeatureInformationTitle(selectedFeature)}
682
+ ></RvfOverlayHeader>
683
+ <RvfFeatureDetails className="relative flex h-full flex-col gap-4 overflow-y-auto overflow-x-hidden p-4" />
684
+ </>
685
+ )}
639
686
  {hasToolbar && hasPrint && isExportMenuOpen && (
640
687
  <>
641
688
  <RvfOverlayHeader
@@ -29,10 +29,8 @@ import {
29
29
  SOURCE_STATIONS_BIKE,
30
30
  SOURCE_STATIONS_CAR,
31
31
  SOURCE_STATIONS_CARGO_BIKE,
32
- WFS_BIKE_TYPE,
33
- WFS_CAR_TYPE,
34
- WFS_CARGO_BIKE_TYPE,
35
32
  WFS_FREE_FLOAT_TYPE,
33
+ WFS_STATIONS_TYPE,
36
34
  } from "../utils/constants";
37
35
  import useMapContext from "../utils/hooks/useMapContext";
38
36
  import useRvfContext from "../utils/hooks/useRvfContext";
@@ -87,16 +85,12 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
87
85
  useState<FeatureCollection<Point, SharingStationWFS>>();
88
86
  const [carStationsData, setCarStationsData] =
89
87
  useState<FeatureCollection<Point, SharingStationWFS>>();
90
-
91
88
  const [bikeFreeFloatData, setBikeFreeFloatData] =
92
89
  useState<FeatureCollection<Point, SharingVehicleWFS>>();
93
-
94
90
  const [cargoBikeFreeFloatData, setCargoBikeFreeFloatData] =
95
91
  useState<FeatureCollection<Point, SharingVehicleWFS>>();
96
-
97
92
  const [carFreeFloatData, setCarFreeFloatData] =
98
93
  useState<FeatureCollection<Point, SharingVehicleWFS>>();
99
-
100
94
  const [scooterFreeFloatData, setScooterFreeFloatData] =
101
95
  useState<FeatureCollection<Point, SharingVehicleWFS>>();
102
96
 
@@ -114,25 +108,43 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
114
108
 
115
109
  const fetchData = async () => {
116
110
  // Fill stations data
117
- const bikeStationsData = await fetchStations(
118
- WFS_BIKE_TYPE,
111
+ const stations = await fetchStations(
112
+ WFS_STATIONS_TYPE,
119
113
  null,
120
114
  abortController,
121
115
  );
122
- setBikeStationsData(bikeStationsData);
116
+ const bikeStationsData: FeatureCollection<Point, SharingStationWFS> = {
117
+ features: [],
118
+ type: "FeatureCollection",
119
+ };
120
+ const cargoBikeStationsData: FeatureCollection<Point, SharingStationWFS> =
121
+ {
122
+ features: [],
123
+ type: "FeatureCollection",
124
+ };
125
+ const carStationsData: FeatureCollection<Point, SharingStationWFS> = {
126
+ features: [],
127
+ type: "FeatureCollection",
128
+ };
123
129
 
124
- const cargoBikeStationsData = await fetchStations(
125
- WFS_CARGO_BIKE_TYPE,
126
- null,
127
- abortController,
128
- );
129
- setCargoBikeStationsData(cargoBikeStationsData);
130
+ stations?.features.forEach((station) => {
131
+ if (station.properties.num_bicycles_available !== null) {
132
+ station.properties.num_vehicles_available =
133
+ station.properties.num_bicycles_available;
134
+ bikeStationsData.features.push(station);
135
+ } else if (station.properties.num_cargo_bicycles_available !== null) {
136
+ station.properties.num_vehicles_available =
137
+ station.properties.num_cargo_bicycles_available;
138
+ cargoBikeStationsData.features.push(station);
139
+ } else if (station.properties.num_cars_available !== null) {
140
+ station.properties.num_vehicles_available =
141
+ station.properties.num_cars_available;
142
+ carStationsData.features.push(station);
143
+ }
144
+ });
130
145
 
131
- const carStationsData = await fetchStations(
132
- WFS_CAR_TYPE,
133
- null,
134
- abortController,
135
- );
146
+ setBikeStationsData(bikeStationsData);
147
+ setCargoBikeStationsData(cargoBikeStationsData);
136
148
  setCarStationsData(carStationsData);
137
149
 
138
150
  // Fill free float data