@geops/rvf-mobility-web-component 0.1.28 → 0.1.29

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.
Files changed (30) hide show
  1. package/.vscode/settings.json +2 -0
  2. package/CHANGELOG.md +8 -0
  3. package/docutils.js +1 -1
  4. package/index.html +1 -1
  5. package/index.js +119 -107
  6. package/package.json +1 -1
  7. package/src/NotificationLayer/NotificationLayer.tsx +2 -2
  8. package/src/RouteIcon/RouteIcon.tsx +21 -12
  9. package/src/RvfExportMenu/RvfExportMenu.tsx +2 -0
  10. package/src/RvfFeatureDetails/RVFSellingPointDetails/RVFSellingPointDetails.tsx +47 -0
  11. package/src/RvfFeatureDetails/RVFSellingPointDetails/index.js +1 -0
  12. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +4 -1
  13. package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +4 -1
  14. package/src/RvfMobilityMap/RvfMobilityMap.tsx +55 -12
  15. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +58 -32
  16. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +364 -123
  17. package/src/StationsLayer/StationsLayer.tsx +1 -1
  18. package/src/icons/NoRealtime/NoRealtime.tsx +44 -0
  19. package/src/icons/NoRealtime/index.tsx +1 -0
  20. package/src/icons/NoRealtime/norealtime.svg +6 -0
  21. package/src/utils/constants.ts +22 -1
  22. package/src/utils/exportPdf.ts +3 -3
  23. package/src/utils/fullTrajectoryStyle.ts +0 -1
  24. package/src/utils/getMainColorForVehicle.test.ts +21 -23
  25. package/src/utils/getRadius.ts +25 -24
  26. package/src/utils/hooks/useUpdatePermalink.tsx +5 -0
  27. package/src/utils/realtimeRVFStyle.ts +70 -8
  28. package/src/utils/sharingWFSUtils.ts +46 -16
  29. package/src/RvfSharedMobilityLayerGroup2/RvfSharedMobilityLayerGroup2.tsx +0 -740
  30. package/src/RvfSharedMobilityLayerGroup2/index.tsx +0 -1
@@ -1,13 +1,13 @@
1
1
  import type { Options } from "ol/layer/Group";
2
2
 
3
+ import { FeatureCollection, Point } from "geojson";
4
+ import { GeoJSONSource } from "maplibre-gl";
3
5
  import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
4
- import { Feature } from "ol";
5
- import { Point } from "ol/geom";
6
- import { Group, Vector } from "ol/layer";
7
- import { Cluster } from "ol/source";
8
- import VectorSource from "ol/source/Vector";
6
+ import { Group } from "ol/layer";
7
+ import { unByKey } from "ol/Observable";
8
+ import { transformExtent } from "ol/proj";
9
9
  import { memo } from "preact/compat";
10
- import { useEffect, useMemo } from "preact/hooks";
10
+ import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
11
11
 
12
12
  import Bicycle from "../icons/Bike";
13
13
  import Car from "../icons/Car";
@@ -15,134 +15,382 @@ import CargoBike from "../icons/CargoBike";
15
15
  import Ride from "../icons/Ride";
16
16
  import Scooter from "../icons/Scooter";
17
17
  import {
18
- API_REQUEST_FEATURE_TYPE,
18
+ BIKE_FORM_FACTOR,
19
+ CAR_FORM_FACTOR,
20
+ CARGOBIKE_FORM_FACTOR,
21
+ LAYER_PROP_IS_EXPORTING,
19
22
  RVF_LAYERS_NAMES,
20
23
  RVF_LAYERS_TITLES,
24
+ SCOOTER_FORM_FACTOR,
25
+ SOURCE_FREE_FLOAT_BIKE,
26
+ SOURCE_FREE_FLOAT_CAR,
27
+ SOURCE_FREE_FLOAT_CARGO_BIKE,
28
+ SOURCE_FREE_FLOAT_SCOOTER,
29
+ SOURCE_STATIONS_BIKE,
30
+ SOURCE_STATIONS_CAR,
31
+ SOURCE_STATIONS_CARGO_BIKE,
32
+ WFS_BIKE_TYPE,
33
+ WFS_CAR_TYPE,
34
+ WFS_CARGO_BIKE_TYPE,
35
+ WFS_FREE_FLOAT_TYPE,
21
36
  } from "../utils/constants";
