@geops/rvf-mobility-web-component 0.1.83 → 0.1.84
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 +33 -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 +29 -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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { realtimeStyleUtils } from "mobility-toolbox-js/ol";
|
|
2
|
+
|
|
1
3
|
import getBgColor from "./getBgColor";
|
|
2
4
|
|
|
3
5
|
import type {
|
|
@@ -8,11 +10,13 @@ import type {
|
|
|
8
10
|
RealtimeTrajectory,
|
|
9
11
|
} from "mobility-toolbox-js/types";
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Return the color depending on an object representing a vehicle or a line.
|
|
15
|
+
* This function is used to have the same color on the map and on other components.
|
|
16
|
+
*/
|
|
12
17
|
const getMainColorForVehicle = (object: unknown = null): string => {
|
|
13
18
|
const line =
|
|
14
19
|
(object as RealtimeTrajectory)?.properties?.line ||
|
|
15
|
-
// @ts-expect-error bad type definition
|
|
16
20
|
(object as RealtimeStopSequence)?.line ||
|
|
17
21
|
(object as RealtimeLine);
|
|
18
22
|
|
|
@@ -35,10 +39,13 @@ const getMainColorForVehicle = (object: unknown = null): string => {
|
|
|
35
39
|
type = "rail";
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
color =
|
|
42
|
+
color =
|
|
43
|
+
getBgColor(type, line) ||
|
|
44
|
+
realtimeStyleUtils.getColorForType(type) ||
|
|
45
|
+
realtimeStyleUtils.getColorForType("rail");
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
if (color && color
|
|
48
|
+
if (color && !color.startsWith("#")) {
|
|
42
49
|
color = `#${color}`;
|
|
43
50
|
}
|
|
44
51
|
|
package/src/utils/getRadius.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { realtimeConfig } from "mobility-toolbox-js/ol";
|
|
2
2
|
|
|
3
|
+
import type { RealtimeMot } from "mobility-toolbox-js/types";
|
|
4
|
+
|
|
3
5
|
// mobility-portal config
|
|
4
6
|
const trackerRadiusMapping = {
|
|
5
7
|
0: [0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 4, 4, 6, 7, 7], // Tram
|
|
@@ -28,11 +30,15 @@ const radiusMapping: Record<number, number[]> = trackerRadiusMapping;
|
|
|
28
30
|
// [0, 0, 0, 0, 0, 2, 2, 3, 7, 7, 7, 7, 7, 7, 12, 12, 15],
|
|
29
31
|
// ];
|
|
30
32
|
|
|
31
|
-
export const getRadius = (
|
|
33
|
+
export const getRadius = (
|
|
34
|
+
type: number | RealtimeMot = 0,
|
|
35
|
+
zoom = 0,
|
|
36
|
+
cancelled = false,
|
|
37
|
+
) => {
|
|
38
|
+
const z = Math.min(Math.floor(zoom || 1), 16);
|
|
32
39
|
try {
|
|
33
|
-
// @ts-expect-error - bad type definition
|
|
34
40
|
const typeIdx = realtimeConfig.getTypeIndex(type);
|
|
35
|
-
return radiusMapping[typeIdx][
|
|
41
|
+
return radiusMapping[typeIdx][z] * (cancelled ? 2 : 1);
|
|
36
42
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
37
43
|
} catch (e) {
|
|
38
44
|
return 1;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { realtimeConfig } from "mobility-toolbox-js/ol";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
RealtimeDeparture,
|
|
5
|
+
RealtimeLine,
|
|
6
|
+
RealtimeStopSequence,
|
|
7
|
+
RealtimeTrajectory,
|
|
8
|
+
} from "mobility-toolbox-js/types";
|
|
9
|
+
|
|
10
|
+
import type { LnpLineInfo } from "./hooks/useLnp";
|
|
11
|
+
|
|
12
|
+
const getTextColorForVehicle = (object: unknown) => {
|
|
13
|
+
const textColor =
|
|
14
|
+
(object as LnpLineInfo | RealtimeLine).text_color ||
|
|
15
|
+
(object as RealtimeDeparture | RealtimeStopSequence).line?.text_color ||
|
|
16
|
+
(object as RealtimeTrajectory).properties?.line?.text_color;
|
|
17
|
+
|
|
18
|
+
if (textColor) {
|
|
19
|
+
return textColor;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const type =
|
|
23
|
+
(object as RealtimeStopSequence).type ||
|
|
24
|
+
(object as RealtimeTrajectory).properties?.type;
|
|
25
|
+
|
|
26
|
+
return realtimeConfig.getTextColorForType(type);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default getTextColorForVehicle;
|
|
@@ -2,6 +2,6 @@ import getTextFontForVehicle from "./getTextFontForVehicle";
|
|
|
2
2
|
|
|
3
3
|
describe("getTextFontForVehicle", () => {
|
|
4
4
|
it("returns font that inherit", () => {
|
|
5
|
-
expect(getTextFontForVehicle(12)).toBe("bold 12px arial");
|
|
5
|
+
expect(getTextFontForVehicle(null, null, 12)).toBe("bold 12px arial");
|
|
6
6
|
});
|
|
7
7
|
});
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import type { ViewState } from "mobility-toolbox-js/types";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Return the font
|
|
4
|
+
* Return the font depending on an object representing a vehicle or a line.
|
|
5
|
+
* This function is used to have the same font on the map and on other components.
|
|
3
6
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
const getTextFontForVehicle = (
|
|
8
|
+
object?: unknown,
|
|
9
|
+
viewState?: ViewState,
|
|
10
|
+
fontSize?: number,
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
12
|
+
text?: string,
|
|
13
|
+
) => {
|
|
6
14
|
return `bold ${fontSize}px arial`;
|
|
7
15
|
};
|
|
8
16
|
|
|
@@ -4,12 +4,18 @@ import type {
|
|
|
4
4
|
RealtimeTrajectory,
|
|
5
5
|
} from "mobility-toolbox-js/types";
|
|
6
6
|
|
|
7
|
+
import type { LnpLineInfo } from "./hooks/useLnp";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Return the text depending on an object representing a vehicle or a line.
|
|
11
|
+
* This function is used to have the same text on the map and on other components.
|
|
12
|
+
*/
|
|
7
13
|
const getTextForVehicle = (object: unknown = ""): string => {
|
|
8
14
|
const name =
|
|
9
15
|
(object as RealtimeTrajectory)?.properties?.line?.name ||
|
|
10
|
-
// @ts-expect-error bad type definition
|
|
11
16
|
(object as RealtimeStopSequence)?.line?.name ||
|
|
12
17
|
(object as RealtimeLine)?.name ||
|
|
18
|
+
(object as LnpLineInfo)?.short_name ||
|
|
13
19
|
(object as string) ||
|
|
14
20
|
"";
|
|
15
21
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Feature } from "ol";
|
|
2
|
+
import { Point } from "ol/geom";
|
|
3
|
+
import { fromExtent } from "ol/geom/Polygon";
|
|
4
|
+
import { useEffect, useRef } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
import useFitOnFeatures from "./useFitOnFeatures";
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
RealtimeRouteIdentifierMatch,
|
|
10
|
+
RealtimeTrainDetail,
|
|
11
|
+
} from "mobility-toolbox-js/types";
|
|
12
|
+
import type { GeoJSONFeature } from "ol/format/GeoJSON";
|
|
13
|
+
|
|
14
|
+
import type { LnpLineInfo, LnpStopInfo } from "./useLnp";
|
|
15
|
+
import type { StopsFeature } from "./useSearchStops";
|
|
16
|
+
|
|
17
|
+
export type FitOnObject = (obj: FitObject, willOverlayOpen?: boolean) => void;
|
|
18
|
+
|
|
19
|
+
export type FitObject =
|
|
20
|
+
| LnpLineInfo
|
|
21
|
+
| LnpStopInfo
|
|
22
|
+
| RealtimeRouteIdentifierMatch
|
|
23
|
+
| RealtimeTrainDetail
|
|
24
|
+
| StopsFeature;
|
|
25
|
+
|
|
26
|
+
function useFit() {
|
|
27
|
+
const fitOnFeatures = useFitOnFeatures();
|
|
28
|
+
const fit = useRef<FitOnObject>();
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fit.current = (obj: FitObject, willOverlayOpen = false) => {
|
|
32
|
+
if (
|
|
33
|
+
(obj as StopsFeature)?.type === "Feature" &&
|
|
34
|
+
(obj as StopsFeature).geometry
|
|
35
|
+
) {
|
|
36
|
+
fitOnFeatures.current([obj as GeoJSONFeature], willOverlayOpen);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let extent =
|
|
40
|
+
(obj as LnpLineInfo)?.extent || (obj as RealtimeTrainDetail)?.bounds;
|
|
41
|
+
|
|
42
|
+
if ((obj as RealtimeRouteIdentifierMatch)?.trains?.length) {
|
|
43
|
+
extent = (obj as RealtimeRouteIdentifierMatch).trains[0].bounds as [
|
|
44
|
+
number,
|
|
45
|
+
number,
|
|
46
|
+
number,
|
|
47
|
+
number,
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (extent) {
|
|
52
|
+
const feature = new Feature(fromExtent(extent));
|
|
53
|
+
fitOnFeatures.current([feature], willOverlayOpen);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const coordinate = (obj as LnpStopInfo)?.coordinates;
|
|
58
|
+
if (coordinate) {
|
|
59
|
+
const feature = new Feature(new Point(coordinate));
|
|
60
|
+
fitOnFeatures.current([feature], willOverlayOpen);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}, [fitOnFeatures]);
|
|
65
|
+
|
|
66
|
+
return fit;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default useFit;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { GeoJSON } from "ol/format";
|
|
2
|
+
import { Vector } from "ol/source";
|
|
3
|
+
import { useEffect, useRef } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
import { FIT_ON_FEATURES_MAX_ZOOM_POINT } from "../constants";
|
|
6
|
+
|
|
7
|
+
import useMapContext from "./useMapContext";
|
|
8
|
+
|
|
9
|
+
import type { Feature, Map } from "ol";
|
|
10
|
+
import type { GeoJSONFeature } from "ol/format/GeoJSON";
|
|
11
|
+
const geojson = new GeoJSON();
|
|
12
|
+
|
|
13
|
+
export type FitOnFeatures = (
|
|
14
|
+
features: (Feature | GeoJSONFeature)[],
|
|
15
|
+
willOverlayOpen?: boolean,
|
|
16
|
+
map?: Map,
|
|
17
|
+
) => void;
|
|
18
|
+
|
|
19
|
+
const useFitOnFeatures = () => {
|
|
20
|
+
const { isOverlayOpen, map: contextMap } = useMapContext();
|
|
21
|
+
|
|
22
|
+
const isOverlayOpenRef = useRef(isOverlayOpen);
|
|
23
|
+
const fitOnFeatures = useRef<FitOnFeatures>();
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
isOverlayOpenRef.current = isOverlayOpen;
|
|
27
|
+
}, [isOverlayOpen]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
fitOnFeatures.current = (
|
|
31
|
+
features: (Feature | GeoJSONFeature)[],
|
|
32
|
+
willOverlayOpen = false,
|
|
33
|
+
map?: Map,
|
|
34
|
+
) => {
|
|
35
|
+
if ((!map && !contextMap) || !features?.length) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let feats = features as Feature[];
|
|
39
|
+
|
|
40
|
+
// Convert to ol features if GeoJSON
|
|
41
|
+
const geoJSONFeature = features?.[0] as GeoJSONFeature;
|
|
42
|
+
if (geoJSONFeature?.geometry && geoJSONFeature?.type === "Feature") {
|
|
43
|
+
// Single feature case
|
|
44
|
+
feats = geojson.readFeatures(
|
|
45
|
+
{
|
|
46
|
+
features,
|
|
47
|
+
type: "FeatureCollection",
|
|
48
|
+
},
|
|
49
|
+
{ featureProjection: "EPSG:3857" },
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mapToUse = map || contextMap;
|
|
54
|
+
const extent = new Vector({ features: feats }).getExtent();
|
|
55
|
+
mapToUse.getView().fit(extent, {
|
|
56
|
+
duration: 500,
|
|
57
|
+
maxZoom:
|
|
58
|
+
extent[0] === extent[2] || extent[1] === extent[3]
|
|
59
|
+
? FIT_ON_FEATURES_MAX_ZOOM_POINT
|
|
60
|
+
: undefined,
|
|
61
|
+
padding: [
|
|
62
|
+
50,
|
|
63
|
+
50,
|
|
64
|
+
50,
|
|
65
|
+
willOverlayOpen || isOverlayOpenRef.current ? 350 : 50,
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
mapToUse?.getView().cancelAnimations();
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}, [contextMap]);
|
|
73
|
+
|
|
74
|
+
return fitOnFeatures;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default useFitOnFeatures;
|
|
@@ -4,10 +4,13 @@ import { LAYERS_NAMES } from "../constants";
|
|
|
4
4
|
|
|
5
5
|
import useMapContext from "./useMapContext";
|
|
6
6
|
|
|
7
|
+
import type { LAYER_TREE_HIDE_PROP } from "../constants";
|
|
8
|
+
|
|
7
9
|
export interface LayerConfig {
|
|
8
10
|
featurelink?: {
|
|
9
11
|
href?: string;
|
|
10
12
|
};
|
|
13
|
+
[LAYER_TREE_HIDE_PROP]?: boolean;
|
|
11
14
|
link?: {
|
|
12
15
|
href?: string;
|
|
13
16
|
show?: boolean;
|
|
@@ -6,8 +6,9 @@ import useMapContext from "./useMapContext";
|
|
|
6
6
|
|
|
7
7
|
import type { VectorTileSource } from "maplibre-gl";
|
|
8
8
|
|
|
9
|
-
export interface
|
|
9
|
+
export interface LnpLineInfo {
|
|
10
10
|
color: string;
|
|
11
|
+
extent: [number, number, number, number];
|
|
11
12
|
external_id: string;
|
|
12
13
|
id: string;
|
|
13
14
|
long_name: string;
|
|
@@ -18,16 +19,19 @@ export interface LineInfo {
|
|
|
18
19
|
text_color: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export interface
|
|
22
|
+
export interface LnpStopInfo {
|
|
23
|
+
codes: string[];
|
|
24
|
+
coordinates: [number, number];
|
|
22
25
|
external_id: string;
|
|
23
26
|
importance: number;
|
|
24
27
|
long_name: string;
|
|
25
28
|
short_name: string;
|
|
29
|
+
tralis_network: string;
|
|
26
30
|
visibility_level: number;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
export type LinesInfos = Record<string,
|
|
30
|
-
export type StopsInfos = Record<string,
|
|
33
|
+
export type LinesInfos = Record<string, LnpLineInfo>;
|
|
34
|
+
export type StopsInfos = Record<string, LnpStopInfo>;
|
|
31
35
|
|
|
32
36
|
let cacheLnpSourceInfo: {
|
|
33
37
|
[LNP_MD_LINES]: LinesInfos;
|
|
@@ -91,7 +95,7 @@ export function useLnpStopsInfos(): StopsInfos {
|
|
|
91
95
|
* This hook search line informations from lnp data. It takes a string in
|
|
92
96
|
* parameter then it will search if there is a property that exactly match this value.
|
|
93
97
|
*/
|
|
94
|
-
function useLnpLineInfo(text: string):
|
|
98
|
+
function useLnpLineInfo(text: string): LnpLineInfo {
|
|
95
99
|
const linesInfos = useLnpLinesInfos();
|
|
96
100
|
|
|
97
101
|
if (!linesInfos || !text) {
|
|
@@ -109,4 +113,34 @@ function useLnpLineInfo(text: string): LineInfo {
|
|
|
109
113
|
});
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
/**
|
|
117
|
+
* This hook search line informations from lnp data. It takes a string in
|
|
118
|
+
* parameter then it will search if there is a property that exactly match this value.
|
|
119
|
+
*/
|
|
120
|
+
export function useLnpStopInfo(text: string): LnpStopInfo {
|
|
121
|
+
const stationsInfos = useLnpStopsInfos();
|
|
122
|
+
|
|
123
|
+
if (!stationsInfos || !text) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (stationsInfos[text]) {
|
|
128
|
+
return stationsInfos[text];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return Object.values(stationsInfos).find((info) => {
|
|
132
|
+
return (
|
|
133
|
+
["id", "external_id", "short_name", "long_name"].find((key) => {
|
|
134
|
+
return !!info[key] && info[key].toLowerCase() === text.toLowerCase();
|
|
135
|
+
}) ||
|
|
136
|
+
info?.codes?.find((code) => {
|
|
137
|
+
return (
|
|
138
|
+
code.toLowerCase() === text.toLowerCase() ||
|
|
139
|
+
code.split(":")[1]?.toLowerCase() === text.toLowerCase()
|
|
140
|
+
);
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
112
146
|
export default useLnpLineInfo;
|
|
@@ -97,14 +97,12 @@ export type MapContextType = {
|
|
|
97
97
|
setSelectedFeature: (feature: Feature) => void;
|
|
98
98
|
setSelectedFeatures: (features: Feature[]) => void;
|
|
99
99
|
setStation: (station?: RealtimeStation) => void;
|
|
100
|
-
setStationId: (stationId?: RealtimeStationId) => void;
|
|
100
|
+
setStationId: (stationId?: RealtimeStationId | string) => void;
|
|
101
101
|
setStationsLayer: (stationsLayer?: MaplibreStyleLayer) => void;
|
|
102
|
-
setStopSequence: (stopSequence?: RealtimeStopSequence) => void;
|
|
103
102
|
setTrainId: (trainId?: RealtimeTrainId) => void;
|
|
104
103
|
station: RealtimeStation;
|
|
105
|
-
stationId: RealtimeStationId;
|
|
104
|
+
stationId: RealtimeStationId | string;
|
|
106
105
|
stationsLayer: MaplibreStyleLayer;
|
|
107
|
-
stopSequence: RealtimeStopSequence;
|
|
108
106
|
trainId: RealtimeTrainId;
|
|
109
107
|
} & MobilityMapProps;
|
|
110
108
|
|
|
@@ -150,7 +148,6 @@ export const MapContext = createContext<MapContextType>({
|
|
|
150
148
|
setStation: (station?: RealtimeStation) => {},
|
|
151
149
|
setStationId: (stationId?: RealtimeStationId) => {},
|
|
152
150
|
setStationsLayer: (stationsLayer?: MaplibreStyleLayer) => {},
|
|
153
|
-
setStopSequence: (stopSequence?: RealtimeStopSequence) => {},
|
|
154
151
|
setTrainId: (trainId?: RealtimeTrainId) => {},
|
|
155
152
|
} as MapContextType);
|
|
156
153
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { debounceDeparturesMessages } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
3
|
+
|
|
4
|
+
import useMapContext from "./useMapContext";
|
|
5
|
+
|
|
6
|
+
import type { RealtimeDeparture } from "mobility-toolbox-js/types";
|
|
7
|
+
|
|
8
|
+
function useRealtimeDepartures(stationId: number | string) {
|
|
9
|
+
const { realtimeLayer } = useMapContext();
|
|
10
|
+
const [departures, setDepartures] = useState<RealtimeDeparture[]>();
|
|
11
|
+
|
|
12
|
+
const api = useMemo(() => {
|
|
13
|
+
return realtimeLayer?.api;
|
|
14
|
+
}, [realtimeLayer?.api]);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!stationId || !api) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!api.wsApi.open) {
|
|
22
|
+
api.open();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const onMessage = debounceDeparturesMessages(
|
|
26
|
+
(newDepartures: RealtimeDeparture[]) => {
|
|
27
|
+
setDepartures(newDepartures);
|
|
28
|
+
return null;
|
|
29
|
+
},
|
|
30
|
+
false,
|
|
31
|
+
180,
|
|
32
|
+
);
|
|
33
|
+
// @ts-expect-error bad type definition
|
|
34
|
+
api.subscribeTimetable(stationId, onMessage);
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
setDepartures(null);
|
|
38
|
+
api.unsubscribeTimetable(stationId as number);
|
|
39
|
+
};
|
|
40
|
+
}, [stationId, api]);
|
|
41
|
+
|
|
42
|
+
return departures;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default useRealtimeDepartures;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect, useState } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
import useMapContext from "./useMapContext";
|
|
4
|
+
|
|
5
|
+
import type { RealtimeTrajectory } from "mobility-toolbox-js/types";
|
|
6
|
+
|
|
7
|
+
function useRealtimeRenderedTrajectories(trainId: number | string) {
|
|
8
|
+
const { realtimeLayer } = useMapContext();
|
|
9
|
+
const [trajectory, setTrajectory] = useState<RealtimeTrajectory>();
|
|
10
|
+
const [trainIdFound, setTrainIdFound] = useState<string>();
|
|
11
|
+
|
|
12
|
+
// We try to find the trainId every second until we have it
|
|
13
|
+
// TODO: find a efficient way to find the trajectory without polling
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (trainIdFound || !trainId || !realtimeLayer) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const timeout = setInterval(() => {
|
|
19
|
+
let traj = realtimeLayer?.trajectories?.[trainId];
|
|
20
|
+
|
|
21
|
+
if (!traj) {
|
|
22
|
+
traj = Object.values(realtimeLayer?.trajectories)?.find((item) => {
|
|
23
|
+
return item.properties.route_identifier === trainId;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (traj) {
|
|
28
|
+
setTrajectory(traj);
|
|
29
|
+
setTrainIdFound(traj.properties.train_id);
|
|
30
|
+
}
|
|
31
|
+
}, 1000);
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
clearInterval(timeout);
|
|
35
|
+
setTrainIdFound(undefined);
|
|
36
|
+
};
|
|
37
|
+
}, [trainId, realtimeLayer, trainIdFound]);
|
|
38
|
+
|
|
39
|
+
return trajectory;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default useRealtimeRenderedTrajectories;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
import useMapContext from "./useMapContext";
|
|
4
|
+
|
|
5
|
+
import type { RealtimeStation } from "mobility-toolbox-js/types";
|
|
6
|
+
|
|
7
|
+
function useRealtimeStation(stationId: number | string) {
|
|
8
|
+
const { realtimeLayer } = useMapContext();
|
|
9
|
+
const [station, setStation] = useState<RealtimeStation>();
|
|
10
|
+
const api = useMemo(() => {
|
|
11
|
+
return realtimeLayer?.api;
|
|
12
|
+
}, [realtimeLayer?.api]);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!stationId || !api) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!api.wsApi.open) {
|
|
20
|
+
api.wsApi.connect(api.url);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
api.subscribe(`station ${stationId}`, ({ content }) => {
|
|
24
|
+
if (content) {
|
|
25
|
+
setStation(content as RealtimeStation);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
setStation(undefined);
|
|
31
|
+
if (stationId) {
|
|
32
|
+
api?.unsubscribe(`station ${stationId}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}, [stationId, api]);
|
|
36
|
+
return station;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default useRealtimeStation;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
import useMapContext from "./useMapContext";
|
|
4
|
+
|
|
5
|
+
import type { RealtimeStopSequence } from "mobility-toolbox-js/types";
|
|
6
|
+
|
|
7
|
+
function useRealtimeStopSequences(trainId: string) {
|
|
8
|
+
const { realtimeLayer } = useMapContext();
|
|
9
|
+
const [stopSequences, setStopSequences] = useState<RealtimeStopSequence[]>();
|
|
10
|
+
|
|
11
|
+
const api = useMemo(() => {
|
|
12
|
+
return realtimeLayer?.api;
|
|
13
|
+
}, [realtimeLayer?.api]);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
let trainIdSubscribed = null;
|
|
17
|
+
if (!trainId || !api) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!api.wsApi.open) {
|
|
22
|
+
api.open();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
trainIdSubscribed = trainId;
|
|
26
|
+
|
|
27
|
+
const onMessage = ({ content }) => {
|
|
28
|
+
if (content) {
|
|
29
|
+
setStopSequences([...content]);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
api.subscribeStopSequence(trainId, onMessage);
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
setStopSequences([]);
|
|
36
|
+
api.unsubscribeStopSequence(trainIdSubscribed);
|
|
37
|
+
};
|
|
38
|
+
}, [trainId, api]);
|
|
39
|
+
|
|
40
|
+
return stopSequences;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default useRealtimeStopSequences;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { RealtimeRestAPI } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { type RealtimeTrainDetail } from "mobility-toolbox-js/types";
|
|
3
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
import useMapContext from "./useMapContext";
|
|
6
|
+
|
|
7
|
+
function useRealtimeTrainByRouteIdentifier(
|
|
8
|
+
routeIdentifier: string,
|
|
9
|
+
): RealtimeTrainDetail {
|
|
10
|
+
const { apikey, hasRealtime, realtimeresturl, realtimetenant, tenant } =
|
|
11
|
+
useMapContext();
|
|
12
|
+
const [train, setTrain] = useState<RealtimeTrainDetail>();
|
|
13
|
+
|
|
14
|
+
const tenantMemo = useMemo(() => {
|
|
15
|
+
return realtimetenant || tenant;
|
|
16
|
+
}, [realtimetenant, tenant]);
|
|
17
|
+
|
|
18
|
+
const api = useMemo(() => {
|
|
19
|
+
if (!apikey || !tenantMemo || !hasRealtime) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return new RealtimeRestAPI({
|
|
23
|
+
apiKey: apikey,
|
|
24
|
+
tenant: tenantMemo,
|
|
25
|
+
url: realtimeresturl,
|
|
26
|
+
});
|
|
27
|
+
}, [apikey, tenantMemo, hasRealtime, realtimeresturl]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!api || !routeIdentifier) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const abortCtrl: AbortController = new AbortController();
|
|
35
|
+
|
|
36
|
+
api
|
|
37
|
+
.trainsByRouteIdentifier(
|
|
38
|
+
{
|
|
39
|
+
exact_match: true,
|
|
40
|
+
query: routeIdentifier,
|
|
41
|
+
},
|
|
42
|
+
{ signal: abortCtrl.signal },
|
|
43
|
+
)
|
|
44
|
+
.then((res) => {
|
|
45
|
+
if (res.matches.length > 0) {
|
|
46
|
+
setTrain(res.matches[0].trains[0]);
|
|
47
|
+
} else {
|
|
48
|
+
setTrain(undefined);
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
.catch((e) => {
|
|
52
|
+
// AbortError is expected
|
|
53
|
+
if (e.code !== 20) {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.error(
|
|
56
|
+
"Failed to fetch train by route identifier",
|
|
57
|
+
routeIdentifier,
|
|
58
|
+
e,
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
abortCtrl?.abort();
|
|
66
|
+
};
|
|
67
|
+
}, [api, routeIdentifier]);
|
|
68
|
+
|
|
69
|
+
return train;
|
|
70
|
+
}
|
|
71
|
+
export default useRealtimeTrainByRouteIdentifier;
|