@geops/rvf-mobility-web-component 0.1.15 → 0.1.17
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 +38 -0
- package/docutils.js +8 -2
- package/index.html +14 -2
- package/index.js +152 -78
- package/package.json +2 -1
- package/src/RealtimeLayer/RealtimeLayer.tsx +2 -0
- package/src/RvfExportMenu/RvfExportMenu.tsx +12 -1
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -4
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +53 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +123 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +32 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/index.tsx +1 -0
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +123 -34
- package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +1 -1
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -0
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +64 -0
- package/src/RvfSelectedFeatureHighlightLayer/index.tsx +1 -0
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -0
- package/src/RvfShare/RvfShare.tsx +1 -1
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +59 -23
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +2 -10
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -0
- package/src/RvfTopics/RvfTopics.tsx +40 -8
- package/src/StationsLayer/StationsLayer.tsx +2 -0
- package/src/icons/Bike/rvf_shared_bike.svg +2 -2
- package/src/icons/Car/rvf_shared_car.svg +3 -3
- package/src/icons/CargoBike/rvf_shared_cargo_bike.svg +3 -3
- package/src/icons/Scooter/rvf_shared_scooter.svg +2 -2
- package/src/index.tsx +4 -0
- package/src/logos/callabike_logo.png +0 -0
- package/src/logos/flinkster_logo.png +0 -0
- package/src/logos/gruene_flotte_logo.png +0 -0
- package/src/logos/logo_frelo_web_rgb.png +0 -0
- package/src/logos/natur_energie_logo.png +0 -0
- package/src/logos/yoio_logo.png +0 -0
- package/src/logos/zeus_logo.png +0 -0
- package/src/utils/applyInitialLayerVisibility.ts +41 -0
- package/src/utils/constants.ts +63 -0
- package/src/utils/{createSharedMobilityLayer.ts → createFreeFloatMobilityLayer.ts} +41 -69
- package/src/utils/createMobiDataBwWfsLayer.ts +108 -67
- package/src/utils/exportPdf.ts +1 -4
- package/src/utils/getFeatureInformationTitle.ts +37 -0
- package/src/utils/getLayersAsFlatArray.ts +22 -0
- package/src/utils/getLinkByDevice.ts +23 -0
- package/src/utils/getPermalinkParameters.ts +27 -0
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +28 -0
- package/src/utils/hooks/useMapContext.tsx +2 -0
- package/src/utils/hooks/useUpdatePermalink.tsx +44 -11
|
@@ -4,15 +4,98 @@ import { Point } from "ol/geom";
|
|
|
4
4
|
import VectorLayer, { Options } from "ol/layer/Vector";
|
|
5
5
|
import { bbox as bboxStrategy } from "ol/loadingstrategy.js";
|
|
6
6
|
import { Vector } from "ol/source";
|
|
7
|
-
import { Circle, Fill, Stroke, Style, Text } from "ol/style";
|
|
7
|
+
import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style";
|
|
8
8
|
|
|
9
|
+
// @ts-expect-error - load svg as data url
|
|
10
|
+
import bicycle from "../icons/Bike/rvf_shared_bike.svg";
|
|
11
|
+
// @ts-expect-error - load svg as data url
|
|
12
|
+
import car from "../icons/Car/rvf_shared_car.svg";
|
|
13
|
+
// @ts-expect-error - load svg as data url
|
|
14
|
+
import cargo_bicycle from "../icons/CargoBike/rvf_shared_cargo_bike.svg";
|
|
15
|
+
// @ts-expect-error - load svg as data url
|
|
16
|
+
import scooter from "../icons/Scooter/rvf_shared_scooter.svg";
|
|
9
17
|
import { LAYER_PROP_IS_EXPORTING } from "./constants";
|
|
10
18
|
|
|
19
|
+
const iconByFormFactor = {
|
|
20
|
+
bicycle,
|
|
21
|
+
car,
|
|
22
|
+
cargo_bicycle,
|
|
23
|
+
scooter,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const iconStyleByFormFactor = {};
|
|
27
|
+
Object.entries(iconByFormFactor).forEach(([key, value]) => {
|
|
28
|
+
iconStyleByFormFactor[key] = new Style({
|
|
29
|
+
image: new Icon({
|
|
30
|
+
src: value,
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const getCircleStyle = (color: string) => {
|
|
36
|
+
return new Style({
|
|
37
|
+
image: new Circle({
|
|
38
|
+
displacement: [12, 12],
|
|
39
|
+
fill: new Fill({
|
|
40
|
+
color: "white",
|
|
41
|
+
}),
|
|
42
|
+
radius: 9,
|
|
43
|
+
stroke: new Stroke({
|
|
44
|
+
color: color,
|
|
45
|
+
width: 1,
|
|
46
|
+
}),
|
|
47
|
+
}),
|
|
48
|
+
text: new Text({
|
|
49
|
+
fill: new Fill({
|
|
50
|
+
color: color,
|
|
51
|
+
}),
|
|
52
|
+
font: "bolder 10px arial",
|
|
53
|
+
offsetX: 12,
|
|
54
|
+
offsetY: -11,
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getCompanyNameTextStyle = (feedId: string) => {
|
|
60
|
+
const offsetY =
|
|
61
|
+
feedId === "naturenergie_sharing" || feedId === "gruene_flotte_freiburg"
|
|
62
|
+
? 25
|
|
63
|
+
: 20;
|
|
64
|
+
return new Style({
|
|
65
|
+
text: new Text({
|
|
66
|
+
declutterMode: "declutter",
|
|
67
|
+
fill: new Fill({ color: "rgba(6, 76, 10, 1)" }),
|
|
68
|
+
font: "bold 12px/1.1 source-sans-pro",
|
|
69
|
+
offsetY,
|
|
70
|
+
stroke: new Stroke({ color: "white", width: 4 }),
|
|
71
|
+
text: companyTextByFeedId[feedId] || feedId,
|
|
72
|
+
// textBaseline: "middle",
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const companyTextByFeedId = {
|
|
78
|
+
callabike_ice: "Call a Bike",
|
|
79
|
+
flinkster_carsharing: "Flinkster",
|
|
80
|
+
gruene_flotte_freiburg: "Die Grüne\nFlotte",
|
|
81
|
+
lastenvelo_fr: "LastenVelo",
|
|
82
|
+
naturenergie_sharing: "naturenergie\nsharing",
|
|
83
|
+
nextbike_df: "Frelo",
|
|
84
|
+
// "nextbike",
|
|
85
|
+
// "EinfachMobil",
|
|
86
|
+
// "Donkey Republic"
|
|
87
|
+
// "PubliBike",
|
|
88
|
+
// "Voi",
|
|
89
|
+
// "Velospot",
|
|
90
|
+
// "carvelo",
|
|
91
|
+
// "CarSharing"
|
|
92
|
+
};
|
|
93
|
+
|
|
11
94
|
function createMobiDataBwWfsLayer(
|
|
12
95
|
name: string,
|
|
13
96
|
color: string,
|
|
14
97
|
layerOptions: Options = {
|
|
15
|
-
minZoom:
|
|
98
|
+
minZoom: 18,
|
|
16
99
|
},
|
|
17
100
|
): VectorLayer<Vector<Feature<Point>>> {
|
|
18
101
|
const source = new Vector({
|
|
@@ -36,86 +119,44 @@ function createMobiDataBwWfsLayer(
|
|
|
36
119
|
},
|
|
37
120
|
});
|
|
38
121
|
|
|
39
|
-
const style =
|
|
40
|
-
image: new Circle({
|
|
41
|
-
declutterMode: "declutter",
|
|
42
|
-
displacement: [11, 12],
|
|
43
|
-
fill: new Fill({
|
|
44
|
-
color: "white",
|
|
45
|
-
}),
|
|
46
|
-
radius: 10,
|
|
47
|
-
stroke: new Stroke({
|
|
48
|
-
color: color,
|
|
49
|
-
width: 2,
|
|
50
|
-
}),
|
|
51
|
-
}),
|
|
52
|
-
text: new Text({
|
|
53
|
-
declutterMode: "declutter",
|
|
54
|
-
fill: new Fill({
|
|
55
|
-
color: color,
|
|
56
|
-
}),
|
|
57
|
-
font: "bold 12px inherit",
|
|
58
|
-
offsetX: 11,
|
|
59
|
-
offsetY: -12,
|
|
60
|
-
// stroke: new Stroke({ color: "black", width: 1 }),
|
|
61
|
-
// text: ["to-string", ["get", "num_vehicles_available"]],
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
122
|
+
const style = getCircleStyle(color);
|
|
64
123
|
|
|
65
124
|
const layer = new VectorLayer({
|
|
66
125
|
// @ts-expect-error - custom properties
|
|
67
126
|
isQueryable: true,
|
|
68
|
-
name,
|
|
69
127
|
// declutter: true,
|
|
70
128
|
source: source,
|
|
71
129
|
...layerOptions,
|
|
72
|
-
// style: (feature) => {
|
|
73
|
-
// console.log("feature", feature);
|
|
74
|
-
// style
|
|
75
|
-
// .getText()
|
|
76
|
-
// .setText(feature.get("num_vehicles_available").toString());
|
|
77
|
-
// return style;
|
|
78
|
-
// },
|
|
79
|
-
|
|
80
|
-
// style: {
|
|
81
|
-
// "circle-fill-color": "white",
|
|
82
|
-
// "circle-radius": 15,
|
|
83
|
-
// "circle-stroke-color": color,
|
|
84
|
-
// "circle-stroke-width": 2,
|
|
85
|
-
// // "fill-color": "rgba(100,100,100,0.25)",
|
|
86
|
-
// // "stroke-color": "white",
|
|
87
|
-
// // "stroke-width": 0.75,
|
|
88
|
-
// // "text-background-fill-color": "black",
|
|
89
|
-
// "text-fill-color": color,
|
|
90
|
-
// "text-font": "bold 12px sans-serif",
|
|
91
|
-
// "text-stroke-color": color,
|
|
92
|
-
// "text-value": ["to-string", ["get", "num_vehicles_available"]],
|
|
93
|
-
// },
|
|
94
130
|
});
|
|
95
131
|
|
|
96
132
|
const styleFunction = (feature: Feature) => {
|
|
97
133
|
if (layer.get(LAYER_PROP_IS_EXPORTING)) {
|
|
98
134
|
return null;
|
|
99
135
|
}
|
|
100
|
-
const
|
|
136
|
+
const circleStyle = style.clone();
|
|
101
137
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// }),
|
|
138
|
+
const numVehiclesAvailable = feature
|
|
139
|
+
.get("num_vehicles_available")
|
|
140
|
+
?.toString();
|
|
141
|
+
circleStyle.getText().setText(numVehiclesAvailable);
|
|
142
|
+
|
|
143
|
+
const formFactor = (feature.getId() as string)
|
|
144
|
+
.split(".")[0]
|
|
145
|
+
.replace("sharing_stations_", "");
|
|
146
|
+
|
|
147
|
+
const feedId = feature.get("feed_id").replace("-", "_"); // to handle id's like gruene-flotte_freiburg
|
|
148
|
+
|
|
149
|
+
const styles = [
|
|
150
|
+
iconStyleByFormFactor[formFactor],
|
|
151
|
+
getCompanyNameTextStyle(feedId),
|
|
152
|
+
circleStyle,
|
|
118
153
|
];
|
|
154
|
+
|
|
155
|
+
styles.forEach((style) => {
|
|
156
|
+
style.setZIndex(parseInt(getUid(feature), 10));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return styles;
|
|
119
160
|
};
|
|
120
161
|
|
|
121
162
|
source.on("addfeature", function (event) {
|
package/src/utils/exportPdf.ts
CHANGED
|
@@ -554,9 +554,7 @@ async function exportPdf(
|
|
|
554
554
|
const size = sizePt.map((n) => {
|
|
555
555
|
return (n * 96) / 72;
|
|
556
556
|
});
|
|
557
|
-
const extent = useMaxExtent
|
|
558
|
-
? RVF_EXTENT_3857
|
|
559
|
-
: map.getView().calculateExtent();
|
|
557
|
+
const extent = useMaxExtent ? RVF_EXTENT_3857 : null; //map.getView().calculateExtent();
|
|
560
558
|
|
|
561
559
|
// Save current pixel ratio
|
|
562
560
|
const actualPixelRatio = window.devicePixelRatio;
|
|
@@ -602,7 +600,6 @@ async function exportPdf(
|
|
|
602
600
|
const mapToExport = await createMapToExport(map, layers, extent, size, {
|
|
603
601
|
pixelRatio: pixelRatio,
|
|
604
602
|
});
|
|
605
|
-
|
|
606
603
|
Object.defineProperty(window, "devicePixelRatio", {
|
|
607
604
|
get() {
|
|
608
605
|
return actualPixelRatio;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Feature } from "ol";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
RVF_LAYERS_TITLES,
|
|
5
|
+
TITLE_BY_CATEGORY,
|
|
6
|
+
TITLE_BY_FEED_ID,
|
|
7
|
+
} from "./constants";
|
|
8
|
+
|
|
9
|
+
const defaultTitle = "Informations";
|
|
10
|
+
|
|
11
|
+
const getFeatureInformationTitle = (feature: Feature) => {
|
|
12
|
+
const features = feature.get("features");
|
|
13
|
+
const selectedFeature = features?.[0] || feature;
|
|
14
|
+
const {
|
|
15
|
+
category,
|
|
16
|
+
feed_id: feedId,
|
|
17
|
+
long_name: longName,
|
|
18
|
+
tickets,
|
|
19
|
+
} = selectedFeature.getProperties();
|
|
20
|
+
|
|
21
|
+
if (category) {
|
|
22
|
+
return TITLE_BY_CATEGORY[category] || defaultTitle;
|
|
23
|
+
}
|
|
24
|
+
if (feedId) {
|
|
25
|
+
return TITLE_BY_FEED_ID[feedId] || defaultTitle;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (longName) {
|
|
29
|
+
return RVF_LAYERS_TITLES.liniennetz;
|
|
30
|
+
}
|
|
31
|
+
if (tickets) {
|
|
32
|
+
return RVF_LAYERS_TITLES.verkaufsstellen;
|
|
33
|
+
}
|
|
34
|
+
return defaultTitle;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default getFeatureInformationTitle;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Group } from "ol/layer";
|
|
2
|
+
import BaseLayer from "ol/layer/Base";
|
|
3
|
+
|
|
4
|
+
const getLayersAsFlatArray = (
|
|
5
|
+
layersOrLayer: BaseLayer | BaseLayer[],
|
|
6
|
+
): BaseLayer[] => {
|
|
7
|
+
let layers = layersOrLayer;
|
|
8
|
+
if (!Array.isArray(layers)) {
|
|
9
|
+
layers = [layersOrLayer as BaseLayer];
|
|
10
|
+
}
|
|
11
|
+
let flatLayers: BaseLayer[] = [];
|
|
12
|
+
layers.forEach((layer: BaseLayer) => {
|
|
13
|
+
flatLayers.push(layer);
|
|
14
|
+
// Handle children property and ol.layer.Group
|
|
15
|
+
const children =
|
|
16
|
+
layer.get("children") || (layer as Group).getLayers?.()?.getArray();
|
|
17
|
+
flatLayers = flatLayers.concat(getLayersAsFlatArray(children || []));
|
|
18
|
+
});
|
|
19
|
+
return flatLayers;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default getLayersAsFlatArray;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Feature } from "ol";
|
|
2
|
+
|
|
3
|
+
function getLinkByDevice(feature: Feature): string {
|
|
4
|
+
const iosLink = feature.get("rental_uris_ios");
|
|
5
|
+
const androidLink = feature.get("rental_uris_android");
|
|
6
|
+
const webLink = feature.get("rental_uris_web");
|
|
7
|
+
if (
|
|
8
|
+
iosLink &&
|
|
9
|
+
(navigator.userAgent.includes("iPhone") ||
|
|
10
|
+
navigator.userAgent.includes("iPad") ||
|
|
11
|
+
navigator.userAgent.includes("iPod"))
|
|
12
|
+
) {
|
|
13
|
+
return iosLink;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (androidLink && navigator.userAgent.includes("Android")) {
|
|
17
|
+
return androidLink;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return webLink;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default getLinkByDevice;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Map } from "ol";
|
|
2
|
+
|
|
3
|
+
import getLayersAsFlatArray from "./getLayersAsFlatArray";
|
|
4
|
+
|
|
5
|
+
// This function return URL parameters representing a map.
|
|
6
|
+
const getPermalinkParameters = (
|
|
7
|
+
map: Map,
|
|
8
|
+
urlParams: URLSearchParams = new URLSearchParams(),
|
|
9
|
+
): URLSearchParams => {
|
|
10
|
+
const z = map.getView().getZoom();
|
|
11
|
+
const [x, y] = map.getView().getCenter();
|
|
12
|
+
const layers = getLayersAsFlatArray(map.getLayers().getArray())
|
|
13
|
+
.filter((layer) => {
|
|
14
|
+
return layer.get("name") && layer.getVisible();
|
|
15
|
+
})
|
|
16
|
+
.map((layer) => {
|
|
17
|
+
return layer.get("name");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
urlParams.set("layers", layers.join(","));
|
|
21
|
+
urlParams.set("x", x.toFixed(2));
|
|
22
|
+
urlParams.set("y", y.toFixed(2));
|
|
23
|
+
urlParams.set("z", z.toFixed(1));
|
|
24
|
+
return urlParams;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default getPermalinkParameters;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Map } from "ol";
|
|
2
|
+
import { unByKey } from "ol/Observable";
|
|
3
|
+
import { useEffect } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
import applyInitialLayerVisibility from "../applyInitialLayerVisibility";
|
|
6
|
+
import getLayersAsFlatArray from "../getLayersAsFlatArray";
|
|
7
|
+
|
|
8
|
+
const useInitialLayersVisiblity = (map: Map, layers: string) => {
|
|
9
|
+
// Apply initial visibility of layers from layers attribute
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!map) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
getLayersAsFlatArray(map.getLayers().getArray()).forEach((layer) => {
|
|
15
|
+
applyInitialLayerVisibility(layers, layer);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const key = map.getLayers().on("add", (event) => {
|
|
19
|
+
applyInitialLayerVisibility(layers, event.element);
|
|
20
|
+
});
|
|
21
|
+
return () => {
|
|
22
|
+
unByKey(key);
|
|
23
|
+
};
|
|
24
|
+
}, [map, layers]);
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default useInitialLayersVisiblity;
|
|
@@ -21,6 +21,7 @@ export type MapContextType = {
|
|
|
21
21
|
baseLayer: MaplibreLayer;
|
|
22
22
|
isFollowing: boolean;
|
|
23
23
|
isTracking: boolean;
|
|
24
|
+
layers: string;
|
|
24
25
|
map: Map;
|
|
25
26
|
realtimeLayer: RealtimeLayer;
|
|
26
27
|
setBaseLayer: (baseLayer: MaplibreLayer) => void;
|
|
@@ -44,6 +45,7 @@ export const MapContext = createContext<MapContextType>({
|
|
|
44
45
|
baseLayer: null,
|
|
45
46
|
isFollowing: false,
|
|
46
47
|
isTracking: false,
|
|
48
|
+
layers: null,
|
|
47
49
|
map: null,
|
|
48
50
|
realtimeLayer: null,
|
|
49
51
|
setBaseLayer: (baseLayer?: MaplibreLayer) => {},
|
|
@@ -1,26 +1,59 @@
|
|
|
1
|
+
import debounce from "lodash.debounce";
|
|
1
2
|
import { Map } from "ol";
|
|
3
|
+
import { EventsKey } from "ol/events";
|
|
2
4
|
import { unByKey } from "ol/Observable";
|
|
3
5
|
import { useEffect } from "preact/hooks";
|
|
4
6
|
|
|
7
|
+
import getLayersAsFlatArray from "../getLayersAsFlatArray";
|
|
8
|
+
import getPermalinkParameters from "../getPermalinkParameters";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* This hook only update parameters in the url, it does not apply the url parameters.
|
|
12
|
+
*/
|
|
13
|
+
|
|
5
14
|
const useUpdatePermalink = (map: Map, permalink: boolean) => {
|
|
6
15
|
useEffect(() => {
|
|
7
|
-
let
|
|
16
|
+
let moveEndKey: EventsKey;
|
|
17
|
+
let loadEndKey: EventsKey;
|
|
18
|
+
let changeVisibleKeys: EventsKey[];
|
|
19
|
+
|
|
8
20
|
if (map && permalink) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
updatePermalinkDebounced(map);
|
|
22
|
+
|
|
23
|
+
// Update x,y,z in URL on moveend
|
|
24
|
+
moveEndKey = map?.on("moveend", (evt) => {
|
|
25
|
+
updatePermalinkDebounced(evt.map);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Update layers in URL on change:visible event
|
|
29
|
+
loadEndKey = map.once("loadend", (evt) => {
|
|
30
|
+
updatePermalinkDebounced(evt.map);
|
|
31
|
+
changeVisibleKeys = getLayersAsFlatArray(
|
|
32
|
+
evt.map.getLayers().getArray(),
|
|
33
|
+
).map((layer) => {
|
|
34
|
+
return layer.on("change:visible", () => {
|
|
35
|
+
updatePermalinkDebounced(evt.map);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
18
38
|
});
|
|
19
39
|
}
|
|
40
|
+
|
|
20
41
|
return () => {
|
|
21
|
-
unByKey(
|
|
42
|
+
unByKey(moveEndKey);
|
|
43
|
+
unByKey(loadEndKey);
|
|
44
|
+
unByKey(changeVisibleKeys);
|
|
22
45
|
};
|
|
23
46
|
}, [map, permalink]);
|
|
24
47
|
return null;
|
|
25
48
|
};
|
|
49
|
+
|
|
50
|
+
const updatePermalink = (map: Map) => {
|
|
51
|
+
const currentUrlParams = new URLSearchParams(window.location.search);
|
|
52
|
+
const urlParams = getPermalinkParameters(map, currentUrlParams);
|
|
53
|
+
urlParams.set("permalink", "true");
|
|
54
|
+
window.history.replaceState(null, null, `?${urlParams.toString()}`);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const updatePermalinkDebounced = debounce(updatePermalink, 1000);
|
|
58
|
+
|
|
26
59
|
export default useUpdatePermalink;
|