@geops/rvf-mobility-web-component 0.1.74 → 0.1.76
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 +28 -0
- package/README.md +17 -55
- package/docutils.js +1 -1
- package/index.html +9 -1
- package/index.js +114 -110
- package/package.json +2 -2
- package/src/FeatureDetails/FeatureDetails.tsx +9 -18
- package/src/LayerTreeMenu/LayerTreeMenu.tsx +1 -1
- package/src/LayoutState/LayoutState.tsx +14 -1
- package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +37 -68
- package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +11 -3
- package/src/MobilityMap/MobilityMap.tsx +1 -5
- package/src/MobilityMap/MobilityMapAttributes.ts +9 -5
- package/src/MobilityNotifications/MobilityNotifications.tsx +1 -6
- package/src/NotificationsLayer/NotificationsLayer.tsx +1 -1
- package/src/OverlayContent/OverlayContent.tsx +1 -2
- package/src/OverlayDetails/OverlayDetails.tsx +23 -2
- package/src/OverlayDetailsHeader/OverlayDetailsHeader.tsx +3 -0
- package/src/OverlayHeader/OverlayHeader.tsx +1 -1
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +5 -4
- package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +1 -1
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +95 -84
- package/src/SingleClickListener/SingleClickListener.tsx +1 -1
- package/src/utils/constants.ts +2 -1
- package/src/utils/hooks/useLayersConfig.tsx +3 -0
- package/src/utils/hooks/{useLnpLineInfo.tsx → useLnp.tsx} +24 -9
- package/src/utils/hooks/useMapContext.tsx +4 -0
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +0 -238
- package/src/RvfSingleClickListener/index.tsx +0 -1
|
@@ -95,90 +95,6 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
95
95
|
return baseLayer?.mapLibreMap;
|
|
96
96
|
}, [baseLayer?.mapLibreMap]);
|
|
97
97
|
|
|
98
|
-
const updateData = useCallback(() => {
|
|
99
|
-
const abortController = new AbortController();
|
|
100
|
-
const extent = transformExtent(
|
|
101
|
-
map.getView().calculateExtent(),
|
|
102
|
-
"EPSG:3857",
|
|
103
|
-
"EPSG:4326",
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const fetchData = async () => {
|
|
107
|
-
// Fill stations data
|
|
108
|
-
// The stations are also loaded in the style to have them displayed during export
|
|
109
|
-
const stations = await fetchStations(
|
|
110
|
-
WFS_STATIONS_TYPE,
|
|
111
|
-
abortController,
|
|
112
|
-
extent,
|
|
113
|
-
);
|
|
114
|
-
setStationsData(stations);
|
|
115
|
-
|
|
116
|
-
// Fill free float data
|
|
117
|
-
const freeFloatData = (await fetchSharingWFS(
|
|
118
|
-
WFS_FREE_FLOAT_TYPE,
|
|
119
|
-
abortController,
|
|
120
|
-
extent,
|
|
121
|
-
)) as FeatureCollection<Point, SharingVehicleWFS>;
|
|
122
|
-
|
|
123
|
-
const bikes: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
124
|
-
features: [],
|
|
125
|
-
type: "FeatureCollection",
|
|
126
|
-
};
|
|
127
|
-
const cargoBikes: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
128
|
-
features: [],
|
|
129
|
-
type: "FeatureCollection",
|
|
130
|
-
};
|
|
131
|
-
const cars: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
132
|
-
features: [],
|
|
133
|
-
type: "FeatureCollection",
|
|
134
|
-
};
|
|
135
|
-
const scooter: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
136
|
-
features: [],
|
|
137
|
-
type: "FeatureCollection",
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
freeFloatData.features.forEach((feature) => {
|
|
141
|
-
if (feature.properties.form_factor === BIKE_FORM_FACTOR) {
|
|
142
|
-
bikes.features.push(feature);
|
|
143
|
-
} else if (feature.properties.form_factor === CARGOBIKE_FORM_FACTOR) {
|
|
144
|
-
cargoBikes.features.push(feature);
|
|
145
|
-
} else if (feature.properties.form_factor === CAR_FORM_FACTOR) {
|
|
146
|
-
cars.features.push(feature);
|
|
147
|
-
} else if (feature.properties.form_factor === SCOOTER_FORM_FACTOR) {
|
|
148
|
-
scooter.features.push(feature);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
setBikeFreeFloatData(bikes);
|
|
153
|
-
setCargoBikeFreeFloatData(cargoBikes);
|
|
154
|
-
setCarFreeFloatData(cars);
|
|
155
|
-
setScooterFreeFloatData(scooter);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
void fetchData();
|
|
159
|
-
|
|
160
|
-
return () => {
|
|
161
|
-
abortController.abort();
|
|
162
|
-
};
|
|
163
|
-
}, [map]);
|
|
164
|
-
|
|
165
|
-
// Request all stations and vehicleson each moveend event
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
const key = map?.on("moveend", updateData);
|
|
168
|
-
|
|
169
|
-
// @ts-expect-error - change property can have custom values
|
|
170
|
-
const key2 = map?.on(`change:${LAYER_PROP_IS_EXPORTING}`, (evt) => {
|
|
171
|
-
// Reupdate the data after finishing eporting the map
|
|
172
|
-
if (!evt.target.get(LAYER_PROP_IS_EXPORTING)) {
|
|
173
|
-
updateData();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
const key3 = map?.once("rendercomplete", updateData);
|
|
177
|
-
return () => {
|
|
178
|
-
unByKey([key, key2, key3]);
|
|
179
|
-
};
|
|
180
|
-
}, [map, updateData, mbMap]);
|
|
181
|
-
|
|
182
98
|
useEffect(() => {
|
|
183
99
|
if (!mbMap?.style || !stationsData) {
|
|
184
100
|
return;
|
|
@@ -402,6 +318,101 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
402
318
|
} as GroupOptions);
|
|
403
319
|
}, [baseLayer, props, t]);
|
|
404
320
|
|
|
321
|
+
const updateData = useCallback(() => {
|
|
322
|
+
// Only update when layers are visible
|
|
323
|
+
if (map?.getView()?.getZoom() < 15.5 || !group.getVisible()) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const abortController = new AbortController();
|
|
327
|
+
const extent = transformExtent(
|
|
328
|
+
map.getView().calculateExtent(),
|
|
329
|
+
"EPSG:3857",
|
|
330
|
+
"EPSG:4326",
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const fetchData = async () => {
|
|
334
|
+
// Fill stations data
|
|
335
|
+
// The stations are also loaded in the style to have them displayed during export
|
|
336
|
+
const stations = await fetchStations(
|
|
337
|
+
WFS_STATIONS_TYPE,
|
|
338
|
+
abortController,
|
|
339
|
+
extent,
|
|
340
|
+
);
|
|
341
|
+
setStationsData(stations);
|
|
342
|
+
|
|
343
|
+
// Fill free float data
|
|
344
|
+
const freeFloatData = (await fetchSharingWFS(
|
|
345
|
+
WFS_FREE_FLOAT_TYPE,
|
|
346
|
+
abortController,
|
|
347
|
+
extent,
|
|
348
|
+
)) as FeatureCollection<Point, SharingVehicleWFS>;
|
|
349
|
+
|
|
350
|
+
const bikes: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
351
|
+
features: [],
|
|
352
|
+
type: "FeatureCollection",
|
|
353
|
+
};
|
|
354
|
+
const cargoBikes: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
355
|
+
features: [],
|
|
356
|
+
type: "FeatureCollection",
|
|
357
|
+
};
|
|
358
|
+
const cars: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
359
|
+
features: [],
|
|
360
|
+
type: "FeatureCollection",
|
|
361
|
+
};
|
|
362
|
+
const scooter: FeatureCollection<Point, SharingVehicleWFS> = {
|
|
363
|
+
features: [],
|
|
364
|
+
type: "FeatureCollection",
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
freeFloatData.features.forEach((feature) => {
|
|
368
|
+
if (feature.properties.form_factor === BIKE_FORM_FACTOR) {
|
|
369
|
+
bikes.features.push(feature);
|
|
370
|
+
} else if (feature.properties.form_factor === CARGOBIKE_FORM_FACTOR) {
|
|
371
|
+
cargoBikes.features.push(feature);
|
|
372
|
+
} else if (feature.properties.form_factor === CAR_FORM_FACTOR) {
|
|
373
|
+
cars.features.push(feature);
|
|
374
|
+
} else if (feature.properties.form_factor === SCOOTER_FORM_FACTOR) {
|
|
375
|
+
scooter.features.push(feature);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
setBikeFreeFloatData(bikes);
|
|
380
|
+
setCargoBikeFreeFloatData(cargoBikes);
|
|
381
|
+
setCarFreeFloatData(cars);
|
|
382
|
+
setScooterFreeFloatData(scooter);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
void fetchData();
|
|
386
|
+
|
|
387
|
+
return () => {
|
|
388
|
+
abortController.abort();
|
|
389
|
+
};
|
|
390
|
+
}, [map, group]);
|
|
391
|
+
|
|
392
|
+
// Request all stations and vehicleson each moveend event
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
const key = map?.on("moveend", () => {
|
|
395
|
+
return updateData();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// @ts-expect-error - change property can have custom values
|
|
399
|
+
const key2 = map?.on(`change:${LAYER_PROP_IS_EXPORTING}`, (evt) => {
|
|
400
|
+
// Reupdate the data after finishing eporting the map
|
|
401
|
+
if (!evt.target.get(LAYER_PROP_IS_EXPORTING)) {
|
|
402
|
+
updateData();
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
const key3 = map?.once("rendercomplete", updateData);
|
|
406
|
+
const key4 = group?.on("change:visible", (evt) => {
|
|
407
|
+
if (evt.target.getVisible()) {
|
|
408
|
+
updateData();
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return () => {
|
|
412
|
+
unByKey([key, key2, key3, key4]);
|
|
413
|
+
};
|
|
414
|
+
}, [map, updateData, mbMap, group]);
|
|
415
|
+
|
|
405
416
|
// Reload features every minute
|
|
406
417
|
useEffect(() => {
|
|
407
418
|
const interval = window.setInterval(() => {
|
package/src/utils/constants.ts
CHANGED
|
@@ -18,9 +18,10 @@ export const LAYER_NAME_LINESNETWORKPLAN = "liniennetz";
|
|
|
18
18
|
export const LAYER_NAME_MAPSET = "mapset";
|
|
19
19
|
|
|
20
20
|
export const RVF_EXTENT_4326 = [7.5, 47.7, 8.45, 48.4];
|
|
21
|
+
export const MAX_EXTENT_4326 = RVF_EXTENT_4326;
|
|
21
22
|
|
|
22
23
|
export const MAX_EXTENT = transformExtent(
|
|
23
|
-
|
|
24
|
+
MAX_EXTENT_4326,
|
|
24
25
|
"EPSG:4326",
|
|
25
26
|
"EPSG:3857",
|
|
26
27
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useState } from "preact/hooks";
|
|
2
2
|
|
|
3
3
|
import { LNP_MD_LINES, LNP_MD_STOPS, LNP_SOURCE_ID } from "../constants";
|
|
4
4
|
|
|
@@ -36,15 +36,30 @@ let cacheLnpSourceInfo: {
|
|
|
36
36
|
|
|
37
37
|
export function useLnpSourceInfos() {
|
|
38
38
|
const { baseLayer } = useMapContext();
|
|
39
|
+
const [sourceUrl, setSourceUrl] = useState<string>(null);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
?.url;
|
|
43
|
-
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const url =
|
|
43
|
+
baseLayer?.mapLibreMap?.getSource<VectorTileSource>(LNP_SOURCE_ID)?.url;
|
|
44
|
+
const onSourceData = (e) => {
|
|
45
|
+
if (e.sourceId === LNP_SOURCE_ID && e.isSourceLoaded) {
|
|
46
|
+
setSourceUrl(e.source?.url);
|
|
47
|
+
baseLayer?.mapLibreMap?.off("sourcedata", onSourceData);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
if (!url) {
|
|
51
|
+
baseLayer?.mapLibreMap?.on("sourcedata", onSourceData);
|
|
52
|
+
} else {
|
|
53
|
+
setSourceUrl(url);
|
|
54
|
+
}
|
|
55
|
+
return () => {
|
|
56
|
+
baseLayer?.mapLibreMap?.off("sourcedata", onSourceData);
|
|
57
|
+
};
|
|
58
|
+
}, [baseLayer?.mapLibreMap]);
|
|
44
59
|
|
|
45
60
|
useEffect(() => {
|
|
46
61
|
const abortController = new AbortController();
|
|
47
|
-
const fetchInfos = async (url) => {
|
|
62
|
+
const fetchInfos = async (url: string) => {
|
|
48
63
|
if (!cacheLnpSourceInfo) {
|
|
49
64
|
const response = await fetch(url, { signal: abortController.signal });
|
|
50
65
|
const data = await response.json();
|
|
@@ -73,13 +88,13 @@ export function useLnpStopsInfos(): StopsInfos {
|
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
/**
|
|
76
|
-
* This hook search
|
|
91
|
+
* This hook search line informations from lnp data. It takes a string in
|
|
77
92
|
* parameter then it will search if there is a property that exactly match this value.
|
|
78
93
|
*/
|
|
79
94
|
function useLnpLineInfo(text: string): LineInfo {
|
|
80
95
|
const linesInfos = useLnpLinesInfos();
|
|
81
96
|
|
|
82
|
-
if (!linesInfos) {
|
|
97
|
+
if (!linesInfos || !text) {
|
|
83
98
|
return null;
|
|
84
99
|
}
|
|
85
100
|
|
|
@@ -89,7 +104,7 @@ function useLnpLineInfo(text: string): LineInfo {
|
|
|
89
104
|
|
|
90
105
|
return Object.values(linesInfos).find((info) => {
|
|
91
106
|
return ["id", "external_id", "short_name", "long_name"].find((key) => {
|
|
92
|
-
return info[key] === text;
|
|
107
|
+
return !!info[key] && info[key] === text;
|
|
93
108
|
});
|
|
94
109
|
});
|
|
95
110
|
}
|
|
@@ -50,6 +50,7 @@ export type MapContextType = {
|
|
|
50
50
|
linesNetworkPlanLayer: MaplibreStyleLayer;
|
|
51
51
|
map: Map;
|
|
52
52
|
mapsetLayer?: MapsetLayer;
|
|
53
|
+
notificationId?: string;
|
|
53
54
|
notificationsLayer?: MocoLayer;
|
|
54
55
|
permalinkUrlSearchParams: URLSearchParams;
|
|
55
56
|
previewNotifications?: SituationType[];
|
|
@@ -86,6 +87,7 @@ export type MapContextType = {
|
|
|
86
87
|
setLinesNetworkPlanLayer: (layer?: MaplibreStyleLayer) => void;
|
|
87
88
|
setMap: (map?: Map) => void;
|
|
88
89
|
setMapsetLayer: (mapsetLayer?: MapsetLayer) => void;
|
|
90
|
+
setNotificationId: (notificationId?: string) => void;
|
|
89
91
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => void;
|
|
90
92
|
setPermalinkUrlSearchParams: (
|
|
91
93
|
setPermalinkUrlSearchParams: URLSearchParams,
|
|
@@ -132,9 +134,11 @@ export const MapContext = createContext<MapContextType>({
|
|
|
132
134
|
setBaseLayer: (baseLayer?: MaplibreLayer) => {},
|
|
133
135
|
setIsFollowing: (isFollowing: boolean) => {},
|
|
134
136
|
setIsTracking: (isTracking: boolean) => {},
|
|
137
|
+
setLinesIds: (linesIds: string[]) => {},
|
|
135
138
|
setLinesNetworkPlanLayer: (linesNetworkPlanLayer: MaplibreStyleLayer) => {},
|
|
136
139
|
setMap: (map?: Map) => {},
|
|
137
140
|
setMapsetLayer: (mapsetLayer?: MapsetLayer) => {},
|
|
141
|
+
setNotificationId: (notificationId?: string) => {},
|
|
138
142
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => {},
|
|
139
143
|
setPermalinkUrlSearchParams: (
|
|
140
144
|
permalinkUrlSearchParams: URLSearchParams,
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { getFeatureInfoAtCoordinate } from "mobility-toolbox-js/ol";
|
|
2
|
-
import { GeoJSON } from "ol/format";
|
|
3
|
-
import { unByKey } from "ol/Observable";
|
|
4
|
-
import { useCallback, useEffect } from "preact/hooks";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
LAYER_NAME_REALTIME,
|
|
8
|
-
LAYER_NAME_STATIONS,
|
|
9
|
-
PROVIDER_LOGOS_BY_FEED_ID,
|
|
10
|
-
} from "../utils/constants";
|
|
11
|
-
import useMapContext from "../utils/hooks/useMapContext";
|
|
12
|
-
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
13
|
-
import MobilityEvent from "../utils/MobilityEvent";
|
|
14
|
-
import { fetchSharingStation } from "../utils/sharingGraphqlUtils";
|
|
15
|
-
|
|
16
|
-
import type { GeoJSONSource } from "maplibre-gl";
|
|
17
|
-
import type { Feature, MapBrowserEvent } from "ol";
|
|
18
|
-
|
|
19
|
-
const geojson = new GeoJSON();
|
|
20
|
-
|
|
21
|
-
function SingleClickListener({ eventNode }: { eventNode: HTMLElement }) {
|
|
22
|
-
const {
|
|
23
|
-
baseLayer,
|
|
24
|
-
map,
|
|
25
|
-
queryablelayers,
|
|
26
|
-
realtimeLayer,
|
|
27
|
-
setStationId,
|
|
28
|
-
setTrainId,
|
|
29
|
-
stationId,
|
|
30
|
-
stationsLayer,
|
|
31
|
-
tenant,
|
|
32
|
-
trainId,
|
|
33
|
-
} = useMapContext();
|
|
34
|
-
const { selectedFeature, setSelectedFeature, setSelectedFeatures } =
|
|
35
|
-
useRvfContext();
|
|
36
|
-
|
|
37
|
-
// Send the selctedFEature to the parent window
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
eventNode?.dispatchEvent(
|
|
40
|
-
new MobilityEvent("mwc:selectedfeature", {
|
|
41
|
-
feature: selectedFeature
|
|
42
|
-
? geojson.writeFeatureObject(selectedFeature)
|
|
43
|
-
: null,
|
|
44
|
-
}),
|
|
45
|
-
);
|
|
46
|
-
}, [eventNode, selectedFeature]);
|
|
47
|
-
|
|
48
|
-
const onPointerMove = useCallback(
|
|
49
|
-
(evt: MapBrowserEvent<PointerEvent>) => {
|
|
50
|
-
const [realtimeFeature] = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
51
|
-
hitTolerance: 5,
|
|
52
|
-
layerFilter: (l) => {
|
|
53
|
-
return l === realtimeLayer;
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
realtimeLayer?.highlight(realtimeFeature as Feature);
|
|
57
|
-
|
|
58
|
-
const stationsFeatures = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
59
|
-
layerFilter: (l) => {
|
|
60
|
-
return l === stationsLayer;
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
65
|
-
return feat.get("tralis_network")?.includes(tenant);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// Send all the features under the cursor
|
|
69
|
-
const features = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
70
|
-
layerFilter: (l) => {
|
|
71
|
-
return queryablelayers
|
|
72
|
-
? queryablelayers.split(",").includes(l.get("name"))
|
|
73
|
-
: l.get("isQueryable");
|
|
74
|
-
},
|
|
75
|
-
}) as Feature[];
|
|
76
|
-
|
|
77
|
-
evt.map.getTargetElement().style.cursor =
|
|
78
|
-
realtimeFeature || stationFeature || features?.length
|
|
79
|
-
? "pointer"
|
|
80
|
-
: "default";
|
|
81
|
-
},
|
|
82
|
-
[queryablelayers, realtimeLayer, stationsLayer, tenant],
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const onSingleClick = useCallback(
|
|
86
|
-
async (evt: MapBrowserEvent<PointerEvent>) => {
|
|
87
|
-
if (!baseLayer?.mapLibreMap) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const [realtimeFeature] = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
91
|
-
hitTolerance: 5,
|
|
92
|
-
layerFilter: (l) => {
|
|
93
|
-
return l === realtimeLayer;
|
|
94
|
-
},
|
|
95
|
-
}) as Feature[];
|
|
96
|
-
realtimeFeature?.set("layerName", LAYER_NAME_REALTIME);
|
|
97
|
-
|
|
98
|
-
const stationsFeatures = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
99
|
-
layerFilter: (l) => {
|
|
100
|
-
return l === stationsLayer;
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
104
|
-
return feat.get("tralis_network")?.includes(tenant);
|
|
105
|
-
}) as Feature[];
|
|
106
|
-
stationFeature?.set("layerName", LAYER_NAME_STATIONS);
|
|
107
|
-
|
|
108
|
-
const newStationId = stationFeature?.get("uid");
|
|
109
|
-
|
|
110
|
-
const newTrainId = realtimeFeature?.get("train_id");
|
|
111
|
-
|
|
112
|
-
if (newStationId && stationId !== newStationId) {
|
|
113
|
-
setStationId(newStationId);
|
|
114
|
-
setTrainId(null);
|
|
115
|
-
} else if (newTrainId && newTrainId !== trainId) {
|
|
116
|
-
setTrainId(realtimeFeature.get("train_id"));
|
|
117
|
-
setStationId(null);
|
|
118
|
-
} else {
|
|
119
|
-
setTrainId(null);
|
|
120
|
-
setStationId(null);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Send all the features under the cursor
|
|
124
|
-
// const features = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
125
|
-
// hitTolerance: 5,
|
|
126
|
-
// layerFilter: (l) => {
|
|
127
|
-
// console.log(queryablelayers);
|
|
128
|
-
// return queryablelayers
|
|
129
|
-
// ? queryablelayers.split(",").includes(l.get("name"))
|
|
130
|
-
// : l.get("isQueryable");
|
|
131
|
-
// },
|
|
132
|
-
// }) as Feature[];
|
|
133
|
-
|
|
134
|
-
const queryableLayers = evt.map.getAllLayers().filter((l) => {
|
|
135
|
-
return queryablelayers
|
|
136
|
-
? queryablelayers.split(",").includes(l.get("name"))
|
|
137
|
-
: l.get("isQueryable");
|
|
138
|
-
});
|
|
139
|
-
const featuresInfos = await getFeatureInfoAtCoordinate(
|
|
140
|
-
evt.coordinate,
|
|
141
|
-
queryableLayers,
|
|
142
|
-
5,
|
|
143
|
-
true,
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
// Set the layerName as property of the feature
|
|
147
|
-
featuresInfos.forEach((f) => {
|
|
148
|
-
f.features.forEach((feat) => {
|
|
149
|
-
feat.set("layerName", f.layer.get("name"));
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const features = featuresInfos.flatMap((f) => {
|
|
154
|
-
return f.features;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Append more infos about the features
|
|
158
|
-
for (const feature of features) {
|
|
159
|
-
const clusterId = feature.get("cluster_id");
|
|
160
|
-
if (clusterId) {
|
|
161
|
-
const vtFeat = feature.get("vectorTileFeature");
|
|
162
|
-
const sourceId = vtFeat.layer.source;
|
|
163
|
-
const leaves =
|
|
164
|
-
(await baseLayer.mapLibreMap
|
|
165
|
-
.getSource<GeoJSONSource>(sourceId)
|
|
166
|
-
?.getClusterLeaves(clusterId, 1000, 0)) || [];
|
|
167
|
-
|
|
168
|
-
feature.set(
|
|
169
|
-
"clusterLeaves",
|
|
170
|
-
leaves.map((l) => {
|
|
171
|
-
return geojson.readFeature(l);
|
|
172
|
-
}),
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Sharing station
|
|
177
|
-
const sharingStationId = selectedFeature?.get("station_id");
|
|
178
|
-
if (sharingStationId) {
|
|
179
|
-
const sharingStationInfo =
|
|
180
|
-
await fetchSharingStation(sharingStationId);
|
|
181
|
-
selectedFeature.setProperties(sharingStationInfo);
|
|
182
|
-
selectedFeature.set(
|
|
183
|
-
"provider_logo",
|
|
184
|
-
PROVIDER_LOGOS_BY_FEED_ID[selectedFeature?.get("feed_id")],
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (newStationId || newTrainId || !features.length) {
|
|
190
|
-
setSelectedFeature(null);
|
|
191
|
-
setSelectedFeatures([]);
|
|
192
|
-
} else {
|
|
193
|
-
setSelectedFeatures(features);
|
|
194
|
-
|
|
195
|
-
// We prioritize some layers like the notification one over the others
|
|
196
|
-
const notificationFeature = features.find((feat) => {
|
|
197
|
-
return feat.get("situation");
|
|
198
|
-
});
|
|
199
|
-
if (notificationFeature) {
|
|
200
|
-
setSelectedFeature(notificationFeature);
|
|
201
|
-
} else {
|
|
202
|
-
setSelectedFeature(features[0]);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
[
|
|
207
|
-
baseLayer?.mapLibreMap,
|
|
208
|
-
stationId,
|
|
209
|
-
trainId,
|
|
210
|
-
realtimeLayer,
|
|
211
|
-
stationsLayer,
|
|
212
|
-
tenant,
|
|
213
|
-
setStationId,
|
|
214
|
-
setTrainId,
|
|
215
|
-
queryablelayers,
|
|
216
|
-
selectedFeature,
|
|
217
|
-
setSelectedFeature,
|
|
218
|
-
setSelectedFeatures,
|
|
219
|
-
],
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
const key = map?.on("singleclick", onSingleClick);
|
|
224
|
-
return () => {
|
|
225
|
-
unByKey(key);
|
|
226
|
-
};
|
|
227
|
-
}, [map, onSingleClick]);
|
|
228
|
-
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
const key = map?.on("pointermove", onPointerMove);
|
|
231
|
-
return () => {
|
|
232
|
-
unByKey(key);
|
|
233
|
-
};
|
|
234
|
-
}, [map, onPointerMove]);
|
|
235
|
-
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
export default SingleClickListener;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./RvfSingleClickListener";
|