@geops/rvf-mobility-web-component 0.1.15 → 0.1.17

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 (51) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/docutils.js +8 -2
  3. package/index.html +14 -2
  4. package/index.js +152 -78
  5. package/package.json +2 -1
  6. package/src/RealtimeLayer/RealtimeLayer.tsx +2 -0
  7. package/src/RvfExportMenu/RvfExportMenu.tsx +12 -1
  8. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -4
  9. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +53 -0
  10. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/index.tsx +1 -0
  11. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +123 -0
  12. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +32 -0
  13. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/index.tsx +1 -0
  14. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/index.tsx +1 -0
  15. package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -0
  16. package/src/RvfMobilityMap/RvfMobilityMap.tsx +123 -34
  17. package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +1 -1
  18. package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -0
  19. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +64 -0
  20. package/src/RvfSelectedFeatureHighlightLayer/index.tsx +1 -0
  21. package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -0
  22. package/src/RvfShare/RvfShare.tsx +1 -1
  23. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +59 -23
  24. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +2 -10
  25. package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -0
  26. package/src/RvfTopics/RvfTopics.tsx +40 -8
  27. package/src/StationsLayer/StationsLayer.tsx +2 -0
  28. package/src/icons/Bike/rvf_shared_bike.svg +2 -2
  29. package/src/icons/Car/rvf_shared_car.svg +3 -3
  30. package/src/icons/CargoBike/rvf_shared_cargo_bike.svg +3 -3
  31. package/src/icons/Scooter/rvf_shared_scooter.svg +2 -2
  32. package/src/index.tsx +4 -0
  33. package/src/logos/callabike_logo.png +0 -0
  34. package/src/logos/flinkster_logo.png +0 -0
  35. package/src/logos/gruene_flotte_logo.png +0 -0
  36. package/src/logos/logo_frelo_web_rgb.png +0 -0
  37. package/src/logos/natur_energie_logo.png +0 -0
  38. package/src/logos/yoio_logo.png +0 -0
  39. package/src/logos/zeus_logo.png +0 -0
  40. package/src/utils/applyInitialLayerVisibility.ts +41 -0
  41. package/src/utils/constants.ts +63 -0
  42. package/src/utils/{createSharedMobilityLayer.ts → createFreeFloatMobilityLayer.ts} +41 -69
  43. package/src/utils/createMobiDataBwWfsLayer.ts +108 -67
  44. package/src/utils/exportPdf.ts +1 -4
  45. package/src/utils/getFeatureInformationTitle.ts +37 -0
  46. package/src/utils/getLayersAsFlatArray.ts +22 -0
  47. package/src/utils/getLinkByDevice.ts +23 -0
  48. package/src/utils/getPermalinkParameters.ts +27 -0
  49. package/src/utils/hooks/useInitialLayersVisiblity.tsx +28 -0
  50. package/src/utils/hooks/useMapContext.tsx +2 -0
  51. package/src/utils/hooks/useUpdatePermalink.tsx +44 -11
@@ -39,6 +39,7 @@ import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
39
39
  import Modal from "../RvfModal";
40
40
  import RvfOverlayHeader from "../RvfOverlayHeader";
41
41
  import RvfPoisLayer from "../RvfPoisLayer";
42
+ import RvfSelectedFeatureHighlightLayer from "../RvfSelectedFeatureHighlightLayer";
42
43
  import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
43
44
  import RvfShare from "../RvfShare";
44
45
  import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
@@ -53,8 +54,10 @@ import Station from "../Station";
53
54
  import StationsLayer from "../StationsLayer";
54
55
  // @ts-expect-error bad type definition
55
56
  import tailwind from "../style.css";
56
- import { RVF_EXTENT_3857 } from "../utils/constants";
57
+ import { RVF_EXTENT_3857, RVF_LAYERS_TITLES } from "../utils/constants";
58
+ import getFeatureInformationTitle from "../utils/getFeatureInformationTitle";
57
59
  import { I18nContext } from "../utils/hooks/useI18n";
60
+ import useInitialLayersVisiblity from "../utils/hooks/useInitialLayersVisiblity";
58
61
  import { MapContext } from "../utils/hooks/useMapContext";
59
62
  import { RvfContext } from "../utils/hooks/useRvfContext";
60
63
  import useUpdatePermalink from "../utils/hooks/useUpdatePermalink";
