@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.
- package/CHANGELOG.md +38 -0
- package/docutils.js +8 -2
- package/index.html +14 -2
- package/index.js +152 -78
- package/package.json +2 -1
- package/src/RealtimeLayer/RealtimeLayer.tsx +2 -0
- package/src/RvfExportMenu/RvfExportMenu.tsx +12 -1
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -4
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +53 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +123 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +32 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/index.tsx +1 -0
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +123 -34
- package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +1 -1
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -0
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +64 -0
- package/src/RvfSelectedFeatureHighlightLayer/index.tsx +1 -0
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -0
- package/src/RvfShare/RvfShare.tsx +1 -1
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +59 -23
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +2 -10
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -0
- package/src/RvfTopics/RvfTopics.tsx +40 -8
- package/src/StationsLayer/StationsLayer.tsx +2 -0
- package/src/icons/Bike/rvf_shared_bike.svg +2 -2
- package/src/icons/Car/rvf_shared_car.svg +3 -3
- package/src/icons/CargoBike/rvf_shared_cargo_bike.svg +3 -3
- package/src/icons/Scooter/rvf_shared_scooter.svg +2 -2
- package/src/index.tsx +4 -0
- package/src/logos/callabike_logo.png +0 -0
- package/src/logos/flinkster_logo.png +0 -0
- package/src/logos/gruene_flotte_logo.png +0 -0
- package/src/logos/logo_frelo_web_rgb.png +0 -0
- package/src/logos/natur_energie_logo.png +0 -0
- package/src/logos/yoio_logo.png +0 -0
- package/src/logos/zeus_logo.png +0 -0
- package/src/utils/applyInitialLayerVisibility.ts +41 -0
- package/src/utils/constants.ts +63 -0
- package/src/utils/{createSharedMobilityLayer.ts → createFreeFloatMobilityLayer.ts} +41 -69
- package/src/utils/createMobiDataBwWfsLayer.ts +108 -67
- package/src/utils/exportPdf.ts +1 -4
- package/src/utils/getFeatureInformationTitle.ts +37 -0
- package/src/utils/getLayersAsFlatArray.ts +22 -0
- package/src/utils/getLinkByDevice.ts +23 -0
- package/src/utils/getPermalinkParameters.ts +27 -0
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +28 -0
- package/src/utils/hooks/useMapContext.tsx +2 -0
- 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 = {
|
|
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
|
-
|
|
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
|
-
{
|
|
464
|
+
{hasRealtime && (
|
|
465
|
+
<RealtimeLayer title={RVF_LAYERS_TITLES.echtzeit} />
|
|
466
|
+
)}
|
|
404
467
|
{
|
|
405
468
|
<StationsLayer
|
|
406
469
|
minZoom={10}
|
|
407
|
-
title=
|
|
470
|
+
title={RVF_LAYERS_TITLES.haltestellen}
|
|
408
471
|
{...stationsLayerProps}
|
|
409
472
|
/>
|
|
410
473
|
}
|
|
411
|
-
{
|
|
412
|
-
<
|
|
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=
|
|
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
|
-
{
|
|
432
|
-
{!hasToolbar && <RvfExportMenuButton />}
|
|
499
|
+
{hasGeolocation && <GeolocationButton />}
|
|
500
|
+
{!hasToolbar && hasPrint && <RvfExportMenuButton />}
|
|
433
501
|
</div>
|
|
434
502
|
|
|
435
|
-
{
|
|
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
|
-
{
|
|
527
|
+
{hasRealtime && trainId && (
|
|
460
528
|
<>
|
|
461
529
|
<RvfOverlayHeader
|
|
462
530
|
onClose={() => {
|
|
463
531
|
setTrainId(null);
|
|
464
532
|
}}
|
|
465
|
-
title=
|
|
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=
|
|
555
|
+
title={getFeatureInformationTitle(selectedFeature)}
|
|
488
556
|
></RvfOverlayHeader>
|
|
489
|
-
<RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden
|
|
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="
|
|
566
|
+
title="Drücken"
|
|
499
567
|
></RvfOverlayHeader>
|
|
500
|
-
<RvfExportMenu className="relative flex flex-col gap-
|
|
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
|
-
|
|
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
|
|
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
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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>
|
|
@@ -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-
|
|
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(
|
|
43
|
-
|
|
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
|
-
|
|
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(
|
|
63
|
-
|
|
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
|
-
|
|
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(
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
features
|
|
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]);
|