@geops/rvf-mobility-web-component 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +2 -0
- package/CHANGELOG.md +8 -0
- package/docutils.js +1 -1
- package/index.html +1 -1
- package/index.js +119 -107
- package/package.json +1 -1
- package/src/NotificationLayer/NotificationLayer.tsx +2 -2
- package/src/RouteIcon/RouteIcon.tsx +21 -12
- package/src/RvfExportMenu/RvfExportMenu.tsx +2 -0
- package/src/RvfFeatureDetails/RVFSellingPointDetails/RVFSellingPointDetails.tsx +47 -0
- package/src/RvfFeatureDetails/RVFSellingPointDetails/index.js +1 -0
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +4 -1
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +4 -1
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +55 -12
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +58 -32
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +364 -123
- package/src/StationsLayer/StationsLayer.tsx +1 -1
- package/src/icons/NoRealtime/NoRealtime.tsx +44 -0
- package/src/icons/NoRealtime/index.tsx +1 -0
- package/src/icons/NoRealtime/norealtime.svg +6 -0
- package/src/utils/constants.ts +22 -1
- package/src/utils/exportPdf.ts +3 -3
- package/src/utils/fullTrajectoryStyle.ts +0 -1
- package/src/utils/getMainColorForVehicle.test.ts +21 -23
- package/src/utils/getRadius.ts +25 -24
- package/src/utils/hooks/useUpdatePermalink.tsx +5 -0
- package/src/utils/realtimeRVFStyle.ts +70 -8
- package/src/utils/sharingWFSUtils.ts +46 -16
- package/src/RvfSharedMobilityLayerGroup2/RvfSharedMobilityLayerGroup2.tsx +0 -740
- package/src/RvfSharedMobilityLayerGroup2/index.tsx +0 -1
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.29",
|
|
6
6
|
"homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|
|
@@ -39,10 +39,10 @@ const useNotifications = () => {
|
|
|
39
39
|
if (!baseLayer.loaded) {
|
|
40
40
|
// @ts-expect-error bad type definition
|
|
41
41
|
baseLayer.once("load", () => {
|
|
42
|
-
return setStyleMetadata(baseLayer.
|
|
42
|
+
return setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
|
|
43
43
|
});
|
|
44
44
|
} else {
|
|
45
|
-
setStyleMetadata(baseLayer.
|
|
45
|
+
setStyleMetadata(baseLayer.mapLibreMap?.getStyle()?.metadata);
|
|
46
46
|
}
|
|
47
47
|
}, [baseLayer, baselayer]);
|
|
48
48
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "mobility-toolbox-js/types";
|
|
7
7
|
import { JSX, PreactDOMAttributes } from "preact";
|
|
8
8
|
|
|
9
|
+
import NoRealtime from "../icons/NoRealtime";
|
|
9
10
|
import getMainColorForVehicle from "../utils/getMainColorForVehicle";
|
|
10
11
|
import getTextColor from "../utils/getTextColor";
|
|
11
12
|
import getTextFontForVehicle from "../utils/getTextFontForVehicle";
|
|
@@ -45,24 +46,32 @@ function RouteIcon({
|
|
|
45
46
|
|
|
46
47
|
const fontSize = fontSizesByNbLetters[text.length] || 12;
|
|
47
48
|
const font = getTextFontForVehicle(fontSize, text);
|
|
49
|
+
const hasRealtime = stopSequence?.has_realtime_journey;
|
|
50
|
+
const isCancelled = stopSequence?.stations[0]?.state === "JOURNEY_CANCELLED";
|
|
48
51
|
|
|
49
52
|
if (borderColor === backgroundColor) {
|
|
50
53
|
borderColor = "black";
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
return (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
<>
|
|
58
|
+
<span
|
|
59
|
+
className="relative flex h-[40px] min-w-[40px] items-center justify-center rounded-full border px-1"
|
|
60
|
+
style={{
|
|
61
|
+
backgroundColor,
|
|
62
|
+
borderColor,
|
|
63
|
+
color,
|
|
64
|
+
font,
|
|
65
|
+
}}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children || text}
|
|
69
|
+
|
|
70
|
+
{!isCancelled && !hasRealtime && (
|
|
71
|
+
<NoRealtime className={"absolute -left-2 -top-2"} />
|
|
72
|
+
)}
|
|
73
|
+
</span>
|
|
74
|
+
</>
|
|
66
75
|
);
|
|
67
76
|
}
|
|
68
77
|
export default RouteIcon;
|
|
@@ -74,9 +74,11 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
74
74
|
getAllLayers(layers).forEach((layer) => {
|
|
75
75
|
layer.set(LAYER_PROP_IS_EXPORTING, false);
|
|
76
76
|
});
|
|
77
|
+
map.set(LAYER_PROP_IS_EXPORTING, false);
|
|
77
78
|
},
|
|
78
79
|
|
|
79
80
|
onBefore: (map, layers) => {
|
|
81
|
+
map.set(LAYER_PROP_IS_EXPORTING, true);
|
|
80
82
|
prevRealtimeLayerVisibility = realtimeLayer.getVisible();
|
|
81
83
|
if (realtimeLayer.getVisible()) {
|
|
82
84
|
realtimeLayer.setVisible(false);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Feature } from "ol";
|
|
2
|
+
|
|
3
|
+
import Automat from "../../icons/Automat";
|
|
4
|
+
import InPerson from "../../icons/InPerson";
|
|
5
|
+
import Video from "../../icons/Video";
|
|
6
|
+
|
|
7
|
+
const ICON_BY_OPERATED_BY = {
|
|
8
|
+
Automat: <Automat />,
|
|
9
|
+
Persönlich: <InPerson />,
|
|
10
|
+
Videozentrum: <Video />,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function RVFSellingPointDetails({ feature }: { feature: Feature }) {
|
|
14
|
+
if (!feature) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const {
|
|
18
|
+
city,
|
|
19
|
+
name,
|
|
20
|
+
operated_by: operatedBy,
|
|
21
|
+
street,
|
|
22
|
+
telephone_info: phone,
|
|
23
|
+
zip_code: zip,
|
|
24
|
+
} = feature.getProperties();
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex flex-col gap-4">
|
|
27
|
+
<div className="flex gap-2">
|
|
28
|
+
<span className={"min-w-[26px]"}>
|
|
29
|
+
{ICON_BY_OPERATED_BY[operatedBy]}
|
|
30
|
+
</span>
|
|
31
|
+
<span>{name}</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex flex-col">
|
|
34
|
+
{phone && <div>{phone}</div>}
|
|
35
|
+
{street && <div>{street}</div>}
|
|
36
|
+
{(zip || city) && (
|
|
37
|
+
<div className="flex gap-2">
|
|
38
|
+
<span>{zip}</span>
|
|
39
|
+
<span>{city}</span>
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default RVFSellingPointDetails;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RVFSellingPointDetails";
|
|
@@ -3,6 +3,7 @@ import type { JSX, PreactDOMAttributes } from "preact";
|
|
|
3
3
|
import { memo } from "preact/compat";
|
|
4
4
|
|
|
5
5
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
6
|
+
import RVFSellingPointDetails from "./RVFSellingPointDetails/RVFSellingPointDetails";
|
|
6
7
|
import RvfSharedMobilityDetails from "./RvfSharedMobilityDetail";
|
|
7
8
|
|
|
8
9
|
export type RvfFeatureDetailsProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
@@ -21,6 +22,7 @@ const getIsSharedMobility = (selectedFeature): boolean => {
|
|
|
21
22
|
function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
|
|
22
23
|
const { selectedFeature } = useRvfContext();
|
|
23
24
|
const isSharedMobility = getIsSharedMobility(selectedFeature);
|
|
25
|
+
const isSellingPoint = !!selectedFeature.get("tickets");
|
|
24
26
|
|
|
25
27
|
const showDefaultData = () => {
|
|
26
28
|
return Object.entries(selectedFeature.getProperties()).map(
|
|
@@ -39,10 +41,11 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
|
|
|
39
41
|
|
|
40
42
|
return (
|
|
41
43
|
<div {...props}>
|
|
44
|
+
{isSellingPoint && <RVFSellingPointDetails feature={selectedFeature} />}
|
|
42
45
|
{isSharedMobility && (
|
|
43
46
|
<RvfSharedMobilityDetails selectedFeature={selectedFeature} />
|
|
44
47
|
)}
|
|
45
|
-
{!isSharedMobility && showDefaultData()}
|
|
48
|
+
{!isSharedMobility && !isSellingPoint && showDefaultData()}
|
|
46
49
|
</div>
|
|
47
50
|
);
|
|
48
51
|
}
|
|
@@ -18,7 +18,10 @@ function RvfLineNetworkPlanLayer(props: MaplibreStyleLayerOptions) {
|
|
|
18
18
|
return new MaplibreStyleLayer({
|
|
19
19
|
isQueryable: true,
|
|
20
20
|
layersFilter: ({ metadata }) => {
|
|
21
|
-
return
|
|
21
|
+
return (
|
|
22
|
+
metadata?.["rvf.filter"] === "netzplan_lines" ||
|
|
23
|
+
metadata?.["rvf.filter"] === "netzplan_stations"
|
|
24
|
+
);
|
|
22
25
|
},
|
|
23
26
|
maplibreLayer: baseLayer,
|
|
24
27
|
minZoom: 10,
|
|
@@ -42,7 +42,7 @@ import RvfPoisLayer from "../RvfPoisLayer";
|
|
|
42
42
|
import RvfSelectedFeatureHighlightLayer from "../RvfSelectedFeatureHighlightLayer";
|
|
43
43
|
import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
|
|
44
44
|
import RvfShare from "../RvfShare";
|
|
45
|
-
import
|
|
45
|
+
import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
|
|
46
46
|
import RvfShareMenuButton from "../RvfShareMenuButton";
|
|
47
47
|
import RvfTarifZonenLayer from "../RvfTarifZonenLayer";
|
|
48
48
|
import Topics from "../RvfTopics";
|
|
@@ -71,6 +71,13 @@ import realtimeRVFStyle from "../utils/realtimeRVFStyle";
|
|
|
71
71
|
// @ts-expect-error bad type definition
|
|
72
72
|
import style from "./index.css";
|
|
73
73
|
|
|
74
|
+
const PRIORITY_FROM_TYPE = {
|
|
75
|
+
bus: 25,
|
|
76
|
+
coach: 15,
|
|
77
|
+
train: 30,
|
|
78
|
+
tram: 20,
|
|
79
|
+
};
|
|
80
|
+
|
|
74
81
|
export type RvfMobilityMapProps = {
|
|
75
82
|
details: string;
|
|
76
83
|
layers: string; // list of visible layers on load
|
|
@@ -100,9 +107,43 @@ const realtimeLayerProps = {
|
|
|
100
107
|
line_tags: "RVF",
|
|
101
108
|
},
|
|
102
109
|
fullTrajectoryStyle: fullTrajectoryStyle,
|
|
110
|
+
getMotsByZoom: (z) => {
|
|
111
|
+
if (z < 9) {
|
|
112
|
+
return ["rail"];
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
},
|
|
116
|
+
sort: (
|
|
117
|
+
{ properties: { delay: delayA, type: typeA } },
|
|
118
|
+
{ properties: { delay: delayB, type: typeB } },
|
|
119
|
+
) => {
|
|
120
|
+
// console.log(trajA, trajB);
|
|
121
|
+
if (typeA !== typeB) {
|
|
122
|
+
// console.log(trajA.properties.type, trajB.properties.type);
|
|
123
|
+
if (PRIORITY_FROM_TYPE[typeA] < PRIORITY_FROM_TYPE[typeB]) {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (delayA === delayB) {
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (delayA < delayB) {
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
return 1;
|
|
137
|
+
},
|
|
103
138
|
style: realtimeRVFStyle,
|
|
104
139
|
styleOptions: {
|
|
105
140
|
getBgColor: getBgColor,
|
|
141
|
+
getMaxRadiusForStrokeAndDelay: () => {
|
|
142
|
+
return 4;
|
|
143
|
+
},
|
|
144
|
+
getMaxRadiusForText: () => {
|
|
145
|
+
return 8;
|
|
146
|
+
},
|
|
106
147
|
getRadius: getRadius,
|
|
107
148
|
getTextColor: getTextColor,
|
|
108
149
|
},
|
|
@@ -110,6 +151,7 @@ const realtimeLayerProps = {
|
|
|
110
151
|
|
|
111
152
|
function RvfMobilityMap({
|
|
112
153
|
apikey = "5cc87b12d7c5370001c1d655820abcc37dfd4d968d7bab5b2a74a935",
|
|
154
|
+
// baselayer = "review-geops-tgma-085vyz.de.rvf",
|
|
113
155
|
baselayer = "de.rvf",
|
|
114
156
|
center = null,
|
|
115
157
|
details = "true",
|
|
@@ -117,7 +159,9 @@ function RvfMobilityMap({
|
|
|
117
159
|
geolocation = "true",
|
|
118
160
|
layers = null,
|
|
119
161
|
layertree = "true",
|
|
120
|
-
mapsurl = "https://
|
|
162
|
+
// mapsurl = "https://style-review.geops.io",
|
|
163
|
+
mapsurl = "https://maps.style-dev.geops.io",
|
|
164
|
+
// mapsurl = "https://maps.geops.io",
|
|
121
165
|
maxextent = bbox,
|
|
122
166
|
maxzoom = "20",
|
|
123
167
|
minzoom = null,
|
|
@@ -402,7 +446,7 @@ function RvfMobilityMap({
|
|
|
402
446
|
const stationsLayerProps = useMemo(() => {
|
|
403
447
|
return {
|
|
404
448
|
layersFilter: ({ metadata }) => {
|
|
405
|
-
return metadata?.["
|
|
449
|
+
return metadata?.["general.filter"] === "stations";
|
|
406
450
|
},
|
|
407
451
|
};
|
|
408
452
|
}, []);
|
|
@@ -492,17 +536,16 @@ function RvfMobilityMap({
|
|
|
492
536
|
{...realtimeLayerProps}
|
|
493
537
|
/>
|
|
494
538
|
)}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
539
|
+
|
|
540
|
+
<StationsLayer
|
|
541
|
+
minZoom={10}
|
|
542
|
+
title={RVF_LAYERS_TITLES.haltestellen}
|
|
543
|
+
{...stationsLayerProps}
|
|
544
|
+
/>
|
|
502
545
|
{hasNotification && <NotificationLayer />}
|
|
503
546
|
<RvfTarifZonenLayer title={RVF_LAYERS_TITLES.tarifzonen} />
|
|
504
547
|
<RvfSellingPointsLayer
|
|
505
|
-
isQueryable={
|
|
548
|
+
isQueryable={true}
|
|
506
549
|
title={RVF_LAYERS_TITLES.verkaufsstellen}
|
|
507
550
|
/>
|
|
508
551
|
<RvfLineNetworkPlanLayer
|
|
@@ -511,7 +554,7 @@ function RvfMobilityMap({
|
|
|
511
554
|
title={RVF_LAYERS_TITLES.liniennetz}
|
|
512
555
|
/>
|
|
513
556
|
<RvfPoisLayer title={RVF_LAYERS_TITLES.pois} />
|
|
514
|
-
<
|
|
557
|
+
<RvfSharedMobilityLayerGroup
|
|
515
558
|
title={RVF_LAYERS_TITLES.sharedMobility}
|
|
516
559
|
/>
|
|
517
560
|
|
|
@@ -1,62 +1,88 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { Circle, Stroke, Style } from "ol/style";
|
|
1
|
+
import { GeoJSONSource } from "maplibre-gl";
|
|
2
|
+
import GeoJSON from "ol/format/GeoJSON";
|
|
4
3
|
import { useEffect, useMemo } from "preact/hooks";
|
|
5
4
|
|
|
5
|
+
import { EMPTY_FEATURE_COLLECTION, SOURCE_SELECT } from "../utils/constants";
|
|
6
6
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
7
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
8
8
|
|
|
9
|
+
const geojson = new GeoJSON({
|
|
10
|
+
dataProjection: "EPSG:4326",
|
|
11
|
+
featureProjection: "EPSG:3857",
|
|
12
|
+
});
|
|
13
|
+
|
|
9
14
|
function SingleClickListener() {
|
|
10
|
-
const { baseLayer
|
|
15
|
+
const { baseLayer } = useMapContext();
|
|
11
16
|
const { selectedFeature } = useRvfContext();
|
|
12
17
|
|
|
13
|
-
const layer = useMemo(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}, []);
|
|
18
|
+
// const layer = useMemo(() => {
|
|
19
|
+
// const layer = new VectorLayer({
|
|
20
|
+
// source: new Vector(),
|
|
21
|
+
// style: new Style({
|
|
22
|
+
// image: new Circle({
|
|
23
|
+
// fill: null,
|
|
24
|
+
// radius: 15,
|
|
25
|
+
// stroke: new Stroke({
|
|
26
|
+
// color: "red",
|
|
27
|
+
// width: 3,
|
|
28
|
+
// }),
|
|
29
|
+
// }),
|
|
30
|
+
// }),
|
|
31
|
+
// });
|
|
32
|
+
// return layer;
|
|
33
|
+
// }, []);
|
|
34
|
+
|
|
35
|
+
const mbMap = useMemo(() => {
|
|
36
|
+
return baseLayer?.mapLibreMap;
|
|
37
|
+
}, [baseLayer?.mapLibreMap]);
|
|
29
38
|
|
|
30
39
|
useEffect(() => {
|
|
31
40
|
const vectorTileFeature = selectedFeature?.get("vectorTileFeature");
|
|
32
41
|
selectedFeature?.set("selected", true);
|
|
42
|
+
const source = mbMap?.getSource(SOURCE_SELECT) as GeoJSONSource;
|
|
33
43
|
if (selectedFeature) {
|
|
34
44
|
const feature = selectedFeature.clone();
|
|
35
45
|
feature.setStyle(null);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
|
|
47
|
+
// Special highlight for the sharing mobility layer
|
|
48
|
+
if (
|
|
49
|
+
vectorTileFeature ||
|
|
50
|
+
// feature.get("station_id") ||
|
|
51
|
+
// feature.get("vehicle_id") ||
|
|
52
|
+
// feature.get("cluster_id") ||
|
|
53
|
+
feature.get("cluster_id")
|
|
54
|
+
) {
|
|
55
|
+
mbMap?.setFeatureState(vectorTileFeature, {
|
|
39
56
|
hover: true,
|
|
40
57
|
});
|
|
58
|
+
if (feature.get("cluster_id")) {
|
|
59
|
+
feature.set("mot", feature.get("features")[0].get("form_factor"));
|
|
60
|
+
feature.set("provider_name", "");
|
|
61
|
+
feature.set("num_vehicles_available", feature.get("point_count"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
source?.setData(geojson.writeFeatureObject(feature));
|
|
41
65
|
}
|
|
42
66
|
}
|
|
67
|
+
|
|
43
68
|
return () => {
|
|
44
|
-
|
|
69
|
+
source?.setData(EMPTY_FEATURE_COLLECTION);
|
|
70
|
+
// layer?.getSource().clear();
|
|
45
71
|
selectedFeature?.set("selected", false);
|
|
46
72
|
if (vectorTileFeature) {
|
|
47
|
-
|
|
73
|
+
mbMap?.setFeatureState(vectorTileFeature, {
|
|
48
74
|
hover: false,
|
|
49
75
|
});
|
|
50
76
|
}
|
|
51
77
|
};
|
|
52
|
-
}, [
|
|
78
|
+
}, [mbMap, selectedFeature]);
|
|
53
79
|
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}, [layer, map]);
|
|
80
|
+
// useEffect(() => {
|
|
81
|
+
// layer?.setMap(map);
|
|
82
|
+
// return () => {
|
|
83
|
+
// layer?.setMap(null);
|
|
84
|
+
// };
|
|
85
|
+
// }, [layer, map]);
|
|
60
86
|
|
|
61
87
|
return null;
|
|
62
88
|
}
|