@geospatial-sdk/openlayers 0.0.5-dev.50 → 0.0.5-dev.52
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/dist/map/constants.d.ts +2 -0
- package/dist/map/constants.d.ts.map +1 -0
- package/dist/map/constants.js +1 -0
- package/dist/map/create-map.d.ts.map +1 -1
- package/dist/map/create-map.js +3 -2
- package/dist/map/feature-hover.d.ts +7 -0
- package/dist/map/feature-hover.d.ts.map +1 -0
- package/dist/map/feature-hover.js +82 -0
- package/dist/map/get-features.d.ts +13 -0
- package/dist/map/get-features.d.ts.map +1 -0
- package/dist/map/get-features.js +77 -0
- package/dist/map/index.d.ts +0 -1
- package/dist/map/index.d.ts.map +1 -1
- package/dist/map/index.js +0 -1
- package/dist/map/layer-update.d.ts.map +1 -1
- package/dist/map/layer-update.js +10 -0
- package/dist/map/register-events.d.ts +0 -8
- package/dist/map/register-events.d.ts.map +1 -1
- package/dist/map/register-events.js +4 -58
- package/lib/map/constants.ts +1 -0
- package/lib/map/create-map.test.ts +16 -13
- package/lib/map/create-map.ts +3 -1
- package/lib/map/feature-hover.ts +113 -0
- package/lib/map/get-features.test.ts +120 -0
- package/lib/map/get-features.ts +122 -0
- package/lib/map/handle-errors.test.ts +4 -5
- package/lib/map/index.ts +0 -1
- package/lib/map/layer-update.test.ts +27 -1
- package/lib/map/layer-update.ts +27 -3
- package/lib/map/register-events.test.ts +74 -122
- package/lib/map/register-events.ts +5 -98
- package/package.json +5 -5
- package/dist/map/styles.d.ts +0 -16
- package/dist/map/styles.d.ts.map +0 -1
- package/dist/map/styles.js +0 -77
- package/lib/map/styles.test.ts +0 -217
- package/lib/map/styles.ts +0 -103
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../lib/map/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const GEOSPATIAL_SDK_PREFIX = "--geospatial-sdk-";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-map.d.ts","sourceRoot":"","sources":["../../lib/map/create-map.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"create-map.d.ts","sourceRoot":"","sources":["../../lib/map/create-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,eAAe,EACf,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,GAAG,MAAM,WAAW,CAAC;AAC5B,OAAO,IAAI,MAAM,YAAY,CAAC;AAC9B,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAsCtC,wBAAsB,WAAW,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CAyN7E;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,eAAe,EAC3B,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,eAAe,QAgBpC;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAkC3E;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,UAAU,EACnB,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAC5B,OAAO,CAAC,GAAG,CAAC,CAKd;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,GAAG,CAAC,CASd"}
|
package/dist/map/create-map.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { removeSearchParams, } from "@geospatial-sdk/core";
|
|
1
|
+
import { defaultStyle, removeSearchParams, } from "@geospatial-sdk/core";
|
|
2
2
|
import Map from "ol/Map.js";
|
|
3
3
|
import View from "ol/View.js";
|
|
4
4
|
import TileLayer from "ol/layer/Tile.js";
|
|
@@ -9,7 +9,6 @@ import VectorSource from "ol/source/Vector.js";
|
|
|
9
9
|
import GeoJSON from "ol/format/GeoJSON.js";
|
|
10
10
|
import { fromLonLat, transformExtent } from "ol/proj.js";
|
|
11
11
|
import { bbox as bboxStrategy } from "ol/loadingstrategy.js";
|
|
12
|
-
import { defaultStyle } from "./styles.js";
|
|
13
12
|
import VectorTileLayer from "ol/layer/VectorTile.js";
|
|
14
13
|
import OGCMapTile from "ol/source/OGCMapTile.js";
|
|
15
14
|
import OGCVectorTile from "ol/source/OGCVectorTile.js";
|
|
@@ -20,6 +19,7 @@ import { MapboxVectorLayer } from "ol-mapbox-style";
|
|
|
20
19
|
import { handleEndpointError, tileLoadErrorCatchFunction, } from "./handle-errors.js";
|
|
21
20
|
import VectorTile from "ol/source/VectorTile.js";
|
|
22
21
|
import { canDoIncrementalUpdate, updateLayerProperties, } from "./layer-update.js";
|
|
22
|
+
import { initHoverLayer } from "./feature-hover.js";
|
|
23
23
|
const GEOJSON = new GeoJSON();
|
|
24
24
|
const WFS_MAX_FEATURES = 10000;
|
|
25
25
|
export async function createLayer(layerModel) {
|
|
@@ -291,5 +291,6 @@ export async function resetMapFromContext(map, context) {
|
|
|
291
291
|
const layer = await createLayer(layerModel);
|
|
292
292
|
map.addLayer(layer);
|
|
293
293
|
}
|
|
294
|
+
initHoverLayer(map);
|
|
294
295
|
return map;
|
|
295
296
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type OlMap from "ol/Map.js";
|
|
2
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
3
|
+
import VectorSource from "ol/source/Vector.js";
|
|
4
|
+
export declare function initHoverLayer(map: OlMap): void;
|
|
5
|
+
export declare function getHoverLayer(map: OlMap): VectorLayer<VectorSource>;
|
|
6
|
+
export declare function clearHoverLayer(map: OlMap): void;
|
|
7
|
+
//# sourceMappingURL=feature-hover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-hover.d.ts","sourceRoot":"","sources":["../../lib/map/feature-hover.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,WAAW,CAAC;AACnC,OAAO,WAAW,MAAM,oBAAoB,CAAC;AAC7C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAe/C,wBAAgB,cAAc,CAAC,GAAG,EAAE,KAAK,QAkFxC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,CAEnE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,KAAK,QAMzC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
2
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
3
|
+
import VectorSource from "ol/source/Vector.js";
|
|
4
|
+
import { defaultHighlightStyle, FeaturesHoverEventType, } from "@geospatial-sdk/core";
|
|
5
|
+
import OlFeature from "ol/Feature.js";
|
|
6
|
+
import { unByKey } from "ol/Observable.js";
|
|
7
|
+
import { readFeaturesAtPixel } from "./get-features.js";
|
|
8
|
+
const hoverLayerKey = `${GEOSPATIAL_SDK_PREFIX}hover-layer`;
|
|
9
|
+
const unsubscribeKey = `${GEOSPATIAL_SDK_PREFIX}hover-unsub`;
|
|
10
|
+
export function initHoverLayer(map) {
|
|
11
|
+
if (map.get(hoverLayerKey)) {
|
|
12
|
+
clearHoverLayer(map);
|
|
13
|
+
}
|
|
14
|
+
// create layer & add on top of everything else
|
|
15
|
+
const hoverLayer = new VectorLayer({
|
|
16
|
+
source: new VectorSource({
|
|
17
|
+
features: [],
|
|
18
|
+
useSpatialIndex: false,
|
|
19
|
+
}),
|
|
20
|
+
style: defaultHighlightStyle,
|
|
21
|
+
});
|
|
22
|
+
map.set(hoverLayerKey, hoverLayer);
|
|
23
|
+
hoverLayer.setMap(map);
|
|
24
|
+
// store original cursor style in order to change it later
|
|
25
|
+
const originalCursorStyle = map.getTargetElement()?.style.cursor ?? "";
|
|
26
|
+
const layerFilter = (layer) => layer !== hoverLayer && layer.get(`${GEOSPATIAL_SDK_PREFIX}enable-hover`);
|
|
27
|
+
const unKey = map.on("pointermove", async (event) => {
|
|
28
|
+
// skip hit detection if the view is moving as it can have an impact on performance
|
|
29
|
+
if (map.getView().getInteracting() || map.getView().getAnimating()) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// change cursor if above a feature
|
|
33
|
+
const hasFeature = map.hasFeatureAtPixel(event.pixel, {
|
|
34
|
+
layerFilter,
|
|
35
|
+
});
|
|
36
|
+
if (map.getTargetElement()) {
|
|
37
|
+
map.getTargetElement().style.cursor = hasFeature
|
|
38
|
+
? "pointer"
|
|
39
|
+
: originalCursorStyle;
|
|
40
|
+
}
|
|
41
|
+
const hoveredSource = hoverLayer.getSource();
|
|
42
|
+
hoveredSource.clear(true);
|
|
43
|
+
if (!hasFeature) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// add hovered feature to the layer
|
|
47
|
+
let firstFeature = null;
|
|
48
|
+
map.forEachFeatureAtPixel(event.pixel, (feature) => {
|
|
49
|
+
if (feature instanceof OlFeature) {
|
|
50
|
+
firstFeature = feature;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}, {
|
|
54
|
+
layerFilter,
|
|
55
|
+
});
|
|
56
|
+
if (!firstFeature) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
hoveredSource.addFeature(firstFeature);
|
|
60
|
+
// dispatch event if subscribed to
|
|
61
|
+
if (map.get(FeaturesHoverEventType)) {
|
|
62
|
+
const featuresByLayer = await readFeaturesAtPixel(map, event, layerFilter);
|
|
63
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
64
|
+
map.dispatchEvent({
|
|
65
|
+
type: FeaturesHoverEventType,
|
|
66
|
+
features,
|
|
67
|
+
featuresByLayer,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
map.set(unsubscribeKey, unKey);
|
|
72
|
+
}
|
|
73
|
+
export function getHoverLayer(map) {
|
|
74
|
+
return map.get(hoverLayerKey);
|
|
75
|
+
}
|
|
76
|
+
export function clearHoverLayer(map) {
|
|
77
|
+
const hoverLayer = getHoverLayer(map);
|
|
78
|
+
hoverLayer.setMap(null);
|
|
79
|
+
hoverLayer.dispose();
|
|
80
|
+
map.set(hoverLayerKey, null);
|
|
81
|
+
unByKey(map.get(unsubscribeKey));
|
|
82
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import OlMap from "ol/Map.js";
|
|
2
|
+
import { Pixel } from "ol/pixel.js";
|
|
3
|
+
import TileWMS from "ol/source/TileWMS.js";
|
|
4
|
+
import ImageWMS from "ol/source/ImageWMS.js";
|
|
5
|
+
import { Coordinate } from "ol/coordinate.js";
|
|
6
|
+
import Layer from "ol/layer/Layer.js";
|
|
7
|
+
import type MapBrowserEvent from "ol/MapBrowserEvent.js";
|
|
8
|
+
import type { FeaturesByLayerIndex } from "@geospatial-sdk/core";
|
|
9
|
+
export declare function getFeaturesFromVectorSources(olMap: OlMap, pixel: Pixel, layerFilter?: (layer: Layer) => boolean): FeaturesByLayerIndex;
|
|
10
|
+
export declare function getGFIUrl(source: TileWMS | ImageWMS, map: OlMap, coordinate: Coordinate): string | null;
|
|
11
|
+
export declare function getFeaturesFromWmsSources(olMap: OlMap, coordinate: Coordinate, layerFilter?: (layer: Layer) => boolean): Promise<FeaturesByLayerIndex>;
|
|
12
|
+
export declare function readFeaturesAtPixel(map: OlMap, event: MapBrowserEvent<PointerEvent>, layerFilter?: (layer: Layer) => boolean): Promise<FeaturesByLayerIndex>;
|
|
13
|
+
//# sourceMappingURL=get-features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-features.d.ts","sourceRoot":"","sources":["../../lib/map/get-features.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,WAAW,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,OAAO,OAAO,MAAM,sBAAsB,CAAC;AAC3C,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAEtC,OAAO,KAAK,eAAe,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GACtC,oBAAoB,CAqBtB;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,OAAO,GAAG,QAAQ,EAC1B,GAAG,EAAE,KAAK,EACV,UAAU,EAAE,UAAU,GACrB,MAAM,GAAG,IAAI,CAWf;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GACtC,OAAO,CAAC,oBAAoB,CAAC,CAsC/B;AAOD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,KAAK,EACV,KAAK,EAAE,eAAe,CAAC,YAAY,CAAC,EACpC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GACtC,OAAO,CAAC,oBAAoB,CAAC,CAS/B"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import GeoJSON from "ol/format/GeoJSON.js";
|
|
2
|
+
import TileWMS from "ol/source/TileWMS.js";
|
|
3
|
+
import ImageWMS from "ol/source/ImageWMS.js";
|
|
4
|
+
import Layer from "ol/layer/Layer.js";
|
|
5
|
+
import throttle from "lodash.throttle";
|
|
6
|
+
const GEOJSON = new GeoJSON();
|
|
7
|
+
export function getFeaturesFromVectorSources(olMap, pixel, layerFilter) {
|
|
8
|
+
const result = new Map();
|
|
9
|
+
const layerArray = olMap.getLayers().getArray();
|
|
10
|
+
olMap.forEachFeatureAtPixel(pixel, (feature, layer) => {
|
|
11
|
+
// can happen for unmanaged layer (i.e. hover layer)
|
|
12
|
+
if (layer === null) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const layerIndex = layerArray.indexOf(layer);
|
|
16
|
+
if (!result.has(layerIndex)) {
|
|
17
|
+
result.set(layerIndex, []);
|
|
18
|
+
}
|
|
19
|
+
result
|
|
20
|
+
.get(layerIndex)
|
|
21
|
+
.push(GEOJSON.writeFeatureObject(feature));
|
|
22
|
+
}, { layerFilter });
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
export function getGFIUrl(source, map, coordinate) {
|
|
26
|
+
const view = map.getView();
|
|
27
|
+
const projection = view.getProjection();
|
|
28
|
+
const resolution = view.getResolution();
|
|
29
|
+
const params = {
|
|
30
|
+
...source.getParams(),
|
|
31
|
+
INFO_FORMAT: "application/json",
|
|
32
|
+
};
|
|
33
|
+
return (source.getFeatureInfoUrl(coordinate, resolution, projection, params) ?? null);
|
|
34
|
+
}
|
|
35
|
+
export async function getFeaturesFromWmsSources(olMap, coordinate, layerFilter) {
|
|
36
|
+
const result = new Map();
|
|
37
|
+
const layerArray = olMap.getLayers().getArray();
|
|
38
|
+
const hasWms = layerArray.some((layer) => {
|
|
39
|
+
const source = layer instanceof Layer ? layer.getSource() : null;
|
|
40
|
+
return source instanceof TileWMS || source instanceof ImageWMS;
|
|
41
|
+
});
|
|
42
|
+
if (!hasWms) {
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
const gfiPromises = layerArray.map((layer) => {
|
|
46
|
+
if (!(layer instanceof Layer)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (layerFilter && !layerFilter(layer)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const source = layer.getSource();
|
|
53
|
+
if (!(source instanceof TileWMS) && !(source instanceof ImageWMS)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const gfiUrl = getGFIUrl(source, olMap, coordinate);
|
|
57
|
+
return gfiUrl
|
|
58
|
+
? fetch(gfiUrl)
|
|
59
|
+
.then((response) => response.json())
|
|
60
|
+
.then((collection) => collection.features)
|
|
61
|
+
: null;
|
|
62
|
+
});
|
|
63
|
+
const responses = await Promise.all(gfiPromises);
|
|
64
|
+
responses.forEach((features, index) => {
|
|
65
|
+
if (features !== null && features.length > 0) {
|
|
66
|
+
result.set(index, features);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const getFeaturesFromWmsSourcesThrottled = throttle(getFeaturesFromWmsSources, 250);
|
|
72
|
+
export async function readFeaturesAtPixel(map, event, layerFilter) {
|
|
73
|
+
return new Map([
|
|
74
|
+
...getFeaturesFromVectorSources(map, event.pixel, layerFilter),
|
|
75
|
+
...(await getFeaturesFromWmsSourcesThrottled(map, event.coordinate, layerFilter)),
|
|
76
|
+
]);
|
|
77
|
+
}
|
package/dist/map/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* @hideGroups
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
|
-
export * from "./styles.js";
|
|
6
5
|
export { createMapFromContext, resetMapFromContext } from "./create-map.js";
|
|
7
6
|
export { applyContextDiffToMap } from "./apply-context-diff.js";
|
|
8
7
|
export { listen } from "./register-events.js";
|
package/dist/map/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/map/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/map/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/map/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* @hideGroups
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
|
-
export * from "./styles.js";
|
|
6
5
|
export { createMapFromContext, resetMapFromContext } from "./create-map.js";
|
|
7
6
|
export { applyContextDiffToMap } from "./apply-context-diff.js";
|
|
8
7
|
export { listen } from "./register-events.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layer-update.d.ts","sourceRoot":"","sources":["../../lib/map/layer-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"layer-update.d.ts","sourceRoot":"","sources":["../../lib/map/layer-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKhE,OAAO,KAAK,MAAM,mBAAmB,CAAC;AAmBtC;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAIT;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,KAAK,EACd,kBAAkB,CAAC,EAAE,eAAe,QA2CrC"}
|
package/dist/map/layer-update.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getHash } from "@geospatial-sdk/core";
|
|
2
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
2
3
|
const UPDATABLE_PROPERTIES = [
|
|
3
4
|
"opacity",
|
|
4
5
|
"visibility",
|
|
@@ -6,6 +7,8 @@ const UPDATABLE_PROPERTIES = [
|
|
|
6
7
|
"attributions",
|
|
7
8
|
"extras",
|
|
8
9
|
"version",
|
|
10
|
+
"enableHover",
|
|
11
|
+
"style",
|
|
9
12
|
// TODO (when available) "zIndex"
|
|
10
13
|
];
|
|
11
14
|
/**
|
|
@@ -55,5 +58,12 @@ export function updateLayerProperties(layerModel, olLayer, previousLayerModel) {
|
|
|
55
58
|
if (shouldApplyProperty("label")) {
|
|
56
59
|
olLayer.set("label", layerModel.label);
|
|
57
60
|
}
|
|
61
|
+
if (shouldApplyProperty("enableHover")) {
|
|
62
|
+
olLayer.set(`${GEOSPATIAL_SDK_PREFIX}enable-hover`, layerModel.enableHover);
|
|
63
|
+
}
|
|
64
|
+
if (shouldApplyProperty("style") &&
|
|
65
|
+
"setStyle" in olLayer) {
|
|
66
|
+
olLayer.setStyle(layerModel.style);
|
|
67
|
+
}
|
|
58
68
|
// TODO: z-index
|
|
59
69
|
}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import Map from "ol/Map.js";
|
|
2
2
|
import { MapEventsByType } from "@geospatial-sdk/core";
|
|
3
|
-
import { Coordinate } from "ol/coordinate.js";
|
|
4
|
-
import TileWMS from "ol/source/TileWMS.js";
|
|
5
|
-
import ImageWMS from "ol/source/ImageWMS.js";
|
|
6
|
-
import { Pixel } from "ol/pixel.js";
|
|
7
|
-
import type { Feature } from "geojson";
|
|
8
|
-
export declare function getFeaturesFromVectorSources(olMap: Map, pixel: Pixel): Feature[];
|
|
9
|
-
export declare function getGFIUrl(source: TileWMS | ImageWMS, map: Map, coordinate: Coordinate): string | null;
|
|
10
|
-
export declare function getFeaturesFromWmsSources(olMap: Map, coordinate: Coordinate): Promise<Feature[]>;
|
|
11
3
|
export declare function listen<T extends keyof MapEventsByType>(map: Map, eventType: T, callback: (event: MapEventsByType[T]) => void): void;
|
|
12
4
|
//# sourceMappingURL=register-events.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-events.d.ts","sourceRoot":"","sources":["../../lib/map/register-events.ts"],"names":[],"mappings":"AAAA,OAAO,GAA4B,MAAM,WAAW,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"register-events.d.ts","sourceRoot":"","sources":["../../lib/map/register-events.ts"],"names":[],"mappings":"AAAA,OAAO,GAA4B,MAAM,WAAW,CAAC;AACrD,OAAO,EAIL,eAAe,EAGhB,MAAM,sBAAsB,CAAC;AA8D9B,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,eAAe,EACpD,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,QA0E9C"}
|
|
@@ -1,64 +1,17 @@
|
|
|
1
1
|
import { FeaturesClickEventType, FeaturesHoverEventType, MapClickEventType, MapExtentChangeEventType, SourceLoadErrorType, } from "@geospatial-sdk/core";
|
|
2
2
|
import { toLonLat, transformExtent } from "ol/proj.js";
|
|
3
|
-
import GeoJSON from "ol/format/GeoJSON.js";
|
|
4
|
-
import TileWMS from "ol/source/TileWMS.js";
|
|
5
|
-
import ImageWMS from "ol/source/ImageWMS.js";
|
|
6
|
-
import Layer from "ol/layer/Layer.js";
|
|
7
|
-
import throttle from "lodash.throttle";
|
|
8
3
|
import { equals } from "ol/extent.js";
|
|
9
|
-
|
|
10
|
-
export function getFeaturesFromVectorSources(olMap, pixel) {
|
|
11
|
-
const olFeatures = olMap.getFeaturesAtPixel(pixel);
|
|
12
|
-
const { features } = GEOJSON.writeFeaturesObject(olFeatures);
|
|
13
|
-
if (!features) {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
return features;
|
|
17
|
-
}
|
|
18
|
-
export function getGFIUrl(source, map, coordinate) {
|
|
19
|
-
const view = map.getView();
|
|
20
|
-
const projection = view.getProjection();
|
|
21
|
-
const resolution = view.getResolution();
|
|
22
|
-
const params = {
|
|
23
|
-
...source.getParams(),
|
|
24
|
-
INFO_FORMAT: "application/json",
|
|
25
|
-
};
|
|
26
|
-
return (source.getFeatureInfoUrl(coordinate, resolution, projection, params) ?? null);
|
|
27
|
-
}
|
|
28
|
-
export function getFeaturesFromWmsSources(olMap, coordinate) {
|
|
29
|
-
const wmsSources = olMap
|
|
30
|
-
.getLayers()
|
|
31
|
-
.getArray()
|
|
32
|
-
.filter((layer) => layer instanceof Layer &&
|
|
33
|
-
(layer.getSource() instanceof TileWMS ||
|
|
34
|
-
layer.getSource() instanceof ImageWMS))
|
|
35
|
-
.map((layer) => layer.getSource());
|
|
36
|
-
if (!wmsSources.length) {
|
|
37
|
-
return Promise.resolve([]);
|
|
38
|
-
}
|
|
39
|
-
const gfiUrls = wmsSources.reduce((urls, source) => {
|
|
40
|
-
const gfiUrl = getGFIUrl(source, olMap, coordinate);
|
|
41
|
-
return gfiUrl ? [...urls, gfiUrl] : urls;
|
|
42
|
-
}, []);
|
|
43
|
-
return Promise.all(gfiUrls.map((url) => fetch(url)
|
|
44
|
-
.then((response) => response.json())
|
|
45
|
-
.then((collection) => collection.features))).then((features) => features.flat());
|
|
46
|
-
}
|
|
47
|
-
const getFeaturesFromWmsSourcesThrottled = throttle(getFeaturesFromWmsSources, 250);
|
|
48
|
-
async function readFeaturesAtPixel(map, event) {
|
|
49
|
-
return [
|
|
50
|
-
...getFeaturesFromVectorSources(map, event.pixel),
|
|
51
|
-
...(await getFeaturesFromWmsSourcesThrottled(map, event.coordinate)),
|
|
52
|
-
];
|
|
53
|
-
}
|
|
4
|
+
import { readFeaturesAtPixel } from "./get-features.js";
|
|
54
5
|
function registerFeatureClickEvent(map) {
|
|
55
6
|
if (map.get(FeaturesClickEventType))
|
|
56
7
|
return;
|
|
57
8
|
map.on("click", async (event) => {
|
|
58
|
-
const
|
|
9
|
+
const featuresByLayer = await readFeaturesAtPixel(map, event);
|
|
10
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
59
11
|
map.dispatchEvent({
|
|
60
12
|
type: FeaturesClickEventType,
|
|
61
13
|
features,
|
|
14
|
+
featuresByLayer,
|
|
62
15
|
});
|
|
63
16
|
});
|
|
64
17
|
map.set(FeaturesClickEventType, true);
|
|
@@ -66,13 +19,6 @@ function registerFeatureClickEvent(map) {
|
|
|
66
19
|
function registerFeatureHoverEvent(map) {
|
|
67
20
|
if (map.get(FeaturesHoverEventType))
|
|
68
21
|
return;
|
|
69
|
-
map.on("pointermove", async (event) => {
|
|
70
|
-
const features = await readFeaturesAtPixel(map, event);
|
|
71
|
-
map.dispatchEvent({
|
|
72
|
-
type: FeaturesHoverEventType,
|
|
73
|
-
features,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
22
|
map.set(FeaturesHoverEventType, true);
|
|
77
23
|
}
|
|
78
24
|
function registerMapExtentChangeEvent(map) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const GEOSPATIAL_SDK_PREFIX = "--geospatial-sdk-";
|
|
@@ -44,6 +44,7 @@ import ImageTile from "ol/ImageTile.js";
|
|
|
44
44
|
import TileState from "ol/TileState.js";
|
|
45
45
|
import VectorTileLayer from "ol/layer/VectorTile.js";
|
|
46
46
|
import { FeatureUrlFunction } from "ol/featureloader.js";
|
|
47
|
+
import { beforeEach } from "vitest";
|
|
47
48
|
|
|
48
49
|
vi.mock("./handle-errors", async (importOriginal) => {
|
|
49
50
|
const actual =
|
|
@@ -480,9 +481,10 @@ describe("MapContextService", () => {
|
|
|
480
481
|
});
|
|
481
482
|
});
|
|
482
483
|
describe("#resetMapFromContext", () => {
|
|
483
|
-
|
|
484
|
-
const mapContext = MAP_CTX_FIXTURE;
|
|
484
|
+
let map: Map;
|
|
485
485
|
beforeEach(() => {
|
|
486
|
+
map = new Map({});
|
|
487
|
+
const mapContext = MAP_CTX_FIXTURE;
|
|
486
488
|
resetMapFromContext(map, mapContext);
|
|
487
489
|
});
|
|
488
490
|
it("create a map", () => {
|
|
@@ -500,17 +502,18 @@ describe("MapContextService", () => {
|
|
|
500
502
|
});
|
|
501
503
|
describe("uses default fallback view", () => {
|
|
502
504
|
let view: View;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
505
|
+
let map: Map;
|
|
506
|
+
beforeEach(async () => {
|
|
507
|
+
map = new Map({});
|
|
508
|
+
const mapContext: MapContext = {
|
|
509
|
+
view: null,
|
|
510
|
+
layers: [
|
|
511
|
+
MAP_CTX_LAYER_XYZ_FIXTURE,
|
|
512
|
+
MAP_CTX_LAYER_WMS_FIXTURE,
|
|
513
|
+
MAP_CTX_LAYER_GEOJSON_FIXTURE,
|
|
514
|
+
],
|
|
515
|
+
};
|
|
516
|
+
await resetMapFromContext(map, mapContext);
|
|
514
517
|
});
|
|
515
518
|
it("create a map", () => {
|
|
516
519
|
expect(map).toBeTruthy();
|
package/lib/map/create-map.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
defaultStyle,
|
|
2
3
|
MapContext,
|
|
3
4
|
MapContextLayer,
|
|
4
5
|
MapContextView,
|
|
@@ -18,7 +19,6 @@ import Geometry from "ol/geom/Geometry.js";
|
|
|
18
19
|
import SimpleGeometry from "ol/geom/SimpleGeometry.js";
|
|
19
20
|
import { fromLonLat, transformExtent } from "ol/proj.js";
|
|
20
21
|
import { bbox as bboxStrategy } from "ol/loadingstrategy.js";
|
|
21
|
-
import { defaultStyle } from "./styles.js";
|
|
22
22
|
import VectorTileLayer from "ol/layer/VectorTile.js";
|
|
23
23
|
import OGCMapTile from "ol/source/OGCMapTile.js";
|
|
24
24
|
import OGCVectorTile from "ol/source/OGCVectorTile.js";
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
canDoIncrementalUpdate,
|
|
41
41
|
updateLayerProperties,
|
|
42
42
|
} from "./layer-update.js";
|
|
43
|
+
import { initHoverLayer } from "./feature-hover.js";
|
|
43
44
|
|
|
44
45
|
const GEOJSON = new GeoJSON();
|
|
45
46
|
const WFS_MAX_FEATURES = 10000;
|
|
@@ -351,5 +352,6 @@ export async function resetMapFromContext(
|
|
|
351
352
|
const layer = await createLayer(layerModel);
|
|
352
353
|
map.addLayer(layer);
|
|
353
354
|
}
|
|
355
|
+
initHoverLayer(map);
|
|
354
356
|
return map;
|
|
355
357
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
2
|
+
import type OlMap from "ol/Map.js";
|
|
3
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
4
|
+
import VectorSource from "ol/source/Vector.js";
|
|
5
|
+
import {
|
|
6
|
+
defaultHighlightStyle,
|
|
7
|
+
FeaturesHoverEventType,
|
|
8
|
+
} from "@geospatial-sdk/core";
|
|
9
|
+
import type BaseEvent from "ol/events/Event.js";
|
|
10
|
+
import { MapBrowserEvent } from "ol";
|
|
11
|
+
import OlFeature from "ol/Feature.js";
|
|
12
|
+
import type BaseLayer from "ol/layer/Base.js";
|
|
13
|
+
import { unByKey } from "ol/Observable.js";
|
|
14
|
+
import { readFeaturesAtPixel } from "./get-features.js";
|
|
15
|
+
|
|
16
|
+
const hoverLayerKey = `${GEOSPATIAL_SDK_PREFIX}hover-layer`;
|
|
17
|
+
const unsubscribeKey = `${GEOSPATIAL_SDK_PREFIX}hover-unsub`;
|
|
18
|
+
|
|
19
|
+
export function initHoverLayer(map: OlMap) {
|
|
20
|
+
if (map.get(hoverLayerKey)) {
|
|
21
|
+
clearHoverLayer(map);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// create layer & add on top of everything else
|
|
25
|
+
const hoverLayer = new VectorLayer({
|
|
26
|
+
source: new VectorSource({
|
|
27
|
+
features: [],
|
|
28
|
+
useSpatialIndex: false,
|
|
29
|
+
}),
|
|
30
|
+
style: defaultHighlightStyle,
|
|
31
|
+
});
|
|
32
|
+
map.set(hoverLayerKey, hoverLayer);
|
|
33
|
+
hoverLayer.setMap(map);
|
|
34
|
+
|
|
35
|
+
// store original cursor style in order to change it later
|
|
36
|
+
const originalCursorStyle = map.getTargetElement()?.style.cursor ?? "";
|
|
37
|
+
|
|
38
|
+
const layerFilter = (layer: BaseLayer) =>
|
|
39
|
+
layer !== hoverLayer && layer.get(`${GEOSPATIAL_SDK_PREFIX}enable-hover`);
|
|
40
|
+
|
|
41
|
+
const unKey = map.on(
|
|
42
|
+
"pointermove",
|
|
43
|
+
async (event: MapBrowserEvent<PointerEvent>) => {
|
|
44
|
+
// skip hit detection if the view is moving as it can have an impact on performance
|
|
45
|
+
if (map.getView().getInteracting() || map.getView().getAnimating()) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// change cursor if above a feature
|
|
50
|
+
const hasFeature = map.hasFeatureAtPixel(event.pixel, {
|
|
51
|
+
layerFilter,
|
|
52
|
+
});
|
|
53
|
+
if (map.getTargetElement()) {
|
|
54
|
+
map.getTargetElement().style.cursor = hasFeature
|
|
55
|
+
? "pointer"
|
|
56
|
+
: originalCursorStyle;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const hoveredSource = hoverLayer.getSource() as VectorSource;
|
|
60
|
+
hoveredSource.clear(true);
|
|
61
|
+
if (!hasFeature) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// add hovered feature to the layer
|
|
66
|
+
let firstFeature: OlFeature | null = null;
|
|
67
|
+
map.forEachFeatureAtPixel(
|
|
68
|
+
event.pixel,
|
|
69
|
+
(feature) => {
|
|
70
|
+
if (feature instanceof OlFeature) {
|
|
71
|
+
firstFeature = feature;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
layerFilter,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
if (!firstFeature) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
hoveredSource.addFeature(firstFeature);
|
|
83
|
+
|
|
84
|
+
// dispatch event if subscribed to
|
|
85
|
+
if (map.get(FeaturesHoverEventType)) {
|
|
86
|
+
const featuresByLayer = await readFeaturesAtPixel(
|
|
87
|
+
map,
|
|
88
|
+
event,
|
|
89
|
+
layerFilter,
|
|
90
|
+
);
|
|
91
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
92
|
+
map.dispatchEvent({
|
|
93
|
+
type: FeaturesHoverEventType,
|
|
94
|
+
features,
|
|
95
|
+
featuresByLayer,
|
|
96
|
+
} as unknown as BaseEvent);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
map.set(unsubscribeKey, unKey);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getHoverLayer(map: OlMap): VectorLayer<VectorSource> {
|
|
104
|
+
return map.get(hoverLayerKey) as VectorLayer<VectorSource>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function clearHoverLayer(map: OlMap) {
|
|
108
|
+
const hoverLayer = getHoverLayer(map);
|
|
109
|
+
hoverLayer.setMap(null);
|
|
110
|
+
hoverLayer.dispose();
|
|
111
|
+
map.set(hoverLayerKey, null);
|
|
112
|
+
unByKey(map.get(unsubscribeKey));
|
|
113
|
+
}
|