@geops/rvf-mobility-web-component 0.1.83 → 0.1.85
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 +40 -0
- package/index.js +206 -187
- package/package.json +2 -2
- package/src/Departure/Departure.tsx +6 -3
- package/src/FeatureDetails/FeatureDetails.tsx +4 -4
- package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +22 -0
- package/src/LayerTreeMenu/LayerTreeMenu.tsx +12 -4
- package/src/LayoutState/LayoutState.tsx +144 -63
- package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +2 -2
- package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +2 -40
- package/src/MapLayout/MapLayout.tsx +2 -7
- package/src/MobilityMap/MobilityMap.tsx +1 -6
- package/src/MobilityMap/MobilityMapAttributes.ts +28 -7
- package/src/NotificationDetails/NotificationDetails.tsx +21 -22
- package/src/NotificationsLayer/NotificationsLayer.tsx +47 -3
- package/src/OverlayDetails/OverlayDetails.tsx +5 -0
- package/src/RealtimeLayer/RealtimeLayer.tsx +44 -50
- package/src/RouteIcon/RouteIcon.tsx +25 -14
- package/src/RouteSchedule/RouteSchedule.tsx +14 -11
- package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +6 -3
- package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +8 -2
- package/src/RouteStop/RouteStop.tsx +14 -29
- package/src/RouteStopPlatform/RouteStopPlatform.tsx +1 -3
- package/src/RouteStopProgress/RouteStopProgress.tsx +11 -18
- package/src/RouteStopStation/RouteStopStation.tsx +3 -3
- package/src/RvfRealtimeLayer/RvfRealtimeLayer.tsx +16 -3
- package/src/RvfSearch/RvfSearch.tsx +5 -4
- package/src/Search/Search.tsx +41 -20
- package/src/Search/SearchBase.tsx +169 -0
- package/src/Search/SearchHeadless.tsx +79 -0
- package/src/Search/index.tsx +2 -0
- package/src/SearchLinesResult/SearchLinesResult.tsx +43 -0
- package/src/SearchLinesResult/index.tsx +1 -0
- package/src/SearchLinesResults/SearchLinesResults.tsx +106 -0
- package/src/SearchLinesResults/index.tsx +1 -0
- package/src/SearchLnpStopsResult/SearchLnpStopsResult.tsx +42 -0
- package/src/SearchLnpStopsResult/index.tsx +1 -0
- package/src/SearchLnpStopsResults/SearchLnpStopsResults.tsx +106 -0
- package/src/SearchLnpStopsResults/index.tsx +1 -0
- package/src/SearchResult/SearchResult.tsx +25 -0
- package/src/SearchResult/index.tsx +1 -0
- package/src/SearchResults/SearchResults.tsx +81 -0
- package/src/SearchResults/index.tsx +1 -0
- package/src/SearchResultsHeader/SearchResultsHeader.tsx +36 -0
- package/src/SearchResultsHeader/index.tsx +1 -0
- package/src/SearchStopsResult/SearchStopsResult.tsx +43 -0
- package/src/SearchStopsResult/index.tsx +1 -0
- package/src/SearchStopsResults/SearchStopsResults.tsx +101 -0
- package/src/SearchStopsResults/index.tsx +1 -0
- package/src/SearchTrainsResult/SearchTrainsResult.tsx +42 -0
- package/src/SearchTrainsResult/index.tsx +1 -0
- package/src/SearchTrainsResults/SearchTrainsResults.tsx +109 -0
- package/src/SearchTrainsResults/index.tsx +1 -0
- package/src/SingleClickListener/SingleClickListener.tsx +38 -2
- package/src/Station/Station.tsx +23 -48
- package/src/StationHeader/StationHeader.tsx +3 -3
- package/src/StationsLayer/StationsLayer.tsx +9 -1
- package/src/StopsSearch/StopsSearch.tsx +2 -2
- package/src/ui/InputSearch/InputSearch.tsx +105 -0
- package/src/ui/InputSearch/index.tsx +1 -0
- package/src/utils/centerOnVehicle.ts +9 -2
- package/src/utils/constants.ts +8 -1
- package/src/utils/fullTrajectoryStyle.ts +4 -7
- package/src/utils/getBgColor.ts +4 -2
- package/src/utils/getDelayColorForVehicle.test.ts +21 -11
- package/src/utils/getDelayColorForVehicle.ts +7 -5
- package/src/utils/getDelayTextForVehicle.test.ts +12 -12
- package/src/utils/getDelayTextForVehicle.ts +4 -0
- package/src/utils/getMainColorForVehicle.ts +11 -4
- package/src/utils/getRadius.ts +9 -3
- package/src/utils/getTextColor.ts +1 -1
- package/src/utils/getTextColorForVehicle.ts +31 -0
- package/src/utils/getTextFontForVehicle.test.ts +1 -1
- package/src/utils/getTextFontForVehicle.tsx +11 -3
- package/src/utils/getTextForVehicle.ts +7 -1
- package/src/utils/hooks/useFit.tsx +69 -0
- package/src/utils/hooks/useFitOnFeatures.tsx +77 -0
- package/src/utils/hooks/useLayersConfig.tsx +3 -0
- package/src/utils/hooks/useLnp.tsx +39 -5
- package/src/utils/hooks/useMapContext.tsx +2 -5
- package/src/utils/hooks/useRealtimeDepartures.tsx +45 -0
- package/src/utils/hooks/useRealtimeRenderedTrajectory.tsx +42 -0
- package/src/utils/hooks/useRealtimeStation.tsx +39 -0
- package/src/utils/hooks/useRealtimeStopSequences.tsx +43 -0
- package/src/utils/hooks/useRealtimeTrainsByRouteIdentifier.tsx +71 -0
- package/src/utils/hooks/useRouteStop.tsx +7 -1
- package/src/utils/hooks/useSearchLines.tsx +34 -0
- package/src/utils/hooks/useSearchLnpStops.tsx +38 -0
- package/src/utils/hooks/useSearchStops.tsx +85 -0
- package/src/utils/hooks/useSearchTrains.tsx +83 -0
- package/src/utils/realtimeRVFStyle.ts +38 -30
- package/src/utils/translations.ts +17 -0
- package/tash +58 -0
- package/src/utils/centerOnStation.ts +0 -18
- package/src/utils/getDelayFontForVehicle.test.ts +0 -7
- package/src/utils/getDelayFontForVehicle.tsx +0 -8
- package/src/utils/hooks/useStation.tsx +0 -22
|
@@ -35,8 +35,8 @@ export type MobilityMapAttributeName =
|
|
|
35
35
|
| "layers"
|
|
36
36
|
| "layersconfig"
|
|
37
37
|
| "layertree"
|
|
38
|
+
| "lineid"
|
|
38
39
|
| "lnp"
|
|
39
|
-
| "lnpid"
|
|
40
40
|
| "mainlink"
|
|
41
41
|
| "mainlinktitle"
|
|
42
42
|
| "mapset"
|
|
@@ -52,6 +52,7 @@ export type MobilityMapAttributeName =
|
|
|
52
52
|
| "mots"
|
|
53
53
|
| "notification"
|
|
54
54
|
| "notificationat"
|
|
55
|
+
| "notificationid"
|
|
55
56
|
| "notificationtenant"
|
|
56
57
|
| "notificationurl"
|
|
57
58
|
| "permalink"
|
|
@@ -60,14 +61,17 @@ export type MobilityMapAttributeName =
|
|
|
60
61
|
| "queryablelayers"
|
|
61
62
|
| "realtime"
|
|
62
63
|
| "realtimebboxparameters"
|
|
64
|
+
| "realtimeresturl"
|
|
63
65
|
| "realtimetenant"
|
|
64
66
|
| "realtimeurl"
|
|
65
67
|
| "runs"
|
|
66
68
|
| "search"
|
|
67
69
|
| "share"
|
|
70
|
+
| "stationid"
|
|
68
71
|
| "stopsurl"
|
|
69
72
|
| "tenant"
|
|
70
73
|
| "toolbar"
|
|
74
|
+
| "trainid"
|
|
71
75
|
| "zoom";
|
|
72
76
|
|
|
73
77
|
export type MobilityMapAttributes = Record<
|
|
@@ -165,16 +169,16 @@ where:
|
|
|
165
169
|
public: true,
|
|
166
170
|
type: "boolean",
|
|
167
171
|
},
|
|
172
|
+
lineid: {
|
|
173
|
+
description: `An id or a short/long name of a line to highlight. <br/>Ex: S1`,
|
|
174
|
+
public: false,
|
|
175
|
+
},
|
|
168
176
|
lnp: {
|
|
169
177
|
defaultValue: "true",
|
|
170
178
|
description: `Add the linesnetworkplans layer to the map. This layer will display lines network plans on the map.`,
|
|
171
179
|
public: false,
|
|
172
180
|
type: "boolean",
|
|
173
181
|
},
|
|
174
|
-
lnpid: {
|
|
175
|
-
description: `An id or a short/long name of a line to highlight. <br/>Ex: S1`,
|
|
176
|
-
public: false,
|
|
177
|
-
},
|
|
178
182
|
mainlink: {
|
|
179
183
|
description:
|
|
180
184
|
"A link displayed on bottom left of the map. The link can be a template, for example you can use {{x}} {{y}} {{z}} to insert the current position of the map in the url.<br/>Ex: http://mywebsite/mypage#map/{{x}}/{{y}}/{{z}}.",
|
|
@@ -192,7 +196,7 @@ where:
|
|
|
192
196
|
},
|
|
193
197
|
mapsetplanid: {
|
|
194
198
|
description:
|
|
195
|
-
"
|
|
199
|
+
"An id of the mapset plan to display. Mostly for debugging purposes.",
|
|
196
200
|
public: false,
|
|
197
201
|
},
|
|
198
202
|
mapsettags: {
|
|
@@ -206,7 +210,7 @@ where:
|
|
|
206
210
|
},
|
|
207
211
|
mapsettimestamp: {
|
|
208
212
|
description: `The ${geopsMapsetApiLink} timestamp used to load valid standard plan. If not defined it will use the current time.`,
|
|
209
|
-
public:
|
|
213
|
+
public: true,
|
|
210
214
|
},
|
|
211
215
|
mapseturl: {
|
|
212
216
|
defaultValue: "https://editor.mapset.io/api/v1/",
|
|
@@ -250,6 +254,10 @@ where:
|
|
|
250
254
|
"An ISO date string used to display active notification at this date in the notification layer. If not defined the current date will be used.<br/>Ex: 2025-08-01T00:00:00Z .",
|
|
251
255
|
public: false,
|
|
252
256
|
},
|
|
257
|
+
notificationid: {
|
|
258
|
+
description: `An id of a notification to show details of.`,
|
|
259
|
+
public: false,
|
|
260
|
+
},
|
|
253
261
|
notificationtenant: {
|
|
254
262
|
defaultValue: "rvf",
|
|
255
263
|
description: `The ${geopsMocoApiLink} tenant to get the notification from.`,
|
|
@@ -298,6 +306,11 @@ where:
|
|
|
298
306
|
"A space separated list of parameters to add to the realtime BBOX request to define custom behavior.<br/>Ex: graph=XXX line_tags=XXX.",
|
|
299
307
|
public: false,
|
|
300
308
|
},
|
|
309
|
+
realtimeresturl: {
|
|
310
|
+
defaultValue: "https://api.geops.io/tracker-http/v1/",
|
|
311
|
+
description: `The ${geopsRealtimeApiLink} REST API url to use for fetching infos about realtime data.`,
|
|
312
|
+
public: false,
|
|
313
|
+
},
|
|
301
314
|
realtimetenant: {
|
|
302
315
|
description: `The ${geopsRealtimeApiLink} tenant to get the realtime data from.`,
|
|
303
316
|
public: false,
|
|
@@ -326,6 +339,10 @@ where:
|
|
|
326
339
|
public: true,
|
|
327
340
|
type: "boolean",
|
|
328
341
|
},
|
|
342
|
+
stationid: {
|
|
343
|
+
description: `An id or a short/long name of a station to show details of.`,
|
|
344
|
+
public: false,
|
|
345
|
+
},
|
|
329
346
|
stopsurl: {
|
|
330
347
|
defaultValue: "https://api.geops.io/stops/v1/",
|
|
331
348
|
description: `The ${geopsStopsApiLink} to use.`,
|
|
@@ -341,6 +358,10 @@ where:
|
|
|
341
358
|
public: true,
|
|
342
359
|
type: "boolean",
|
|
343
360
|
},
|
|
361
|
+
trainid: {
|
|
362
|
+
description: `An id of a route to highlight on the map and to show details of.`,
|
|
363
|
+
public: false,
|
|
364
|
+
},
|
|
344
365
|
zoom: {
|
|
345
366
|
description: "The zoom level of the map.",
|
|
346
367
|
public: true,
|
|
@@ -9,6 +9,7 @@ import Warning from "../icons/Warning";
|
|
|
9
9
|
import ShadowOverflow from "../ShadowOverflow";
|
|
10
10
|
import Link from "../ui/Link";
|
|
11
11
|
import useI18n from "../utils/hooks/useI18n";
|
|
12
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
12
13
|
import useMocoSituation from "../utils/hooks/useMocoSituation";
|
|
13
14
|
|
|
14
15
|
import type {
|
|
@@ -36,18 +37,6 @@ const toShortDate = (date: Date, showTime, showYear?: boolean) => {
|
|
|
36
37
|
.replace(/\.$/, "");
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
// const getLine = (name: string, lines: NotificationLine[]): NotificationLine => {
|
|
40
|
-
// if (lines?.length) {
|
|
41
|
-
// const line = lines.find((linee) => {
|
|
42
|
-
// return linee.name === name;
|
|
43
|
-
// });
|
|
44
|
-
// if (line) {
|
|
45
|
-
// return line;
|
|
46
|
-
// }
|
|
47
|
-
// }
|
|
48
|
-
// return { mot: "bus", name } as NotificationLine;
|
|
49
|
-
// };
|
|
50
|
-
|
|
51
40
|
export type NotificationLine = {
|
|
52
41
|
mot?: RealtimeMot;
|
|
53
42
|
operator_name?: string;
|
|
@@ -57,15 +46,14 @@ export type NotificationLine = {
|
|
|
57
46
|
|
|
58
47
|
function NotificationDetails({
|
|
59
48
|
className,
|
|
60
|
-
feature,
|
|
61
49
|
...props
|
|
62
50
|
}: {
|
|
63
51
|
className?: string;
|
|
64
52
|
feature: Feature;
|
|
65
53
|
}) {
|
|
66
54
|
const { locale, t } = useI18n();
|
|
67
|
-
const {
|
|
68
|
-
const situationParsed = useMocoSituation(
|
|
55
|
+
const { notificationId } = useMapContext();
|
|
56
|
+
const situationParsed = useMocoSituation(notificationId);
|
|
69
57
|
|
|
70
58
|
// moco export v2
|
|
71
59
|
let textualContentMultilingual: Partial<MultilingualTextualContentType> = {};
|
|
@@ -81,6 +69,17 @@ function NotificationDetails({
|
|
|
81
69
|
// Find the current publication(s) at the current date
|
|
82
70
|
publicationsToDisplay =
|
|
83
71
|
publicationsArr?.filter(({ publicationWindows }) => {
|
|
72
|
+
// In some cases publicationWindows can be undefined here but defined at the
|
|
73
|
+
// root of the object so we apply the root publicationWindows to all publications with empty one
|
|
74
|
+
if (
|
|
75
|
+
!publicationWindows?.length &&
|
|
76
|
+
situationParsed?.publicationWindows?.length
|
|
77
|
+
) {
|
|
78
|
+
// @ts-expect-error we should not set this value directly
|
|
79
|
+
// eslint-disable-next-line no-param-reassign
|
|
80
|
+
publicationWindows = situationParsed.publicationWindows;
|
|
81
|
+
}
|
|
82
|
+
|
|
84
83
|
return publicationWindows.find(({ endTime, startTime }) => {
|
|
85
84
|
const now = new Date();
|
|
86
85
|
const startT = new Date(startTime);
|
|
@@ -209,13 +208,13 @@ function NotificationDetails({
|
|
|
209
208
|
},
|
|
210
209
|
)}
|
|
211
210
|
<div className={"my-4 flex flex-col gap-4"}>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
textualContent?.description
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
211
|
+
{textualContent?.description && (
|
|
212
|
+
<div
|
|
213
|
+
dangerouslySetInnerHTML={{
|
|
214
|
+
__html: textualContent?.description,
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
)}
|
|
219
218
|
{!!textualContentMultilingual?.images?.length && (
|
|
220
219
|
<div className="flex flex-wrap gap-2">
|
|
221
220
|
{textualContentMultilingual.images.map(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MocoLayer, type MocoLayerOptions } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { unByKey } from "ol/Observable";
|
|
2
3
|
import { memo } from "preact/compat";
|
|
3
|
-
import { useEffect, useMemo } from "preact/hooks";
|
|
4
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
4
5
|
|
|
5
6
|
import { LAYER_NAME_NOTIFICATIONS } from "../utils/constants";
|
|
6
7
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
@@ -9,6 +10,7 @@ function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
|
|
|
9
10
|
const {
|
|
10
11
|
apikey,
|
|
11
12
|
baseLayer,
|
|
13
|
+
linesNetworkPlanLayer,
|
|
12
14
|
map,
|
|
13
15
|
notificationat,
|
|
14
16
|
notificationtenant,
|
|
@@ -16,6 +18,9 @@ function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
|
|
|
16
18
|
previewNotifications,
|
|
17
19
|
setNotificationsLayer,
|
|
18
20
|
} = useMapContext();
|
|
21
|
+
const [isLnpVisible, setIsLnpVisible] = useState(
|
|
22
|
+
!!linesNetworkPlanLayer?.getVisible(),
|
|
23
|
+
);
|
|
19
24
|
|
|
20
25
|
const layer = useMemo(() => {
|
|
21
26
|
if (!baseLayer) {
|
|
@@ -42,12 +47,12 @@ function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
|
|
|
42
47
|
}
|
|
43
48
|
return mocoLayer;
|
|
44
49
|
}, [
|
|
45
|
-
apikey,
|
|
46
50
|
baseLayer,
|
|
51
|
+
apikey,
|
|
47
52
|
notificationat,
|
|
53
|
+
previewNotifications,
|
|
48
54
|
notificationtenant,
|
|
49
55
|
notificationurl,
|
|
50
|
-
previewNotifications,
|
|
51
56
|
props,
|
|
52
57
|
]);
|
|
53
58
|
|
|
@@ -66,6 +71,45 @@ function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
|
|
|
66
71
|
};
|
|
67
72
|
}, [map, layer]);
|
|
68
73
|
|
|
74
|
+
// Watch lnp visibility
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const key = linesNetworkPlanLayer?.on("change:visible", () => {
|
|
77
|
+
const visible = linesNetworkPlanLayer.getVisible();
|
|
78
|
+
setIsLnpVisible(visible);
|
|
79
|
+
});
|
|
80
|
+
setIsLnpVisible(linesNetworkPlanLayer?.getVisible() || false);
|
|
81
|
+
return () => {
|
|
82
|
+
unByKey(key);
|
|
83
|
+
};
|
|
84
|
+
}, [linesNetworkPlanLayer]);
|
|
85
|
+
|
|
86
|
+
// Change the layersFilter based on lnp visibility
|
|
87
|
+
// Moco layers must de hidden by default otherwise, on load, everything is displayed
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!layer) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const visible = layer.getVisible();
|
|
93
|
+
if (visible) {
|
|
94
|
+
layer.setVisible(false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isLnpVisible) {
|
|
98
|
+
layer.layersFilter = (layerSpec) => {
|
|
99
|
+
return layerSpec.metadata?.["general.filter"] === "moco.lnp";
|
|
100
|
+
};
|
|
101
|
+
} else {
|
|
102
|
+
layer.layersFilter = (layerSpec) => {
|
|
103
|
+
return layerSpec.metadata?.["general.filter"] === "moco";
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
layer.setVisible(visible);
|
|
108
|
+
return () => {
|
|
109
|
+
layer.applyLayoutVisibility();
|
|
110
|
+
};
|
|
111
|
+
}, [isLnpVisible, layer, linesNetworkPlanLayer]);
|
|
112
|
+
|
|
69
113
|
return null;
|
|
70
114
|
}
|
|
71
115
|
|
|
@@ -39,15 +39,19 @@ function OverlayDetails() {
|
|
|
39
39
|
if (featuresInfo?.layer) {
|
|
40
40
|
return featuresInfo.layer;
|
|
41
41
|
}
|
|
42
|
+
|
|
42
43
|
if (trainId) {
|
|
43
44
|
return realtimeLayer;
|
|
44
45
|
}
|
|
46
|
+
|
|
45
47
|
if (stationId) {
|
|
46
48
|
return stationsLayer;
|
|
47
49
|
}
|
|
50
|
+
|
|
48
51
|
if (linesIds) {
|
|
49
52
|
return linesNetworkPlanLayer;
|
|
50
53
|
}
|
|
54
|
+
|
|
51
55
|
if (notificationId) {
|
|
52
56
|
return notificationsLayer;
|
|
53
57
|
}
|
|
@@ -63,6 +67,7 @@ function OverlayDetails() {
|
|
|
63
67
|
linesNetworkPlanLayer,
|
|
64
68
|
notificationsLayer,
|
|
65
69
|
]);
|
|
70
|
+
|
|
66
71
|
return (
|
|
67
72
|
<>
|
|
68
73
|
<OverlayDetailsHeader
|
|
@@ -2,15 +2,18 @@ import {
|
|
|
2
2
|
getGraphByZoom,
|
|
3
3
|
RealtimeLayer as MtbRealtimeLayer,
|
|
4
4
|
} from "mobility-toolbox-js/ol";
|
|
5
|
+
import GeoJSON from "ol/format/GeoJSON";
|
|
5
6
|
import { unByKey } from "ol/Observable";
|
|
7
|
+
import { Vector } from "ol/source";
|
|
6
8
|
import { memo } from "preact/compat";
|
|
7
9
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
8
10
|
|
|
9
11
|
import centerOnVehicle from "../utils/centerOnVehicle";
|
|
10
12
|
import { LAYER_NAME_REALTIME } from "../utils/constants";
|
|
11
13
|
import getDelayColorForVehicle from "../utils/getDelayColorForVehicle";
|
|
12
|
-
import getDelayFontForVehicle from "../utils/getDelayFontForVehicle";
|
|
13
14
|
import getDelayTextForVehicle from "../utils/getDelayTextForVehicle";
|
|
15
|
+
import getMainColorForVehicle from "../utils/getMainColorForVehicle";
|
|
16
|
+
import getTextColorForVehicle from "../utils/getTextColorForVehicle";
|
|
14
17
|
import getTextFontForVehicle from "../utils/getTextFontForVehicle";
|
|
15
18
|
import getTextForVehicle from "../utils/getTextForVehicle";
|
|
16
19
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
@@ -18,7 +21,6 @@ import useMapContext from "../utils/hooks/useMapContext";
|
|
|
18
21
|
import type { RealtimeLayerOptions } from "mobility-toolbox-js/ol/layers/RealtimeLayer";
|
|
19
22
|
import type {
|
|
20
23
|
RealtimeMot,
|
|
21
|
-
RealtimeStation,
|
|
22
24
|
RealtimeTrainId,
|
|
23
25
|
StyleMetadataGraphs,
|
|
24
26
|
} from "mobility-toolbox-js/types";
|
|
@@ -26,6 +28,7 @@ import type {
|
|
|
26
28
|
const TRACKING_ZOOM = 16;
|
|
27
29
|
|
|
28
30
|
const useGraphs = true; //;window.location?.href?.includes("graphs=true");
|
|
31
|
+
const geojson = new GeoJSON();
|
|
29
32
|
|
|
30
33
|
function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
31
34
|
const {
|
|
@@ -41,10 +44,6 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
41
44
|
setIsFollowing,
|
|
42
45
|
setIsTracking,
|
|
43
46
|
setRealtimeLayer,
|
|
44
|
-
setStation,
|
|
45
|
-
setStopSequence,
|
|
46
|
-
stationId,
|
|
47
|
-
stopSequence,
|
|
48
47
|
tenant,
|
|
49
48
|
trainId,
|
|
50
49
|
} = useMapContext();
|
|
@@ -74,16 +73,18 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
74
73
|
}
|
|
75
74
|
: undefined,
|
|
76
75
|
isQueryable: true,
|
|
76
|
+
minZoom: 5, // It depends fo the radius mapping in realtimeStyleUtils
|
|
77
77
|
name: LAYER_NAME_REALTIME,
|
|
78
78
|
tenant,
|
|
79
79
|
url: realtimeurl,
|
|
80
80
|
zIndex: 1,
|
|
81
81
|
...props,
|
|
82
82
|
styleOptions: {
|
|
83
|
+
getColor: getMainColorForVehicle,
|
|
83
84
|
getDelayColor: getDelayColorForVehicle,
|
|
84
|
-
getDelayFont: getDelayFontForVehicle,
|
|
85
85
|
getDelayText: getDelayTextForVehicle,
|
|
86
86
|
getText: getTextForVehicle,
|
|
87
|
+
getTextColor: getTextColorForVehicle,
|
|
87
88
|
getTextFont: getTextFontForVehicle,
|
|
88
89
|
...(props?.styleOptions || {}),
|
|
89
90
|
},
|
|
@@ -114,10 +115,10 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
114
115
|
|
|
115
116
|
// Behavior when vehicle is selected or not.
|
|
116
117
|
useEffect(() => {
|
|
117
|
-
if (!
|
|
118
|
+
if (!trainId) {
|
|
118
119
|
setIsFollowing(false);
|
|
119
120
|
}
|
|
120
|
-
}, [
|
|
121
|
+
}, [trainId, setIsFollowing]);
|
|
121
122
|
|
|
122
123
|
// Behavior when user tracking is activated or not.
|
|
123
124
|
useEffect(() => {
|
|
@@ -152,11 +153,13 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
152
153
|
|
|
153
154
|
if (layer) {
|
|
154
155
|
layer.engine.useThrottle = !isFollowing;
|
|
156
|
+
// We deactivate the bbox on moveend when following to have smoother experience.
|
|
157
|
+
// The correct bbox will be set later by getting the full trajectory.
|
|
155
158
|
layer.engine.isUpdateBboxOnMoveEnd = !isFollowing;
|
|
156
159
|
// layer.useRequestAnimationFrame = isFollowing;
|
|
157
160
|
layer.allowRenderWhenAnimating = !!isFollowing;
|
|
158
161
|
}
|
|
159
|
-
if (!isFollowing || !
|
|
162
|
+
if (!isFollowing || !trainId || !map || !layer) {
|
|
160
163
|
return;
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -166,11 +169,29 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
166
169
|
let vehicle = id && layer?.trajectories?.[id];
|
|
167
170
|
|
|
168
171
|
if (!vehicle) {
|
|
169
|
-
const message = await layer.api.getTrajectory(
|
|
170
|
-
|
|
172
|
+
const message = await layer.api.getTrajectory(trainId, layer.mode);
|
|
173
|
+
vehicle = message?.content;
|
|
174
|
+
|
|
175
|
+
// We get the fulltrajectory to set the api bbox correctly.
|
|
176
|
+
const fullMessage = await layer.api.getFullTrajectory(
|
|
177
|
+
trainId,
|
|
171
178
|
layer.mode,
|
|
179
|
+
undefined,
|
|
172
180
|
);
|
|
173
|
-
|
|
181
|
+
if (fullMessage?.content) {
|
|
182
|
+
try {
|
|
183
|
+
const features = geojson.readFeatures(fullMessage.content);
|
|
184
|
+
const extent = new Vector({ features }).getExtent();
|
|
185
|
+
layer.api.bbox = [...extent, ...layer.api.bbox.slice(4)];
|
|
186
|
+
} catch (err) {
|
|
187
|
+
// eslint-disable-next-line no-console
|
|
188
|
+
console.warn(
|
|
189
|
+
"Error parsing full trajectory feature:",
|
|
190
|
+
err,
|
|
191
|
+
fullMessage?.content,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
174
195
|
}
|
|
175
196
|
|
|
176
197
|
const success = await centerOnVehicle(vehicle, map, TRACKING_ZOOM);
|
|
@@ -178,16 +199,19 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
178
199
|
// Once the map is zoomed on the vehicle we follow him, only recenter , no zoom changes.
|
|
179
200
|
if (success === true) {
|
|
180
201
|
interval = setInterval(() => {
|
|
181
|
-
void centerOnVehicle(
|
|
202
|
+
void centerOnVehicle(
|
|
203
|
+
layer?.trajectories?.[trainId],
|
|
204
|
+
map,
|
|
205
|
+
TRACKING_ZOOM,
|
|
206
|
+
);
|
|
182
207
|
}, 1000);
|
|
183
208
|
}
|
|
184
209
|
};
|
|
185
|
-
void followVehicle(
|
|
186
|
-
|
|
210
|
+
void followVehicle(trainId);
|
|
187
211
|
return () => {
|
|
188
212
|
clearInterval(interval);
|
|
189
213
|
};
|
|
190
|
-
}, [isFollowing, map, layer,
|
|
214
|
+
}, [isFollowing, map, layer, trainId, setIsTracking]);
|
|
191
215
|
|
|
192
216
|
// DO NOT CENTER ON THE TRAIN
|
|
193
217
|
// useEffect(() => {
|
|
@@ -200,26 +224,7 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
200
224
|
// }
|
|
201
225
|
// }, [map, trainId, layer]);
|
|
202
226
|
|
|
203
|
-
//
|
|
204
|
-
useEffect(() => {
|
|
205
|
-
if (!stationId || !layer?.api) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
layer?.api?.subscribe(`station ${stationId}`, ({ content }) => {
|
|
209
|
-
if (content) {
|
|
210
|
-
setStation(content as RealtimeStation);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return () => {
|
|
215
|
-
setStation(null);
|
|
216
|
-
if (stationId) {
|
|
217
|
-
layer?.api?.unsubscribe(`station ${stationId}`);
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}, [stationId, layer?.api, setStation]);
|
|
221
|
-
|
|
222
|
-
// Subscribe to the stop sequence of the selected vehicle.
|
|
227
|
+
// Subscribe to the full trajectory of the selected vehicle.
|
|
223
228
|
useEffect(() => {
|
|
224
229
|
if (!trainId || !layer?.api) {
|
|
225
230
|
return;
|
|
@@ -230,25 +235,14 @@ function RealtimeLayer(props: Partial<RealtimeLayerOptions>) {
|
|
|
230
235
|
console.error("Error highlighting trajectory:", err);
|
|
231
236
|
});
|
|
232
237
|
|
|
233
|
-
layer?.api?.subscribeStopSequence(trainId, ({ content }) => {
|
|
234
|
-
if (content) {
|
|
235
|
-
const [firstStopSequence] = content;
|
|
236
|
-
if (firstStopSequence) {
|
|
237
|
-
setStopSequence(firstStopSequence);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
238
|
return () => {
|
|
243
|
-
|
|
244
|
-
if (trainId && layer) {
|
|
245
|
-
layer.api?.unsubscribeStopSequence(trainId);
|
|
239
|
+
if (layer?.selectedVehicleId) {
|
|
246
240
|
layer.api?.unsubscribeFullTrajectory(layer.selectedVehicleId);
|
|
247
241
|
layer.selectedVehicleId = null;
|
|
248
242
|
layer.vectorLayer.getSource().clear();
|
|
249
243
|
}
|
|
250
244
|
};
|
|
251
|
-
}, [trainId, layer, layer?.api
|
|
245
|
+
}, [trainId, layer, layer?.api]);
|
|
252
246
|
|
|
253
247
|
// Get graphs value
|
|
254
248
|
useEffect(() => {
|
|
@@ -2,9 +2,10 @@ import { twMerge } from "tailwind-merge";
|
|
|
2
2
|
|
|
3
3
|
import NoRealtime from "../icons/NoRealtime";
|
|
4
4
|
import getMainColorForVehicle from "../utils/getMainColorForVehicle";
|
|
5
|
-
import
|
|
5
|
+
import getTextColorForVehicle from "../utils/getTextColorForVehicle";
|
|
6
6
|
import getTextFontForVehicle from "../utils/getTextFontForVehicle";
|
|
7
7
|
import getTextForVehicle from "../utils/getTextForVehicle";
|
|
8
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
8
9
|
|
|
9
10
|
import type {
|
|
10
11
|
RealtimeDeparture,
|
|
@@ -14,11 +15,14 @@ import type {
|
|
|
14
15
|
} from "mobility-toolbox-js/types";
|
|
15
16
|
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
16
17
|
|
|
18
|
+
import type { LnpLineInfo } from "../utils/hooks/useLnp";
|
|
19
|
+
|
|
17
20
|
export type RouteIconProps = {
|
|
18
21
|
className?: string;
|
|
19
22
|
departure?: RealtimeDeparture;
|
|
20
23
|
displayNoRealtimeIcon?: boolean;
|
|
21
24
|
line?: RealtimeLine;
|
|
25
|
+
lineInfo?: LnpLineInfo;
|
|
22
26
|
stopSequence?: RealtimeStopSequence;
|
|
23
27
|
trajectory?: RealtimeTrajectory;
|
|
24
28
|
} & HTMLAttributes<HTMLSpanElement> &
|
|
@@ -31,33 +35,40 @@ function RouteIcon({
|
|
|
31
35
|
departure,
|
|
32
36
|
displayNoRealtimeIcon = false,
|
|
33
37
|
line,
|
|
38
|
+
lineInfo,
|
|
34
39
|
stopSequence,
|
|
35
40
|
trajectory,
|
|
36
41
|
...props
|
|
37
42
|
}: RouteIconProps) {
|
|
43
|
+
const { realtimeLayer } = useMapContext();
|
|
38
44
|
const lineToUse =
|
|
39
45
|
line ||
|
|
46
|
+
lineInfo ||
|
|
40
47
|
departure?.line ||
|
|
41
48
|
stopSequence?.line ||
|
|
42
49
|
trajectory?.properties?.line;
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const color = lineToUse?.text_color || getTextColor(type);
|
|
48
|
-
let borderColor = lineToUse?.stroke || "black";
|
|
49
|
-
const text = getTextForVehicle(
|
|
50
|
-
line || departure || stopSequence || trajectory,
|
|
51
|
-
);
|
|
50
|
+
const trainId = stopSequence?.id || departure?.train_id;
|
|
51
|
+
const trajectoryToUse = trajectory || realtimeLayer?.trajectories[trainId];
|
|
52
|
+
const objectToUse =
|
|
53
|
+
line || lineInfo || departure || stopSequence || trajectory;
|
|
52
54
|
|
|
55
|
+
const backgroundColor = getMainColorForVehicle(objectToUse);
|
|
56
|
+
const color = getTextColorForVehicle(objectToUse);
|
|
57
|
+
const text = getTextForVehicle(objectToUse);
|
|
53
58
|
const fontSize = fontSizesByNbLetters[text.length] || 12;
|
|
54
|
-
const font = getTextFontForVehicle(fontSize, text);
|
|
59
|
+
const font = getTextFontForVehicle(objectToUse, null, fontSize, text);
|
|
55
60
|
|
|
56
61
|
// RealtimeIcon only for stopsequence for now
|
|
57
|
-
const hasRealtime =
|
|
58
|
-
|
|
62
|
+
const hasRealtime =
|
|
63
|
+
stopSequence?.has_realtime_journey ||
|
|
64
|
+
departure?.has_realtime_journey ||
|
|
65
|
+
trajectoryToUse?.properties?.has_realtime_journey ||
|
|
66
|
+
(stopSequence?.stations?.[0]?.state &&
|
|
67
|
+
stopSequence?.stations?.[0]?.state !== "TIME_BASED");
|
|
68
|
+
const showNoRealtimeIcon = !!stopSequence || !!departure || !!trajectoryToUse;
|
|
59
69
|
const isCancelled = stopSequence?.stations[0]?.state === "JOURNEY_CANCELLED";
|
|
60
70
|
|
|
71
|
+
let borderColor = lineToUse?.stroke || "black";
|
|
61
72
|
if (borderColor === backgroundColor) {
|
|
62
73
|
borderColor = "black";
|
|
63
74
|
}
|
|
@@ -81,7 +92,7 @@ function RouteIcon({
|
|
|
81
92
|
{displayNoRealtimeIcon &&
|
|
82
93
|
showNoRealtimeIcon &&
|
|
83
94
|
!isCancelled &&
|
|
84
|
-
!hasRealtime && <NoRealtime className={"absolute -top-
|
|
95
|
+
!hasRealtime && <NoRealtime className={"absolute -top-3 -right-3"} />}
|
|
85
96
|
</span>
|
|
86
97
|
);
|
|
87
98
|
}
|
|
@@ -7,15 +7,19 @@ import RouteScheduleHeader from "../RouteScheduleHeader";
|
|
|
7
7
|
import RouteStop from "../RouteStop";
|
|
8
8
|
import ShadowOverflow from "../ShadowOverflow";
|
|
9
9
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
10
|
+
import useRealtimeStopSequences from "../utils/hooks/useRealtimeStopSequences";
|
|
10
11
|
|
|
11
12
|
import type { RealtimeStop } from "mobility-toolbox-js/types";
|
|
12
|
-
import type {
|
|
13
|
+
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
13
14
|
|
|
14
|
-
export type RouteScheduleProps =
|
|
15
|
+
export type RouteScheduleProps = {
|
|
16
|
+
className?: string;
|
|
17
|
+
} & HTMLAttributes<HTMLDivElement> &
|
|
15
18
|
PreactDOMAttributes;
|
|
16
19
|
|
|
17
|
-
function RouteSchedule(
|
|
18
|
-
const {
|
|
20
|
+
function RouteSchedule({ className }: RouteScheduleProps) {
|
|
21
|
+
const { trainId } = useMapContext();
|
|
22
|
+
const stopSequences = useRealtimeStopSequences(trainId);
|
|
19
23
|
const ref = useRef();
|
|
20
24
|
|
|
21
25
|
useEffect(() => {
|
|
@@ -38,17 +42,15 @@ function RouteSchedule(props: RouteScheduleProps) {
|
|
|
38
42
|
clearTimeout(interval);
|
|
39
43
|
};
|
|
40
44
|
// Scroll automatically when a new scroll infos is set.
|
|
41
|
-
}, [
|
|
45
|
+
}, [stopSequences]);
|
|
42
46
|
|
|
43
|
-
if (!
|
|
47
|
+
if (!stopSequences?.[0]) {
|
|
44
48
|
return null;
|
|
45
49
|
}
|
|
46
|
-
|
|
47
|
-
const { className } = props;
|
|
48
|
-
|
|
50
|
+
const stopSequence = stopSequences[0];
|
|
49
51
|
return (
|
|
50
52
|
<>
|
|
51
|
-
<RouteScheduleHeader />
|
|
53
|
+
<RouteScheduleHeader stopSequence={stopSequence} />
|
|
52
54
|
<ShadowOverflow>
|
|
53
55
|
<div className={twMerge("text-base", className)} ref={ref}>
|
|
54
56
|
{stopSequence.stations.map((stop: RealtimeStop, index: number) => {
|
|
@@ -62,10 +64,11 @@ function RouteSchedule(props: RouteScheduleProps) {
|
|
|
62
64
|
(`${stationId}` || stationName) + arrivalTime + departureTime
|
|
63
65
|
}
|
|
64
66
|
stop={stop}
|
|
67
|
+
stopSequence={stopSequence}
|
|
65
68
|
/>
|
|
66
69
|
);
|
|
67
70
|
})}
|
|
68
|
-
<RouteScheduleFooter />
|
|
71
|
+
<RouteScheduleFooter stopSequence={stopSequence} />
|
|
69
72
|
</div>
|
|
70
73
|
</ShadowOverflow>
|
|
71
74
|
</>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { memo } from "preact/compat";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import type { RealtimeStopSequence } from "mobility-toolbox-js/types";
|
|
4
4
|
|
|
5
5
|
const defaultRenderLink = (text: string, url: string) => {
|
|
6
6
|
return url ? (
|
|
@@ -17,8 +17,11 @@ const defaultRenderLink = (text: string, url: string) => {
|
|
|
17
17
|
);
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
function RouteScheduleFooter(
|
|
21
|
-
|
|
20
|
+
function RouteScheduleFooter({
|
|
21
|
+
stopSequence,
|
|
22
|
+
}: {
|
|
23
|
+
stopSequence: RealtimeStopSequence;
|
|
24
|
+
}) {
|
|
22
25
|
if (!stopSequence.operator && !stopSequence.publisher) {
|
|
23
26
|
return null;
|
|
24
27
|
}
|