@geops/rvf-mobility-web-component 0.1.10 → 0.1.12
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 +52 -0
- package/docutils.js +198 -0
- package/index.html +48 -217
- package/index.js +680 -87
- package/input.css +11 -1
- package/jest-setup.js +3 -2
- package/package.json +4 -3
- package/scripts/build.mjs +3 -2
- package/scripts/dev.mjs +2 -1
- package/search.html +38 -69
- package/src/BaseLayer/BaseLayer.tsx +20 -12
- package/src/FloatingMenu/FloatingMenu.tsx +42 -0
- package/src/FloatingMenu/index.tsx +1 -0
- package/src/GeolocationButton/GeolocationButton.tsx +6 -5
- package/src/Map/Map.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +10 -9
- package/src/MobilityMap/index.css +0 -13
- package/src/RealtimeLayer/RealtimeLayer.tsx +2 -3
- package/src/RvfButton/RvfButton.tsx +28 -21
- package/src/RvfCheckbox/RvfCheckbox.tsx +24 -0
- package/src/RvfCheckbox/index.tsx +1 -0
- package/src/RvfExportMenu/RvfExportMenu.tsx +103 -0
- package/src/RvfExportMenu/index.tsx +1 -0
- package/src/RvfExportMenuButton/RvfExportMenuButton.tsx +27 -0
- package/src/RvfExportMenuButton/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +29 -0
- package/src/RvfFeatureDetails/index.tsx +1 -0
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +44 -0
- package/src/RvfFloatingMenu/index.tsx +1 -0
- package/src/RvfIconButton/RvfIconButton.tsx +35 -0
- package/src/RvfIconButton/index.tsx +1 -0
- package/src/RvfLayerTree/RvfLayerTree.tsx +41 -0
- package/src/RvfLayerTree/TreeItem/TreeItem.tsx +120 -0
- package/src/RvfLayerTree/TreeItem/index.tsx +1 -0
- package/src/RvfLayerTree/index.tsx +1 -0
- package/src/RvfLayerTree/layersTreeContext.ts +4 -0
- package/src/RvfLayerTree/layersTreeReducer.ts +152 -0
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +42 -0
- package/src/RvfLineNetworkPlanLayer/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +122 -83
- package/src/RvfMobilityMap/index.css +0 -13
- package/src/RvfModal/RvfModal.tsx +52 -0
- package/src/RvfModal/index.tsx +1 -0
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +39 -0
- package/src/RvfPoisLayer/index.tsx +1 -0
- package/src/RvfRadioButton/RvfRadioButton.tsx +16 -0
- package/src/RvfRadioButton/index.tsx +1 -0
- package/src/RvfSelect/RvfSelect.tsx +22 -0
- package/src/RvfSelect/index.tsx +1 -0
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +41 -0
- package/src/RvfSellingPointsLayer/index.tsx +1 -0
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +100 -0
- package/src/RvfSharedMobilityLayerGroup/index.tsx +1 -0
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +146 -0
- package/src/RvfSingleClickListener/index.tsx +1 -0
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +41 -0
- package/src/RvfTarifZonenLayer/index.tsx +1 -0
- package/src/RvfTopics/RvfTopics.tsx +47 -0
- package/src/RvfTopics/index.tsx +1 -0
- package/src/RvfZoomButtons/RvfZoomButtons.tsx +36 -29
- package/src/Search/Search.tsx +11 -9
- package/src/SingleClickListener/index.tsx +1 -1
- package/src/StationsLayer/StationsLayer.tsx +0 -1
- package/src/StopsSearch/StopsSearch.tsx +38 -6
- package/src/icons/ArrowDown/ArrowDown.tsx +22 -0
- package/src/icons/ArrowDown/down-open.svg +7 -0
- package/src/icons/ArrowDown/index.tsx +1 -0
- package/src/icons/ArrowUp/ArrowUp.tsx +22 -0
- package/src/icons/ArrowUp/index.tsx +1 -0
- package/src/icons/ArrowUp/up-open.svg +7 -0
- package/src/icons/Bicycle/verkehrstraeger-rad-2px-white.svg +19 -0
- package/src/icons/Cancel/Cancel.tsx +21 -0
- package/src/icons/Cancel/cancel.svg +7 -0
- package/src/icons/Cancel/index.tsx +1 -0
- package/src/icons/Car/verkehrstraeger-auto-2px-white.svg +14 -0
- package/src/icons/CargoBicycle/verkehrstraeger-lastenrad-2px-white.svg +27 -0
- package/src/icons/DownOpen/DownOpen.tsx +24 -0
- package/src/icons/DownOpen/down-open.svg +7 -0
- package/src/icons/DownOpen/index.tsx +1 -0
- package/src/icons/Download/Download.tsx +20 -0
- package/src/icons/Download/download.svg +15 -0
- package/src/icons/Download/index.tsx +1 -0
- package/src/icons/Elevator/Elevator.tsx +1 -1
- package/src/icons/Menu/Menu.tsx +32 -0
- package/src/icons/Menu/index.tsx +1 -0
- package/src/icons/Menu/menu.svg +9 -0
- package/src/icons/Ok/ok-grey.svg +7 -0
- package/src/icons/Ok/ok.svg +4 -0
- package/src/icons/Scooter/scooter.svg +10 -0
- package/src/utils/constants.ts +9 -0
- package/src/utils/createMobiDataBwWfsLayer.ts +120 -0
- package/src/utils/createSharedMobilityLayer.ts +165 -0
- package/src/utils/exportPdf.ts +657 -0
- package/src/utils/hooks/useRvfContext.tsx +37 -0
- package/src/utils/hooks/useUpdatePermalink.tsx +2 -9
- package/tailwind.config.mjs +41 -19
- package/src/RvfSharedMobilityLayer/RvfSharedMobilityLayer.tsx +0 -147
- package/src/RvfSharedMobilityLayer/index.tsx +0 -1
|
@@ -9,10 +9,9 @@ import {
|
|
|
9
9
|
RealtimeStopSequence,
|
|
10
10
|
RealtimeTrainId,
|
|
11
11
|
} from "mobility-toolbox-js/types";
|
|
12
|
-
import { Map as OlMap } from "ol";
|
|
13
|
-
import { transformExtent } from "ol/proj";
|
|
12
|
+
import { Feature, Map as OlMap } from "ol";
|
|
14
13
|
import { memo } from "preact/compat";
|
|
15
|
-
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
14
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
16
15
|
|
|
17
16
|
import BaseLayer from "../BaseLayer";
|
|
18
17
|
import Copyright from "../Copyright";
|
|
@@ -23,33 +22,46 @@ import NotificationLayer from "../NotificationLayer";
|
|
|
23
22
|
import Overlay from "../Overlay";
|
|
24
23
|
import RealtimeLayer from "../RealtimeLayer";
|
|
25
24
|
import RouteSchedule from "../RouteSchedule";
|
|
26
|
-
import
|
|
25
|
+
import RvfExportMenu from "../RvfExportMenu";
|
|
26
|
+
import RvfExportMenuButton from "../RvfExportMenuButton";
|
|
27
|
+
import RvfFeatureDetails from "../RvfFeatureDetails";
|
|
28
|
+
import RvfFloatingMenu from "../RvfFloatingMenu";
|
|
29
|
+
// 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¬ificationat=2024-01-25T22%3A59%3A00Z
|
|
30
|
+
import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
|
|
31
|
+
import Modal from "../RvfModal";
|
|
32
|
+
import RvfPoisLayer from "../RvfPoisLayer";
|
|
33
|
+
import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
|
|
34
|
+
import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
|
|
35
|
+
import RvfTarifZonenLayer from "../RvfTarifZonenLayer";
|
|
36
|
+
import Topics from "../RvfTopics";
|
|
27
37
|
import RvfZoomButtons from "../RvfZoomButtons";
|
|
28
38
|
import ScaleLine from "../ScaleLine";
|
|
29
39
|
import Search from "../Search";
|
|
30
|
-
import SingleClickListener from "../SingleClickListener
|
|
40
|
+
import SingleClickListener from "../SingleClickListener";
|
|
31
41
|
import Station from "../Station";
|
|
32
42
|
import StationsLayer from "../StationsLayer";
|
|
33
43
|
// @ts-expect-error bad type definition
|
|
34
44
|
import tailwind from "../style.css";
|
|
45
|
+
import { RVF_EXTENT_3857 } from "../utils/constants";
|
|
35
46
|
import { I18nContext } from "../utils/hooks/useI18n";
|
|
36
47
|
import { MapContext } from "../utils/hooks/useMapContext";
|
|
48
|
+
import { RvfContext } from "../utils/hooks/useRvfContext";
|
|
37
49
|
import useUpdatePermalink from "../utils/hooks/useUpdatePermalink";
|
|
38
50
|
import i18n from "../utils/i18n";
|
|
39
51
|
import MobilityEvent from "../utils/MobilityEvent";
|
|
40
52
|
// @ts-expect-error bad type definition
|
|
41
53
|
import style from "./index.css";
|
|
42
|
-
// 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¬ificationat=2024-01-25T22%3A59%3A00Z
|
|
43
54
|
|
|
44
55
|
export type RvfMobilityMapProps = {} & MobilityMapProps;
|
|
45
56
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
const bbox = RVF_EXTENT_3857.join(",");
|
|
58
|
+
|
|
59
|
+
const baseLayerProps = {
|
|
60
|
+
mapLibreOptions: {
|
|
61
|
+
maxCanvasSize: [20000, 20000], // remove 4096 limitations
|
|
62
|
+
preserveDrawingBuffer: true,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
53
65
|
|
|
54
66
|
function RvfMobilityMap({
|
|
55
67
|
apikey = "5cc87b12d7c5370001c1d655820abcc37dfd4d968d7bab5b2a74a935",
|
|
@@ -59,7 +71,7 @@ function RvfMobilityMap({
|
|
|
59
71
|
geolocation = "true",
|
|
60
72
|
mapsurl = "https://maps.geops.io",
|
|
61
73
|
maxextent = bbox,
|
|
62
|
-
maxzoom =
|
|
74
|
+
maxzoom = "20",
|
|
63
75
|
minzoom = null,
|
|
64
76
|
mots = null,
|
|
65
77
|
notification = "true",
|
|
@@ -74,6 +86,7 @@ function RvfMobilityMap({
|
|
|
74
86
|
tenant = null,
|
|
75
87
|
zoom = null,
|
|
76
88
|
}: RvfMobilityMapProps) {
|
|
89
|
+
const eventNodeRef = useRef<HTMLDivElement>();
|
|
77
90
|
const [baseLayer, setBaseLayer] = useState<MaplibreLayer>();
|
|
78
91
|
const [isFollowing, setIsFollowing] = useState(false);
|
|
79
92
|
const [isTracking, setIsTracking] = useState(false);
|
|
@@ -84,10 +97,14 @@ function RvfMobilityMap({
|
|
|
84
97
|
const [map, setMap] = useState<OlMap>();
|
|
85
98
|
const [stationId, setStationId] = useState<RealtimeStationId>();
|
|
86
99
|
const [trainId, setTrainId] = useState<RealtimeTrainId>();
|
|
100
|
+
const [isExportMenuOpen, setIsExportMenuOpen] = useState<boolean>(false);
|
|
101
|
+
const [selectedFeature, setSelectedFeature] = useState<Feature>();
|
|
102
|
+
const [selectedFeatures, setSelectedFeatures] = useState<Feature[]>();
|
|
103
|
+
const [isLayerTreeOpen, setIsLayerTreeOpen] = useState<boolean>(false);
|
|
87
104
|
|
|
88
105
|
// TODO: this should be removed. The parent application should be responsible to do this
|
|
89
106
|
// or we should find something that fit more usecases
|
|
90
|
-
|
|
107
|
+
useUpdatePermalink(map, permalink === "true");
|
|
91
108
|
|
|
92
109
|
const mapContextValue = useMemo(() => {
|
|
93
110
|
return {
|
|
@@ -165,10 +182,10 @@ function RvfMobilityMap({
|
|
|
165
182
|
]);
|
|
166
183
|
|
|
167
184
|
useEffect(() => {
|
|
168
|
-
dispatchEvent(
|
|
185
|
+
eventNodeRef.current?.dispatchEvent(
|
|
169
186
|
new MobilityEvent<RvfMobilityMapProps>("mwc:attribute", {
|
|
170
187
|
baselayer,
|
|
171
|
-
center
|
|
188
|
+
center,
|
|
172
189
|
extent,
|
|
173
190
|
geolocation,
|
|
174
191
|
mapsurl,
|
|
@@ -184,7 +201,7 @@ function RvfMobilityMap({
|
|
|
184
201
|
realtimeurl,
|
|
185
202
|
search,
|
|
186
203
|
tenant,
|
|
187
|
-
zoom
|
|
204
|
+
zoom,
|
|
188
205
|
}),
|
|
189
206
|
);
|
|
190
207
|
}, [
|
|
@@ -204,85 +221,107 @@ function RvfMobilityMap({
|
|
|
204
221
|
search,
|
|
205
222
|
tenant,
|
|
206
223
|
zoom,
|
|
207
|
-
x,
|
|
208
|
-
y,
|
|
209
|
-
z,
|
|
210
224
|
extent,
|
|
211
225
|
maxextent,
|
|
212
226
|
]);
|
|
213
227
|
|
|
228
|
+
const rvfContextValue = useMemo(() => {
|
|
229
|
+
return {
|
|
230
|
+
isExportMenuOpen,
|
|
231
|
+
isLayerTreeOpen,
|
|
232
|
+
selectedFeature,
|
|
233
|
+
selectedFeatures,
|
|
234
|
+
setIsExportMenuOpen,
|
|
235
|
+
setIsLayerTreeOpen,
|
|
236
|
+
setSelectedFeature,
|
|
237
|
+
setSelectedFeatures,
|
|
238
|
+
};
|
|
239
|
+
}, [isExportMenuOpen, selectedFeature, selectedFeatures, isLayerTreeOpen]);
|
|
240
|
+
|
|
214
241
|
return (
|
|
215
242
|
<I18nContext.Provider value={i18n}>
|
|
216
243
|
<style>{tailwind}</style>
|
|
217
244
|
<style>{style}</style>
|
|
218
245
|
<MapContext.Provider value={mapContextValue}>
|
|
219
|
-
<
|
|
220
|
-
<div
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
color="green"
|
|
239
|
-
name="MobiData-BW:sharing_stations_car"
|
|
240
|
-
url="https://api.mobidata-bw.de/geoserver/MobiData-BW/sharing_stations_car/ows"
|
|
241
|
-
/>
|
|
242
|
-
<RvfSharedMobilityLayer
|
|
243
|
-
color="orange"
|
|
244
|
-
name="MobiData-BW:sharing_stations_scooters_standing"
|
|
245
|
-
url="https://api.mobidata-bw.de/geoserver/MobiData-BW/sharing_stations_scooters_standing/ows"
|
|
246
|
-
/>
|
|
247
|
-
<RvfSharedMobilityLayer
|
|
248
|
-
color="orange"
|
|
249
|
-
minZoom={14.999}
|
|
250
|
-
name="MobiData-BW:sharing_vehicles"
|
|
251
|
-
url="https://api.mobidata-bw.de/geoserver/MobiData-BW/sharing_vehicles/ows"
|
|
252
|
-
/>
|
|
246
|
+
<RvfContext.Provider value={rvfContextValue}>
|
|
247
|
+
<div
|
|
248
|
+
className="relative size-full border font-sans @container/main"
|
|
249
|
+
ref={eventNodeRef}
|
|
250
|
+
style={{ fontSize: 16 }}
|
|
251
|
+
>
|
|
252
|
+
<div className="relative flex size-full flex-col @lg/main:flex-row-reverse">
|
|
253
|
+
<Map className="relative flex-1 overflow-visible ">
|
|
254
|
+
<RvfFloatingMenu
|
|
255
|
+
isOpen={isLayerTreeOpen}
|
|
256
|
+
onClick={() => {
|
|
257
|
+
setIsLayerTreeOpen(!isLayerTreeOpen);
|
|
258
|
+
}}
|
|
259
|
+
title="Kartendaten"
|
|
260
|
+
>
|
|
261
|
+
<Topics className={"w-full px-2"} />
|
|
262
|
+
</RvfFloatingMenu>
|
|
263
|
+
<BaseLayer {...baseLayerProps} isNotInLayerTree />
|
|
264
|
+
<SingleClickListener />
|
|
253
265
|
|
|
254
|
-
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
{realtime === "true" && <RealtimeLayer title="Echtzeit" />}
|
|
267
|
+
{tenant && <StationsLayer />}
|
|
268
|
+
{notification === "true" && <NotificationLayer />}
|
|
269
|
+
<RvfSellingPointsLayer title="Verkaufsstellen" />
|
|
270
|
+
<RvfLineNetworkPlanLayer title="Liniennetz" />
|
|
271
|
+
<RvfTarifZonenLayer title="Tarifzonen" />
|
|
272
|
+
<RvfPoisLayer title="POIs" />
|
|
273
|
+
<RvfSharedMobilityLayerGroup title="Shared Mobility" />
|
|
274
|
+
|
|
275
|
+
<div className="absolute inset-x-2 bottom-2 z-10 flex items-end justify-between gap-2 text-[10px]">
|
|
276
|
+
<ScaleLine className="bg-slate-50/70" />
|
|
277
|
+
<Copyright className="bg-slate-50/70" />
|
|
264
278
|
</div>
|
|
265
|
-
)}
|
|
266
|
-
<div className="absolute bottom-10 right-2 z-10 flex flex-col justify-between gap-2">
|
|
267
|
-
<RvfZoomButtons />
|
|
268
|
-
</div>
|
|
269
|
-
</Map>
|
|
270
279
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
<div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
|
|
281
|
+
{geolocation === "true" && <GeolocationButton />}
|
|
282
|
+
<RvfExportMenuButton />
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
{search === "true" && (
|
|
286
|
+
<div className="absolute left-2 right-12 top-2 z-10 flex max-h-[90%] min-w-64 max-w-96 flex-col">
|
|
287
|
+
<Search />
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
<div className="absolute bottom-10 right-2 z-10 flex flex-col justify-between gap-2">
|
|
292
|
+
<RvfZoomButtons />
|
|
293
|
+
</div>
|
|
294
|
+
</Map>
|
|
295
|
+
|
|
296
|
+
<Overlay
|
|
297
|
+
className={"z-50"}
|
|
298
|
+
ScrollableHandlerProps={{
|
|
299
|
+
style: { width: "calc(100% - 60px)" },
|
|
300
|
+
}}
|
|
301
|
+
>
|
|
302
|
+
{realtime === "true" && trainId && (
|
|
303
|
+
<RouteSchedule className="relative overflow-y-auto overflow-x-hidden" />
|
|
304
|
+
)}
|
|
305
|
+
{tenant && stationId && (
|
|
306
|
+
<Station className="relative overflow-y-auto overflow-x-hidden" />
|
|
307
|
+
)}
|
|
308
|
+
{selectedFeature && (
|
|
309
|
+
<RvfFeatureDetails className="relative overflow-y-auto overflow-x-hidden" />
|
|
310
|
+
)}
|
|
311
|
+
</Overlay>
|
|
312
|
+
|
|
313
|
+
{isExportMenuOpen && (
|
|
314
|
+
<Modal
|
|
315
|
+
onClose={() => {
|
|
316
|
+
setIsExportMenuOpen(false);
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
<RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden" />
|
|
320
|
+
</Modal>
|
|
282
321
|
)}
|
|
283
|
-
</
|
|
322
|
+
</div>
|
|
284
323
|
</div>
|
|
285
|
-
</
|
|
324
|
+
</RvfContext.Provider>
|
|
286
325
|
</MapContext.Provider>
|
|
287
326
|
</I18nContext.Provider>
|
|
288
327
|
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
import { useEffect, useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
export type ModalProps = {
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
} & JSX.HTMLAttributes<HTMLDialogElement> &
|
|
8
|
+
PreactDOMAttributes;
|
|
9
|
+
|
|
10
|
+
function RvfModal({ children, onClose, ...props }: ModalProps) {
|
|
11
|
+
const [node, setNode] = useState<HTMLDialogElement>();
|
|
12
|
+
let hasChildren = !!children;
|
|
13
|
+
if (Array.isArray(children)) {
|
|
14
|
+
hasChildren =
|
|
15
|
+
children?.length &&
|
|
16
|
+
(children || []).find((c) => {
|
|
17
|
+
return !!c;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
node?.addEventListener("close", onClose);
|
|
23
|
+
return () => {
|
|
24
|
+
node?.removeEventListener("close", onClose);
|
|
25
|
+
};
|
|
26
|
+
}, [node, onClose]);
|
|
27
|
+
|
|
28
|
+
if (!hasChildren) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<dialog
|
|
34
|
+
className={
|
|
35
|
+
"absolute inset-0 z-50 flex size-full items-center justify-center bg-transparent"
|
|
36
|
+
}
|
|
37
|
+
ref={(n) => {
|
|
38
|
+
setNode(n);
|
|
39
|
+
n?.focus();
|
|
40
|
+
}}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<button
|
|
44
|
+
className="absolute inset-0 z-0 size-full bg-black/60"
|
|
45
|
+
onClick={onClose}
|
|
46
|
+
></button>
|
|
47
|
+
<div className="z-10 h-3/4 w-1/2 bg-white">{children}</div>
|
|
48
|
+
</dialog>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default memo(RvfModal);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfModal";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
|
|
8
|
+
function RvfPoisLayer(props: MaplibreStyleLayerOptions) {
|
|
9
|
+
const { baseLayer, map } = useMapContext();
|
|
10
|
+
|
|
11
|
+
const layer = useMemo(() => {
|
|
12
|
+
if (!baseLayer) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return new MaplibreStyleLayer({
|
|
16
|
+
layersFilter: ({ metadata }) => {
|
|
17
|
+
return metadata?.["mapset.filter"] === "mapset_poi";
|
|
18
|
+
},
|
|
19
|
+
maplibreLayer: baseLayer,
|
|
20
|
+
visible: false,
|
|
21
|
+
...(props || {}),
|
|
22
|
+
});
|
|
23
|
+
}, [baseLayer, props]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!map || !layer) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
map.addLayer(layer);
|
|
31
|
+
return () => {
|
|
32
|
+
map.removeLayer(layer);
|
|
33
|
+
};
|
|
34
|
+
}, [map, layer]);
|
|
35
|
+
|
|
36
|
+
return null; // <RegisterForSelectFeaturesOnClick />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default memo(RvfPoisLayer);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfPoisLayer";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JSX } from "preact";
|
|
2
|
+
|
|
3
|
+
export type RvfRadioButtonProps =
|
|
4
|
+
{} & JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
5
|
+
|
|
6
|
+
function RvfRadioButton(props: RvfRadioButtonProps) {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
className="peer mr-2 h-inputControl w-inputControl rounded-full border-2 border-grey accent-red"
|
|
10
|
+
{...props}
|
|
11
|
+
type="radio"
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default RvfRadioButton;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfRadioButton";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import ArrowDown from "../icons/ArrowDown";
|
|
4
|
+
|
|
5
|
+
export type RvfSelectProps = {} & JSX.HTMLAttributes<HTMLSelectElement> &
|
|
6
|
+
PreactDOMAttributes;
|
|
7
|
+
|
|
8
|
+
function RvfSelect({ children, className, onChange }: RvfSelectProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="relative flex items-center text-grey">
|
|
11
|
+
<select
|
|
12
|
+
className={`min-w-12 appearance-none rounded-xl border border-grey p-2 text-base focus:outline-none ${className}`}
|
|
13
|
+
onChange={onChange}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</select>
|
|
17
|
+
<ArrowDown className="pointer-events-none absolute right-1" />
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default RvfSelect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfSelect";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
|
|
8
|
+
function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
|
|
9
|
+
const { baseLayer, map } = useMapContext();
|
|
10
|
+
|
|
11
|
+
const layer = useMemo(() => {
|
|
12
|
+
if (!baseLayer) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return new MaplibreStyleLayer({
|
|
16
|
+
layersFilter: ({ metadata, source, "source-layer": sourceLayer }) => {
|
|
17
|
+
return (
|
|
18
|
+
metadata?.["rvf.filter"] === "selling_points" ||
|
|
19
|
+
(source === "rvf" && sourceLayer === "selling_points")
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
maplibreLayer: baseLayer,
|
|
23
|
+
...(props || {}),
|
|
24
|
+
});
|
|
25
|
+
}, [baseLayer, props]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!map || !layer) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
map.addLayer(layer);
|
|
33
|
+
return () => {
|
|
34
|
+
map.removeLayer(layer);
|
|
35
|
+
};
|
|
36
|
+
}, [map, layer]);
|
|
37
|
+
|
|
38
|
+
return null; // <RegisterForSelectFeaturesOnClick />;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default memo(RvfSellingPointsLayer);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfSellingPointsLayer";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Options } from "ol/layer/Group";
|
|
2
|
+
|
|
3
|
+
import { Feature } from "ol";
|
|
4
|
+
import { Point } from "ol/geom";
|
|
5
|
+
import { Group, Vector } from "ol/layer";
|
|
6
|
+
import { Cluster } from "ol/source";
|
|
7
|
+
import VectorSource from "ol/source/Vector";
|
|
8
|
+
import { memo } from "preact/compat";
|
|
9
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
10
|
+
|
|
11
|
+
import createMobiDataBwWfsLayer from "../utils/createMobiDataBwWfsLayer";
|
|
12
|
+
import createSharedMobilityLayer from "../utils/createSharedMobilityLayer";
|
|
13
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
14
|
+
|
|
15
|
+
function RvfSharedMobilityLayerGroup(props: Options & Record<string, unknown>) {
|
|
16
|
+
const { map } = useMapContext();
|
|
17
|
+
|
|
18
|
+
const group = useMemo(() => {
|
|
19
|
+
const sharingStationsBicycle = createMobiDataBwWfsLayer(
|
|
20
|
+
"sharing_stations_bicycle",
|
|
21
|
+
"green",
|
|
22
|
+
);
|
|
23
|
+
sharingStationsBicycle.set("title", "Stations Bicycle");
|
|
24
|
+
|
|
25
|
+
const sharingStationsCar = createMobiDataBwWfsLayer(
|
|
26
|
+
"sharing_stations_car",
|
|
27
|
+
"green",
|
|
28
|
+
);
|
|
29
|
+
sharingStationsCar.set("title", "Stations Car");
|
|
30
|
+
|
|
31
|
+
const sharingStationsCargoBicycle = createMobiDataBwWfsLayer(
|
|
32
|
+
"sharing_stations_cargo_bicycle",
|
|
33
|
+
"green",
|
|
34
|
+
);
|
|
35
|
+
sharingStationsCargoBicycle.set("title", "Stations Cargo Bicycle");
|
|
36
|
+
|
|
37
|
+
const sharingStationsScooterStanding = createMobiDataBwWfsLayer(
|
|
38
|
+
"sharing_stations_scooters_standing",
|
|
39
|
+
"purple",
|
|
40
|
+
);
|
|
41
|
+
sharingStationsScooterStanding.set("title", "Stations Scooter");
|
|
42
|
+
|
|
43
|
+
const sharingVehicles = createSharedMobilityLayer(
|
|
44
|
+
"sharing_vehicles",
|
|
45
|
+
"green",
|
|
46
|
+
);
|
|
47
|
+
sharingVehicles.set("title", "Free Floating");
|
|
48
|
+
|
|
49
|
+
return new Group({
|
|
50
|
+
layers: [
|
|
51
|
+
sharingStationsBicycle,
|
|
52
|
+
sharingStationsCar,
|
|
53
|
+
sharingStationsCargoBicycle,
|
|
54
|
+
sharingStationsScooterStanding,
|
|
55
|
+
sharingVehicles,
|
|
56
|
+
],
|
|
57
|
+
...props,
|
|
58
|
+
});
|
|
59
|
+
}, [props]);
|
|
60
|
+
|
|
61
|
+
// Reload features every minute
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const interval = window.setInterval(() => {
|
|
64
|
+
group.getLayers().forEach((layer: Vector) => {
|
|
65
|
+
// @ts-expect-error - private property
|
|
66
|
+
layer.getSource().loadedExtentsRtree_.clear();
|
|
67
|
+
layer.getSource().clear(true);
|
|
68
|
+
layer.getSource().changed();
|
|
69
|
+
|
|
70
|
+
// For cluster source
|
|
71
|
+
(
|
|
72
|
+
(
|
|
73
|
+
layer.getSource() as Cluster<Feature<Point>>
|
|
74
|
+
)?.getSource?.() as VectorSource<Feature<Point>>
|
|
75
|
+
)?.refresh();
|
|
76
|
+
});
|
|
77
|
+
}, 60000);
|
|
78
|
+
return () => {
|
|
79
|
+
window.clearInterval(interval);
|
|
80
|
+
};
|
|
81
|
+
}, [group]);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!map || !group) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
map.on("moveend", () => {
|
|
88
|
+
console.log("ZOOM", map.getView().getZoom());
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
map.addLayer(group);
|
|
92
|
+
|
|
93
|
+
return () => {
|
|
94
|
+
map.removeLayer(group);
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
export default memo(RvfSharedMobilityLayerGroup);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfSharedMobilityLayerGroup";
|