@geops/rvf-mobility-web-component 0.1.23 → 0.1.24-beta.0
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/index.html +1 -0
- package/index.js +196 -140
- package/package.json +1 -1
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +6 -6
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +11 -87
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +2 -158
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +21 -5
- package/src/RvfSharedMobilityLayerGroup2/RvfSharedMobilityLayerGroup2.tsx +740 -0
- package/src/RvfSharedMobilityLayerGroup2/index.tsx +1 -0
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +76 -19
- package/src/index.tsx +2 -1
- package/src/utils/constants.ts +65 -1
- package/src/utils/createFreeFloatMobilityLayer.ts +5 -2
- package/src/utils/createMobiDataBwWfsLayer.ts +6 -3
- package/src/utils/sharingGraphqlUtils.ts +313 -0
- package/src/utils/sharingStylesUtils.ts +222 -0
- package/src/utils/sharingWFSUtils.ts +74 -0
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.24-beta.0",
|
|
6
6
|
"homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|
|
@@ -9,12 +9,12 @@ export type RvfFeatureDetailsProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
|
9
9
|
PreactDOMAttributes;
|
|
10
10
|
|
|
11
11
|
const getIsSharedMobility = (selectedFeature): boolean => {
|
|
12
|
-
|
|
13
|
-
selectedFeature.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
12
|
+
if (
|
|
13
|
+
selectedFeature.get("station_id") ||
|
|
14
|
+
selectedFeature.get("vehicle_id") ||
|
|
15
|
+
selectedFeature.get("cluster_id")
|
|
16
|
+
) {
|
|
17
|
+
return true;
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
|
|
@@ -2,15 +2,7 @@ import { Feature } from "ol";
|
|
|
2
2
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
3
3
|
import { Fragment } from "preact/jsx-runtime";
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import flinkster from "../../logos/flinkster_logo.png";
|
|
7
|
-
import grueneFlotteLogo from "../../logos/gruene_flotte_logo.png";
|
|
8
|
-
import lastenVeloLogo from "../../logos/lasten_velo_freiburg.png";
|
|
9
|
-
import freloLogo from "../../logos/logo_frelo_web_rgb.png";
|
|
10
|
-
import naturEnergieLogo from "../../logos/natur_energie_logo.png";
|
|
11
|
-
import yoioLogo from "../../logos/yoio_logo.png";
|
|
12
|
-
import zeusLogo from "../../logos/zeus_logo.png";
|
|
13
|
-
import { API_REQUEST_FEATURE_TYPE } from "../../utils/constants";
|
|
5
|
+
import { PROVIDER_LOGOS_BY_FEED_ID } from "../../utils/constants";
|
|
14
6
|
import FloatingVehiclesDetails from "./FloatingVehiclesDetails";
|
|
15
7
|
import StationDetails from "./StationDetails";
|
|
16
8
|
|
|
@@ -18,89 +10,21 @@ export interface RvfSharedMobilityDetailsProps {
|
|
|
18
10
|
selectedFeature: Feature;
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
const logos = {
|
|
22
|
-
callabike_ice: callBike,
|
|
23
|
-
flinkster_carsharing: flinkster,
|
|
24
|
-
"gruene-flotte_freiburg": grueneFlotteLogo,
|
|
25
|
-
lastenvelo_fr: lastenVeloLogo,
|
|
26
|
-
naturenergie_sharing: naturEnergieLogo,
|
|
27
|
-
nextbike_df: freloLogo,
|
|
28
|
-
yoio_freiburg: yoioLogo,
|
|
29
|
-
zeus_freiburg: zeusLogo,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
13
|
function RvfSharedMobilityDetails({
|
|
33
14
|
selectedFeature,
|
|
34
15
|
}: RvfSharedMobilityDetailsProps) {
|
|
35
16
|
const [features, setFeatures] = useState([]);
|
|
36
|
-
const isStationDetails =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const isFloatingVehicle = !!selectedFeature.get("features")?.length;
|
|
17
|
+
const isStationDetails = !!selectedFeature.get("station_id");
|
|
18
|
+
const isFloatingVehicle = !!selectedFeature.get("vehicle_id");
|
|
19
|
+
const isCluster = !!selectedFeature.get("cluster_id");
|
|
40
20
|
|
|
41
21
|
useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
extent[1] = extent[1] - 2;
|
|
45
|
-
extent[2] = extent[2] + 2;
|
|
46
|
-
extent[3] = extent[3] + 2;
|
|
47
|
-
const categoryName = selectedFeature.get("category")?.toLowerCase();
|
|
48
|
-
let featureType;
|
|
49
|
-
let abortController;
|
|
50
|
-
|
|
51
|
-
if (categoryName) {
|
|
52
|
-
if (categoryName === "bike sharing") {
|
|
53
|
-
featureType = API_REQUEST_FEATURE_TYPE.stations.bike;
|
|
54
|
-
} else if (categoryName === "car sharing") {
|
|
55
|
-
featureType = API_REQUEST_FEATURE_TYPE.stations.car;
|
|
56
|
-
} else if (categoryName === "cargo bike sharing") {
|
|
57
|
-
featureType = API_REQUEST_FEATURE_TYPE.stations.cargoBike;
|
|
58
|
-
}
|
|
59
|
-
abortController = new AbortController();
|
|
60
|
-
|
|
61
|
-
fetch(createRequestLink(featureType, extent), {
|
|
62
|
-
signal: new AbortController().signal,
|
|
63
|
-
})
|
|
64
|
-
.then((res) => {
|
|
65
|
-
return res.json();
|
|
66
|
-
})
|
|
67
|
-
.then((data) => {
|
|
68
|
-
if (data.features?.[0].properties) {
|
|
69
|
-
selectedFeature.setProperties(data.features[0].properties);
|
|
70
|
-
}
|
|
71
|
-
setFeatures([selectedFeature]);
|
|
72
|
-
})
|
|
73
|
-
.catch((err) => {
|
|
74
|
-
console.error(err);
|
|
75
|
-
});
|
|
22
|
+
if (isCluster) {
|
|
23
|
+
setFeatures(selectedFeature.get("features"));
|
|
76
24
|
} else {
|
|
77
|
-
|
|
78
|
-
setFeatures(selectedFeature.get("features"));
|
|
79
|
-
} else {
|
|
80
|
-
setFeatures([selectedFeature]);
|
|
81
|
-
}
|
|
25
|
+
setFeatures([selectedFeature]);
|
|
82
26
|
}
|
|
83
|
-
|
|
84
|
-
abortController?.abort();
|
|
85
|
-
};
|
|
86
|
-
}, [selectedFeature, isFloatingVehicle]);
|
|
87
|
-
|
|
88
|
-
const createRequestLink = (name, extent) => {
|
|
89
|
-
return (
|
|
90
|
-
"https://api.mobidata-bw.de/geoserver/MobiData-BW/" +
|
|
91
|
-
name +
|
|
92
|
-
"/ows" +
|
|
93
|
-
"?service=WFS&" +
|
|
94
|
-
"version=1.1.0&request=GetFeature&typename=" +
|
|
95
|
-
"MobiData-BW:" +
|
|
96
|
-
name +
|
|
97
|
-
"&" +
|
|
98
|
-
"outputFormat=application/json&srsname=EPSG:3857&" +
|
|
99
|
-
"bbox=" +
|
|
100
|
-
extent.join(",") +
|
|
101
|
-
",EPSG:3857"
|
|
102
|
-
);
|
|
103
|
-
};
|
|
27
|
+
}, [selectedFeature, isFloatingVehicle, isCluster]);
|
|
104
28
|
|
|
105
29
|
const featuresByFeedId: Record<string, Feature[]> = useMemo(() => {
|
|
106
30
|
const featuresByFeedId = {};
|
|
@@ -125,14 +49,14 @@ function RvfSharedMobilityDetails({
|
|
|
125
49
|
<img
|
|
126
50
|
alt="logo"
|
|
127
51
|
className="max-w-24"
|
|
128
|
-
src={
|
|
52
|
+
src={PROVIDER_LOGOS_BY_FEED_ID[features[0]?.get("feed_id")]}
|
|
129
53
|
/>
|
|
130
54
|
{features.length && isStationDetails && (
|
|
131
55
|
<StationDetails feature={features[0]} />
|
|
132
56
|
)}
|
|
133
57
|
</>
|
|
134
58
|
)}
|
|
135
|
-
{isFloatingVehicle &&
|
|
59
|
+
{(isFloatingVehicle || isCluster) &&
|
|
136
60
|
featuresByFeedId &&
|
|
137
61
|
Object.entries(featuresByFeedId).map(([key, feats]) => {
|
|
138
62
|
return (
|
|
@@ -140,7 +64,7 @@ function RvfSharedMobilityDetails({
|
|
|
140
64
|
<img
|
|
141
65
|
alt="logo"
|
|
142
66
|
className="max-w-24"
|
|
143
|
-
src={
|
|
67
|
+
src={PROVIDER_LOGOS_BY_FEED_ID[feats[0]?.get("feed_id")]}
|
|
144
68
|
/>
|
|
145
69
|
<FloatingVehiclesDetails features={feats} />
|
|
146
70
|
</Fragment>
|
|
@@ -1,162 +1,13 @@
|
|
|
1
|
-
import { gql, GraphQLClient } from "graphql-request";
|
|
2
1
|
import { Feature } from "ol";
|
|
3
2
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
4
3
|
|
|
5
4
|
import RvfLink from "../../../RvfLink";
|
|
6
5
|
import getLinkByDevice from "../../../utils/getLinkByDevice";
|
|
6
|
+
import { fetchSharingStation } from "../../../utils/sharingGraphqlUtils";
|
|
7
7
|
export interface StationDetailsProps {
|
|
8
8
|
feature: Feature;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const document = gql`
|
|
12
|
-
query station($id: String!) {
|
|
13
|
-
station(id: $id) {
|
|
14
|
-
name {
|
|
15
|
-
translation {
|
|
16
|
-
language
|
|
17
|
-
value
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
shortName {
|
|
21
|
-
translation {
|
|
22
|
-
language
|
|
23
|
-
value
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
lat
|
|
27
|
-
lon
|
|
28
|
-
region {
|
|
29
|
-
id
|
|
30
|
-
}
|
|
31
|
-
rentalUris {
|
|
32
|
-
android
|
|
33
|
-
ios
|
|
34
|
-
web
|
|
35
|
-
}
|
|
36
|
-
isVirtualStation
|
|
37
|
-
rentalMethods
|
|
38
|
-
parkingHoop
|
|
39
|
-
parkingType
|
|
40
|
-
contactPhone
|
|
41
|
-
address
|
|
42
|
-
rentalMethods
|
|
43
|
-
capacity
|
|
44
|
-
vehicleTypesAvailable {
|
|
45
|
-
count
|
|
46
|
-
vehicleType {
|
|
47
|
-
id
|
|
48
|
-
formFactor
|
|
49
|
-
riderCapacity
|
|
50
|
-
cargoVolumeCapacity
|
|
51
|
-
cargoLoadCapacity
|
|
52
|
-
propulsionType
|
|
53
|
-
ecoLabels {
|
|
54
|
-
countryCode
|
|
55
|
-
ecoSticker
|
|
56
|
-
}
|
|
57
|
-
maxRangeMeters
|
|
58
|
-
name {
|
|
59
|
-
translation {
|
|
60
|
-
language
|
|
61
|
-
value
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
description {
|
|
65
|
-
translation {
|
|
66
|
-
language
|
|
67
|
-
value
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
vehicleAccessories
|
|
71
|
-
gCO2km
|
|
72
|
-
vehicleImage
|
|
73
|
-
make
|
|
74
|
-
model
|
|
75
|
-
color
|
|
76
|
-
wheelCount
|
|
77
|
-
maxPermittedSpeed
|
|
78
|
-
ratedPower
|
|
79
|
-
defaultReserveTime
|
|
80
|
-
returnConstraint
|
|
81
|
-
vehicleAssets {
|
|
82
|
-
iconUrl
|
|
83
|
-
iconUrlDark
|
|
84
|
-
iconLastModified
|
|
85
|
-
}
|
|
86
|
-
defaultReserveTime
|
|
87
|
-
defaultPricingPlan {
|
|
88
|
-
id
|
|
89
|
-
url
|
|
90
|
-
currency
|
|
91
|
-
isTaxable
|
|
92
|
-
description {
|
|
93
|
-
translation {
|
|
94
|
-
language
|
|
95
|
-
value
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
perKmPricing {
|
|
99
|
-
start
|
|
100
|
-
rate
|
|
101
|
-
interval
|
|
102
|
-
end
|
|
103
|
-
}
|
|
104
|
-
perMinPricing {
|
|
105
|
-
start
|
|
106
|
-
rate
|
|
107
|
-
interval
|
|
108
|
-
end
|
|
109
|
-
}
|
|
110
|
-
surgePricing
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
count
|
|
114
|
-
}
|
|
115
|
-
vehicleDocksCapacity {
|
|
116
|
-
vehicleTypes {
|
|
117
|
-
id
|
|
118
|
-
}
|
|
119
|
-
count
|
|
120
|
-
}
|
|
121
|
-
numVehiclesAvailable
|
|
122
|
-
numDocksDisabled
|
|
123
|
-
numVehiclesDisabled
|
|
124
|
-
numDocksAvailable
|
|
125
|
-
isInstalled
|
|
126
|
-
isRenting
|
|
127
|
-
isReturning
|
|
128
|
-
pricingPlans {
|
|
129
|
-
id
|
|
130
|
-
url
|
|
131
|
-
currency
|
|
132
|
-
isTaxable
|
|
133
|
-
description {
|
|
134
|
-
translation {
|
|
135
|
-
language
|
|
136
|
-
value
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
perKmPricing {
|
|
140
|
-
start
|
|
141
|
-
rate
|
|
142
|
-
interval
|
|
143
|
-
end
|
|
144
|
-
}
|
|
145
|
-
perMinPricing {
|
|
146
|
-
start
|
|
147
|
-
rate
|
|
148
|
-
interval
|
|
149
|
-
end
|
|
150
|
-
}
|
|
151
|
-
surgePricing
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
`;
|
|
156
|
-
const client = new GraphQLClient("https://api.mobidata-bw.de/sharing/graphql", {
|
|
157
|
-
method: "GET",
|
|
158
|
-
});
|
|
159
|
-
|
|
160
11
|
function StationDetails({ feature }: StationDetailsProps) {
|
|
161
12
|
const [data, setData] = useState(null);
|
|
162
13
|
const link = useMemo(() => {
|
|
@@ -167,15 +18,8 @@ function StationDetails({ feature }: StationDetailsProps) {
|
|
|
167
18
|
const stationId = feature.get("station_id");
|
|
168
19
|
if (stationId) {
|
|
169
20
|
const fetchData = async () => {
|
|
170
|
-
const
|
|
171
|
-
document,
|
|
172
|
-
{
|
|
173
|
-
__operation: "station",
|
|
174
|
-
id: feature.get("station_id"),
|
|
175
|
-
},
|
|
176
|
-
);
|
|
21
|
+
const station = await fetchSharingStation(feature.get("station_id"));
|
|
177
22
|
setData(station);
|
|
178
|
-
// console.log(station);
|
|
179
23
|
};
|
|
180
24
|
fetchData();
|
|
181
25
|
}
|
|
@@ -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 RvfSharedMobilityLayerGroup2 from "../RvfSharedMobilityLayerGroup2";
|
|
46
46
|
import RvfShareMenuButton from "../RvfShareMenuButton";
|
|
47
47
|
import RvfTarifZonenLayer from "../RvfTarifZonenLayer";
|
|
48
48
|
import Topics from "../RvfTopics";
|
|
@@ -67,6 +67,7 @@ import MobilityEvent from "../utils/MobilityEvent";
|
|
|
67
67
|
import style from "./index.css";
|
|
68
68
|
|
|
69
69
|
export type RvfMobilityMapProps = {
|
|
70
|
+
details: string;
|
|
70
71
|
layers: string; // list of visible layers on load
|
|
71
72
|
layertree: string;
|
|
72
73
|
print: string;
|
|
@@ -89,11 +90,17 @@ const styleProps = {
|
|
|
89
90
|
const scrollableHandlerProps = {
|
|
90
91
|
style: { width: "calc(100% - 60px)" },
|
|
91
92
|
};
|
|
93
|
+
const realtimeLayerProps = {
|
|
94
|
+
bboxParameters: {
|
|
95
|
+
line_tags: "RVF",
|
|
96
|
+
},
|
|
97
|
+
};
|
|
92
98
|
|
|
93
99
|
function RvfMobilityMap({
|
|
94
100
|
apikey = "5cc87b12d7c5370001c1d655820abcc37dfd4d968d7bab5b2a74a935",
|
|
95
101
|
baselayer = "de.rvf",
|
|
96
102
|
center = null,
|
|
103
|
+
details = "true",
|
|
97
104
|
extent = bbox,
|
|
98
105
|
geolocation = "true",
|
|
99
106
|
layers = null,
|
|
@@ -114,7 +121,7 @@ function RvfMobilityMap({
|
|
|
114
121
|
search = "false",
|
|
115
122
|
share = "true",
|
|
116
123
|
stopsurl = "https://api.geops.io/stops/v1/",
|
|
117
|
-
tenant =
|
|
124
|
+
tenant = "de-gtfs-de",
|
|
118
125
|
toolbar = "true",
|
|
119
126
|
zoom = null,
|
|
120
127
|
}: RvfMobilityMapProps) {
|
|
@@ -176,6 +183,10 @@ function RvfMobilityMap({
|
|
|
176
183
|
return print === "true";
|
|
177
184
|
}, [print]);
|
|
178
185
|
|
|
186
|
+
const hasDetails = useMemo(() => {
|
|
187
|
+
return details === "true";
|
|
188
|
+
}, [details]);
|
|
189
|
+
|
|
179
190
|
// Apply initial visibility of layers
|
|
180
191
|
useInitialLayersVisiblity(map, layers);
|
|
181
192
|
|
|
@@ -265,6 +276,7 @@ function RvfMobilityMap({
|
|
|
265
276
|
new MobilityEvent<RvfMobilityMapProps>("mwc:attribute", {
|
|
266
277
|
baselayer,
|
|
267
278
|
center,
|
|
279
|
+
details,
|
|
268
280
|
extent,
|
|
269
281
|
geolocation,
|
|
270
282
|
layers,
|
|
@@ -312,6 +324,7 @@ function RvfMobilityMap({
|
|
|
312
324
|
maxextent,
|
|
313
325
|
print,
|
|
314
326
|
share,
|
|
327
|
+
details,
|
|
315
328
|
]);
|
|
316
329
|
|
|
317
330
|
const rvfContextValue = useMemo(() => {
|
|
@@ -462,7 +475,10 @@ function RvfMobilityMap({
|
|
|
462
475
|
<RvfSelectedFeatureHighlightLayer />
|
|
463
476
|
|
|
464
477
|
{hasRealtime && (
|
|
465
|
-
<RealtimeLayer
|
|
478
|
+
<RealtimeLayer
|
|
479
|
+
title={RVF_LAYERS_TITLES.echtzeit}
|
|
480
|
+
{...realtimeLayerProps}
|
|
481
|
+
/>
|
|
466
482
|
)}
|
|
467
483
|
{
|
|
468
484
|
<StationsLayer
|
|
@@ -483,7 +499,7 @@ function RvfMobilityMap({
|
|
|
483
499
|
title={RVF_LAYERS_TITLES.liniennetz}
|
|
484
500
|
/>
|
|
485
501
|
<RvfPoisLayer title={RVF_LAYERS_TITLES.pois} />
|
|
486
|
-
<
|
|
502
|
+
<RvfSharedMobilityLayerGroup2
|
|
487
503
|
title={RVF_LAYERS_TITLES.sharedMobility}
|
|
488
504
|
/>
|
|
489
505
|
|
|
@@ -546,7 +562,7 @@ function RvfMobilityMap({
|
|
|
546
562
|
<Station className="relative flex flex-col overflow-y-auto overflow-x-hidden p-2" />
|
|
547
563
|
</>
|
|
548
564
|
)}
|
|
549
|
-
{selectedFeature && (
|
|
565
|
+
{hasDetails && selectedFeature && (
|
|
550
566
|
<>
|
|
551
567
|
<RvfOverlayHeader
|
|
552
568
|
onClose={() => {
|