@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,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFeaturesFromVectorSources,
|
|
3
|
+
getFeaturesFromWmsSources,
|
|
4
|
+
getGFIUrl,
|
|
5
|
+
} from "./get-features.js";
|
|
6
|
+
import OlMap from "ol/Map.js";
|
|
7
|
+
import TileWMS from "ol/source/TileWMS.js";
|
|
8
|
+
import TileLayer from "ol/layer/Tile.js";
|
|
9
|
+
import OlFeature from "ol/Feature.js";
|
|
10
|
+
import Point from "ol/geom/Point.js";
|
|
11
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
12
|
+
import VectorSource from "ol/source/Vector.js";
|
|
13
|
+
import View from "ol/View.js";
|
|
14
|
+
import { Collection, Object as BaseObject } from "ol";
|
|
15
|
+
|
|
16
|
+
const gfiResult = {
|
|
17
|
+
type: "Feature",
|
|
18
|
+
properties: {
|
|
19
|
+
density: 123,
|
|
20
|
+
},
|
|
21
|
+
geometry: null,
|
|
22
|
+
};
|
|
23
|
+
function createWmsSource() {
|
|
24
|
+
return new TileWMS({
|
|
25
|
+
url: "http://my.service.org/wms?SERVICE=WMS",
|
|
26
|
+
params: {
|
|
27
|
+
LAYERS: "layerName",
|
|
28
|
+
FORMAT: "image/png",
|
|
29
|
+
TRANSPARENT: true,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function createMap(): OlMap {
|
|
34
|
+
const wmsLayer = new TileLayer({
|
|
35
|
+
source: createWmsSource(),
|
|
36
|
+
});
|
|
37
|
+
const feature = new OlFeature({
|
|
38
|
+
geometry: new Point([100, 200]),
|
|
39
|
+
});
|
|
40
|
+
const vectorLayer = new VectorLayer({
|
|
41
|
+
source: new VectorSource({
|
|
42
|
+
features: [feature],
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
const view = new View({
|
|
46
|
+
projection: "EPSG:3857",
|
|
47
|
+
resolution: 1,
|
|
48
|
+
center: [0, 0],
|
|
49
|
+
});
|
|
50
|
+
const map = new BaseObject() as OlMap;
|
|
51
|
+
Object.defineProperties(map, {
|
|
52
|
+
getView: { value: vi.fn(() => view) },
|
|
53
|
+
getLayers: { value: vi.fn(() => new Collection([wmsLayer, vectorLayer])) },
|
|
54
|
+
getEventPixel: { value: vi.fn(() => [10, 10]) },
|
|
55
|
+
getCoordinateFromPixel: { value: vi.fn(() => [123, 123]) },
|
|
56
|
+
forEachFeatureAtPixel: {
|
|
57
|
+
value: vi.fn((pixel, callback) => {
|
|
58
|
+
callback(feature, vectorLayer);
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
getSize: { value: vi.fn(() => [800, 600]) },
|
|
62
|
+
});
|
|
63
|
+
return map;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("get features utils", () => {
|
|
67
|
+
let map: OlMap;
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
map = createMap();
|
|
70
|
+
vi.spyOn(globalThis, "fetch").mockImplementation(() =>
|
|
71
|
+
Promise.resolve({
|
|
72
|
+
ok: true,
|
|
73
|
+
json: () => Promise.resolve({ features: [gfiResult] }),
|
|
74
|
+
} as Response),
|
|
75
|
+
);
|
|
76
|
+
vi.useFakeTimers();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("getFeaturesFromVectorSources", () => {
|
|
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
|
+
);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe("getGFIUrl", () => {
|
|
102
|
+
let url: string;
|
|
103
|
+
const coordinate = [-182932.49329334166, 6125319.813853541];
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
const wmsSource = createWmsSource();
|
|
106
|
+
url = getGFIUrl(wmsSource, map, coordinate) as string;
|
|
107
|
+
});
|
|
108
|
+
it("returns true", () => {
|
|
109
|
+
expect(url).toEqual(
|
|
110
|
+
"http://my.service.org/wms?SERVICE=WMS&REQUEST=GetFeatureInfo&QUERY_LAYERS=layerName&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=&TRANSPARENT=true&LAYERS=layerName&INFO_FORMAT=application%2Fjson&I=176&J=31&WIDTH=256&HEIGHT=256&CRS=EPSG%3A3857&BBOX=-183143.11977128312%2C6125051.950547744%2C-182837.37165814242%2C6125357.698660884",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe("getFeaturesFromWmsSources", () => {
|
|
115
|
+
it("queries the WMS sources", async () => {
|
|
116
|
+
const featuresByLayerIndex = await getFeaturesFromWmsSources(map, [0, 0]);
|
|
117
|
+
expect(featuresByLayerIndex).toEqual(new Map([[0, [gfiResult]]]));
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import GeoJSON from "ol/format/GeoJSON.js";
|
|
2
|
+
import OlMap from "ol/Map.js";
|
|
3
|
+
import { Pixel } from "ol/pixel.js";
|
|
4
|
+
import type { Feature, FeatureCollection } from "geojson";
|
|
5
|
+
import OlFeature from "ol/Feature.js";
|
|
6
|
+
import TileWMS from "ol/source/TileWMS.js";
|
|
7
|
+
import ImageWMS from "ol/source/ImageWMS.js";
|
|
8
|
+
import { Coordinate } from "ol/coordinate.js";
|
|
9
|
+
import Layer from "ol/layer/Layer.js";
|
|
10
|
+
import throttle from "lodash.throttle";
|
|
11
|
+
import type MapBrowserEvent from "ol/MapBrowserEvent.js";
|
|
12
|
+
import type { FeaturesByLayerIndex } from "@geospatial-sdk/core";
|
|
13
|
+
|
|
14
|
+
const GEOJSON = new GeoJSON();
|
|
15
|
+
|
|
16
|
+
export function getFeaturesFromVectorSources(
|
|
17
|
+
olMap: OlMap,
|
|
18
|
+
pixel: Pixel,
|
|
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;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getGFIUrl(
|
|
44
|
+
source: TileWMS | ImageWMS,
|
|
45
|
+
map: OlMap,
|
|
46
|
+
coordinate: Coordinate,
|
|
47
|
+
): string | null {
|
|
48
|
+
const view = map.getView();
|
|
49
|
+
const projection = view.getProjection();
|
|
50
|
+
const resolution = view.getResolution() as number;
|
|
51
|
+
const params = {
|
|
52
|
+
...source.getParams(),
|
|
53
|
+
INFO_FORMAT: "application/json",
|
|
54
|
+
};
|
|
55
|
+
return (
|
|
56
|
+
source.getFeatureInfoUrl(coordinate, resolution, projection, params) ?? null
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function getFeaturesFromWmsSources(
|
|
61
|
+
olMap: OlMap,
|
|
62
|
+
coordinate: Coordinate,
|
|
63
|
+
layerFilter?: (layer: Layer) => boolean,
|
|
64
|
+
): Promise<FeaturesByLayerIndex> {
|
|
65
|
+
const result = new Map<number, Feature[]>();
|
|
66
|
+
const layerArray = olMap.getLayers().getArray();
|
|
67
|
+
|
|
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;
|
|
74
|
+
}
|
|
75
|
+
|
|
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
|
+
}
|
|
87
|
+
const gfiUrl = getGFIUrl(source, olMap, coordinate);
|
|
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;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const getFeaturesFromWmsSourcesThrottled = throttle(
|
|
105
|
+
getFeaturesFromWmsSources,
|
|
106
|
+
250,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export async function readFeaturesAtPixel(
|
|
110
|
+
map: OlMap,
|
|
111
|
+
event: MapBrowserEvent<PointerEvent>,
|
|
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
|
+
]);
|
|
122
|
+
}
|
|
@@ -5,12 +5,11 @@ import {
|
|
|
5
5
|
handleTileError,
|
|
6
6
|
tileLoadErrorCatchFunction,
|
|
7
7
|
} from "./handle-errors.js";
|
|
8
|
-
import { describe } from "node:test";
|
|
9
8
|
import TileLayer from "ol/layer/Tile.js";
|
|
10
9
|
import VectorLayer from "ol/layer/Vector.js";
|
|
11
10
|
import { EndpointError } from "@camptocamp/ogc-client";
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
globalThis.URL.createObjectURL = vi.fn(() => "blob:http://example.com/blob");
|
|
14
13
|
|
|
15
14
|
const mockBlob = new Blob();
|
|
16
15
|
const RESPONSE_OK = {
|
|
@@ -20,10 +19,10 @@ const RESPONSE_OK = {
|
|
|
20
19
|
const RESPONSE_ERROR = {
|
|
21
20
|
status: 404,
|
|
22
21
|
};
|
|
23
|
-
|
|
22
|
+
globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
|
24
23
|
return url.includes("error")
|
|
25
|
-
? Promise.reject(RESPONSE_ERROR
|
|
26
|
-
: Promise.resolve(RESPONSE_OK
|
|
24
|
+
? Promise.reject(RESPONSE_ERROR)
|
|
25
|
+
: Promise.resolve(RESPONSE_OK);
|
|
27
26
|
});
|
|
28
27
|
|
|
29
28
|
describe("handle-errors", () => {
|
package/lib/map/index.ts
CHANGED
|
@@ -5,8 +5,12 @@ import {
|
|
|
5
5
|
import { MapContextLayer } from "@geospatial-sdk/core";
|
|
6
6
|
import Layer from "ol/layer/Layer.js";
|
|
7
7
|
import { Source } from "ol/source.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
SAMPLE_LAYER1,
|
|
10
|
+
SAMPLE_LAYER3,
|
|
11
|
+
} from "@geospatial-sdk/core/fixtures/map-context.fixtures.js";
|
|
9
12
|
import VectorSource from "ol/source/Vector.js";
|
|
13
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
10
14
|
|
|
11
15
|
describe("Layer update utils", () => {
|
|
12
16
|
describe("canDoIncrementalUpdate", () => {
|
|
@@ -111,5 +115,27 @@ describe("Layer update utils", () => {
|
|
|
111
115
|
expect(olSource.setAttributions).toHaveBeenCalledWith("hello world");
|
|
112
116
|
expect(olLayer.set).toHaveBeenCalledWith("label", "Test Layer");
|
|
113
117
|
});
|
|
118
|
+
|
|
119
|
+
it("applies properties specific to vector layers without recreating them", async () => {
|
|
120
|
+
// mocking a vector layer
|
|
121
|
+
(olLayer as VectorLayer).setStyle = vi.fn();
|
|
122
|
+
|
|
123
|
+
const layerModel = {
|
|
124
|
+
...SAMPLE_LAYER3,
|
|
125
|
+
style: {
|
|
126
|
+
"circle-fill-color": "blue",
|
|
127
|
+
},
|
|
128
|
+
enableHover: true,
|
|
129
|
+
};
|
|
130
|
+
const prevLayerModel = SAMPLE_LAYER3;
|
|
131
|
+
updateLayerProperties(layerModel, olLayer, prevLayerModel);
|
|
132
|
+
expect((olLayer as VectorLayer).setStyle).toHaveBeenCalledWith(
|
|
133
|
+
layerModel.style,
|
|
134
|
+
);
|
|
135
|
+
expect(olLayer.set).toHaveBeenCalledWith(
|
|
136
|
+
"--geospatial-sdk-enable-hover",
|
|
137
|
+
true,
|
|
138
|
+
);
|
|
139
|
+
});
|
|
114
140
|
});
|
|
115
141
|
});
|
package/lib/map/layer-update.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { getHash, MapContextLayer } from "@geospatial-sdk/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MapContextBaseLayer,
|
|
4
|
+
MapContextLayerVector,
|
|
5
|
+
} from "@geospatial-sdk/core/lib/model/map-context.js";
|
|
3
6
|
import Layer from "ol/layer/Layer.js";
|
|
7
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
8
|
+
import VectorLayer from "ol/layer/Vector.js";
|
|
4
9
|
|
|
5
|
-
const UPDATABLE_PROPERTIES: (
|
|
10
|
+
const UPDATABLE_PROPERTIES: (
|
|
11
|
+
| keyof MapContextBaseLayer
|
|
12
|
+
| keyof MapContextLayerVector
|
|
13
|
+
)[] = [
|
|
6
14
|
"opacity",
|
|
7
15
|
"visibility",
|
|
8
16
|
"label",
|
|
9
17
|
"attributions",
|
|
10
18
|
"extras",
|
|
11
19
|
"version",
|
|
20
|
+
"enableHover",
|
|
21
|
+
"style",
|
|
12
22
|
// TODO (when available) "zIndex"
|
|
13
23
|
];
|
|
14
24
|
|
|
@@ -43,7 +53,7 @@ export function updateLayerProperties(
|
|
|
43
53
|
olLayer: Layer,
|
|
44
54
|
previousLayerModel?: MapContextLayer,
|
|
45
55
|
) {
|
|
46
|
-
function shouldApplyProperty(prop: keyof
|
|
56
|
+
function shouldApplyProperty(prop: keyof MapContextLayer): boolean {
|
|
47
57
|
// if the new layer model does not define that property, skip it
|
|
48
58
|
// (setting or resetting it to a default value would be counter-intuitive)
|
|
49
59
|
if (!(prop in layerModel) || typeof layerModel[prop] === "undefined")
|
|
@@ -69,5 +79,19 @@ export function updateLayerProperties(
|
|
|
69
79
|
if (shouldApplyProperty("label")) {
|
|
70
80
|
olLayer.set("label", layerModel.label);
|
|
71
81
|
}
|
|
82
|
+
if (shouldApplyProperty("enableHover" as keyof MapContextLayer)) {
|
|
83
|
+
olLayer.set(
|
|
84
|
+
`${GEOSPATIAL_SDK_PREFIX}enable-hover`,
|
|
85
|
+
(layerModel as MapContextLayerVector).enableHover,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
shouldApplyProperty("style" as keyof MapContextLayer) &&
|
|
90
|
+
"setStyle" in olLayer
|
|
91
|
+
) {
|
|
92
|
+
(olLayer as VectorLayer).setStyle(
|
|
93
|
+
(layerModel as MapContextLayerVector).style,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
72
96
|
// TODO: z-index
|
|
73
97
|
}
|
|
@@ -1,129 +1,108 @@
|
|
|
1
|
-
import
|
|
1
|
+
import OlMap from "ol/Map.js";
|
|
2
2
|
import { Mock } from "vitest";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
getFeaturesFromWmsSources,
|
|
6
|
-
getGFIUrl,
|
|
7
|
-
listen,
|
|
8
|
-
} from "./register-events.js";
|
|
9
|
-
import { Collection, MapBrowserEvent, Object as BaseObject } from "ol";
|
|
3
|
+
import { listen } from "./register-events.js";
|
|
4
|
+
import { MapBrowserEvent, Object as BaseObject } from "ol";
|
|
10
5
|
import View from "ol/View.js";
|
|
11
6
|
import { toLonLat } from "ol/proj.js";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import VectorLayer from "ol/layer/Vector.js";
|
|
15
|
-
import Point from "ol/geom/Point.js";
|
|
16
|
-
import TileLayer from "ol/layer/Tile.js";
|
|
17
|
-
import OlFeature from "ol/Feature.js";
|
|
7
|
+
import { FeaturesHoverEventType } from "@geospatial-sdk/core";
|
|
8
|
+
import BaseEvent from "ol/events/Event.js";
|
|
18
9
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
vi.mock("./get-features.js", () => ({
|
|
11
|
+
readFeaturesAtPixel() {
|
|
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
|
+
);
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
23
43
|
|
|
24
|
-
const
|
|
44
|
+
const featureA = {
|
|
45
|
+
geometry: {
|
|
46
|
+
coordinates: [100, 200],
|
|
47
|
+
type: "Point",
|
|
48
|
+
},
|
|
49
|
+
properties: null,
|
|
25
50
|
type: "Feature",
|
|
51
|
+
};
|
|
52
|
+
const featureB = {
|
|
53
|
+
geometry: null,
|
|
26
54
|
properties: {
|
|
27
55
|
density: 123,
|
|
28
56
|
},
|
|
29
|
-
|
|
57
|
+
type: "Feature",
|
|
30
58
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function createMap(): Map {
|
|
42
|
-
const wmsLayer = new TileLayer({
|
|
43
|
-
source: createWmsSource(),
|
|
44
|
-
});
|
|
45
|
-
const feature = new OlFeature({
|
|
46
|
-
geometry: new Point([100, 200]),
|
|
47
|
-
});
|
|
48
|
-
const vectorLayer = new VectorLayer({
|
|
49
|
-
source: new VectorSource({
|
|
50
|
-
features: [feature],
|
|
51
|
-
}),
|
|
52
|
-
});
|
|
59
|
+
|
|
60
|
+
const EXPECTED_MAP_EXTENT_EPSG4326 = [
|
|
61
|
+
-0.0035932611364780857, -0.0026949458513598756, 0.0035932611364780857,
|
|
62
|
+
0.0026949458513740865,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
function createMap(): OlMap {
|
|
53
66
|
const view = new View({
|
|
54
67
|
projection: "EPSG:3857",
|
|
55
68
|
resolution: 1,
|
|
56
69
|
center: [0, 0],
|
|
57
70
|
});
|
|
58
|
-
const map = new BaseObject() as
|
|
71
|
+
const map = new BaseObject() as OlMap;
|
|
59
72
|
Object.defineProperties(map, {
|
|
60
73
|
getView: { value: vi.fn(() => view) },
|
|
61
|
-
getLayers: { value: vi.fn(() => new Collection([wmsLayer, vectorLayer])) },
|
|
62
74
|
getEventPixel: { value: vi.fn(() => [10, 10]) },
|
|
63
75
|
getCoordinateFromPixel: { value: vi.fn(() => [123, 123]) },
|
|
64
|
-
getFeaturesAtPixel: { value: vi.fn(() => [feature]) },
|
|
65
76
|
getSize: { value: vi.fn(() => [800, 600]) },
|
|
66
77
|
});
|
|
78
|
+
// simulate hover feature initialization
|
|
79
|
+
map.on("pointermove", () => {
|
|
80
|
+
map.dispatchEvent({
|
|
81
|
+
type: FeaturesHoverEventType,
|
|
82
|
+
features: [featureB],
|
|
83
|
+
featuresByLayer: new Map([[0, [featureB]]]),
|
|
84
|
+
} as unknown as BaseEvent);
|
|
85
|
+
});
|
|
67
86
|
return map;
|
|
68
87
|
}
|
|
69
|
-
function createMapEvent(map:
|
|
88
|
+
function createMapEvent(map: OlMap, type: string) {
|
|
70
89
|
return new MapBrowserEvent(
|
|
71
90
|
type,
|
|
72
91
|
map,
|
|
73
92
|
new MouseEvent(type, {
|
|
74
93
|
clientX: 10,
|
|
75
94
|
clientY: 10,
|
|
76
|
-
}),
|
|
95
|
+
}) as PointerEvent,
|
|
77
96
|
false,
|
|
78
97
|
);
|
|
79
98
|
}
|
|
80
99
|
|
|
81
100
|
describe("event registration", () => {
|
|
82
|
-
let map:
|
|
101
|
+
let map: OlMap;
|
|
83
102
|
beforeEach(() => {
|
|
84
103
|
map = createMap();
|
|
85
|
-
vi.spyOn(global, "fetch").mockImplementation(() =>
|
|
86
|
-
Promise.resolve({
|
|
87
|
-
ok: true,
|
|
88
|
-
json: () => Promise.resolve({ features: [gfiResult] }),
|
|
89
|
-
} as Response),
|
|
90
|
-
);
|
|
91
104
|
vi.useFakeTimers();
|
|
92
105
|
});
|
|
93
|
-
describe("getFeaturesFromVectorSources", () => {
|
|
94
|
-
it("returns an array of features", () => {
|
|
95
|
-
const features = getFeaturesFromVectorSources(map, [0, 0]);
|
|
96
|
-
expect(features).toEqual([
|
|
97
|
-
{
|
|
98
|
-
geometry: {
|
|
99
|
-
coordinates: [100, 200],
|
|
100
|
-
type: "Point",
|
|
101
|
-
},
|
|
102
|
-
properties: null,
|
|
103
|
-
type: "Feature",
|
|
104
|
-
},
|
|
105
|
-
]);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe("getGFIUrl", () => {
|
|
109
|
-
let url: string;
|
|
110
|
-
const coordinate = [-182932.49329334166, 6125319.813853541];
|
|
111
|
-
beforeEach(() => {
|
|
112
|
-
const wmsSource = createWmsSource();
|
|
113
|
-
url = getGFIUrl(wmsSource, map, coordinate) as string;
|
|
114
|
-
});
|
|
115
|
-
it("returns true", () => {
|
|
116
|
-
expect(url).toEqual(
|
|
117
|
-
"http://my.service.org/wms?SERVICE=WMS&REQUEST=GetFeatureInfo&QUERY_LAYERS=layerName&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=&TRANSPARENT=true&LAYERS=layerName&INFO_FORMAT=application%2Fjson&I=176&J=31&WIDTH=256&HEIGHT=256&CRS=EPSG%3A3857&BBOX=-183143.11977128312%2C6125051.950547744%2C-182837.37165814242%2C6125357.698660884",
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe("getFeaturesFromWmsSources", () => {
|
|
122
|
-
it("queries the WMS sources", async () => {
|
|
123
|
-
const features = await getFeaturesFromWmsSources(map, [0, 0]);
|
|
124
|
-
expect(features).toEqual([gfiResult]);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
106
|
describe("features hover event", () => {
|
|
128
107
|
let callback: Mock;
|
|
129
108
|
beforeEach(async () => {
|
|
@@ -135,23 +114,8 @@ describe("event registration", () => {
|
|
|
135
114
|
it("registers the event on the map", () => {
|
|
136
115
|
expect(callback).toHaveBeenCalledWith({
|
|
137
116
|
type: "features-hover",
|
|
138
|
-
features: [
|
|
139
|
-
|
|
140
|
-
geometry: {
|
|
141
|
-
coordinates: [100, 200],
|
|
142
|
-
type: "Point",
|
|
143
|
-
},
|
|
144
|
-
properties: null,
|
|
145
|
-
type: "Feature",
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
geometry: null,
|
|
149
|
-
properties: {
|
|
150
|
-
density: 123,
|
|
151
|
-
},
|
|
152
|
-
type: "Feature",
|
|
153
|
-
},
|
|
154
|
-
],
|
|
117
|
+
features: [featureB],
|
|
118
|
+
featuresByLayer: new Map([[0, [featureB]]]),
|
|
155
119
|
target: expect.anything(),
|
|
156
120
|
});
|
|
157
121
|
});
|
|
@@ -167,23 +131,11 @@ describe("event registration", () => {
|
|
|
167
131
|
it("registers the event on the map", () => {
|
|
168
132
|
expect(callback).toHaveBeenCalledWith({
|
|
169
133
|
type: "features-click",
|
|
170
|
-
features: [
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
},
|
|
176
|
-
properties: null,
|
|
177
|
-
type: "Feature",
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
geometry: null,
|
|
181
|
-
properties: {
|
|
182
|
-
density: 123,
|
|
183
|
-
},
|
|
184
|
-
type: "Feature",
|
|
185
|
-
},
|
|
186
|
-
],
|
|
134
|
+
features: [featureA, featureB],
|
|
135
|
+
featuresByLayer: new Map([
|
|
136
|
+
[0, [featureA]],
|
|
137
|
+
[1, [featureB]],
|
|
138
|
+
]),
|
|
187
139
|
target: expect.anything(),
|
|
188
140
|
});
|
|
189
141
|
});
|