@geospatial-sdk/openlayers 0.0.5-dev.51 → 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/feature-hover.d.ts +4 -4
- package/dist/map/feature-hover.d.ts.map +1 -1
- package/dist/map/feature-hover.js +17 -6
- package/dist/map/get-features.d.ts +8 -7
- package/dist/map/get-features.d.ts.map +1 -1
- package/dist/map/get-features.js +55 -28
- package/dist/map/register-events.d.ts.map +1 -1
- package/dist/map/register-events.js +3 -1
- package/lib/map/feature-hover.ts +33 -16
- package/lib/map/get-features.test.ts +31 -20
- package/lib/map/get-features.ts +75 -42
- package/lib/map/handle-errors.test.ts +4 -4
- package/lib/map/register-events.test.ts +60 -64
- package/lib/map/register-events.ts +3 -1
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type OlMap from "ol/Map.js";
|
|
2
2
|
import VectorLayer from "ol/layer/Vector.js";
|
|
3
3
|
import VectorSource from "ol/source/Vector.js";
|
|
4
|
-
export declare function initHoverLayer(map:
|
|
5
|
-
export declare function getHoverLayer(map:
|
|
6
|
-
export declare function clearHoverLayer(map:
|
|
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
7
|
//# sourceMappingURL=feature-hover.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-hover.d.ts","sourceRoot":"","sources":["../../lib/map/feature-hover.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
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"}
|
|
@@ -2,9 +2,9 @@ import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
|
2
2
|
import VectorLayer from "ol/layer/Vector.js";
|
|
3
3
|
import VectorSource from "ol/source/Vector.js";
|
|
4
4
|
import { defaultHighlightStyle, FeaturesHoverEventType, } from "@geospatial-sdk/core";
|
|
5
|
-
import
|
|
5
|
+
import OlFeature from "ol/Feature.js";
|
|
6
6
|
import { unByKey } from "ol/Observable.js";
|
|
7
|
-
|
|
7
|
+
import { readFeaturesAtPixel } from "./get-features.js";
|
|
8
8
|
const hoverLayerKey = `${GEOSPATIAL_SDK_PREFIX}hover-layer`;
|
|
9
9
|
const unsubscribeKey = `${GEOSPATIAL_SDK_PREFIX}hover-unsub`;
|
|
10
10
|
export function initHoverLayer(map) {
|
|
@@ -44,16 +44,27 @@ export function initHoverLayer(map) {
|
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
// add hovered feature to the layer
|
|
47
|
-
|
|
47
|
+
let firstFeature = null;
|
|
48
|
+
map.forEachFeatureAtPixel(event.pixel, (feature) => {
|
|
49
|
+
if (feature instanceof OlFeature) {
|
|
50
|
+
firstFeature = feature;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}, {
|
|
48
54
|
layerFilter,
|
|
49
55
|
});
|
|
50
|
-
|
|
56
|
+
if (!firstFeature) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
hoveredSource.addFeature(firstFeature);
|
|
51
60
|
// dispatch event if subscribed to
|
|
52
61
|
if (map.get(FeaturesHoverEventType)) {
|
|
53
|
-
const
|
|
62
|
+
const featuresByLayer = await readFeaturesAtPixel(map, event, layerFilter);
|
|
63
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
54
64
|
map.dispatchEvent({
|
|
55
65
|
type: FeaturesHoverEventType,
|
|
56
|
-
features
|
|
66
|
+
features,
|
|
67
|
+
featuresByLayer,
|
|
57
68
|
});
|
|
58
69
|
}
|
|
59
70
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import OlMap from "ol/Map.js";
|
|
2
2
|
import { Pixel } from "ol/pixel.js";
|
|
3
|
-
import type { Feature } from "geojson";
|
|
4
3
|
import TileWMS from "ol/source/TileWMS.js";
|
|
5
4
|
import ImageWMS from "ol/source/ImageWMS.js";
|
|
6
5
|
import { Coordinate } from "ol/coordinate.js";
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
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>;
|
|
12
13
|
//# sourceMappingURL=get-features.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-features.d.ts","sourceRoot":"","sources":["../../lib/map/get-features.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
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"}
|
package/dist/map/get-features.js
CHANGED
|
@@ -4,13 +4,23 @@ import ImageWMS from "ol/source/ImageWMS.js";
|
|
|
4
4
|
import Layer from "ol/layer/Layer.js";
|
|
5
5
|
import throttle from "lodash.throttle";
|
|
6
6
|
const GEOJSON = new GeoJSON();
|
|
7
|
-
export function getFeaturesFromVectorSources(olMap, pixel) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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;
|
|
14
24
|
}
|
|
15
25
|
export function getGFIUrl(source, map, coordinate) {
|
|
16
26
|
const view = map.getView();
|
|
@@ -22,29 +32,46 @@ export function getGFIUrl(source, map, coordinate) {
|
|
|
22
32
|
};
|
|
23
33
|
return (source.getFeatureInfoUrl(coordinate, resolution, projection, params) ?? null);
|
|
24
34
|
}
|
|
25
|
-
export function getFeaturesFromWmsSources(olMap, coordinate) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return Promise.resolve([]);
|
|
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;
|
|
35
44
|
}
|
|
36
|
-
const
|
|
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
|
+
}
|
|
37
56
|
const gfiUrl = getGFIUrl(source, olMap, coordinate);
|
|
38
|
-
return gfiUrl
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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;
|
|
43
70
|
}
|
|
44
71
|
const getFeaturesFromWmsSourcesThrottled = throttle(getFeaturesFromWmsSources, 250);
|
|
45
|
-
export async function readFeaturesAtPixel(map, event) {
|
|
46
|
-
return [
|
|
47
|
-
...getFeaturesFromVectorSources(map, event.pixel),
|
|
48
|
-
...(await getFeaturesFromWmsSourcesThrottled(map, event.coordinate)),
|
|
49
|
-
];
|
|
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
|
+
]);
|
|
50
77
|
}
|
|
@@ -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,EAIL,eAAe,EAGhB,MAAM,sBAAsB,CAAC;
|
|
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"}
|
|
@@ -6,10 +6,12 @@ function registerFeatureClickEvent(map) {
|
|
|
6
6
|
if (map.get(FeaturesClickEventType))
|
|
7
7
|
return;
|
|
8
8
|
map.on("click", async (event) => {
|
|
9
|
-
const
|
|
9
|
+
const featuresByLayer = await readFeaturesAtPixel(map, event);
|
|
10
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
10
11
|
map.dispatchEvent({
|
|
11
12
|
type: FeaturesClickEventType,
|
|
12
13
|
features,
|
|
14
|
+
featuresByLayer,
|
|
13
15
|
});
|
|
14
16
|
});
|
|
15
17
|
map.set(FeaturesClickEventType, true);
|
package/lib/map/feature-hover.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
2
|
-
import type
|
|
2
|
+
import type OlMap from "ol/Map.js";
|
|
3
3
|
import VectorLayer from "ol/layer/Vector.js";
|
|
4
4
|
import VectorSource from "ol/source/Vector.js";
|
|
5
5
|
import {
|
|
6
6
|
defaultHighlightStyle,
|
|
7
7
|
FeaturesHoverEventType,
|
|
8
8
|
} from "@geospatial-sdk/core";
|
|
9
|
-
import BaseEvent from "ol/events/Event.js";
|
|
9
|
+
import type BaseEvent from "ol/events/Event.js";
|
|
10
10
|
import { MapBrowserEvent } from "ol";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import BaseLayer from "ol/layer/Base.js";
|
|
11
|
+
import OlFeature from "ol/Feature.js";
|
|
12
|
+
import type BaseLayer from "ol/layer/Base.js";
|
|
14
13
|
import { unByKey } from "ol/Observable.js";
|
|
15
|
-
|
|
16
|
-
const GEOJSON = new GeoJSON();
|
|
14
|
+
import { readFeaturesAtPixel } from "./get-features.js";
|
|
17
15
|
|
|
18
16
|
const hoverLayerKey = `${GEOSPATIAL_SDK_PREFIX}hover-layer`;
|
|
19
17
|
const unsubscribeKey = `${GEOSPATIAL_SDK_PREFIX}hover-unsub`;
|
|
20
18
|
|
|
21
|
-
export function initHoverLayer(map:
|
|
19
|
+
export function initHoverLayer(map: OlMap) {
|
|
22
20
|
if (map.get(hoverLayerKey)) {
|
|
23
21
|
clearHoverLayer(map);
|
|
24
22
|
}
|
|
@@ -65,17 +63,36 @@ export function initHoverLayer(map: Map) {
|
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
// add hovered feature to the layer
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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);
|
|
72
83
|
|
|
73
84
|
// dispatch event if subscribed to
|
|
74
85
|
if (map.get(FeaturesHoverEventType)) {
|
|
75
|
-
const
|
|
86
|
+
const featuresByLayer = await readFeaturesAtPixel(
|
|
87
|
+
map,
|
|
88
|
+
event,
|
|
89
|
+
layerFilter,
|
|
90
|
+
);
|
|
91
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
76
92
|
map.dispatchEvent({
|
|
77
93
|
type: FeaturesHoverEventType,
|
|
78
|
-
features
|
|
94
|
+
features,
|
|
95
|
+
featuresByLayer,
|
|
79
96
|
} as unknown as BaseEvent);
|
|
80
97
|
}
|
|
81
98
|
},
|
|
@@ -83,11 +100,11 @@ export function initHoverLayer(map: Map) {
|
|
|
83
100
|
map.set(unsubscribeKey, unKey);
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
export function getHoverLayer(map:
|
|
103
|
+
export function getHoverLayer(map: OlMap): VectorLayer<VectorSource> {
|
|
87
104
|
return map.get(hoverLayerKey) as VectorLayer<VectorSource>;
|
|
88
105
|
}
|
|
89
106
|
|
|
90
|
-
export function clearHoverLayer(map:
|
|
107
|
+
export function clearHoverLayer(map: OlMap) {
|
|
91
108
|
const hoverLayer = getHoverLayer(map);
|
|
92
109
|
hoverLayer.setMap(null);
|
|
93
110
|
hoverLayer.dispose();
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
getFeaturesFromWmsSources,
|
|
4
4
|
getGFIUrl,
|
|
5
5
|
} from "./get-features.js";
|
|
6
|
-
import
|
|
6
|
+
import OlMap from "ol/Map.js";
|
|
7
7
|
import TileWMS from "ol/source/TileWMS.js";
|
|
8
8
|
import TileLayer from "ol/layer/Tile.js";
|
|
9
9
|
import OlFeature from "ol/Feature.js";
|
|
@@ -30,7 +30,7 @@ function createWmsSource() {
|
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
function createMap():
|
|
33
|
+
function createMap(): OlMap {
|
|
34
34
|
const wmsLayer = new TileLayer({
|
|
35
35
|
source: createWmsSource(),
|
|
36
36
|
});
|
|
@@ -47,23 +47,27 @@ function createMap(): Map {
|
|
|
47
47
|
resolution: 1,
|
|
48
48
|
center: [0, 0],
|
|
49
49
|
});
|
|
50
|
-
const map = new BaseObject() as
|
|
50
|
+
const map = new BaseObject() as OlMap;
|
|
51
51
|
Object.defineProperties(map, {
|
|
52
52
|
getView: { value: vi.fn(() => view) },
|
|
53
53
|
getLayers: { value: vi.fn(() => new Collection([wmsLayer, vectorLayer])) },
|
|
54
54
|
getEventPixel: { value: vi.fn(() => [10, 10]) },
|
|
55
55
|
getCoordinateFromPixel: { value: vi.fn(() => [123, 123]) },
|
|
56
|
-
|
|
56
|
+
forEachFeatureAtPixel: {
|
|
57
|
+
value: vi.fn((pixel, callback) => {
|
|
58
|
+
callback(feature, vectorLayer);
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
57
61
|
getSize: { value: vi.fn(() => [800, 600]) },
|
|
58
62
|
});
|
|
59
63
|
return map;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
describe("get features utils", () => {
|
|
63
|
-
let map:
|
|
67
|
+
let map: OlMap;
|
|
64
68
|
beforeEach(() => {
|
|
65
69
|
map = createMap();
|
|
66
|
-
vi.spyOn(
|
|
70
|
+
vi.spyOn(globalThis, "fetch").mockImplementation(() =>
|
|
67
71
|
Promise.resolve({
|
|
68
72
|
ok: true,
|
|
69
73
|
json: () => Promise.resolve({ features: [gfiResult] }),
|
|
@@ -73,18 +77,25 @@ describe("get features utils", () => {
|
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
describe("getFeaturesFromVectorSources", () => {
|
|
76
|
-
it("returns
|
|
77
|
-
const
|
|
78
|
-
expect(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
it("returns a map of features by layer index", () => {
|
|
81
|
+
const featuresByLayerIndex = getFeaturesFromVectorSources(map, [0, 0]);
|
|
82
|
+
expect(featuresByLayerIndex).toEqual(
|
|
83
|
+
new Map([
|
|
84
|
+
[
|
|
85
|
+
1,
|
|
86
|
+
[
|
|
87
|
+
{
|
|
88
|
+
geometry: {
|
|
89
|
+
coordinates: [100, 200],
|
|
90
|
+
type: "Point",
|
|
91
|
+
},
|
|
92
|
+
properties: null,
|
|
93
|
+
type: "Feature",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
],
|
|
97
|
+
]),
|
|
98
|
+
);
|
|
88
99
|
});
|
|
89
100
|
});
|
|
90
101
|
describe("getGFIUrl", () => {
|
|
@@ -102,8 +113,8 @@ describe("get features utils", () => {
|
|
|
102
113
|
});
|
|
103
114
|
describe("getFeaturesFromWmsSources", () => {
|
|
104
115
|
it("queries the WMS sources", async () => {
|
|
105
|
-
const
|
|
106
|
-
expect(
|
|
116
|
+
const featuresByLayerIndex = await getFeaturesFromWmsSources(map, [0, 0]);
|
|
117
|
+
expect(featuresByLayerIndex).toEqual(new Map([[0, [gfiResult]]]));
|
|
107
118
|
});
|
|
108
119
|
});
|
|
109
120
|
});
|
package/lib/map/get-features.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import GeoJSON from "ol/format/GeoJSON.js";
|
|
2
|
-
import
|
|
2
|
+
import OlMap from "ol/Map.js";
|
|
3
3
|
import { Pixel } from "ol/pixel.js";
|
|
4
4
|
import type { Feature, FeatureCollection } from "geojson";
|
|
5
5
|
import OlFeature from "ol/Feature.js";
|
|
@@ -8,25 +8,41 @@ import ImageWMS from "ol/source/ImageWMS.js";
|
|
|
8
8
|
import { Coordinate } from "ol/coordinate.js";
|
|
9
9
|
import Layer from "ol/layer/Layer.js";
|
|
10
10
|
import throttle from "lodash.throttle";
|
|
11
|
-
import
|
|
11
|
+
import type MapBrowserEvent from "ol/MapBrowserEvent.js";
|
|
12
|
+
import type { FeaturesByLayerIndex } from "@geospatial-sdk/core";
|
|
12
13
|
|
|
13
14
|
const GEOJSON = new GeoJSON();
|
|
14
15
|
|
|
15
16
|
export function getFeaturesFromVectorSources(
|
|
16
|
-
olMap:
|
|
17
|
+
olMap: OlMap,
|
|
17
18
|
pixel: Pixel,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
layerFilter?: (layer: Layer) => boolean,
|
|
20
|
+
): FeaturesByLayerIndex {
|
|
21
|
+
const result = new Map<number, Feature[]>();
|
|
22
|
+
const layerArray = olMap.getLayers().getArray();
|
|
23
|
+
olMap.forEachFeatureAtPixel(
|
|
24
|
+
pixel,
|
|
25
|
+
(feature, layer) => {
|
|
26
|
+
// can happen for unmanaged layer (i.e. hover layer)
|
|
27
|
+
if (layer === null) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const layerIndex = layerArray.indexOf(layer);
|
|
31
|
+
if (!result.has(layerIndex)) {
|
|
32
|
+
result.set(layerIndex, []);
|
|
33
|
+
}
|
|
34
|
+
result
|
|
35
|
+
.get(layerIndex)!
|
|
36
|
+
.push(GEOJSON.writeFeatureObject(feature as OlFeature));
|
|
37
|
+
},
|
|
38
|
+
{ layerFilter },
|
|
39
|
+
);
|
|
40
|
+
return result;
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
export function getGFIUrl(
|
|
28
44
|
source: TileWMS | ImageWMS,
|
|
29
|
-
map:
|
|
45
|
+
map: OlMap,
|
|
30
46
|
coordinate: Coordinate,
|
|
31
47
|
): string | null {
|
|
32
48
|
const view = map.getView();
|
|
@@ -41,36 +57,48 @@ export function getGFIUrl(
|
|
|
41
57
|
);
|
|
42
58
|
}
|
|
43
59
|
|
|
44
|
-
export function getFeaturesFromWmsSources(
|
|
45
|
-
olMap:
|
|
60
|
+
export async function getFeaturesFromWmsSources(
|
|
61
|
+
olMap: OlMap,
|
|
46
62
|
coordinate: Coordinate,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.filter(
|
|
52
|
-
(layer): layer is Layer<ImageWMS | TileWMS> =>
|
|
53
|
-
layer instanceof Layer &&
|
|
54
|
-
(layer.getSource() instanceof TileWMS ||
|
|
55
|
-
layer.getSource() instanceof ImageWMS),
|
|
56
|
-
)
|
|
57
|
-
.map((layer) => layer.getSource()!);
|
|
63
|
+
layerFilter?: (layer: Layer) => boolean,
|
|
64
|
+
): Promise<FeaturesByLayerIndex> {
|
|
65
|
+
const result = new Map<number, Feature[]>();
|
|
66
|
+
const layerArray = olMap.getLayers().getArray();
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
const hasWms = layerArray.some((layer) => {
|
|
69
|
+
const source = layer instanceof Layer ? layer.getSource() : null;
|
|
70
|
+
return source instanceof TileWMS || source instanceof ImageWMS;
|
|
71
|
+
});
|
|
72
|
+
if (!hasWms) {
|
|
73
|
+
return result;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
|
-
const
|
|
76
|
+
const gfiPromises: (Promise<Feature[]> | null)[] = layerArray.map((layer) => {
|
|
77
|
+
if (!(layer instanceof Layer)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (layerFilter && !layerFilter(layer)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const source = layer.getSource();
|
|
84
|
+
if (!(source instanceof TileWMS) && !(source instanceof ImageWMS)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
64
87
|
const gfiUrl = getGFIUrl(source, olMap, coordinate);
|
|
65
|
-
return gfiUrl
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
return gfiUrl
|
|
89
|
+
? fetch(gfiUrl)
|
|
90
|
+
.then((response) => response.json())
|
|
91
|
+
.then((collection: FeatureCollection) => collection.features)
|
|
92
|
+
: null;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const responses = await Promise.all(gfiPromises);
|
|
96
|
+
responses.forEach((features, index) => {
|
|
97
|
+
if (features !== null && features.length > 0) {
|
|
98
|
+
result.set(index, features);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return result;
|
|
74
102
|
}
|
|
75
103
|
|
|
76
104
|
const getFeaturesFromWmsSourcesThrottled = throttle(
|
|
@@ -79,11 +107,16 @@ const getFeaturesFromWmsSourcesThrottled = throttle(
|
|
|
79
107
|
);
|
|
80
108
|
|
|
81
109
|
export async function readFeaturesAtPixel(
|
|
82
|
-
map:
|
|
110
|
+
map: OlMap,
|
|
83
111
|
event: MapBrowserEvent<PointerEvent>,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
...(
|
|
88
|
-
|
|
112
|
+
layerFilter?: (layer: Layer) => boolean,
|
|
113
|
+
): Promise<FeaturesByLayerIndex> {
|
|
114
|
+
return new Map([
|
|
115
|
+
...getFeaturesFromVectorSources(map, event.pixel, layerFilter),
|
|
116
|
+
...(await getFeaturesFromWmsSourcesThrottled(
|
|
117
|
+
map,
|
|
118
|
+
event.coordinate,
|
|
119
|
+
layerFilter,
|
|
120
|
+
)),
|
|
121
|
+
]);
|
|
89
122
|
}
|
|
@@ -9,7 +9,7 @@ import TileLayer from "ol/layer/Tile.js";
|
|
|
9
9
|
import VectorLayer from "ol/layer/Vector.js";
|
|
10
10
|
import { EndpointError } from "@camptocamp/ogc-client";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
globalThis.URL.createObjectURL = vi.fn(() => "blob:http://example.com/blob");
|
|
13
13
|
|
|
14
14
|
const mockBlob = new Blob();
|
|
15
15
|
const RESPONSE_OK = {
|
|
@@ -19,10 +19,10 @@ const RESPONSE_OK = {
|
|
|
19
19
|
const RESPONSE_ERROR = {
|
|
20
20
|
status: 404,
|
|
21
21
|
};
|
|
22
|
-
|
|
22
|
+
globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
|
23
23
|
return url.includes("error")
|
|
24
|
-
? Promise.reject(RESPONSE_ERROR
|
|
25
|
-
: Promise.resolve(RESPONSE_OK
|
|
24
|
+
? Promise.reject(RESPONSE_ERROR)
|
|
25
|
+
: Promise.resolve(RESPONSE_OK);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
describe("handle-errors", () => {
|
|
@@ -1,89 +1,104 @@
|
|
|
1
|
-
import
|
|
1
|
+
import OlMap from "ol/Map.js";
|
|
2
2
|
import { Mock } from "vitest";
|
|
3
3
|
import { listen } from "./register-events.js";
|
|
4
4
|
import { MapBrowserEvent, Object as BaseObject } from "ol";
|
|
5
5
|
import View from "ol/View.js";
|
|
6
6
|
import { toLonLat } from "ol/proj.js";
|
|
7
|
-
import Point from "ol/geom/Point.js";
|
|
8
|
-
import OlFeature from "ol/Feature.js";
|
|
9
7
|
import { FeaturesHoverEventType } from "@geospatial-sdk/core";
|
|
10
8
|
import BaseEvent from "ol/events/Event.js";
|
|
11
9
|
|
|
12
10
|
vi.mock("./get-features.js", () => ({
|
|
13
11
|
readFeaturesAtPixel() {
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
12
|
+
return Promise.resolve(
|
|
13
|
+
new Map([
|
|
14
|
+
[
|
|
15
|
+
0,
|
|
16
|
+
[
|
|
17
|
+
{
|
|
18
|
+
geometry: {
|
|
19
|
+
coordinates: [100, 200],
|
|
20
|
+
type: "Point",
|
|
21
|
+
},
|
|
22
|
+
properties: null,
|
|
23
|
+
type: "Feature",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
1,
|
|
29
|
+
[
|
|
30
|
+
{
|
|
31
|
+
geometry: null,
|
|
32
|
+
properties: {
|
|
33
|
+
density: 123,
|
|
34
|
+
},
|
|
35
|
+
type: "Feature",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
],
|
|
39
|
+
]),
|
|
40
|
+
);
|
|
31
41
|
},
|
|
32
42
|
}));
|
|
33
43
|
|
|
44
|
+
const featureA = {
|
|
45
|
+
geometry: {
|
|
46
|
+
coordinates: [100, 200],
|
|
47
|
+
type: "Point",
|
|
48
|
+
},
|
|
49
|
+
properties: null,
|
|
50
|
+
type: "Feature",
|
|
51
|
+
};
|
|
52
|
+
const featureB = {
|
|
53
|
+
geometry: null,
|
|
54
|
+
properties: {
|
|
55
|
+
density: 123,
|
|
56
|
+
},
|
|
57
|
+
type: "Feature",
|
|
58
|
+
};
|
|
59
|
+
|
|
34
60
|
const EXPECTED_MAP_EXTENT_EPSG4326 = [
|
|
35
61
|
-0.0035932611364780857, -0.0026949458513598756, 0.0035932611364780857,
|
|
36
62
|
0.0026949458513740865,
|
|
37
63
|
];
|
|
38
64
|
|
|
39
|
-
function createMap():
|
|
40
|
-
const feature = new OlFeature({
|
|
41
|
-
geometry: new Point([100, 200]),
|
|
42
|
-
});
|
|
65
|
+
function createMap(): OlMap {
|
|
43
66
|
const view = new View({
|
|
44
67
|
projection: "EPSG:3857",
|
|
45
68
|
resolution: 1,
|
|
46
69
|
center: [0, 0],
|
|
47
70
|
});
|
|
48
|
-
const map = new BaseObject() as
|
|
71
|
+
const map = new BaseObject() as OlMap;
|
|
49
72
|
Object.defineProperties(map, {
|
|
50
73
|
getView: { value: vi.fn(() => view) },
|
|
51
74
|
getEventPixel: { value: vi.fn(() => [10, 10]) },
|
|
52
75
|
getCoordinateFromPixel: { value: vi.fn(() => [123, 123]) },
|
|
53
|
-
getFeaturesAtPixel: { value: vi.fn(() => [feature]) },
|
|
54
76
|
getSize: { value: vi.fn(() => [800, 600]) },
|
|
55
77
|
});
|
|
56
78
|
// simulate hover feature initialization
|
|
57
79
|
map.on("pointermove", () => {
|
|
58
80
|
map.dispatchEvent({
|
|
59
81
|
type: FeaturesHoverEventType,
|
|
60
|
-
features: [
|
|
61
|
-
|
|
62
|
-
geometry: null,
|
|
63
|
-
properties: {
|
|
64
|
-
density: 123,
|
|
65
|
-
},
|
|
66
|
-
type: "Feature",
|
|
67
|
-
},
|
|
68
|
-
],
|
|
82
|
+
features: [featureB],
|
|
83
|
+
featuresByLayer: new Map([[0, [featureB]]]),
|
|
69
84
|
} as unknown as BaseEvent);
|
|
70
85
|
});
|
|
71
86
|
return map;
|
|
72
87
|
}
|
|
73
|
-
function createMapEvent(map:
|
|
88
|
+
function createMapEvent(map: OlMap, type: string) {
|
|
74
89
|
return new MapBrowserEvent(
|
|
75
90
|
type,
|
|
76
91
|
map,
|
|
77
92
|
new MouseEvent(type, {
|
|
78
93
|
clientX: 10,
|
|
79
94
|
clientY: 10,
|
|
80
|
-
}),
|
|
95
|
+
}) as PointerEvent,
|
|
81
96
|
false,
|
|
82
97
|
);
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
describe("event registration", () => {
|
|
86
|
-
let map:
|
|
101
|
+
let map: OlMap;
|
|
87
102
|
beforeEach(() => {
|
|
88
103
|
map = createMap();
|
|
89
104
|
vi.useFakeTimers();
|
|
@@ -99,15 +114,8 @@ describe("event registration", () => {
|
|
|
99
114
|
it("registers the event on the map", () => {
|
|
100
115
|
expect(callback).toHaveBeenCalledWith({
|
|
101
116
|
type: "features-hover",
|
|
102
|
-
features: [
|
|
103
|
-
|
|
104
|
-
geometry: null,
|
|
105
|
-
properties: {
|
|
106
|
-
density: 123,
|
|
107
|
-
},
|
|
108
|
-
type: "Feature",
|
|
109
|
-
},
|
|
110
|
-
],
|
|
117
|
+
features: [featureB],
|
|
118
|
+
featuresByLayer: new Map([[0, [featureB]]]),
|
|
111
119
|
target: expect.anything(),
|
|
112
120
|
});
|
|
113
121
|
});
|
|
@@ -123,23 +131,11 @@ describe("event registration", () => {
|
|
|
123
131
|
it("registers the event on the map", () => {
|
|
124
132
|
expect(callback).toHaveBeenCalledWith({
|
|
125
133
|
type: "features-click",
|
|
126
|
-
features: [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
132
|
-
properties: null,
|
|
133
|
-
type: "Feature",
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
geometry: null,
|
|
137
|
-
properties: {
|
|
138
|
-
density: 123,
|
|
139
|
-
},
|
|
140
|
-
type: "Feature",
|
|
141
|
-
},
|
|
142
|
-
],
|
|
134
|
+
features: [featureA, featureB],
|
|
135
|
+
featuresByLayer: new Map([
|
|
136
|
+
[0, [featureA]],
|
|
137
|
+
[1, [featureB]],
|
|
138
|
+
]),
|
|
143
139
|
target: expect.anything(),
|
|
144
140
|
});
|
|
145
141
|
});
|
|
@@ -18,10 +18,12 @@ function registerFeatureClickEvent(map: Map) {
|
|
|
18
18
|
if (map.get(FeaturesClickEventType)) return;
|
|
19
19
|
|
|
20
20
|
map.on("click", async (event: any) => {
|
|
21
|
-
const
|
|
21
|
+
const featuresByLayer = await readFeaturesAtPixel(map, event);
|
|
22
|
+
const features = Array.from(featuresByLayer.values()).flat();
|
|
22
23
|
map.dispatchEvent({
|
|
23
24
|
type: FeaturesClickEventType,
|
|
24
25
|
features,
|
|
26
|
+
featuresByLayer,
|
|
25
27
|
} as unknown as BaseEvent);
|
|
26
28
|
});
|
|
27
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geospatial-sdk/openlayers",
|
|
3
|
-
"version": "0.0.5-dev.
|
|
3
|
+
"version": "0.0.5-dev.52+73ea489",
|
|
4
4
|
"description": "OpenLayers-related utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ol",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"ol": ">9.x"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@geospatial-sdk/core": "^0.0.5-dev.
|
|
40
|
+
"@geospatial-sdk/core": "^0.0.5-dev.52+73ea489",
|
|
41
41
|
"chroma-js": "^2.4.2",
|
|
42
42
|
"lodash.throttle": "^4.1.1",
|
|
43
43
|
"ol-mapbox-style": "12.4.0"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "73ea4890e493aae5379e23af6b7679bab253db92"
|
|
46
46
|
}
|