@geospatial-sdk/openlayers 0.0.5-dev.54 → 0.0.5-dev.56
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/apply-context-diff.d.ts.map +1 -1
- package/dist/map/apply-context-diff.js +9 -1
- package/dist/map/create-map.d.ts +1 -1
- package/dist/map/create-map.d.ts.map +1 -1
- package/dist/map/create-map.js +73 -30
- package/dist/map/feature-hover.js +1 -1
- package/dist/map/handle-errors.d.ts +0 -7
- package/dist/map/handle-errors.d.ts.map +1 -1
- package/dist/map/handle-errors.js +10 -14
- package/dist/map/index.d.ts +2 -1
- package/dist/map/index.d.ts.map +1 -1
- package/dist/map/index.js +2 -1
- package/dist/map/layer-update.d.ts.map +1 -1
- package/dist/map/layer-update.js +1 -1
- package/dist/map/listen.d.ts.map +1 -1
- package/dist/map/listen.js +13 -26
- package/dist/map/register-events.d.ts +16 -2
- package/dist/map/register-events.d.ts.map +1 -1
- package/dist/map/register-events.js +172 -81
- package/lib/map/apply-context-diff.ts +16 -5
- package/lib/map/create-map.test.ts +172 -60
- package/lib/map/create-map.ts +100 -37
- package/lib/map/feature-hover.ts +1 -1
- package/lib/map/handle-errors.test.ts +13 -36
- package/lib/map/handle-errors.ts +10 -28
- package/lib/map/index.ts +2 -1
- package/lib/map/layer-update.ts +3 -2
- package/lib/map/listen.test.ts +977 -0
- package/lib/map/listen.ts +123 -0
- package/lib/map/register-events.ts +229 -109
- package/lib/map/resolved-map-state.ts +38 -0
- package/package.json +3 -3
- package/dist/map/feature-selection.d.ts +0 -8
- package/dist/map/feature-selection.d.ts.map +0 -1
- package/dist/map/feature-selection.js +0 -76
- 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/dist/resolved-state/resolved-map-state.d.ts +0 -2
- package/dist/resolved-state/resolved-map-state.d.ts.map +0 -1
- package/dist/resolved-state/resolved-map-state.js +0 -1
- package/lib/map/register-events.test.ts +0 -259
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import Map from "ol/Map.js";
|
|
2
|
+
import MapEvent from "ol/MapEvent.js";
|
|
3
|
+
import { toLonLat } from "ol/proj.js";
|
|
4
|
+
import {
|
|
5
|
+
FeaturesClickEventType,
|
|
6
|
+
FeaturesHoverEventType,
|
|
7
|
+
LayerCreationErrorEventType,
|
|
8
|
+
LayerLoadingErrorEventType,
|
|
9
|
+
MapClickEvent,
|
|
10
|
+
MapClickEventType,
|
|
11
|
+
MapEventsByType,
|
|
12
|
+
MapExtentChangeEvent,
|
|
13
|
+
MapExtentChangeEventType,
|
|
14
|
+
MapLayerStateChangeEventType,
|
|
15
|
+
MapStateChangeEventType,
|
|
16
|
+
MapViewStateChangeEvent,
|
|
17
|
+
MapViewStateChangeEventType,
|
|
18
|
+
SourceLoadErrorEvent,
|
|
19
|
+
SourceLoadErrorType,
|
|
20
|
+
} from "@geospatial-sdk/core";
|
|
21
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
22
|
+
import {
|
|
23
|
+
registerFeatureClickEvent,
|
|
24
|
+
registerFeatureHoverEvent,
|
|
25
|
+
registerLayerCreationErrorEvent,
|
|
26
|
+
registerLayerLoadingErrorEvent,
|
|
27
|
+
registerMapLayerStateChangeEvent,
|
|
28
|
+
registerMapStateChangeEvent,
|
|
29
|
+
registerMapViewStateChangeEvent,
|
|
30
|
+
registerSourceLoadErrorEvent,
|
|
31
|
+
} from "./register-events.js";
|
|
32
|
+
|
|
33
|
+
function addEventListener<T extends keyof MapEventsByType>(
|
|
34
|
+
map: Map,
|
|
35
|
+
eventType: T,
|
|
36
|
+
callback: (event: MapEventsByType[T]) => void,
|
|
37
|
+
) {
|
|
38
|
+
map.on(
|
|
39
|
+
`${GEOSPATIAL_SDK_PREFIX}${eventType}`,
|
|
40
|
+
({
|
|
41
|
+
target: _target,
|
|
42
|
+
...event
|
|
43
|
+
}: MapEventsByType[T] & { target: MapEvent["target"] }) =>
|
|
44
|
+
// we're excluding the `target` property and renaming the `type` here
|
|
45
|
+
callback({
|
|
46
|
+
...event,
|
|
47
|
+
type: eventType,
|
|
48
|
+
} as MapEventsByType[T]),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function listen<T extends keyof MapEventsByType>(
|
|
53
|
+
map: Map,
|
|
54
|
+
eventType: T,
|
|
55
|
+
callback: (event: MapEventsByType[T]) => void,
|
|
56
|
+
) {
|
|
57
|
+
switch (eventType) {
|
|
58
|
+
case FeaturesClickEventType:
|
|
59
|
+
registerFeatureClickEvent(map);
|
|
60
|
+
addEventListener(map, eventType, callback);
|
|
61
|
+
break;
|
|
62
|
+
case FeaturesHoverEventType:
|
|
63
|
+
registerFeatureHoverEvent(map);
|
|
64
|
+
addEventListener(map, eventType, callback);
|
|
65
|
+
break;
|
|
66
|
+
case MapClickEventType:
|
|
67
|
+
map.on("click", (event: MapClickEvent) => {
|
|
68
|
+
const coordinate = toLonLat(
|
|
69
|
+
event.coordinate,
|
|
70
|
+
map.getView().getProjection(),
|
|
71
|
+
) as [number, number];
|
|
72
|
+
callback({
|
|
73
|
+
type: MapClickEventType,
|
|
74
|
+
coordinate,
|
|
75
|
+
} as MapEventsByType[T]);
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
case MapViewStateChangeEventType:
|
|
79
|
+
registerMapViewStateChangeEvent(map);
|
|
80
|
+
addEventListener(map, eventType, callback);
|
|
81
|
+
break;
|
|
82
|
+
case MapLayerStateChangeEventType:
|
|
83
|
+
registerMapLayerStateChangeEvent(map);
|
|
84
|
+
addEventListener(map, eventType, callback);
|
|
85
|
+
break;
|
|
86
|
+
case MapStateChangeEventType:
|
|
87
|
+
registerMapStateChangeEvent(map);
|
|
88
|
+
addEventListener(map, eventType, callback);
|
|
89
|
+
break;
|
|
90
|
+
case LayerCreationErrorEventType:
|
|
91
|
+
registerLayerCreationErrorEvent(map);
|
|
92
|
+
addEventListener(map, eventType, callback);
|
|
93
|
+
break;
|
|
94
|
+
case LayerLoadingErrorEventType:
|
|
95
|
+
registerLayerLoadingErrorEvent(map);
|
|
96
|
+
addEventListener(map, eventType, callback);
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* DEPRECATED
|
|
101
|
+
*/
|
|
102
|
+
case MapExtentChangeEventType:
|
|
103
|
+
registerMapViewStateChangeEvent(map);
|
|
104
|
+
map.on(
|
|
105
|
+
`${GEOSPATIAL_SDK_PREFIX}${MapViewStateChangeEventType}`,
|
|
106
|
+
(event: MapViewStateChangeEvent) =>
|
|
107
|
+
callback({
|
|
108
|
+
type: MapExtentChangeEventType,
|
|
109
|
+
extent: event.viewState.extent,
|
|
110
|
+
} as MapExtentChangeEvent as MapEventsByType[T]),
|
|
111
|
+
);
|
|
112
|
+
break;
|
|
113
|
+
case SourceLoadErrorType: {
|
|
114
|
+
registerSourceLoadErrorEvent(map);
|
|
115
|
+
map.on(SourceLoadErrorType, (event: SourceLoadErrorEvent) =>
|
|
116
|
+
callback(event as MapEventsByType[T]),
|
|
117
|
+
);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
default:
|
|
121
|
+
throw new Error(`Unrecognized event type: ${eventType}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1,33 +1,41 @@
|
|
|
1
|
+
import Map from "ol/Map.js";
|
|
1
2
|
import {
|
|
2
3
|
FeaturesClickEventType,
|
|
3
4
|
FeaturesHoverEventType,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
LayerCreationErrorEventType,
|
|
6
|
+
LayerLoadingErrorEventType,
|
|
7
|
+
MapLayerDataInfo,
|
|
8
|
+
MapLayerLoadingStatus,
|
|
9
|
+
MapLayerStateChangeEvent,
|
|
10
|
+
MapLayerStateChangeEventType,
|
|
11
|
+
MapStateChangeEventType,
|
|
12
|
+
MapViewStateChangeEvent,
|
|
13
|
+
MapViewStateChangeEventType,
|
|
14
|
+
ResolvedMapLayerState,
|
|
15
|
+
ResolvedMapState,
|
|
16
|
+
SourceLoadErrorEvent,
|
|
7
17
|
SourceLoadErrorType,
|
|
8
18
|
} from "@geospatial-sdk/core";
|
|
9
19
|
import BaseEvent from "ol/events/Event.js";
|
|
10
|
-
import { equals } from "ol/extent.js";
|
|
11
20
|
import type BaseLayer from "ol/layer/Base.js";
|
|
12
|
-
import { BaseLayerObjectEventTypes } from "ol/layer/Base.js";
|
|
13
|
-
import Layer from "ol/layer/Layer.js";
|
|
14
|
-
import Map, { MapObjectEventTypes } from "ol/Map.js";
|
|
15
|
-
import { toLonLat, transformExtent } from "ol/proj.js";
|
|
16
|
-
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
17
21
|
import { readFeaturesAtPixel } from "./get-features.js";
|
|
22
|
+
import MapBrowserEvent from "ol/MapBrowserEvent.js";
|
|
23
|
+
import { equals } from "ol/extent.js";
|
|
24
|
+
import { readMapViewState } from "./resolved-map-state.js";
|
|
25
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
18
26
|
|
|
19
|
-
function registerFeatureClickEvent(map: Map) {
|
|
27
|
+
export function registerFeatureClickEvent(map: Map) {
|
|
20
28
|
if (map.get(FeaturesClickEventType)) return;
|
|
21
29
|
|
|
22
30
|
// Filter to only query clickable layers
|
|
23
31
|
const layerFilter = (layer: BaseLayer) =>
|
|
24
32
|
layer.get(`${GEOSPATIAL_SDK_PREFIX}clickable`) !== false;
|
|
25
33
|
|
|
26
|
-
map.on("click", async (event:
|
|
34
|
+
map.on("click", async (event: MapBrowserEvent<PointerEvent>) => {
|
|
27
35
|
const featuresByLayer = await readFeaturesAtPixel(map, event, layerFilter);
|
|
28
36
|
const features = Array.from(featuresByLayer.values()).flat();
|
|
29
37
|
map.dispatchEvent({
|
|
30
|
-
type: FeaturesClickEventType
|
|
38
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${FeaturesClickEventType}`,
|
|
31
39
|
features,
|
|
32
40
|
featuresByLayer,
|
|
33
41
|
} as unknown as BaseEvent);
|
|
@@ -36,119 +44,231 @@ function registerFeatureClickEvent(map: Map) {
|
|
|
36
44
|
map.set(FeaturesClickEventType, true);
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
function registerFeatureHoverEvent(map: Map) {
|
|
47
|
+
export function registerFeatureHoverEvent(map: Map) {
|
|
40
48
|
if (map.get(FeaturesHoverEventType)) return;
|
|
41
49
|
map.set(FeaturesHoverEventType, true);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
function
|
|
45
|
-
if (map.get(
|
|
52
|
+
export function registerMapLayerStateChangeEvent(map: Map) {
|
|
53
|
+
if (map.get(MapLayerStateChangeEventType)) return;
|
|
54
|
+
map.set(MapLayerStateChangeEventType, true);
|
|
55
|
+
}
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
export function emitLayerCreationError(layer: BaseLayer, error: Error) {
|
|
58
|
+
layer.dispatchEvent({
|
|
59
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${LayerCreationErrorEventType}`,
|
|
60
|
+
error,
|
|
61
|
+
} as unknown as BaseEvent);
|
|
62
|
+
}
|
|
63
|
+
export function emitLayerLoadingStatusLoading(layer: BaseLayer) {
|
|
64
|
+
layer.dispatchEvent({
|
|
65
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
66
|
+
layerState: { loading: true },
|
|
67
|
+
} as unknown as BaseEvent);
|
|
68
|
+
}
|
|
69
|
+
export function emitLayerLoadingStatusSuccess(layer: BaseLayer) {
|
|
70
|
+
layer.dispatchEvent({
|
|
71
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
72
|
+
layerState: { loaded: true },
|
|
73
|
+
} as unknown as BaseEvent);
|
|
74
|
+
}
|
|
75
|
+
export function emitLayerLoadingError(
|
|
76
|
+
layer: BaseLayer,
|
|
77
|
+
error: Error,
|
|
78
|
+
httpStatus?: number,
|
|
79
|
+
) {
|
|
80
|
+
layer.dispatchEvent({
|
|
81
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${LayerLoadingErrorEventType}`,
|
|
82
|
+
error,
|
|
83
|
+
...(httpStatus !== undefined ? { httpStatus } : {}),
|
|
84
|
+
} as unknown as BaseEvent);
|
|
85
|
+
}
|
|
86
|
+
export function emitLayerDataInfo(
|
|
87
|
+
layer: BaseLayer,
|
|
88
|
+
dataInfo: MapLayerDataInfo,
|
|
89
|
+
) {
|
|
90
|
+
layer.dispatchEvent({
|
|
91
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-data-info`,
|
|
92
|
+
layerState: dataInfo,
|
|
93
|
+
} as unknown as BaseEvent);
|
|
94
|
+
}
|
|
48
95
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
96
|
+
export function propagateLayerStateChangeEventToMap(
|
|
97
|
+
map: Map,
|
|
98
|
+
layer: BaseLayer,
|
|
99
|
+
) {
|
|
100
|
+
let currentLayerState: Partial<ResolvedMapLayerState> = {
|
|
101
|
+
created: true,
|
|
102
|
+
};
|
|
103
|
+
let currentLoadingStatus: Partial<MapLayerLoadingStatus> = {};
|
|
56
104
|
|
|
57
|
-
|
|
105
|
+
function updateStateAndEmit() {
|
|
106
|
+
if (!map.get(MapLayerStateChangeEventType)) {
|
|
58
107
|
return;
|
|
59
108
|
}
|
|
60
|
-
|
|
61
|
-
lastExtent = reprojectedExtent;
|
|
62
|
-
|
|
109
|
+
const layerIndex = map.getLayers().getArray().indexOf(layer);
|
|
63
110
|
map.dispatchEvent({
|
|
64
|
-
type:
|
|
65
|
-
|
|
111
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${MapLayerStateChangeEventType}`,
|
|
112
|
+
layerState: {
|
|
113
|
+
...currentLayerState,
|
|
114
|
+
...currentLoadingStatus,
|
|
115
|
+
},
|
|
116
|
+
layerIndex,
|
|
66
117
|
} as unknown as BaseEvent);
|
|
67
|
-
}
|
|
118
|
+
}
|
|
68
119
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
120
|
+
// on layer creation error update layer state and redispatch on map
|
|
121
|
+
layer.on(
|
|
122
|
+
`${GEOSPATIAL_SDK_PREFIX}${LayerCreationErrorEventType}`,
|
|
123
|
+
(event: BaseEvent & { error: Error }) => {
|
|
124
|
+
currentLayerState = {
|
|
125
|
+
creationError: true,
|
|
126
|
+
creationErrorMessage: event.error.message,
|
|
127
|
+
};
|
|
128
|
+
updateStateAndEmit();
|
|
73
129
|
|
|
74
|
-
|
|
75
|
-
|
|
130
|
+
if (map.get(LayerCreationErrorEventType)) {
|
|
131
|
+
map.dispatchEvent(event);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
);
|
|
76
135
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
(callback as (event: unknown) => void)(event);
|
|
88
|
-
});
|
|
89
|
-
break;
|
|
90
|
-
case FeaturesHoverEventType:
|
|
91
|
-
registerFeatureHoverEvent(map);
|
|
92
|
-
// see comment above
|
|
93
|
-
map.on(eventType as unknown as MapObjectEventTypes, (event: any) => {
|
|
94
|
-
(callback as (event: unknown) => void)(event);
|
|
95
|
-
});
|
|
96
|
-
break;
|
|
97
|
-
case MapClickEventType:
|
|
98
|
-
map.on("click", (event: any) => {
|
|
99
|
-
const coordinate = toLonLat(
|
|
100
|
-
event.coordinate,
|
|
101
|
-
map.getView().getProjection(),
|
|
102
|
-
) as [number, number];
|
|
103
|
-
(callback as (event: unknown) => void)({
|
|
104
|
-
type: "map-click",
|
|
105
|
-
coordinate,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
break;
|
|
109
|
-
case MapExtentChangeEventType:
|
|
110
|
-
registerMapExtentChangeEvent(map);
|
|
111
|
-
// see comment above
|
|
112
|
-
map.on(eventType as unknown as MapObjectEventTypes, (event: any) => {
|
|
113
|
-
(callback as (event: unknown) => void)(event);
|
|
114
|
-
});
|
|
115
|
-
break;
|
|
116
|
-
case SourceLoadErrorType: {
|
|
117
|
-
const errorCallback = (event: BaseEvent) => {
|
|
118
|
-
(callback as (event: unknown) => void)(event);
|
|
136
|
+
// on layer loading error update layer state and redispatch on map
|
|
137
|
+
layer.on(
|
|
138
|
+
`${GEOSPATIAL_SDK_PREFIX}${LayerLoadingErrorEventType}`,
|
|
139
|
+
(event: BaseEvent & { error: Error; httpStatus?: number }) => {
|
|
140
|
+
currentLoadingStatus = {
|
|
141
|
+
loadingError: true,
|
|
142
|
+
loadingErrorMessage: event.error.message,
|
|
143
|
+
...(event.httpStatus !== undefined && {
|
|
144
|
+
loadingErrorHttpStatus: event.httpStatus,
|
|
145
|
+
}),
|
|
119
146
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const layer = event.element as Layer;
|
|
132
|
-
if (layer) {
|
|
133
|
-
layer.on(
|
|
134
|
-
SourceLoadErrorType as unknown as BaseLayerObjectEventTypes,
|
|
135
|
-
errorCallback,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
//remove event listener when layer is removed
|
|
140
|
-
map.getLayers().on("remove", (event: any) => {
|
|
141
|
-
const layer = event.element as Layer;
|
|
142
|
-
if (layer) {
|
|
143
|
-
layer.un(
|
|
144
|
-
SourceLoadErrorType as unknown as BaseLayerObjectEventTypes,
|
|
145
|
-
errorCallback,
|
|
146
|
-
);
|
|
147
|
+
updateStateAndEmit();
|
|
148
|
+
|
|
149
|
+
if (map.get(LayerLoadingErrorEventType)) {
|
|
150
|
+
map.dispatchEvent(event);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// deprecated event
|
|
154
|
+
if (map.get(SourceLoadErrorType)) {
|
|
155
|
+
const sourceLoadEvent = new SourceLoadErrorEvent(event.error);
|
|
156
|
+
if (event.httpStatus) {
|
|
157
|
+
sourceLoadEvent.httpStatus = event.httpStatus;
|
|
147
158
|
}
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
map.dispatchEvent(sourceLoadEvent as unknown as BaseEvent);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// When new information about a layer state is available, add it to the previous state & emit
|
|
165
|
+
layer.on(
|
|
166
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-data-info`,
|
|
167
|
+
(event: MapLayerStateChangeEvent) => {
|
|
168
|
+
currentLayerState = {
|
|
169
|
+
...currentLayerState,
|
|
170
|
+
...event.layerState,
|
|
171
|
+
};
|
|
172
|
+
updateStateAndEmit();
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// loading state can change over time
|
|
177
|
+
layer.on(
|
|
178
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
179
|
+
(event: MapLayerStateChangeEvent) => {
|
|
180
|
+
currentLoadingStatus = event.layerState;
|
|
181
|
+
updateStateAndEmit();
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function registerMapStateChangeEvent(map: Map) {
|
|
187
|
+
if (map.get(MapStateChangeEventType)) return;
|
|
188
|
+
|
|
189
|
+
// the global map state requires both view and layers state
|
|
190
|
+
registerMapLayerStateChangeEvent(map);
|
|
191
|
+
registerMapViewStateChangeEvent(map);
|
|
192
|
+
|
|
193
|
+
let currentState: ResolvedMapState = {
|
|
194
|
+
layers: [],
|
|
195
|
+
view: null,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
function emitState() {
|
|
199
|
+
// we're making sure to have the right amount of layers in the state and to fill empty slots with null
|
|
200
|
+
currentState.layers.length = map.getLayers().getLength();
|
|
201
|
+
for (let i = 0; i < currentState.layers.length; i++) {
|
|
202
|
+
if (!currentState.layers[i]) {
|
|
203
|
+
currentState.layers[i] = null;
|
|
204
|
+
}
|
|
150
205
|
}
|
|
151
|
-
|
|
152
|
-
|
|
206
|
+
map.dispatchEvent({
|
|
207
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${MapStateChangeEventType}`,
|
|
208
|
+
mapState: currentState as ResolvedMapState,
|
|
209
|
+
} as unknown as BaseEvent);
|
|
153
210
|
}
|
|
211
|
+
|
|
212
|
+
// collect view and layer states to re-emit them as a global state
|
|
213
|
+
map.on(
|
|
214
|
+
`${GEOSPATIAL_SDK_PREFIX}${MapLayerStateChangeEventType}`,
|
|
215
|
+
(event: BaseEvent & MapLayerStateChangeEvent) => {
|
|
216
|
+
const layers = [...currentState.layers];
|
|
217
|
+
layers[event.layerIndex] = event.layerState;
|
|
218
|
+
currentState = { ...currentState, layers };
|
|
219
|
+
emitState();
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
map.on(
|
|
223
|
+
`${GEOSPATIAL_SDK_PREFIX}${MapViewStateChangeEventType}`,
|
|
224
|
+
(event: BaseEvent & MapViewStateChangeEvent) => {
|
|
225
|
+
currentState = { ...currentState, view: event.viewState };
|
|
226
|
+
emitState();
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
map.set(MapStateChangeEventType, true);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function registerLayerCreationErrorEvent(map: Map) {
|
|
234
|
+
if (map.get(LayerCreationErrorEventType)) return;
|
|
235
|
+
map.set(LayerCreationErrorEventType, true);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function registerLayerLoadingErrorEvent(map: Map) {
|
|
239
|
+
if (map.get(LayerLoadingErrorEventType)) return;
|
|
240
|
+
map.set(LayerLoadingErrorEventType, true);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// DEPRECATED EVENTS
|
|
244
|
+
|
|
245
|
+
export function registerMapViewStateChangeEvent(map: Map) {
|
|
246
|
+
if (map.get(MapViewStateChangeEventType)) return;
|
|
247
|
+
|
|
248
|
+
let lastExtent: number[] | null = null;
|
|
249
|
+
|
|
250
|
+
const handleViewChange = () => {
|
|
251
|
+
const viewState = readMapViewState(map);
|
|
252
|
+
if (lastExtent && equals(lastExtent, viewState.extent)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
lastExtent = viewState.extent;
|
|
256
|
+
|
|
257
|
+
map.dispatchEvent({
|
|
258
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${MapViewStateChangeEventType}`,
|
|
259
|
+
viewState,
|
|
260
|
+
} as unknown as BaseEvent);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
map.getView().on("change:center", handleViewChange);
|
|
264
|
+
map.getView().on("change:resolution", handleViewChange);
|
|
265
|
+
map.getView().on("change:rotation", handleViewChange);
|
|
266
|
+
map.on("change:size", handleViewChange);
|
|
267
|
+
|
|
268
|
+
map.set(MapViewStateChangeEventType, true);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function registerSourceLoadErrorEvent(map: Map) {
|
|
272
|
+
if (map.get(SourceLoadErrorType)) return;
|
|
273
|
+
map.set(SourceLoadErrorType, true);
|
|
154
274
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Map from "ol/Map.js";
|
|
2
|
+
import { transform as transformCoordinate, transformExtent } from "ol/proj.js";
|
|
3
|
+
import { Coordinate, Extent, ResolvedMapViewState } from "@geospatial-sdk/core";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This magic value is generally used across OGC services as a reasonable approximation for most displays
|
|
7
|
+
*/
|
|
8
|
+
const PIXEL_SIZE_MM = 0.28;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reads the current view state of the map.
|
|
12
|
+
* @param map
|
|
13
|
+
*/
|
|
14
|
+
export function readMapViewState(map: Map): ResolvedMapViewState {
|
|
15
|
+
const view = map.getView();
|
|
16
|
+
const projection = view.getProjection();
|
|
17
|
+
const extent = transformExtent(
|
|
18
|
+
view.calculateExtent(map.getSize()),
|
|
19
|
+
projection,
|
|
20
|
+
"EPSG:4326",
|
|
21
|
+
) as Extent;
|
|
22
|
+
const center = transformCoordinate(
|
|
23
|
+
view.getCenter() ?? [0, 0],
|
|
24
|
+
projection,
|
|
25
|
+
"EPSG:4326",
|
|
26
|
+
) as Coordinate;
|
|
27
|
+
const resolution = view.getResolution() ?? 1;
|
|
28
|
+
const metersPerUnit = projection.getMetersPerUnit() ?? 1;
|
|
29
|
+
const scaleDenominator = metersPerUnit * resolution * (1000 / PIXEL_SIZE_MM);
|
|
30
|
+
const bearing = view.getRotation() * (180 / Math.PI) + 90; // by default, bearing is North
|
|
31
|
+
return {
|
|
32
|
+
center,
|
|
33
|
+
extent,
|
|
34
|
+
resolution,
|
|
35
|
+
scaleDenominator,
|
|
36
|
+
bearing,
|
|
37
|
+
};
|
|
38
|
+
}
|
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.56+eae591c",
|
|
4
4
|
"description": "OpenLayers-related utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ol",
|
|
@@ -37,9 +37,9 @@
|
|
|
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.56+eae591c",
|
|
41
41
|
"lodash.throttle": "^4.1.1",
|
|
42
42
|
"ol-mapbox-style": "12.4.0"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "eae591cfcca0346c3cac6fe7d5dcddf88fe408f4"
|
|
45
45
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
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 initSelectionLayer(map: OlMap): void;
|
|
5
|
-
export declare function getSelectionLayer(map: OlMap): VectorLayer<VectorSource>;
|
|
6
|
-
export declare function clearSelectionLayer(map: OlMap): void;
|
|
7
|
-
export declare function clearSelection(map: OlMap): void;
|
|
8
|
-
//# sourceMappingURL=feature-selection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"feature-selection.d.ts","sourceRoot":"","sources":["../../lib/map/feature-selection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,WAAW,CAAC;AACnC,OAAO,WAAW,MAAM,oBAAoB,CAAC;AAC7C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAU/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,KAAK,QAqE5C;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,CAEvE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,KAAK,QAM7C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,KAAK,QAQxC"}
|
|
@@ -1,76 +0,0 @@
|
|
|
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 } from "@geospatial-sdk/core";
|
|
5
|
-
import OlFeature from "ol/Feature.js";
|
|
6
|
-
import { unByKey } from "ol/Observable.js";
|
|
7
|
-
const selectionLayerKey = `${GEOSPATIAL_SDK_PREFIX}selection-layer`;
|
|
8
|
-
const unsubscribeKey = `${GEOSPATIAL_SDK_PREFIX}selection-unsub`;
|
|
9
|
-
export function initSelectionLayer(map) {
|
|
10
|
-
if (map.get(selectionLayerKey)) {
|
|
11
|
-
clearSelectionLayer(map);
|
|
12
|
-
}
|
|
13
|
-
// create layer & add on top of everything else
|
|
14
|
-
const selectionLayer = new VectorLayer({
|
|
15
|
-
source: new VectorSource({
|
|
16
|
-
features: [],
|
|
17
|
-
useSpatialIndex: false,
|
|
18
|
-
}),
|
|
19
|
-
style: defaultHighlightStyle,
|
|
20
|
-
});
|
|
21
|
-
map.set(selectionLayerKey, selectionLayer);
|
|
22
|
-
selectionLayer.setMap(map);
|
|
23
|
-
const layerFilter = (layer) => layer !== selectionLayer &&
|
|
24
|
-
layer.get(`${GEOSPATIAL_SDK_PREFIX}enable-selection`);
|
|
25
|
-
const unKey = map.on("click", async (event) => {
|
|
26
|
-
const selectedSource = selectionLayer.getSource();
|
|
27
|
-
selectedSource.clear(true);
|
|
28
|
-
// Check if there's a feature at the clicked pixel
|
|
29
|
-
const hasFeature = map.hasFeatureAtPixel(event.pixel, {
|
|
30
|
-
layerFilter,
|
|
31
|
-
});
|
|
32
|
-
if (!hasFeature) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
// Find the selected feature and its source layer
|
|
36
|
-
const selectedFeatureResult = [];
|
|
37
|
-
map.forEachFeatureAtPixel(event.pixel, (feature, layer) => {
|
|
38
|
-
if (feature instanceof OlFeature) {
|
|
39
|
-
selectedFeatureResult.push({ feature, layer });
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}, {
|
|
43
|
-
layerFilter,
|
|
44
|
-
});
|
|
45
|
-
if (selectedFeatureResult.length === 0) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const { feature: firstFeature, layer: sourceLayer } = selectedFeatureResult[0];
|
|
49
|
-
// Get the selectedStyle from the source layer, fallback to defaultHighlightStyle
|
|
50
|
-
const selectedStyle = sourceLayer.get(`${GEOSPATIAL_SDK_PREFIX}selected-style`) ??
|
|
51
|
-
defaultHighlightStyle;
|
|
52
|
-
// Apply the selected style to the layer (FlatStyleLike works on layers, not features)
|
|
53
|
-
selectionLayer.setStyle(selectedStyle);
|
|
54
|
-
selectedSource.addFeature(firstFeature);
|
|
55
|
-
});
|
|
56
|
-
map.set(unsubscribeKey, unKey);
|
|
57
|
-
}
|
|
58
|
-
export function getSelectionLayer(map) {
|
|
59
|
-
return map.get(selectionLayerKey);
|
|
60
|
-
}
|
|
61
|
-
export function clearSelectionLayer(map) {
|
|
62
|
-
const selectionLayer = getSelectionLayer(map);
|
|
63
|
-
selectionLayer.setMap(null);
|
|
64
|
-
selectionLayer.dispose();
|
|
65
|
-
map.set(selectionLayerKey, null);
|
|
66
|
-
unByKey(map.get(unsubscribeKey));
|
|
67
|
-
}
|
|
68
|
-
export function clearSelection(map) {
|
|
69
|
-
const selectionLayer = getSelectionLayer(map);
|
|
70
|
-
if (selectionLayer) {
|
|
71
|
-
const source = selectionLayer.getSource();
|
|
72
|
-
if (source) {
|
|
73
|
-
source.clear(true);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
package/dist/map/styles.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Style } from "ol/style.js";
|
|
2
|
-
import { StyleFunction } from "ol/style/Style.js";
|
|
3
|
-
export interface CreateStyleOptions {
|
|
4
|
-
color: string;
|
|
5
|
-
isFocused?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface StyleByGeometryType {
|
|
8
|
-
line: Style | Style[];
|
|
9
|
-
polygon: Style | Style[];
|
|
10
|
-
point: Style | Style[];
|
|
11
|
-
}
|
|
12
|
-
export declare function createGeometryStyles(options: CreateStyleOptions): StyleByGeometryType;
|
|
13
|
-
export declare function createStyleFunction(styleByGeometryType: StyleByGeometryType): StyleFunction;
|
|
14
|
-
export declare const defaultStyle: StyleFunction;
|
|
15
|
-
export declare const defaultHighlightStyle: StyleFunction;
|
|
16
|
-
//# sourceMappingURL=styles.d.ts.map
|
package/dist/map/styles.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../lib/map/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIlD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;IACtB,OAAO,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;IACzB,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,kBAAkB,GAC1B,mBAAmB,CA4CrB;AAED,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,mBAAmB,GACvC,aAAa,CAmBf;AAMD,eAAO,MAAM,YAAY,eAIxB,CAAC;AAEF,eAAO,MAAM,qBAAqB,eAKjC,CAAC"}
|