@geops/rvf-mobility-web-component 0.1.43 → 0.1.44
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/.yarnrc.yml +1 -0
- package/CHANGELOG.md +10 -0
- package/README.md +31 -3
- package/docutils.js +1 -1
- package/iframe.html +221 -11
- package/index.html +16 -5
- package/index.js +111 -106
- package/package.json +2 -2
- package/src/MobilityMap/MobilityMap.tsx +2 -2
- package/src/NotificationLayer/NotificationLayer.tsx +11 -189
- package/src/RouteStopTime/RouteStopTime.tsx +2 -2
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +4 -3
- package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +9 -3
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +69 -22
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +33 -21
- package/src/RvfTopics/RvfTopics.tsx +25 -1
- package/src/utils/constants.ts +7 -4
- package/src/utils/hooks/useMapContext.tsx +5 -0
- package/src/utils/sharingWFSUtils.ts +6 -0
- package/testNotification.json +103 -50641
- package/src/NotificationLayer/notificationUtils.ts +0 -437
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@geops/rvf-mobility-web-component",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
4
|
"description": "Web components for rvf in the domains of mobility and logistics.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.44",
|
|
6
6
|
"homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"jspdf": "^2.5.2",
|
|
13
13
|
"lodash.debounce": "^4.0.8",
|
|
14
14
|
"maplibre-gl": "^4.7.1",
|
|
15
|
-
"mobility-toolbox-js": "3.
|
|
15
|
+
"mobility-toolbox-js": "3.3.0-beta.32",
|
|
16
16
|
"ol": "^10.3.1",
|
|
17
17
|
"preact": "^10.25.4",
|
|
18
18
|
"preact-custom-element": "^4.3.0",
|
|
@@ -51,7 +51,7 @@ export interface MobilityMapProps {
|
|
|
51
51
|
notification?: string;
|
|
52
52
|
notificationat?: string; // 2024-01-25T22:59:00Z
|
|
53
53
|
notificationbeforelayerid?: string;
|
|
54
|
-
notificationurl?: string; // https://moco.geops.io/api/v1/
|
|
54
|
+
notificationurl?: string; // https://moco.geops.io/api/v1/
|
|
55
55
|
permalink?: string;
|
|
56
56
|
realtime?: string;
|
|
57
57
|
realtimeurl?: string;
|
|
@@ -75,7 +75,7 @@ function MobilityMap({
|
|
|
75
75
|
notification = "true",
|
|
76
76
|
notificationat = null,
|
|
77
77
|
notificationbeforelayerid = null,
|
|
78
|
-
notificationurl =
|
|
78
|
+
notificationurl = "https://moco.geops.io/api/v1/",
|
|
79
79
|
permalink = "false",
|
|
80
80
|
realtime = "true",
|
|
81
81
|
realtimeurl = "wss://api.geops.io/tracker-ws/v1/ws",
|
|
@@ -1,203 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { useEffect, useMemo
|
|
1
|
+
import { MocoLayer } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { MocoLayerOptions } from "mobility-toolbox-js/ol/layers/MocoLayer";
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
5
5
|
|
|
6
6
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
-
import useZoom from "../utils/hooks/useZoom";
|
|
8
|
-
import {
|
|
9
|
-
addNotificationsLayers,
|
|
10
|
-
getCurrentGraph,
|
|
11
|
-
getNotificationsWithStatus,
|
|
12
|
-
parsePreviewNotification,
|
|
13
|
-
} from "./notificationUtils";
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
interface Metadata {
|
|
18
|
-
graphs?: Graphs;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const useStyleName = (): string => {
|
|
22
|
-
const { baselayer } = useMapContext();
|
|
23
|
-
return baselayer;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const useStyleMetadata = (): Metadata => {
|
|
27
|
-
const { baseLayer } = useMapContext();
|
|
28
|
-
const [styleMetadata, setStyleMetadata] = useState<Metadata>();
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (!baseLayer) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (!baseLayer.loaded) {
|
|
34
|
-
// @ts-expect-error bad type definition
|
|
35
|
-
baseLayer.once("load", () => {
|
|
36
|
-
return setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
|
|
37
|
-
});
|
|
38
|
-
} else {
|
|
39
|
-
setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
|
|
40
|
-
}
|
|
41
|
-
}, [baseLayer]);
|
|
42
|
-
return styleMetadata;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const useGraphMapping = (
|
|
46
|
-
defaultMapping: Record<number, string> = { 1: "osm" },
|
|
47
|
-
): Record<number, string> => {
|
|
48
|
-
const styleMetadata = useStyleMetadata();
|
|
49
|
-
const graphMapping = useMemo(() => {
|
|
50
|
-
return styleMetadata?.graphs || defaultMapping;
|
|
51
|
-
}, [defaultMapping, styleMetadata?.graphs]);
|
|
52
|
-
return graphMapping;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const useNotifications = (): FeatureCollection => {
|
|
56
|
-
const { notificationat, notificationurl } = useMapContext();
|
|
57
|
-
const [notifications, setNotifications] = useState([]);
|
|
58
|
-
const [previewNotification, setPreviewNotification] = useState(null);
|
|
59
|
-
const [shouldAddPreviewNotifications, setShouldAddPreviewNotifications] =
|
|
60
|
-
useState<boolean>(true);
|
|
61
|
-
|
|
62
|
-
const styleName = useStyleName();
|
|
63
|
-
const graphMapping = useGraphMapping();
|
|
64
|
-
|
|
65
|
-
const now = useMemo(() => {
|
|
66
|
-
return notificationat ? new Date(notificationat) : new Date();
|
|
67
|
-
}, [notificationat]);
|
|
68
|
-
|
|
69
|
-
const graphsString = useMemo(() => {
|
|
70
|
-
return [...new Set(Object.values(graphMapping))].join(",");
|
|
71
|
-
}, [graphMapping]);
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
// Listen for incoming messages through the MOCO iframe
|
|
75
|
-
window.addEventListener("message", (event) => {
|
|
76
|
-
if (event.data.notification) {
|
|
77
|
-
setPreviewNotification(event.data.notification);
|
|
78
|
-
setShouldAddPreviewNotifications(true);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
}, []);
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
let abortCtrl: AbortController;
|
|
85
|
-
|
|
86
|
-
// Fetch the main MOCO notifications
|
|
87
|
-
const fetchNotifications = async () => {
|
|
88
|
-
const suffix = /\?/.test(notificationurl) ? "&" : "?";
|
|
89
|
-
const url = `${notificationurl}${suffix}graph=${graphsString}`;
|
|
90
|
-
|
|
91
|
-
abortCtrl?.abort();
|
|
92
|
-
abortCtrl = new AbortController();
|
|
93
|
-
const response = await fetch(url, { signal: abortCtrl.signal });
|
|
94
|
-
const data = await response.json();
|
|
95
|
-
|
|
96
|
-
const notifications = getNotificationsWithStatus(data, now);
|
|
97
|
-
setNotifications(notifications);
|
|
98
|
-
setShouldAddPreviewNotifications(true);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
if (notificationurl && graphsString) {
|
|
102
|
-
fetchNotifications();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return () => {
|
|
106
|
-
abortCtrl?.abort();
|
|
107
|
-
};
|
|
108
|
-
}, [notificationurl, graphsString, now]);
|
|
109
|
-
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
// Merge notifications with the previewNotification
|
|
112
|
-
const newNotifications = [...notifications];
|
|
113
|
-
const notifByStyle = previewNotification?.[styleName];
|
|
114
|
-
|
|
115
|
-
if (shouldAddPreviewNotifications && notifByStyle) {
|
|
116
|
-
const parsedPreviewNotification = parsePreviewNotification(notifByStyle);
|
|
117
|
-
const index = newNotifications.findIndex((n) => {
|
|
118
|
-
return n.properties.id === notifByStyle.id;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (index > -1) {
|
|
122
|
-
newNotifications[index] = parsedPreviewNotification;
|
|
123
|
-
} else {
|
|
124
|
-
newNotifications.push(parsedPreviewNotification);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
setNotifications(getNotificationsWithStatus(newNotifications, now));
|
|
128
|
-
setShouldAddPreviewNotifications(false);
|
|
129
|
-
}
|
|
130
|
-
}, [
|
|
131
|
-
previewNotification,
|
|
132
|
-
notifications,
|
|
133
|
-
shouldAddPreviewNotifications,
|
|
134
|
-
styleName,
|
|
135
|
-
now,
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
if (previewNotification?.id) {
|
|
139
|
-
const index = notifications.findIndex((n) => {
|
|
140
|
-
return n.properties.id === previewNotification.id;
|
|
141
|
-
});
|
|
142
|
-
if (index > -1) {
|
|
143
|
-
notifications[index].isPreview = true;
|
|
144
|
-
notifications[index].features.forEach((feature) => {
|
|
145
|
-
feature.properties.isPreview = true;
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const features =
|
|
151
|
-
notifications
|
|
152
|
-
?.map((notification) => {
|
|
153
|
-
return notification.features;
|
|
154
|
-
})
|
|
155
|
-
.flat() || [];
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
features: features,
|
|
159
|
-
type: "FeatureCollection",
|
|
160
|
-
};
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
export default function NotificationLayer(props: MaplibreStyleLayerOptions) {
|
|
164
|
-
const notifications = useNotifications();
|
|
165
|
-
const { baseLayer, map, notificationbeforelayerid } = useMapContext();
|
|
166
|
-
const graphMapping = useGraphMapping();
|
|
167
|
-
const zoom = useZoom();
|
|
8
|
+
function NotificationLayer(props: MocoLayerOptions) {
|
|
9
|
+
const { apikey, baseLayer, map } = useMapContext();
|
|
168
10
|
|
|
169
11
|
const layer = useMemo(() => {
|
|
170
12
|
if (!baseLayer) {
|
|
171
13
|
return null;
|
|
172
14
|
}
|
|
173
|
-
return new
|
|
174
|
-
|
|
175
|
-
isQueryable: true,
|
|
176
|
-
layersFilter: ({ metadata }) => {
|
|
177
|
-
return metadata?.["general.filter"] === "notifications";
|
|
178
|
-
},
|
|
15
|
+
return new MocoLayer({
|
|
16
|
+
apiKey: apikey,
|
|
179
17
|
maplibreLayer: baseLayer,
|
|
180
18
|
...(props || {}),
|
|
181
19
|
});
|
|
182
|
-
}, [
|
|
183
|
-
|
|
184
|
-
// Update data and style layers
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
if (!baseLayer || !graphMapping) {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
const graph = getCurrentGraph(graphMapping, zoom);
|
|
190
|
-
if (!graph) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
addNotificationsLayers(
|
|
194
|
-
baseLayer,
|
|
195
|
-
"notifications",
|
|
196
|
-
notifications,
|
|
197
|
-
notificationbeforelayerid,
|
|
198
|
-
graph,
|
|
199
|
-
);
|
|
200
|
-
}, [baseLayer, graphMapping, notificationbeforelayerid, notifications, zoom]);
|
|
20
|
+
}, [apikey, baseLayer, props]);
|
|
201
21
|
|
|
202
22
|
useEffect(() => {
|
|
203
23
|
if (!map || !layer) {
|
|
@@ -212,3 +32,5 @@ export default function NotificationLayer(props: MaplibreStyleLayerOptions) {
|
|
|
212
32
|
|
|
213
33
|
return null;
|
|
214
34
|
}
|
|
35
|
+
|
|
36
|
+
export default memo(NotificationLayer);
|
|
@@ -15,14 +15,14 @@ function RouteStopTime(props: RouteStopTimeProps) {
|
|
|
15
15
|
return (
|
|
16
16
|
<div {...props}>
|
|
17
17
|
<span
|
|
18
|
-
className={`${isCancelled ? "text-
|
|
18
|
+
className={`${isCancelled ? "text-darkred line-through" : ""} ${
|
|
19
19
|
isFirst ? "hidden" : ""
|
|
20
20
|
}`}
|
|
21
21
|
>
|
|
22
22
|
{getHoursAndMinutes(aimedArrivalTime)}
|
|
23
23
|
</span>
|
|
24
24
|
<span
|
|
25
|
-
className={`${status.isCancelled ? "text-
|
|
25
|
+
className={`${status.isCancelled ? "text-darkred line-through" : ""} ${
|
|
26
26
|
isLast ? "hidden" : ""
|
|
27
27
|
}`}
|
|
28
28
|
>
|
|
@@ -4,7 +4,7 @@ import { memo } from "preact/compat";
|
|
|
4
4
|
|
|
5
5
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
6
6
|
import RvfLineNetworkDetails from "./RvfLineNetworkDetails/RvfLineNetworkDetails";
|
|
7
|
-
import RvfNotificationDetails from "./RvfNotificationDetails";
|
|
7
|
+
// import RvfNotificationDetails from "./RvfNotificationDetails";
|
|
8
8
|
import RvfSellingPointDetails from "./RvfSellingPointDetails";
|
|
9
9
|
import RvfSharedMobilityDetails from "./RvfSharedMobilityDetail";
|
|
10
10
|
|
|
@@ -25,7 +25,7 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
|
|
|
25
25
|
const { selectedFeature, selectedFeatures } = useRvfContext();
|
|
26
26
|
const isSharedMobility = getIsSharedMobility(selectedFeature);
|
|
27
27
|
const isSellingPoint = !!selectedFeature.get("tickets");
|
|
28
|
-
const isNotification = !!selectedFeature.get("
|
|
28
|
+
const isNotification = !!selectedFeature.get("severity");
|
|
29
29
|
const isLineNetwork = !!selectedFeature.get("original_line_id");
|
|
30
30
|
|
|
31
31
|
const showDefaultData = () => {
|
|
@@ -49,7 +49,8 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
|
|
|
49
49
|
{isSharedMobility && (
|
|
50
50
|
<RvfSharedMobilityDetails selectedFeature={selectedFeature} />
|
|
51
51
|
)}
|
|
52
|
-
{isNotification &&
|
|
52
|
+
{isNotification && null}
|
|
53
|
+
{/* <RvfNotificationDetails feature={selectedFeature} />} */}
|
|
53
54
|
{isLineNetwork && (
|
|
54
55
|
<RvfLineNetworkDetails
|
|
55
56
|
feature={selectedFeature}
|
|
@@ -52,6 +52,10 @@ function RvfLineNetworkDetails({
|
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
53
53
|
const [stopInfosOpenId, setStopInfosOpenId] = useState<string>(null);
|
|
54
54
|
|
|
55
|
+
const isRunsDisplay = useMemo(() => {
|
|
56
|
+
return new URLSearchParams(window.location.search).get("runs") === "true";
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
55
59
|
useEffect(() => {
|
|
56
60
|
const source = baseLayer?.mapLibreMap?.getSource(
|
|
57
61
|
LNP_SOURCE_ID,
|
|
@@ -117,7 +121,7 @@ function RvfLineNetworkDetails({
|
|
|
117
121
|
const byLineId = {};
|
|
118
122
|
features.forEach((f) => {
|
|
119
123
|
const lineId = f.get(ORIGINAL_LINE_ID_PROP);
|
|
120
|
-
if (lineId && !byLineId[lineId]) {
|
|
124
|
+
if (lineId && !byLineId[lineId] && f.get("stop_ids")) {
|
|
121
125
|
try {
|
|
122
126
|
byLineId[lineId] = JSON.parse(f.get("stop_ids"));
|
|
123
127
|
} catch (e) {
|
|
@@ -158,7 +162,7 @@ function RvfLineNetworkDetails({
|
|
|
158
162
|
// external_id,
|
|
159
163
|
long_name,
|
|
160
164
|
mot,
|
|
161
|
-
|
|
165
|
+
runs,
|
|
162
166
|
short_name: shortName,
|
|
163
167
|
text_color: textColor,
|
|
164
168
|
} = lineInfo;
|
|
@@ -218,7 +222,9 @@ function RvfLineNetworkDetails({
|
|
|
218
222
|
{!!longName && (
|
|
219
223
|
<div className={"flex-1 text-left"}>{longName}</div>
|
|
220
224
|
)}
|
|
221
|
-
{
|
|
225
|
+
{isRunsDisplay && (
|
|
226
|
+
<div className={"text-xs"}>{runs}</div>
|
|
227
|
+
)}
|
|
222
228
|
|
|
223
229
|
{!!stops && (
|
|
224
230
|
<button className={"shrink-0"}>
|
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
MaplibreStyleLayer,
|
|
4
4
|
RealtimeLayer as MbtRealtimeLayer,
|
|
5
5
|
} from "mobility-toolbox-js/ol";
|
|
6
|
+
import { MocoLayerOptions } from "mobility-toolbox-js/ol/layers/MocoLayer";
|
|
6
7
|
import {
|
|
8
|
+
MocoNotification,
|
|
7
9
|
RealtimeStation,
|
|
8
10
|
RealtimeStationId,
|
|
9
11
|
RealtimeStopSequence,
|
|
@@ -35,7 +37,7 @@ import RvfExportMenuButton from "../RvfExportMenuButton";
|
|
|
35
37
|
import RvfFeatureDetails from "../RvfFeatureDetails";
|
|
36
38
|
import RvfFloatingMenu from "../RvfFloatingMenu";
|
|
37
39
|
import RvfLayerTreeButton from "../RvfLayerTreeButton";
|
|
38
|
-
// Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%
|
|
40
|
+
// Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%2Fpublication%2F%3Fsso_config%3Drvf&geolocation=false&realtime=false&search=false¬ificationat=2024-01-25T22%3A59%3A00Z
|
|
39
41
|
import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
|
|
40
42
|
import Modal from "../RvfModal";
|
|
41
43
|
import RvfOverlayHeader from "../RvfOverlayHeader";
|
|
@@ -108,6 +110,7 @@ const realtimeLayerProps = {
|
|
|
108
110
|
bboxParameters: {
|
|
109
111
|
line_tags: "RVF",
|
|
110
112
|
},
|
|
113
|
+
|
|
111
114
|
fullTrajectoryStyle: fullTrajectoryStyle,
|
|
112
115
|
getMotsByZoom: (z) => {
|
|
113
116
|
if (z < 9) {
|
|
@@ -152,7 +155,7 @@ const realtimeLayerProps = {
|
|
|
152
155
|
function RvfMobilityMap({
|
|
153
156
|
apikey = "5cc87b12d7c5370001c1d655820abcc37dfd4d968d7bab5b2a74a935",
|
|
154
157
|
// baselayer = "review-geops-tgma-085vyz.de.rvf",
|
|
155
|
-
baselayer = "de.
|
|
158
|
+
baselayer = "de.rvf_moco",
|
|
156
159
|
center = null,
|
|
157
160
|
details = "true",
|
|
158
161
|
embed = "false",
|
|
@@ -167,10 +170,10 @@ function RvfMobilityMap({
|
|
|
167
170
|
maxzoom = "20",
|
|
168
171
|
minzoom = null,
|
|
169
172
|
mots = null,
|
|
170
|
-
notification = "
|
|
171
|
-
notificationat = null,
|
|
173
|
+
notification = "false",
|
|
174
|
+
notificationat = null, //"2025-09-10T00:00:00Z",
|
|
172
175
|
notificationbeforelayerid = null,
|
|
173
|
-
notificationurl =
|
|
176
|
+
notificationurl = "https://moco.geops.io/api/v1/",
|
|
174
177
|
permalink = "false",
|
|
175
178
|
print = "true",
|
|
176
179
|
realtime = "true",
|
|
@@ -207,6 +210,9 @@ function RvfMobilityMap({
|
|
|
207
210
|
const [sharedMobilityLayerGroup, setSharedMobilityLayerGroup] =
|
|
208
211
|
useState<Group>();
|
|
209
212
|
|
|
213
|
+
const [previewNotification, setPreviewNotification] =
|
|
214
|
+
useState<MocoNotification[]>();
|
|
215
|
+
|
|
210
216
|
// Convert string boolean to boolean
|
|
211
217
|
const hasToolbar = useMemo(() => {
|
|
212
218
|
return toolbar === "true";
|
|
@@ -221,8 +227,8 @@ function RvfMobilityMap({
|
|
|
221
227
|
}, [realtime]);
|
|
222
228
|
|
|
223
229
|
const hasNotification = useMemo(() => {
|
|
224
|
-
return notification === "true";
|
|
225
|
-
}, [notification]);
|
|
230
|
+
return notification === "true" || !!previewNotification;
|
|
231
|
+
}, [notification, previewNotification]);
|
|
226
232
|
|
|
227
233
|
const hasGeolocation = useMemo(() => {
|
|
228
234
|
return geolocation === "true";
|
|
@@ -278,12 +284,14 @@ function RvfMobilityMap({
|
|
|
278
284
|
notificationbeforelayerid,
|
|
279
285
|
notificationurl,
|
|
280
286
|
permalink,
|
|
287
|
+
previewNotification,
|
|
281
288
|
realtimeLayer,
|
|
282
289
|
realtimeurl,
|
|
283
290
|
setBaseLayer,
|
|
284
291
|
setIsFollowing,
|
|
285
292
|
setIsTracking,
|
|
286
293
|
setMap,
|
|
294
|
+
setPreviewNotification,
|
|
287
295
|
setRealtimeLayer,
|
|
288
296
|
setStation,
|
|
289
297
|
setStationId,
|
|
@@ -308,10 +316,10 @@ function RvfMobilityMap({
|
|
|
308
316
|
geolocation,
|
|
309
317
|
isFollowing,
|
|
310
318
|
isTracking,
|
|
319
|
+
layers,
|
|
311
320
|
map,
|
|
312
321
|
mapsurl,
|
|
313
322
|
maxextent,
|
|
314
|
-
layers,
|
|
315
323
|
maxzoom,
|
|
316
324
|
minzoom,
|
|
317
325
|
mots,
|
|
@@ -320,6 +328,7 @@ function RvfMobilityMap({
|
|
|
320
328
|
notificationbeforelayerid,
|
|
321
329
|
notificationurl,
|
|
322
330
|
permalink,
|
|
331
|
+
previewNotification,
|
|
323
332
|
realtimeLayer,
|
|
324
333
|
realtimeurl,
|
|
325
334
|
station,
|
|
@@ -398,6 +407,7 @@ function RvfMobilityMap({
|
|
|
398
407
|
layertree,
|
|
399
408
|
lineNetworkPlanLayer,
|
|
400
409
|
poisLayer,
|
|
410
|
+
previewNotification,
|
|
401
411
|
selectedFeature,
|
|
402
412
|
selectedFeatures,
|
|
403
413
|
sellingPointsLayer,
|
|
@@ -416,11 +426,12 @@ function RvfMobilityMap({
|
|
|
416
426
|
};
|
|
417
427
|
}, [
|
|
418
428
|
isExportMenuOpen,
|
|
419
|
-
layertree,
|
|
420
429
|
isLayerTreeOpen,
|
|
421
430
|
isShareMenuOpen,
|
|
431
|
+
layertree,
|
|
422
432
|
lineNetworkPlanLayer,
|
|
423
433
|
poisLayer,
|
|
434
|
+
previewNotification,
|
|
424
435
|
selectedFeature,
|
|
425
436
|
selectedFeatures,
|
|
426
437
|
sellingPointsLayer,
|
|
@@ -440,11 +451,20 @@ function RvfMobilityMap({
|
|
|
440
451
|
return {
|
|
441
452
|
format: (copyrights) => {
|
|
442
453
|
const newCopyrights = [];
|
|
454
|
+
let alreadyAGeopsLink = false;
|
|
443
455
|
copyrights.forEach((copyright) => {
|
|
444
|
-
if (
|
|
456
|
+
if (/geops/i.test(copyright)) {
|
|
457
|
+
// Hack until all geOps tiles are the same
|
|
458
|
+
// Currently there is some geops.com or geops.ch links
|
|
459
|
+
if (!alreadyAGeopsLink) {
|
|
460
|
+
alreadyAGeopsLink = true;
|
|
461
|
+
newCopyrights.push(copyright);
|
|
462
|
+
}
|
|
463
|
+
} else if (!/(sbb|rvf)/i.test(copyright)) {
|
|
445
464
|
newCopyrights.push(copyright);
|
|
446
465
|
}
|
|
447
466
|
});
|
|
467
|
+
|
|
448
468
|
return newCopyrights.join(" | ");
|
|
449
469
|
},
|
|
450
470
|
};
|
|
@@ -458,6 +478,19 @@ function RvfMobilityMap({
|
|
|
458
478
|
};
|
|
459
479
|
}, []);
|
|
460
480
|
|
|
481
|
+
const notificationsLayerProps: MocoLayerOptions = useMemo(() => {
|
|
482
|
+
return {
|
|
483
|
+
// beforeId: notificationbeforelayerid,
|
|
484
|
+
apiKey: apikey,
|
|
485
|
+
date: notificationat ? new Date(notificationat) : undefined,
|
|
486
|
+
isQueryable: true,
|
|
487
|
+
notifications: previewNotification,
|
|
488
|
+
tenant: "rvf",
|
|
489
|
+
title: RVF_LAYERS_TITLES.meldungen,
|
|
490
|
+
url: notificationurl,
|
|
491
|
+
};
|
|
492
|
+
}, [apikey, notificationat, notificationurl, previewNotification]);
|
|
493
|
+
|
|
461
494
|
useEffect(() => {
|
|
462
495
|
if (hasToolbar && isLayerTreeOpen) {
|
|
463
496
|
setIsExportMenuOpen(false);
|
|
@@ -520,6 +553,15 @@ function RvfMobilityMap({
|
|
|
520
553
|
}
|
|
521
554
|
}, [isShareMenuOpen, hasToolbar]);
|
|
522
555
|
|
|
556
|
+
useEffect(() => {
|
|
557
|
+
window.addEventListener("message", (event) => {
|
|
558
|
+
console.log("Received message from parent:", event.data);
|
|
559
|
+
if (event.data.notification) {
|
|
560
|
+
setPreviewNotification([event.data.notification]);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}, []);
|
|
564
|
+
|
|
523
565
|
return (
|
|
524
566
|
<I18nContext.Provider value={i18n}>
|
|
525
567
|
<style>{tailwind}</style>
|
|
@@ -537,6 +579,10 @@ function RvfMobilityMap({
|
|
|
537
579
|
<SingleClickListener />
|
|
538
580
|
{isEmbed && <RvfEmbedNavigation />}
|
|
539
581
|
|
|
582
|
+
{hasNotification && (
|
|
583
|
+
<NotificationLayer {...notificationsLayerProps} />
|
|
584
|
+
)}
|
|
585
|
+
|
|
540
586
|
<RvfSelectedFeatureHighlightLayer />
|
|
541
587
|
|
|
542
588
|
{hasRealtime && (
|
|
@@ -551,7 +597,6 @@ function RvfMobilityMap({
|
|
|
551
597
|
title={RVF_LAYERS_TITLES.haltestellen}
|
|
552
598
|
{...stationsLayerProps}
|
|
553
599
|
/>
|
|
554
|
-
{hasNotification && <NotificationLayer />}
|
|
555
600
|
<RvfTarifZonenLayer title={RVF_LAYERS_TITLES.tarifzonen} />
|
|
556
601
|
<RvfSellingPointsLayer
|
|
557
602
|
isQueryable={true}
|
|
@@ -625,17 +670,19 @@ function RvfMobilityMap({
|
|
|
625
670
|
<Station className="relative flex flex-col overflow-y-auto overflow-x-hidden p-2" />
|
|
626
671
|
</>
|
|
627
672
|
)}
|
|
628
|
-
{hasDetails &&
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
673
|
+
{hasDetails &&
|
|
674
|
+
selectedFeature &&
|
|
675
|
+
!selectedFeature.get("severity") && (
|
|
676
|
+
<>
|
|
677
|
+
<RvfOverlayHeader
|
|
678
|
+
onClose={() => {
|
|
679
|
+
setSelectedFeature(null);
|
|
680
|
+
}}
|
|
681
|
+
title={getFeatureInformationTitle(selectedFeature)}
|
|
682
|
+
></RvfOverlayHeader>
|
|
683
|
+
<RvfFeatureDetails className="relative flex h-full flex-col gap-4 overflow-y-auto overflow-x-hidden p-4" />
|
|
684
|
+
</>
|
|
685
|
+
)}
|
|
639
686
|
{hasToolbar && hasPrint && isExportMenuOpen && (
|
|
640
687
|
<>
|
|
641
688
|
<RvfOverlayHeader
|
|
@@ -29,10 +29,8 @@ import {
|
|
|
29
29
|
SOURCE_STATIONS_BIKE,
|
|
30
30
|
SOURCE_STATIONS_CAR,
|
|
31
31
|
SOURCE_STATIONS_CARGO_BIKE,
|
|
32
|
-
WFS_BIKE_TYPE,
|
|
33
|
-
WFS_CAR_TYPE,
|
|
34
|
-
WFS_CARGO_BIKE_TYPE,
|
|
35
32
|
WFS_FREE_FLOAT_TYPE,
|
|
33
|
+
WFS_STATIONS_TYPE,
|
|
36
34
|
} from "../utils/constants";
|
|
37
35
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
38
36
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
@@ -87,16 +85,12 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
87
85
|
useState<FeatureCollection<Point, SharingStationWFS>>();
|
|
88
86
|
const [carStationsData, setCarStationsData] =
|
|
89
87
|
useState<FeatureCollection<Point, SharingStationWFS>>();
|
|
90
|
-
|
|
91
88
|
const [bikeFreeFloatData, setBikeFreeFloatData] =
|
|
92
89
|
useState<FeatureCollection<Point, SharingVehicleWFS>>();
|
|
93
|
-
|
|
94
90
|
const [cargoBikeFreeFloatData, setCargoBikeFreeFloatData] =
|
|
95
91
|
useState<FeatureCollection<Point, SharingVehicleWFS>>();
|
|
96
|
-
|
|
97
92
|
const [carFreeFloatData, setCarFreeFloatData] =
|
|
98
93
|
useState<FeatureCollection<Point, SharingVehicleWFS>>();
|
|
99
|
-
|
|
100
94
|
const [scooterFreeFloatData, setScooterFreeFloatData] =
|
|
101
95
|
useState<FeatureCollection<Point, SharingVehicleWFS>>();
|
|
102
96
|
|
|
@@ -114,25 +108,43 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
114
108
|
|
|
115
109
|
const fetchData = async () => {
|
|
116
110
|
// Fill stations data
|
|
117
|
-
const
|
|
118
|
-
|
|
111
|
+
const stations = await fetchStations(
|
|
112
|
+
WFS_STATIONS_TYPE,
|
|
119
113
|
null,
|
|
120
114
|
abortController,
|
|
121
115
|
);
|
|
122
|
-
|
|
116
|
+
const bikeStationsData: FeatureCollection<Point, SharingStationWFS> = {
|
|
117
|
+
features: [],
|
|
118
|
+
type: "FeatureCollection",
|
|
119
|
+
};
|
|
120
|
+
const cargoBikeStationsData: FeatureCollection<Point, SharingStationWFS> =
|
|
121
|
+
{
|
|
122
|
+
features: [],
|
|
123
|
+
type: "FeatureCollection",
|
|
124
|
+
};
|
|
125
|
+
const carStationsData: FeatureCollection<Point, SharingStationWFS> = {
|
|
126
|
+
features: [],
|
|
127
|
+
type: "FeatureCollection",
|
|
128
|
+
};
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
stations?.features.forEach((station) => {
|
|
131
|
+
if (station.properties.num_bicycles_available !== null) {
|
|
132
|
+
station.properties.num_vehicles_available =
|
|
133
|
+
station.properties.num_bicycles_available;
|
|
134
|
+
bikeStationsData.features.push(station);
|
|
135
|
+
} else if (station.properties.num_cargo_bicycles_available !== null) {
|
|
136
|
+
station.properties.num_vehicles_available =
|
|
137
|
+
station.properties.num_cargo_bicycles_available;
|
|
138
|
+
cargoBikeStationsData.features.push(station);
|
|
139
|
+
} else if (station.properties.num_cars_available !== null) {
|
|
140
|
+
station.properties.num_vehicles_available =
|
|
141
|
+
station.properties.num_cars_available;
|
|
142
|
+
carStationsData.features.push(station);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
130
145
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
null,
|
|
134
|
-
abortController,
|
|
135
|
-
);
|
|
146
|
+
setBikeStationsData(bikeStationsData);
|
|
147
|
+
setCargoBikeStationsData(cargoBikeStationsData);
|
|
136
148
|
setCarStationsData(carStationsData);
|
|
137
149
|
|
|
138
150
|
// Fill free float data
|