22
- import createFreeFloatMobilityLayer from "../utils/createFreeFloatMobilityLayer";
23
- import createMobiDataBwWfsLayer from "../utils/createMobiDataBwWfsLayer";
24
37
  import useMapContext from "../utils/hooks/useMapContext";
25
38
  import useRvfContext from "../utils/hooks/useRvfContext";
39
+ import {
40
+ fetchSharingWFS,
41
+ SharingStationWFS,
42
+ SharingVehicleWFS,
43
+ } from "../utils/sharingWFSUtils";
26
44
 
27
45
  export type GroupOptions = Options & Record<string, unknown>;
28
46
 
47
+ const fetchStations = async (
48
+ typeName: string,
49
+ feedIds: string[],
50
+ abortController: AbortController,
51
+ ) => {
52
+ const stations = (await fetchSharingWFS(
53
+ typeName,
54
+ abortController,
55
+ )) as FeatureCollection<Point, SharingStationWFS>;
56
+
57
+ const featureCollection = {
58
+ features: stations.features.filter((feature) => {
59
+ return feedIds ? feedIds.includes(feature.properties.feed_id) : true;
60
+ }),
61
+ type: "FeatureCollection",
62
+ };
63
+
64
+ return featureCollection as FeatureCollection<Point, SharingStationWFS>;
65
+ };
66
+
29
67
  function RvfSharedMobilityLayerGroup(props: GroupOptions) {
30
68
  const { baseLayer, map } = useMapContext();
31
69
  const { setSharedMobilityLayerGroup } = useRvfContext();
70
+
71
+ // Layers
72
+ // const [bikeFreloLayer, setBikeFreloLayer] = useState<MaplibreStyleLayer>();
73
+ // const [bikeOthersLayer, setBikeOthersLayer] = useState<MaplibreStyleLayer>();
74
+ // const [cargobikeFreloLayer, setCargobikeFreloLayer] =
75
+ // useState<MaplibreStyleLayer>();
76
+ // const [cargobikeOthersLayer, setCargobikeOthersLayer] =
77
+ // useState<MaplibreStyleLayer>();
78
+ // const [carGrfLayer, setCarGrfLayer] = useState<MaplibreStyleLayer>();
79
+ // const [carNaturLayer, setCarNaturLayer] = useState<MaplibreStyleLayer>();
80
+ // const [carOthersLayer, setCarOthersLayer] = useState<MaplibreStyleLayer>();
81
+ // const [scooterLayer, setScooterLayer] = useState<MaplibreStyleLayer>();
82
+
83
+ // GeoJSON data
84
+ const [bikeStationsData, setBikeStationsData] =
85
+ useState<FeatureCollection<Point, SharingStationWFS>>();
86
+ const [cargoBikeStationsData, setCargoBikeStationsData] =
87
+ useState<FeatureCollection<Point, SharingStationWFS>>();
88
+ const [carStationsData, setCarStationsData] =
89
+ useState<FeatureCollection<Point, SharingStationWFS>>();
90
+
91
+ const [bikeFreeFloatData, setBikeFreeFloatData] =
92
+ useState<FeatureCollection<Point, SharingVehicleWFS>>();
93
+
94
+ const [cargoBikeFreeFloatData, setCargoBikeFreeFloatData] =
95
+ useState<FeatureCollection<Point, SharingVehicleWFS>>();
96
+
97
+ const [carFreeFloatData, setCarFreeFloatData] =
98
+ useState<FeatureCollection<Point, SharingVehicleWFS>>();
99
+
100
+ const [scooterFreeFloatData, setScooterFreeFloatData] =
101
+ useState<FeatureCollection<Point, SharingVehicleWFS>>();
102
+
103
+ const mbMap = useMemo(() => {
104
+ return baseLayer?.mapLibreMap;
105
+ }, [baseLayer?.mapLibreMap]);
106
+
107
+ const updateData = useCallback(() => {
108
+ const abortController = new AbortController();
109
+ const extent = transformExtent(
110
+ map.getView().calculateExtent(),
111
+ "EPSG:3857",
112
+ "EPSG:4326",
113
+ );
114
+
115
+ const fetchData = async () => {
116
+ // Fill stations data
117
+ const bikeStationsData = await fetchStations(
118
+ WFS_BIKE_TYPE,
119
+ null,
120
+ abortController,
121
+ );
122
+ setBikeStationsData(bikeStationsData);
123
+
124
+ const cargoBikeStationsData = await fetchStations(
125
+ WFS_CARGO_BIKE_TYPE,
126
+ null,
127
+ abortController,
128
+ );
129
+ setCargoBikeStationsData(cargoBikeStationsData);
130
+
131
+ const carStationsData = await fetchStations(
132
+ WFS_CAR_TYPE,
133
+ null,
134
+ abortController,
135
+ );
136
+ setCarStationsData(carStationsData);
137
+
138
+ // Fill free float data
139
+ const freeFloatData = (await fetchSharingWFS(
140
+ WFS_FREE_FLOAT_TYPE,
141
+ abortController,
142
+ extent,
143
+ )) as FeatureCollection<Point, SharingVehicleWFS>;
144
+
145
+ const bikes: FeatureCollection<Point, SharingVehicleWFS> = {
146
+ features: [],
147
+ type: "FeatureCollection",
148
+ };
149
+ const cargoBikes: FeatureCollection<Point, SharingVehicleWFS> = {
150
+ features: [],
151
+ type: "FeatureCollection",
152
+ };
153
+ const cars: FeatureCollection<Point, SharingVehicleWFS> = {
154
+ features: [],
155
+ type: "FeatureCollection",
156
+ };
157
+ const scooter: FeatureCollection<Point, SharingVehicleWFS> = {
158
+ features: [],
159
+ type: "FeatureCollection",
160
+ };
161
+
162
+ freeFloatData.features.forEach((feature) => {
163
+ if (feature.properties.form_factor === BIKE_FORM_FACTOR) {
164
+ bikes.features.push(feature);
165
+ } else if (feature.properties.form_factor === CARGOBIKE_FORM_FACTOR) {
166
+ cargoBikes.features.push(feature);
167
+ } else if (feature.properties.form_factor === CAR_FORM_FACTOR) {
168
+ cars.features.push(feature);
169
+ } else if (feature.properties.form_factor === SCOOTER_FORM_FACTOR) {
170
+ scooter.features.push(feature);
171
+ }
172
+ });
173
+
174
+ setBikeFreeFloatData(bikes);
175
+ setCargoBikeFreeFloatData(cargoBikes);
176
+ setCarFreeFloatData(cars);
177
+ setScooterFreeFloatData(scooter);
178
+ };
179
+
180
+ fetchData();
181
+
182
+ return () => {
183
+ abortController.abort();
184
+ };
185
+ }, [map]);
186
+
187
+ // Request all stations and vehicleson each moveend event
188
+ useEffect(() => {
189
+ const key = map?.on("moveend", updateData);
190
+
191
+ // @ts-expect-error - change property can have custom values
192
+ const key2 = map?.on("change:" + LAYER_PROP_IS_EXPORTING, (evt) => {
193
+ // Reupdate the data after finishing eporting the map
194
+ if (!evt.target.get(LAYER_PROP_IS_EXPORTING)) {
195
+ updateData();
196
+ }
197
+ });
198
+ const key3 = map?.once("rendercomplete", updateData);
199
+ return () => {
200
+ unByKey([key, key2, key3]);
201
+ };
202
+ }, [map, updateData, mbMap]);
203
+
204
+ useEffect(() => {
205
+ if (!mbMap || !bikeStationsData) {
206
+ return;
207
+ }
208
+ const source = mbMap.getSource(SOURCE_STATIONS_BIKE);
209
+ (source as GeoJSONSource)?.setData(bikeStationsData);
210
+ }, [bikeStationsData, mbMap]);
211
+
212
+ useEffect(() => {
213
+ if (!mbMap || !cargoBikeStationsData) {
214
+ return;
215
+ }
216
+ const source = mbMap.getSource(SOURCE_STATIONS_CARGO_BIKE);
217
+ (source as GeoJSONSource)?.setData(cargoBikeStationsData);
218
+ }, [mbMap, cargoBikeStationsData]);
219
+
220
+ useEffect(() => {
221
+ if (!mbMap || !carStationsData) {
222
+ return;
223
+ }
224
+ const source = mbMap.getSource(SOURCE_STATIONS_CAR);
225
+ (source as GeoJSONSource)?.setData(carStationsData);
226
+ }, [mbMap, carStationsData]);
227
+
228
+ useEffect(() => {
229
+ if (!mbMap || !bikeFreeFloatData) {
230
+ return;
231
+ }
232
+ const source = mbMap.getSource(SOURCE_FREE_FLOAT_BIKE);
233
+ (source as GeoJSONSource)?.setData(bikeFreeFloatData);
234
+ }, [mbMap, bikeFreeFloatData]);
235
+
236
+ useEffect(() => {
237
+ if (!mbMap || !cargoBikeFreeFloatData) {
238
+ return;
239
+ }
240
+ const source = mbMap.getSource(SOURCE_FREE_FLOAT_CARGO_BIKE);
241
+ (source as GeoJSONSource)?.setData(cargoBikeFreeFloatData);
242
+ }, [mbMap, cargoBikeFreeFloatData]);
243
+
244
+ useEffect(() => {
245
+ if (!mbMap || !carFreeFloatData) {
246
+ return;
247
+ }
248
+ const source = mbMap.getSource(SOURCE_FREE_FLOAT_CAR);
249
+ (source as GeoJSONSource)?.setData(carFreeFloatData);
250
+ }, [mbMap, carFreeFloatData]);
251
+
252
+ useEffect(() => {
253
+ if (!mbMap || !scooterFreeFloatData) {
254
+ return;
255
+ }
256
+ const source = mbMap.getSource(SOURCE_FREE_FLOAT_SCOOTER);
257
+ (source as GeoJSONSource)?.setData(scooterFreeFloatData);
258
+ }, [mbMap, scooterFreeFloatData]);
259
+
260
+ // useEffect(() => {
261
+ // const mbMap = scooterLayer?.maplibreLayer?.mapLibreMap;
262
+ // if (!mbMap || !scooterData) {
263
+ // return;
264
+ // }
265
+ // const source = mbMap.getSource(SCOOTER_SOURCE_ID);
266
+ // (source as GeoJSONSource)?.setData(scooterData);
267
+ // }, [scooterData, scooterLayer?.maplibreLayer?.mapLibreMap]);
268
+
32
269
  const group = useMemo(() => {
33
270
  if (!baseLayer) {
34
271
  return null;
35
272
  }
36
273
 
37
- const sharingBicycle = new Group({
38
- layers: [
39
- new MaplibreStyleLayer({
40
- isQueryable: true,
41
- layersFilter: ({ metadata }) => {
42
- return metadata?.["rvf.filter"] === "bike";
43
- },
44
- maplibreLayer: baseLayer,
45
- maxZoom: 18,
46
- }),
47
- createMobiDataBwWfsLayer(
48
- API_REQUEST_FEATURE_TYPE.stations.bike,
49
- "green",
50
- ),
51
- createFreeFloatMobilityLayer(
52
- API_REQUEST_FEATURE_TYPE.freeFloat.bike,
53
- "green",
54
- "bicycle",
55
- ),
56
- ],
57
- name: RVF_LAYERS_NAMES.fahrrad,
274
+ const bikeFrelo = new MaplibreStyleLayer({
275
+ isQueryable: true,
276
+ maplibreLayer: baseLayer,
277
+ name: RVF_LAYERS_NAMES.bikeFrelo,
278
+ styleLayersFilter: ({ metadata }) => {
279
+ return metadata?.["rvf.filter"] === "bike.frelo";
280
+ },
281
+
58
282
  title: (
59
283
  <div className="flex items-center justify-between gap-2">
60
- {RVF_LAYERS_TITLES.fahrrad}
284
+ {"Frelo"}
61
285
  <Bicycle />
62
286
  </div>
63
287
  ),
64
- } as GroupOptions);
288
+ });
289
+ // setBikeFreloLayer(bikeFrelo);
65
290
 
66
- const sharingCar = new Group({
67
- layers: [
68
- new MaplibreStyleLayer({
69
- isQueryable: true,
70
- layersFilter: ({ metadata }) => {
71
- return metadata?.["rvf.filter"] === "car";
72
- },
73
- maplibreLayer: baseLayer,
74
- maxZoom: 18,
75
- }),
76
- createMobiDataBwWfsLayer(
77
- API_REQUEST_FEATURE_TYPE.stations.car,
78
- "green",
79
- ),
80
- createFreeFloatMobilityLayer(
81
- API_REQUEST_FEATURE_TYPE.freeFloat.car,
82
- "green",
83
- "car",
84
- ),
85
- ],
86
- name: RVF_LAYERS_NAMES.auto,
291
+ const bikeOthers = new MaplibreStyleLayer({
292
+ isQueryable: true,
293
+ maplibreLayer: baseLayer,
294
+ name: RVF_LAYERS_NAMES.bikeOthers,
295
+ styleLayersFilter: ({ metadata }) => {
296
+ return metadata?.["rvf.filter"] === "bike.other";
297
+ },
87
298
  title: (
88
299
  <div className="flex items-center justify-between gap-2">
89
- {RVF_LAYERS_TITLES.auto}
90
- <Car />
300
+ {"Fahrrad, andere Anbieter"}
301
+ <Bicycle />
91
302
  </div>
92
303
  ),
93
- } as GroupOptions);
304
+ });
305
+ // setBikeOthersLayer(bikeOthers);
94
306
 
95
- const sharingCargoBicycle = new Group({
96
- layers: [
97
- new MaplibreStyleLayer({
98
- isQueryable: true,
99
- layersFilter: ({ metadata }) => {
100
- return metadata?.["rvf.filter"] === "cargo_bike";
101
- },
102
- maplibreLayer: baseLayer,
103
- maxZoom: 18,
104
- }),
105
- createMobiDataBwWfsLayer(
106
- API_REQUEST_FEATURE_TYPE.stations.cargoBike,
107
- "green",
108
- ),
109
- createFreeFloatMobilityLayer(
110
- API_REQUEST_FEATURE_TYPE.freeFloat.cargoBike,
111
- "green",
112
- "cargo_bicycle",
113
- ),
114
- ],
115
- name: RVF_LAYERS_NAMES.cargobike,
307
+ const cargobikeFrelo = new MaplibreStyleLayer({
308
+ isQueryable: true,
309
+ layersFilter: ({ metadata }) => {
310
+ return metadata?.["rvf.filter"] === "cargo_bike.frelo";
311
+ },
312
+ maplibreLayer: baseLayer,
313
+ name: RVF_LAYERS_NAMES.cargobikeFrelo,
116
314
  title: (
117
315
  <div className="flex items-center justify-between gap-2">
118
- {RVF_LAYERS_TITLES.cargobike}
316
+ {"Lastenfrelo"}
119
317
  <CargoBike />
120
318
  </div>
121
319
  ),
122
- } as GroupOptions);
320
+ });
321
+ // setCargobikeFreloLayer(cargobikeFrelo);
123
322
 
124
- const sharingScooter = new Group({
125
- layers: [
126
- // No scooter station data available
127
- // new MaplibreStyleLayer({
128
- // isQueryable: true,
129
- // layersFilter: ({ metadata }) => {
130
- // return metadata?.["rvf.filter"] === "scooter";
131
- // },
132
- // maplibreLayer: baseLayer,
133
- // }),
134
-
135
- // https://www.mobidata-bw.de/dataset/escootch
136
- createMobiDataBwWfsLayer(
137
- API_REQUEST_FEATURE_TYPE.stations.scooter,
138
- "green",
139
- ),
140
- createFreeFloatMobilityLayer(
141
- API_REQUEST_FEATURE_TYPE.freeFloat.scooter,
142
- "green",
143
- "scooter",
144
- ),
145
- ],
323
+ const cargobikeOthers = new MaplibreStyleLayer({
324
+ isQueryable: true,
325
+ layersFilter: ({ metadata }) => {
326
+ return metadata?.["rvf.filter"] === "cargo_bike.other";
327
+ },
328
+ maplibreLayer: baseLayer,
329
+ name: RVF_LAYERS_NAMES.cargobikeOthers,
330
+ title: (
331
+ <div className="flex items-center justify-between gap-2">
332
+ {"Lastenrad, andere Anbieter"}
333
+ <CargoBike />
334
+ </div>
335
+ ),
336
+ });
337
+ // setCargobikeOthersLayer(cargobikeOthers);
338
+
339
+ const carGrf = new MaplibreStyleLayer({
340
+ isQueryable: true,
341
+ layersFilter: ({ metadata }) => {
342
+ return metadata?.["rvf.filter"] === "car.grueneflotte";
343
+ },
344
+ maplibreLayer: baseLayer,
345
+ name: RVF_LAYERS_NAMES.carGrf,
346
+ title: (
347
+ <div className="flex items-center justify-between gap-2">
348
+ {"Grüne Flotte"}
349
+ <Car />
350
+ </div>
351
+ ),
352
+ });
353
+ // setCarGrfLayer(carGrf);
354
+
355
+ const carNatur = new MaplibreStyleLayer({
356
+ isQueryable: true,
357
+ layersFilter: ({ metadata }) => {
358
+ return metadata?.["rvf.filter"] === "car.naturenergy";
359
+ },
360
+ maplibreLayer: baseLayer,
361
+ name: RVF_LAYERS_NAMES.carNatur,
362
+ title: (
363
+ <div className="flex items-center justify-between gap-2">
364
+ {"Naturenergie-Sharing"}
365
+ <Car />
366
+ </div>
367
+ ),
368
+ });
369
+ // setCarNaturLayer(carNatur);
370
+
371
+ const carOthers = new MaplibreStyleLayer({
372
+ isQueryable: true,
373
+ layersFilter: ({ metadata }) => {
374
+ return metadata?.["rvf.filter"] === "car.other";
375
+ },
376
+ maplibreLayer: baseLayer,
377
+
378
+ name: RVF_LAYERS_NAMES.carOthers,
379
+ title: (
380
+ <div className="flex items-center justify-between gap-2">
381
+ {"Auto, andere Anbieter"}
382
+ <Car />
383
+ </div>
384
+ ),
385
+ });
386
+ // setCarOthersLayer(carOthers);
387
+
388
+ const scooter = new MaplibreStyleLayer({
389
+ isQueryable: true,
390
+ layersFilter: ({ metadata }) => {
391
+ return metadata?.["rvf.filter"] === "scooter";
392
+ },
393
+ maplibreLayer: baseLayer,
146
394
  name: RVF_LAYERS_NAMES.eroller,
147
395
  title: (
148
396
  <div className="flex items-center justify-between gap-2">
@@ -150,10 +398,11 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
150
398
  <Scooter />
151
399
  </div>
152
400
  ),
153
- } as GroupOptions);
401
+ });
402
+ // setScooterLayer(scooter);
154
403
 
155
- const sharingHitchHiking = new MaplibreStyleLayer({
156
- isQueryable: true,
404
+ const hitchHiking = new MaplibreStyleLayer({
405
+ isQueryable: false,
157
406
  layersFilter: ({ metadata }) => {
158
407
  return metadata?.["rvf.filter"] === "hitchhiking";
159
408
  },
@@ -169,11 +418,15 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
169
418
 
170
419
  return new Group({
171
420
  layers: [
172
- sharingBicycle,
173
- sharingCar,
174
- sharingCargoBicycle,
175
- sharingScooter,
176
- sharingHitchHiking,
421
+ bikeFrelo,
422
+ bikeOthers,
423
+ cargobikeFrelo,
424
+ cargobikeOthers,
425
+ carGrf,
426
+ carNatur,
427
+ carOthers,
428
+ scooter,
429
+ hitchHiking,
177
430
  ],
178
431
  ...props,
179
432
  } as GroupOptions);
@@ -182,24 +435,12 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
182
435
  // Reload features every minute
183
436
  useEffect(() => {
184
437
  const interval = window.setInterval(() => {
185
- group.getLayers().forEach((layer: Group) => {
186
- layer.getLayers?.().forEach((layer: MaplibreStyleLayer | Vector) => {
187
- const source = layer.getSource();
188
- source?.refresh();
189
-
190
- // For cluster source
191
- (
192
- (source as Cluster<Feature<Point>>)?.getSource?.() as VectorSource<
193
- Feature<Point>
194
- >
195
- )?.refresh();
196
- });
197
- });
438
+ updateData();
198
439
  }, 60000);
199
440
  return () => {
200
441
  window.clearInterval(interval);
201
442
  };
202
- }, [group]);
443
+ }, [group, updateData]);
203
444
 
204
445
  useEffect(() => {
205
446
  setSharedMobilityLayerGroup(group);
@@ -15,7 +15,7 @@ function StationsLayer(props: MaplibreStyleLayerOptions) {
15
15
  }
16
16
  return new MaplibreStyleLayer({
17
17
  layersFilter: ({ metadata }) => {
18
- return metadata?.["tralis.variable"] === "station";
18
+ return metadata?.["general.filter"] === "stations";
19
19
  },
20
20
  maplibreLayer: baseLayer,
21
21
  name: RVF_LAYERS_NAMES.haltestellen,
@@ -0,0 +1,44 @@
1
+ import { SVGProps } from "preact/compat";
2
+
3
+ function NoRealtime(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg
6
+ height="24"
7
+ viewBox="0 0 42 42"
8
+ width="24"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ {...props}
11
+ >
12
+ <path
13
+ d="M40,20A20,20,0,1,1,20,0,20,20,0,0,1,40,20Z"
14
+ data-name="Pfad 1"
15
+ fill="#353535"
16
+ id="Pfad_1"
17
+ transform="translate(0 0)"
18
+ />
19
+ <path
20
+ d="M25.481,11.843a9.993,9.993,0,0,0-10,9.992c0,7.773,10,14.8,10,14.8s9.992-7.5,9.992-14.8a9.991,9.991,0,0,0-9.992-9.992m0,14.662a4.924,4.924,0,1,1,4.924-4.924A4.93,4.93,0,0,1,25.482,26.5"
21
+ data-name="Pfad 2"
22
+ fill="#fff"
23
+ id="Pfad_2"
24
+ transform="translate(-5.477 -4.19)"
25
+ />
26
+ <path
27
+ d="M12.015,8.124,38.081,34.031,35.4,36.536,9.06,10.527Z"
28
+ data-name="Pfad 3"
29
+ fill="#ec0016"
30
+ id="Pfad_3"
31
+ transform="translate(-3.206 -2.874)"
32
+ />
33
+ <path
34
+ d="M20,3.231A16.769,16.769,0,1,1,3.231,20,16.788,16.788,0,0,1,20,3.231M20,0A20,20,0,1,0,40,20,20,20,0,0,0,20,0"
35
+ data-name="Pfad 4"
36
+ fill="#353535"
37
+ id="Pfad_4"
38
+ transform="translate(0 0)"
39
+ />
40
+ </svg>
41
+ );
42
+ }
43
+
44
+ export default NoRealtime;
@@ -0,0 +1 @@
1
+ export { default } from "./NoRealtime";
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 42 42" width="42" height="42">
2
+ <path id="Pfad_1" data-name="Pfad 1" d="M40,20A20,20,0,1,1,20,0,20,20,0,0,1,40,20Z" transform="translate(0 0)" fill="#353535"/>
3
+ <path id="Pfad_2" data-name="Pfad 2" d="M25.481,11.843a9.993,9.993,0,0,0-10,9.992c0,7.773,10,14.8,10,14.8s9.992-7.5,9.992-14.8a9.991,9.991,0,0,0-9.992-9.992m0,14.662a4.924,4.924,0,1,1,4.924-4.924A4.93,4.93,0,0,1,25.482,26.5" transform="translate(-5.477 -4.19)" fill="#fff"/>
4
+ <path id="Pfad_3" data-name="Pfad 3" d="M12.015,8.124,38.081,34.031,35.4,36.536,9.06,10.527Z" transform="translate(-3.206 -2.874)" fill="#ec0016"/>
5
+ <path id="Pfad_4" data-name="Pfad 4" d="M20,3.231A16.769,16.769,0,1,1,3.231,20,16.788,16.788,0,0,1,20,3.231M20,0A20,20,0,1,0,40,20,20,20,0,0,0,20,0" transform="translate(0 0)" fill="#353535"/>
6
+ </svg>
@@ -1,3 +1,4 @@
1
+ import { FeatureCollection } from "geojson";
1
2
  import { fromLonLat, toLonLat, transformExtent } from "ol/proj";
2
3
 
3
4
  import callBike from "../logos/callabike_logo.png";
@@ -60,6 +61,26 @@ export const RVF_LAYERS_NAMES = {
60
61
  verkaufsstellen: "verkaufsstellen",
61
62
  };
62
63
 
64
+ export const WFS_CARGO_BIKE_TYPE = "sharing_stations_cargo_bicycle";
65
+ export const WFS_CAR_TYPE = "sharing_stations_car";
66
+ export const WFS_BIKE_TYPE = "sharing_stations_bicycle";
67
+ export const WFS_FREE_FLOAT_TYPE = "sharing_vehicles";
68
+
69
+ export const SOURCE_STATIONS_BIKE = WFS_BIKE_TYPE;
70
+ export const SOURCE_STATIONS_CARGO_BIKE = WFS_CARGO_BIKE_TYPE;
71
+ export const SOURCE_STATIONS_CAR = WFS_CAR_TYPE;
72
+
73
+ export const SOURCE_FREE_FLOAT_BIKE = "sharing_freefloat_bicycle";
74
+ export const SOURCE_FREE_FLOAT_CARGO_BIKE = "sharing_freefloat_cargo_bicycle";
75
+ export const SOURCE_FREE_FLOAT_CAR = "sharing_freefloat_car";
76
+ export const SOURCE_FREE_FLOAT_SCOOTER = "sharing_freefloat_scooter";
77
+ export const SOURCE_SELECT = "sharing_select";
78
+
79
+ export const EMPTY_FEATURE_COLLECTION: FeatureCollection = {
80
+ features: [],
81
+ type: "FeatureCollection",
82
+ };
83
+
63
84
  export const API_REQUEST_FEATURE_TYPE = {
64
85
  freeFloat: {
65
86
  bike: "sharing_vehicles",
@@ -96,7 +117,7 @@ export const TITLE_BY_CATEGORY = {
96
117
  export const PROVIDER_BY_FEED_ID = {
97
118
  callabike_ice: "Call a Bike",
98
119
  flinkster_carsharing: "Flinkster",
99
- gruene_flotte_freiburg: "Die Grüne\nFlotte",
120
+ "gruene-flotte_freiburg": "Die Grüne\nFlotte",
100
121
  lastenvelo_fr: "LastenVelo",
101
122
  naturenergie_sharing: "naturenergie\nsharing",
102
123
  nextbike_df: "Frelo",
@@ -617,12 +617,12 @@ async function exportPdf(
617
617
 
618
618
  document.body.style.overflow = "auto";
619
619
 
620
- // Undo what you have done on onBefore
621
- onAfter?.(map, layers);
622
-
623
620
  // Reset map to previous state
624
621
  map.setLayers(layers);
625
622
 
623
+ // Undo what you have done on onBefore
624
+ onAfter?.(map, layers);
625
+
626
626
  if (placeholderCanvas) {
627
627
  map.once("rendercomplete", () => {
628
628
  placeholderCanvas.remove();
@@ -29,7 +29,6 @@ const fullTrajectoryStyle = (
29
29
  if (stroke && stroke[0] !== "#") {
30
30
  stroke = `#${stroke}`;
31
31
  }
32
- console.log(feature.getProperties());
33
32
  lineColor =
34
33
  stroke || options?.getBgColor(type, { name: feature.get("line_name") });
35
34