@@ -63,7 +66,13 @@ import MobilityEvent from "../utils/MobilityEvent";
63
66
  // @ts-expect-error bad type definition
64
67
  import style from "./index.css";
65
68
 
66
- export type RvfMobilityMapProps = { toolbar: string } & MobilityMapProps;
69
+ export type RvfMobilityMapProps = {
70
+ layers: string; // list of visible layers on load
71
+ layertree: string;
72
+ print: string;
73
+ share: string;
74
+ toolbar: string;
75
+ } & MobilityMapProps;
67
76
 
68
77
  const bbox = RVF_EXTENT_3857.join(",");
69
78
 
@@ -87,6 +96,8 @@ function RvfMobilityMap({
87
96
  center = null,
88
97
  extent = bbox,
89
98
  geolocation = "true",
99
+ layers = null,
100
+ layertree = "true",
90
101
  mapsurl = "https://maps.geops.io",
91
102
  maxextent = bbox,
92
103
  maxzoom = "20",
@@ -97,9 +108,11 @@ function RvfMobilityMap({
97
108
  notificationbeforelayerid = null,
98
109
  notificationurl = null,
99
110
  permalink = "false",
111
+ print = "true",
100
112
  realtime = "true",
101
113
  realtimeurl = "wss://api.geops.io/tracker-ws/v1/ws",
102
114
  search = "false",
115
+ share = "true",
103
116
  stopsurl = "https://api.geops.io/stops/v1/",
104
117
  tenant = null,
105
118
  toolbar = "true",
@@ -129,7 +142,42 @@ function RvfMobilityMap({
129
142
  useState<MaplibreStyleLayer>();
130
143
  const [sharedMobilityLayerGroup, setSharedMobilityLayerGroup] =
131
144
  useState<Group>();
132
- const [hasToolbar] = useState<boolean>(toolbar === "true");
145
+
146
+ // Convert string boolean to boolean
147
+ const hasToolbar = useMemo(() => {
148
+ return toolbar === "true";
149
+ }, [toolbar]);
150
+
151
+ const hasLayerTree = useMemo(() => {
152
+ return layertree === "true";
153
+ }, [layertree]);
154
+
155
+ const hasRealtime = useMemo(() => {
156
+ return realtime === "true";
157
+ }, [realtime]);
158
+
159
+ const hasNotification = useMemo(() => {
160
+ return notification === "true";
161
+ }, [notification]);
162
+
163
+ const hasGeolocation = useMemo(() => {
164
+ return geolocation === "true";
165
+ }, [geolocation]);
166
+
167
+ const hasSearch = useMemo(() => {
168
+ return search === "true";
169
+ }, [search]);
170
+
171
+ const hasShare = useMemo(() => {
172
+ return share === "true";
173
+ }, [share]);
174
+
175
+ const hasPrint = useMemo(() => {
176
+ return print === "true";
177
+ }, [print]);
178
+
179
+ // Apply initial visibility of layers
180
+ useInitialLayersVisiblity(map, layers);
133
181
 
134
182
  // TODO: this should be removed. The parent application should be responsible to do this
135
183
  // or we should find something that fit more usecases
@@ -146,6 +194,7 @@ function RvfMobilityMap({
146
194
  geolocation,
147
195
  isFollowing,
148
196
  isTracking,
197
+ layers,
149
198
  map,
150
199
  mapsurl,
151
200
  maxextent,
@@ -190,6 +239,7 @@ function RvfMobilityMap({
190
239
  map,
191
240
  mapsurl,
192
241
  maxextent,
242
+ layers,
193
243
  maxzoom,
194
244
  minzoom,
195
245
  mots,
@@ -217,6 +267,8 @@ function RvfMobilityMap({
217
267
  center,
218
268
  extent,
219
269
  geolocation,
270
+ layers,
271
+ layertree,
220
272
  mapsurl,
221
273
  maxextent,
222
274
  maxzoom,
@@ -226,9 +278,11 @@ function RvfMobilityMap({
226
278
  notificationat,
227
279
  notificationbeforelayerid,
228
280
  notificationurl,
281
+ print,
229
282
  realtime,
230
283
  realtimeurl,
231
284
  search,
285
+ share,
232
286
  tenant,
233
287
  toolbar,
234
288
  zoom,
@@ -236,8 +290,10 @@ function RvfMobilityMap({
236
290
  );
237
291
  }, [
238
292
  baselayer,
293
+ layers,
239
294
  center,
240
295
  geolocation,
296
+ layertree,
241
297
  toolbar,
242
298
  mapsurl,
243
299
  maxzoom,
@@ -254,6 +310,8 @@ function RvfMobilityMap({
254
310
  zoom,
255
311
  extent,
256
312
  maxextent,
313
+ print,
314
+ share,
257
315
  ]);
258
316
 
259
317
  const rvfContextValue = useMemo(() => {
@@ -261,6 +319,7 @@ function RvfMobilityMap({
261
319
  isExportMenuOpen,
262
320
  isLayerTreeOpen,
263
321
  isShareMenuOpen,
322
+ layertree,
264
323
  lineNetworkPlanLayer,
265
324
  poisLayer,
266
325
  selectedFeature,
@@ -281,6 +340,7 @@ function RvfMobilityMap({
281
340
  };
282
341
  }, [
283
342
  isExportMenuOpen,
343
+ layertree,
284
344
  isLayerTreeOpen,
285
345
  isShareMenuOpen,
286
346
  lineNetworkPlanLayer,
@@ -399,25 +459,33 @@ function RvfMobilityMap({
399
459
  <Map className="relative flex-1 overflow-visible ">
400
460
  <BaseLayer {...baseLayerProps} />
401
461
  <SingleClickListener />
462
+ <RvfSelectedFeatureHighlightLayer />
402
463
 
403
- {realtime === "true" && <RealtimeLayer title="Echtzeit" />}
464
+ {hasRealtime && (
465
+ <RealtimeLayer title={RVF_LAYERS_TITLES.echtzeit} />
466
+ )}
404
467
  {
405
468
  <StationsLayer
406
469
  minZoom={10}
407
- title="Haltestellen"
470
+ title={RVF_LAYERS_TITLES.haltestellen}
408
471
  {...stationsLayerProps}
409
472
  />
410
473
  }
411
- {notification === "true" && <NotificationLayer />}
412
- <RvfSellingPointsLayer isQueryable title="Verkaufsstellen" />
474
+ {hasNotification && <NotificationLayer />}
475
+ <RvfTarifZonenLayer title={RVF_LAYERS_TITLES.tarifzonen} />
476
+ <RvfSellingPointsLayer
477
+ isQueryable
478
+ title={RVF_LAYERS_TITLES.verkaufsstellen}
479
+ />
413
480
  <RvfLineNetworkPlanLayer
414
481
  isQueryable
415
482
  minZoom={10}
416
- title="Liniennetz"
483
+ title={RVF_LAYERS_TITLES.liniennetz}
484
+ />
485
+ <RvfPoisLayer title={RVF_LAYERS_TITLES.pois} />
486
+ <RvfSharedMobilityLayerGroup
487
+ title={RVF_LAYERS_TITLES.sharedMobility}
417
488
  />
418
- <RvfTarifZonenLayer isQueryable title="Tarifzonen" />
419
- <RvfPoisLayer title="POIs" />
420
- <RvfSharedMobilityLayerGroup title="Shared Mobility" />
421
489
 
422
490
  <div className="absolute inset-x-2 bottom-2 z-10 flex items-end justify-between gap-2 text-[10px]">
423
491
  <ScaleLine className="bg-slate-50/70" />
@@ -428,11 +496,11 @@ function RvfMobilityMap({
428
496
  </div>
429
497
 
430
498
  <div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
431
- {geolocation === "true" && <GeolocationButton />}
432
- {!hasToolbar && <RvfExportMenuButton />}
499
+ {hasGeolocation && <GeolocationButton />}
500
+ {!hasToolbar && hasPrint && <RvfExportMenuButton />}
433
501
  </div>
434
502
 
435
- {search === "true" && (
503
+ {hasSearch && (
436
504
  <div className="absolute left-2 right-12 top-2 z-10 flex max-h-[90%] min-w-64 max-w-96 flex-col">
437
505
  <Search />
438
506
  </div>
@@ -442,7 +510,7 @@ function RvfMobilityMap({
442
510
  <RvfZoomButtons />
443
511
  </div>
444
512
 
445
- {!hasToolbar && (
513
+ {!hasToolbar && hasLayerTree && (
446
514
  <RvfFloatingMenu
447
515
  isOpen={isLayerTreeOpen}
448
516
  onClick={onLayerTreeMenuClick}
@@ -453,16 +521,16 @@ function RvfMobilityMap({
453
521
  )}
454
522
  </Map>
455
523
  <Overlay
456
- className={"z-50"}
524
+ className={"z-50 bg-white"}
457
525
  ScrollableHandlerProps={scrollableHandlerProps}
458
526
  >
459
- {realtime === "true" && trainId && (
527
+ {hasRealtime && trainId && (
460
528
  <>
461
529
  <RvfOverlayHeader
462
530
  onClose={() => {
463
531
  setTrainId(null);
464
532
  }}
465
- title="Train"
533
+ title={RVF_LAYERS_TITLES.echtzeit}
466
534
  ></RvfOverlayHeader>
467
535
  <RouteSchedule className="relative overflow-y-auto overflow-x-hidden" />
468
536
  </>
@@ -484,23 +552,23 @@ function RvfMobilityMap({
484
552
  onClose={() => {
485
553
  setSelectedFeature(null);
486
554
  }}
487
- title="Informations"
555
+ title={getFeatureInformationTitle(selectedFeature)}
488
556
  ></RvfOverlayHeader>
489
- <RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
557
+ <RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden px-2 pt-2" />
490
558
  </>
491
559
  )}
492
- {hasToolbar && isExportMenuOpen && (
560
+ {hasToolbar && hasPrint && isExportMenuOpen && (
493
561
  <>
494
562
  <RvfOverlayHeader
495
563
  onClose={() => {
496
564
  setIsExportMenuOpen(false);
497
565
  }}
498
- title="Export"
566
+ title="Drücken"
499
567
  ></RvfOverlayHeader>
500
- <RvfExportMenu className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
568
+ <RvfExportMenu className="relative flex flex-col gap-8 overflow-y-auto overflow-x-hidden px-4 py-6" />
501
569
  </>
502
570
  )}
503
- {hasToolbar && isLayerTreeOpen && (
571
+ {hasToolbar && hasLayerTree && isLayerTreeOpen && (
504
572
  <>
505
573
  <RvfOverlayHeader
506
574
  onClose={() => {
@@ -508,33 +576,54 @@ function RvfMobilityMap({
508
576
  }}
509
577
  title="Layers"
510
578
  ></RvfOverlayHeader>
511
- <Topics className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
579
+ <Topics className=" relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
512
580
  </>
513
581
  )}
514
- {hasToolbar && isShareMenuOpen && (
582
+ {hasToolbar && hasShare && isShareMenuOpen && (
515
583
  <>
516
584
  <RvfOverlayHeader
517
585
  onClose={() => {
518
- setIsLayerTreeOpen(false);
586
+ setIsShareMenuOpen(false);
519
587
  }}
520
588
  title="Share"
521
589
  ></RvfOverlayHeader>
522
- <RvfShare className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
590
+ <RvfShare className="relative flex h-full flex-col gap-8 overflow-y-auto overflow-x-hidden px-4 py-6" />
523
591
  </>
524
592
  )}
525
593
  </Overlay>
526
594
 
527
595
  {hasToolbar && (
528
- <div className={"overflow-x-hidden border-r"}>
529
- <RvfLayerTreeButton className={"border-none"} />
530
- <RvfExportMenuButton className={"border-none"} />
531
- <RvfShareMenuButton className={"border-none"} />
596
+ <div
597
+ className={
598
+ "z-[100] flex justify-around overflow-x-hidden border-t bg-white p-1 @lg/main:block @lg/main:border-r @lg/main:p-0 "
599
+ }
600
+ >
601
+ {hasLayerTree && (
602
+ <RvfLayerTreeButton
603
+ className={"border-none"}
604
+ title="Layers"
605
+ />
606
+ )}
607
+
608
+ {hasPrint && (
609
+ <RvfExportMenuButton
610
+ className={"border-none"}
611
+ title="Drücken"
612
+ />
613
+ )}
614
+
615
+ {hasShare && (
616
+ <RvfShareMenuButton
617
+ className={"border-none"}
618
+ title="Share"
619
+ />
620
+ )}
532
621
  </div>
533
622
  )}
534
623
 
535
- {!hasToolbar && isExportMenuOpen && (
624
+ {!hasToolbar && hasPrint && isExportMenuOpen && (
536
625
  <Modal onClose={onExportMenuClose}>
537
- <RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden" />
626
+ <RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
538
627
  </Modal>
539
628
  )}
540
629
  </div>
@@ -22,7 +22,7 @@ function RvfOverlayHeader({
22
22
  return (
23
23
  <div
24
24
  className={twMerge(
25
- "flex flex-row items-center justify-between gap-2 p-2 border-b" +
25
+ "flex flex-row items-center justify-between gap-2 p-2 border-b pl-4" +
26
26
  (className || ""),
27
27
  )}
28
28
  {...props}
@@ -3,6 +3,7 @@ import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/Maplibr
3
3
  import { memo } from "preact/compat";
4
4
  import { useEffect, useMemo } from "preact/hooks";
5
5
 
6
+ import { RVF_LAYERS_NAMES } from "../utils/constants";
6
7
  import useMapContext from "../utils/hooks/useMapContext";
7
8
  import useRvfContext from "../utils/hooks/useRvfContext";
8
9
 
@@ -19,6 +20,7 @@ function RvfPoisLayer(props: MaplibreStyleLayerOptions) {
19
20
  return metadata?.["mapset.filter"] === "mapset_poi";
20
21
  },
21
22
  maplibreLayer: baseLayer,
23
+ name: RVF_LAYERS_NAMES.pois,
22
24
  visible: false,
23
25
  ...(props || {}),
24
26
  });
@@ -0,0 +1,64 @@
1
+ import VectorLayer from "ol/layer/Vector";
2
+ import { Vector } from "ol/source";
3
+ import { Circle, Stroke, Style } from "ol/style";
4
+ import { useEffect, useMemo } from "preact/hooks";
5
+
6
+ import useMapContext from "../utils/hooks/useMapContext";
7
+ import useRvfContext from "../utils/hooks/useRvfContext";
8
+
9
+ function SingleClickListener() {
10
+ const { baseLayer, map } = useMapContext();
11
+ const { selectedFeature } = useRvfContext();
12
+
13
+ const layer = useMemo(() => {
14
+ const layer = new VectorLayer({
15
+ source: new Vector(),
16
+ style: new Style({
17
+ image: new Circle({
18
+ fill: null,
19
+ radius: 15,
20
+ stroke: new Stroke({
21
+ color: "red",
22
+ width: 3,
23
+ }),
24
+ }),
25
+ }),
26
+ });
27
+ return layer;
28
+ }, []);
29
+
30
+ useEffect(() => {
31
+ const vectorTileFeature = selectedFeature?.get("vectorTileFeature");
32
+ selectedFeature?.set("selected", true);
33
+ if (selectedFeature) {
34
+ const feature = selectedFeature.clone();
35
+ feature.setStyle(null);
36
+ layer.getSource().addFeature(feature);
37
+ if (vectorTileFeature) {
38
+ console.log(vectorTileFeature);
39
+ baseLayer?.mapLibreMap.setFeatureState(vectorTileFeature, {
40
+ hover: true,
41
+ });
42
+ }
43
+ }
44
+ return () => {
45
+ layer?.getSource().clear();
46
+ selectedFeature?.set("selected", false);
47
+ if (vectorTileFeature) {
48
+ baseLayer?.mapLibreMap?.setFeatureState(vectorTileFeature, {
49
+ hover: false,
50
+ });
51
+ }
52
+ };
53
+ }, [baseLayer?.mapLibreMap, layer, selectedFeature]);
54
+
55
+ useEffect(() => {
56
+ layer?.setMap(map);
57
+ return () => {
58
+ layer?.setMap(null);
59
+ };
60
+ }, [layer, map]);
61
+
62
+ return null;
63
+ }
64
+ export default SingleClickListener;
@@ -0,0 +1 @@
1
+ export { default } from "./RvfSelectedFeatureHighlightLayer";
@@ -6,6 +6,7 @@ import { useEffect, useMemo } from "preact/hooks";
6
6
  import Automat from "../icons/Automat";
7
7
  import InPerson from "../icons/InPerson";
8
8
  import Video from "../icons/Video";
9
+ import { RVF_LAYERS_NAMES } from "../utils/constants";
9
10
  import useMapContext from "../utils/hooks/useMapContext";
10
11
  import useRvfContext from "../utils/hooks/useRvfContext";
11
12
 
@@ -24,6 +25,7 @@ function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
24
25
  return metadata?.["general.filter"] === "selling_points";
25
26
  },
26
27
  maplibreLayer: baseLayer,
28
+ name: RVF_LAYERS_NAMES.verkaufsstellen,
27
29
  ...(props || {}),
28
30
 
29
31
  title: (
@@ -16,7 +16,7 @@ function RvfShare({
16
16
  const { map } = useMapContext();
17
17
 
18
18
  return (
19
- <div className={twMerge("flex flex-col gap-2 " + className)} {...props}>
19
+ <div className={twMerge("flex flex-col gap-6 " + className)} {...props}>
20
20
  <a
21
21
  className="flex items-center gap-2"
22
22
  href={`mailto:?subject=Karte&body=${window?.location.href}`}
@@ -4,7 +4,6 @@ import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
4
4
  import { Feature } from "ol";
5
5
  import { Point } from "ol/geom";
6
6
  import { Group, Vector } from "ol/layer";
7
- import { unByKey } from "ol/Observable";
8
7
  import { Cluster } from "ol/source";
9
8
  import VectorSource from "ol/source/Vector";
10
9
  import { memo } from "preact/compat";
@@ -15,8 +14,13 @@ import Car from "../icons/Car";
15
14
  import CargoBike from "../icons/CargoBike";
16
15
  import Ride from "../icons/Ride";
17
16
  import Scooter from "../icons/Scooter";
17
+ import {
18
+ API_REQUEST_FEATURE_TYPE,
19
+ RVF_LAYERS_NAMES,
20
+ RVF_LAYERS_TITLES,
21
+ } from "../utils/constants";
22
+ import createFreeFloatMobilityLayer from "../utils/createFreeFloatMobilityLayer";
18
23
  import createMobiDataBwWfsLayer from "../utils/createMobiDataBwWfsLayer";
19
- import createFreeFloatMobilityLayer from "../utils/createSharedMobilityLayer";
20
24
  import useMapContext from "../utils/hooks/useMapContext";
21
25
  import useRvfContext from "../utils/hooks/useRvfContext";
22
26
 
@@ -38,13 +42,22 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
38
42
  return metadata?.["rvf.filter"] === "bike";
39
43
  },
40
44
  maplibreLayer: baseLayer,
45
+ maxZoom: 18,
41
46
  }),
42
- createMobiDataBwWfsLayer("sharing_stations_bicycle", "green"),
43
- createFreeFloatMobilityLayer("sharing_vehicles", "green", "bike"),
47
+ createMobiDataBwWfsLayer(
48
+ API_REQUEST_FEATURE_TYPE.stations.bike,
49
+ "green",
50
+ ),
51
+ createFreeFloatMobilityLayer(
52
+ API_REQUEST_FEATURE_TYPE.freeFloat.bike,
53
+ "green",
54
+ "bike",
55
+ ),
44
56
  ],
57
+ name: RVF_LAYERS_NAMES.fahrrad,
45
58
  title: (
46
59
  <div className="flex items-center justify-between gap-2">
47
- Fahrrad
60
+ {RVF_LAYERS_TITLES.fahrrad}
48
61
  <Bicycle />
49
62
  </div>
50
63
  ),
@@ -58,13 +71,22 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
58
71
  return metadata?.["rvf.filter"] === "car";
59
72
  },
60
73
  maplibreLayer: baseLayer,
74
+ maxZoom: 18,
61
75
  }),
62
- createMobiDataBwWfsLayer("sharing_stations_car", "green"),
63
- createFreeFloatMobilityLayer("sharing_vehicles", "green", "car"),
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
+ ),
64
85
  ],
86
+ name: RVF_LAYERS_NAMES.auto,
65
87
  title: (
66
88
  <div className="flex items-center justify-between gap-2">
67
- Auto
89
+ {RVF_LAYERS_TITLES.auto}
68
90
  <Car />
69
91
  </div>
70
92
  ),
@@ -78,13 +100,22 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
78
100
  return metadata?.["rvf.filter"] === "cargo_bike";
79
101
  },
80
102
  maplibreLayer: baseLayer,
103
+ maxZoom: 18,
81
104
  }),
82
- createMobiDataBwWfsLayer("sharing_stations_cargo_bicycle", "green"),
83
- createFreeFloatMobilityLayer("sharing_vehicles", "green", "cargo_bike"),
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_bike",
113
+ ),
84
114
  ],
115
+ name: RVF_LAYERS_NAMES.cargobike,
85
116
  title: (
86
117
  <div className="flex items-center justify-between gap-2">
87
- Cargobike
118
+ {RVF_LAYERS_TITLES.cargobike}
88
119
  <CargoBike />
89
120
  </div>
90
121
  ),
@@ -100,12 +131,22 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
100
131
  // },
101
132
  // maplibreLayer: baseLayer,
102
133
  // }),
103
- createMobiDataBwWfsLayer("sharing_stations_scooters_standing", "green"),
104
- createFreeFloatMobilityLayer("sharing_vehicles", "green", "scooter"),
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
+ ),
105
145
  ],
146
+ name: RVF_LAYERS_NAMES.eroller,
106
147
  title: (
107
148
  <div className="flex items-center justify-between gap-2">
108
- E-Roller
149
+ {RVF_LAYERS_TITLES.eroller}
109
150
  <Scooter />
110
151
  </div>
111
152
  ),
@@ -117,9 +158,10 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
117
158
  return metadata?.["rvf.filter"] === "hitchhiking";
118
159
  },
119
160
  maplibreLayer: baseLayer,
161
+ name: RVF_LAYERS_NAMES.mitfahrpunkte,
120
162
  title: (
121
163
  <div className="flex items-center justify-between gap-2">
122
- Mitfahrpunkte
164
+ {RVF_LAYERS_TITLES.mitfahrpunkte}
123
165
  <Ride />
124
166
  </div>
125
167
  ),
@@ -134,14 +176,14 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
134
176
  sharingHitchHiking,
135
177
  ],
136
178
  ...props,
137
- });
179
+ } as GroupOptions);
138
180
  }, [baseLayer, props]);
139
181
 
140
182
  // Reload features every minute
141
183
  useEffect(() => {
142
184
  const interval = window.setInterval(() => {
143
185
  group.getLayers().forEach((layer: Group) => {
144
- layer.getLayers().forEach((layer: MaplibreStyleLayer | Vector) => {
186
+ layer.getLayers?.().forEach((layer: MaplibreStyleLayer | Vector) => {
145
187
  const source = layer.getSource();
146
188
  source?.refresh();
147
189
 
@@ -167,15 +209,9 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
167
209
  if (!map || !group) {
168
210
  return;
169
211
  }
170
-
171
- const key = map.on("moveend", () => {
172
- console.log("ZOOM", map.getView().getZoom());
173
- });
174
-
175
212
  map.addLayer(group);
176
213
 
177
214
  return () => {
178
- unByKey(key);
179
215
  map.removeLayer(group);
180
216
  };
181
217
  }, [map, group]);
@@ -110,16 +110,8 @@ function SingleClickListener() {
110
110
  setSelectedFeature(null);
111
111
  setSelectedFeatures([]);
112
112
  } else {
113
- const feats = [];
114
- features.forEach((feature) => {
115
- if (feature.get("features")) {
116
- feats.push(...feature.get("features"));
117
- } else {
118
- feats.push(feature);
119
- }
120
- });
121
- setSelectedFeatures(feats);
122
- setSelectedFeature(feats[0]);
113
+ setSelectedFeatures(features);
114
+ setSelectedFeature(features[0]);
123
115
  }
124
116
  },
125
117
  [
@@ -3,6 +3,7 @@ import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/Maplibr
3
3
  import { memo } from "preact/compat";
4
4
  import { useEffect, useMemo } from "preact/hooks";
5
5
 
6
+ import { RVF_LAYERS_NAMES } from "../utils/constants";
6
7
  import useMapContext from "../utils/hooks/useMapContext";
7
8
  import useRvfContext from "../utils/hooks/useRvfContext";
8
9
 
@@ -20,6 +21,7 @@ function RvfTarifZonenLayer(props: MaplibreStyleLayerOptions) {
20
21
  return metadata?.["rvf.filter"] === "zones";
21
22
  },
22
23
  maplibreLayer: baseLayer,
24
+ name: RVF_LAYERS_NAMES.tarifzonen,
23
25
  ...(props || {}),
24
26
  });
25
27
  }, [baseLayer, props]);