@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.
- package/.vscode/settings.json +2 -0
- package/CHANGELOG.md +8 -0
- package/docutils.js +1 -1
- package/index.html +1 -1
- package/index.js +119 -107
- package/package.json +1 -1
- package/src/NotificationLayer/NotificationLayer.tsx +2 -2
- package/src/RouteIcon/RouteIcon.tsx +21 -12
- package/src/RvfExportMenu/RvfExportMenu.tsx +2 -0
- package/src/RvfFeatureDetails/RVFSellingPointDetails/RVFSellingPointDetails.tsx +47 -0
- package/src/RvfFeatureDetails/RVFSellingPointDetails/index.js +1 -0
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +4 -1
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +4 -1
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +55 -12
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +58 -32
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +364 -123
- package/src/StationsLayer/StationsLayer.tsx +1 -1
- package/src/icons/NoRealtime/NoRealtime.tsx +44 -0
- package/src/icons/NoRealtime/index.tsx +1 -0
- package/src/icons/NoRealtime/norealtime.svg +6 -0
- package/src/utils/constants.ts +22 -1
- package/src/utils/exportPdf.ts +3 -3
- package/src/utils/fullTrajectoryStyle.ts +0 -1
- package/src/utils/getMainColorForVehicle.test.ts +21 -23
- package/src/utils/getRadius.ts +25 -24
- package/src/utils/hooks/useUpdatePermalink.tsx +5 -0
- package/src/utils/realtimeRVFStyle.ts +70 -8
- package/src/utils/sharingWFSUtils.ts +46 -16
- package/src/RvfSharedMobilityLayerGroup2/RvfSharedMobilityLayerGroup2.tsx +0 -740
- 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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
{
|
|
284
|
+
{"Frelo"}
|
|
61
285
|
<Bicycle />
|
|
62
286
|
</div>
|
|
63
287
|
),
|
|
64
|
-
}
|
|
288
|
+
});
|
|
289
|
+
// setBikeFreloLayer(bikeFrelo);
|
|
65
290
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
{
|
|
90
|
-
<
|
|
300
|
+
{"Fahrrad, andere Anbieter"}
|
|
301
|
+
<Bicycle />
|
|
91
302
|
</div>
|
|
92
303
|
),
|
|
93
|
-
}
|
|
304
|
+
});
|
|
305
|
+
// setBikeOthersLayer(bikeOthers);
|
|
94
306
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
{
|
|
316
|
+
{"Lastenfrelo"}
|
|
119
317
|
<CargoBike />
|
|
120
318
|
</div>
|
|
121
319
|
),
|
|
122
|
-
}
|
|
320
|
+
});
|
|
321
|
+
// setCargobikeFreloLayer(cargobikeFrelo);
|
|
123
322
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
}
|
|
401
|
+
});
|
|
402
|
+
// setScooterLayer(scooter);
|
|
154
403
|
|
|
155
|
-
const
|
|
156
|
-
isQueryable:
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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?.["
|
|
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>
|
package/src/utils/constants.ts
CHANGED
|
@@ -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
|
-
|
|
120
|
+
"gruene-flotte_freiburg": "Die Grüne\nFlotte",
|
|
100
121
|
lastenvelo_fr: "LastenVelo",
|
|
101
122
|
naturenergie_sharing: "naturenergie\nsharing",
|
|
102
123
|
nextbike_df: "Frelo",
|
package/src/utils/exportPdf.ts
CHANGED
|
@@ -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();
|