@vcmap/core 6.0.7 → 6.1.0-rc.2
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/cesium.d.ts +3 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/ol.d.ts +8 -1
- package/dist/src/featureProvider/featureProviderSymbols.d.ts +5 -0
- package/dist/src/featureProvider/featureProviderSymbols.js +5 -1
- package/dist/src/featureProvider/featureProviderSymbols.js.map +1 -1
- package/dist/src/interaction/featureAtPixelInteraction.js +58 -62
- package/dist/src/interaction/featureAtPixelInteraction.js.map +1 -1
- package/dist/src/interaction/featureProviderInteraction.js +25 -13
- package/dist/src/interaction/featureProviderInteraction.js.map +1 -1
- package/dist/src/layer/cesium/sourceVectorContextSync.d.ts +27 -0
- package/dist/src/layer/cesium/sourceVectorContextSync.js +94 -0
- package/dist/src/layer/cesium/sourceVectorContextSync.js.map +1 -0
- package/dist/src/layer/cesium/vectorCesiumImpl.d.ts +4 -27
- package/dist/src/layer/cesium/vectorCesiumImpl.js +15 -107
- package/dist/src/layer/cesium/vectorCesiumImpl.js.map +1 -1
- package/dist/src/layer/cesium/vectorContext.d.ts +12 -1
- package/dist/src/layer/cesium/vectorContext.js +6 -0
- package/dist/src/layer/cesium/vectorContext.js.map +1 -1
- package/dist/src/layer/layerSymbols.js +1 -1
- package/dist/src/layer/layerSymbols.js.map +1 -1
- package/dist/src/layer/oblique/sourceObliqueSync.d.ts +18 -0
- package/dist/src/layer/oblique/sourceObliqueSync.js +319 -0
- package/dist/src/layer/oblique/sourceObliqueSync.js.map +1 -0
- package/dist/src/layer/oblique/vectorObliqueImpl.d.ts +2 -40
- package/dist/src/layer/oblique/vectorObliqueImpl.js +8 -283
- package/dist/src/layer/oblique/vectorObliqueImpl.js.map +1 -1
- package/dist/src/layer/vectorLayer.d.ts +10 -1
- package/dist/src/layer/vectorLayer.js +23 -1
- package/dist/src/layer/vectorLayer.js.map +1 -1
- package/dist/src/map/baseOLMap.js +8 -1
- package/dist/src/map/baseOLMap.js.map +1 -1
- package/dist/src/map/cesiumMap.d.ts +2 -0
- package/dist/src/map/cesiumMap.js +26 -1
- package/dist/src/map/cesiumMap.js.map +1 -1
- package/dist/src/map/vcsMap.d.ts +24 -12
- package/dist/src/map/vcsMap.js +92 -38
- package/dist/src/map/vcsMap.js.map +1 -1
- package/dist/src/ol/source/ClusterEnhancedVectorSource.d.ts +6 -4
- package/dist/src/ol/source/ClusterEnhancedVectorSource.js +4 -9
- package/dist/src/ol/source/ClusterEnhancedVectorSource.js.map +1 -1
- package/dist/src/ol/source/VcsCluster.d.ts +10 -10
- package/dist/src/ol/source/VcsCluster.js +23 -7
- package/dist/src/ol/source/VcsCluster.js.map +1 -1
- package/dist/src/util/clipping/clippingPolygonHelper.d.ts +7 -0
- package/dist/src/util/clipping/clippingPolygonHelper.js +53 -0
- package/dist/src/util/clipping/clippingPolygonHelper.js.map +1 -0
- package/dist/src/util/clipping/clippingPolygonObject.d.ts +59 -0
- package/dist/src/util/clipping/clippingPolygonObject.js +158 -0
- package/dist/src/util/clipping/clippingPolygonObject.js.map +1 -0
- package/dist/src/util/clipping/clippingPolygonObjectCollection.d.ts +18 -0
- package/dist/src/util/clipping/clippingPolygonObjectCollection.js +167 -0
- package/dist/src/util/clipping/clippingPolygonObjectCollection.js.map +1 -0
- package/dist/src/util/layerCollection.d.ts +11 -1
- package/dist/src/util/layerCollection.js +67 -12
- package/dist/src/util/layerCollection.js.map +1 -1
- package/dist/src/util/mapCollection.d.ts +16 -1
- package/dist/src/util/mapCollection.js +37 -3
- package/dist/src/util/mapCollection.js.map +1 -1
- package/dist/src/util/renderScreenshot.d.ts +9 -0
- package/dist/src/util/renderScreenshot.js +162 -0
- package/dist/src/util/renderScreenshot.js.map +1 -0
- package/dist/src/util/rotation.d.ts +30 -0
- package/dist/src/util/rotation.js +145 -0
- package/dist/src/util/rotation.js.map +1 -0
- package/dist/src/util/vcsTemplate.d.ts +7 -0
- package/dist/src/util/vcsTemplate.js +248 -0
- package/dist/src/util/vcsTemplate.js.map +1 -0
- package/dist/src/vcsApp.d.ts +7 -0
- package/dist/src/vcsApp.js +29 -0
- package/dist/src/vcsApp.js.map +1 -1
- package/dist/src/vcsModule.d.ts +6 -2
- package/dist/src/vcsModule.js.map +1 -1
- package/dist/src/vectorCluster/vectorClusterCesiumContext.d.ts +18 -0
- package/dist/src/{layer/cesium/clusterContext.js → vectorCluster/vectorClusterCesiumContext.js} +28 -42
- package/dist/src/vectorCluster/vectorClusterCesiumContext.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroup.d.ts +96 -0
- package/dist/src/vectorCluster/vectorClusterGroup.js +320 -0
- package/dist/src/vectorCluster/vectorClusterGroup.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.d.ts +20 -0
- package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.js +115 -0
- package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroupCollection.d.ts +19 -0
- package/dist/src/vectorCluster/vectorClusterGroupCollection.js +37 -0
- package/dist/src/vectorCluster/vectorClusterGroupCollection.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroupImpl.d.ts +31 -0
- package/dist/src/vectorCluster/vectorClusterGroupImpl.js +76 -0
- package/dist/src/vectorCluster/vectorClusterGroupImpl.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.d.ts +17 -0
- package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.js +62 -0
- package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.d.ts +17 -0
- package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.js +62 -0
- package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterStyleItem.d.ts +110 -0
- package/dist/src/vectorCluster/vectorClusterStyleItem.js +374 -0
- package/dist/src/vectorCluster/vectorClusterStyleItem.js.map +1 -0
- package/dist/src/vectorCluster/vectorClusterSymbols.d.ts +1 -0
- package/dist/src/vectorCluster/vectorClusterSymbols.js +3 -0
- package/dist/src/vectorCluster/vectorClusterSymbols.js.map +1 -0
- package/index.ts +42 -1
- package/package.json +3 -1
- package/src/cesium/cesium.d.ts +3 -0
- package/src/featureProvider/featureProviderSymbols.ts +6 -1
- package/src/interaction/featureAtPixelInteraction.ts +109 -84
- package/src/interaction/featureProviderInteraction.ts +42 -28
- package/src/layer/cesium/sourceVectorContextSync.ts +134 -0
- package/src/layer/cesium/vcsTile/vcsDebugTile.ts +1 -1
- package/src/layer/cesium/vcsTile/vcsVectorTile.ts +1 -1
- package/src/layer/cesium/vectorCesiumImpl.ts +30 -144
- package/src/layer/cesium/vectorContext.ts +17 -1
- package/src/layer/layerSymbols.ts +1 -1
- package/src/layer/oblique/sourceObliqueSync.ts +436 -0
- package/src/layer/oblique/vectorObliqueImpl.ts +11 -397
- package/src/layer/vectorLayer.ts +35 -2
- package/src/map/baseOLMap.ts +8 -1
- package/src/map/cesiumMap.ts +36 -3
- package/src/map/vcsMap.ts +121 -47
- package/src/ol/ol.d.ts +8 -1
- package/src/ol/source/{ClusterEnhancedVectorSource.js → ClusterEnhancedVectorSource.ts} +7 -10
- package/src/ol/source/VcsCluster.ts +58 -0
- package/src/util/clipping/clippingPolygonHelper.ts +86 -0
- package/src/util/clipping/clippingPolygonObject.ts +223 -0
- package/src/util/clipping/clippingPolygonObjectCollection.ts +249 -0
- package/src/util/layerCollection.ts +90 -12
- package/src/util/mapCollection.ts +53 -2
- package/src/util/renderScreenshot.ts +193 -0
- package/src/util/rotation.ts +215 -0
- package/src/util/vcsTemplate.ts +373 -0
- package/src/vcsApp.ts +65 -0
- package/src/vcsModule.ts +6 -2
- package/src/vectorCluster/vectorClusterCesiumContext.ts +123 -0
- package/src/vectorCluster/vectorClusterGroup.ts +463 -0
- package/src/vectorCluster/vectorClusterGroupCesiumImpl.ts +176 -0
- package/src/vectorCluster/vectorClusterGroupCollection.ts +43 -0
- package/src/vectorCluster/vectorClusterGroupImpl.ts +107 -0
- package/src/vectorCluster/vectorClusterGroupObliqueImpl.ts +84 -0
- package/src/vectorCluster/vectorClusterGroupOpenlayersImpl.ts +81 -0
- package/src/vectorCluster/vectorClusterStyleItem.ts +490 -0
- package/src/vectorCluster/vectorClusterSymbols.ts +2 -0
- package/dist/src/layer/cesium/clusterContext.d.ts +0 -20
- package/dist/src/layer/cesium/clusterContext.js.map +0 -1
- package/src/layer/cesium/clusterContext.ts +0 -140
- package/src/ol/source/VcsCluster.js +0 -37
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { unByKey } from 'ol/Observable.js';
|
|
2
|
+
import Feature from 'ol/Feature.js';
|
|
3
|
+
import { Point } from 'ol/geom.js';
|
|
4
|
+
import { StyleFunction, StyleLike } from 'ol/style/Style.js';
|
|
5
|
+
import { parseInteger } from '@vcsuite/parsers';
|
|
6
|
+
import { check, maybe } from '@vcsuite/check';
|
|
7
|
+
import VcsObject, { VcsObjectOptions } from '../vcsObject.js';
|
|
8
|
+
import VectorLayer from '../layer/vectorLayer.js';
|
|
9
|
+
import ClusterEnhancedVectorSource from '../ol/source/ClusterEnhancedVectorSource.js';
|
|
10
|
+
import FeatureVisibility, {
|
|
11
|
+
synchronizeFeatureVisibility,
|
|
12
|
+
} from '../layer/featureVisibility.js';
|
|
13
|
+
import LayerState from '../layer/layerState.js';
|
|
14
|
+
import VectorClusterStyleItem, {
|
|
15
|
+
VectorClusterStyleItemOptions,
|
|
16
|
+
getDefaultClusterStyleItem,
|
|
17
|
+
} from './vectorClusterStyleItem.js';
|
|
18
|
+
import type VcsMap from '../map/vcsMap.js';
|
|
19
|
+
import GlobalHider from '../layer/globalHider.js';
|
|
20
|
+
import VectorProperties, {
|
|
21
|
+
VectorPropertiesOptions,
|
|
22
|
+
} from '../layer/vectorProperties.js';
|
|
23
|
+
import CesiumMap from '../map/cesiumMap.js';
|
|
24
|
+
import VectorClusterGroupCesiumImpl from './vectorClusterGroupCesiumImpl.js';
|
|
25
|
+
import VectorClusterGroupOpenlayersImpl from './vectorClusterGroupOpenlayersImpl.js';
|
|
26
|
+
import OpenlayersMap from '../map/openlayersMap.js';
|
|
27
|
+
import type VectorClusterGroupImpl from './vectorClusterGroupImpl.js';
|
|
28
|
+
import ObliqueMap from '../map/obliqueMap.js';
|
|
29
|
+
import VectorClusterGroupObliqueImpl from './vectorClusterGroupObliqueImpl.js';
|
|
30
|
+
|
|
31
|
+
export type VectorClusterGroupOptions = VcsObjectOptions & {
|
|
32
|
+
style?: VectorClusterStyleItemOptions | VectorClusterStyleItem;
|
|
33
|
+
highlightStyle?: VectorClusterStyleItemOptions | VectorClusterStyleItem;
|
|
34
|
+
clusterDistance?: number;
|
|
35
|
+
vectorProperties?: VectorPropertiesOptions;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type VectorClusterGroupImplementationOptions = {
|
|
39
|
+
name: string;
|
|
40
|
+
source: ClusterEnhancedVectorSource;
|
|
41
|
+
maxResolution?: number;
|
|
42
|
+
minResolution?: number;
|
|
43
|
+
globalHider?: GlobalHider;
|
|
44
|
+
featureVisibility: FeatureVisibility;
|
|
45
|
+
vectorProperties: VectorProperties;
|
|
46
|
+
style: StyleFunction;
|
|
47
|
+
clusterDistance: number;
|
|
48
|
+
getLayerByName(this: void, layerName: string): VectorLayer | undefined;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function featureIsClusterable(
|
|
52
|
+
feature: Feature | undefined,
|
|
53
|
+
vectorProperties: VectorProperties,
|
|
54
|
+
): feature is Feature<Point> {
|
|
55
|
+
if (feature) {
|
|
56
|
+
const geometry = feature?.getGeometry();
|
|
57
|
+
return (
|
|
58
|
+
!!geometry &&
|
|
59
|
+
geometry.getType() === 'Point' &&
|
|
60
|
+
vectorProperties.renderAs(feature) === 'geometry'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getStyleOrDefaultStyle(
|
|
67
|
+
styleOptions:
|
|
68
|
+
| VectorClusterStyleItemOptions
|
|
69
|
+
| VectorClusterStyleItem
|
|
70
|
+
| undefined,
|
|
71
|
+
defaultValue: VectorClusterStyleItem,
|
|
72
|
+
): VectorClusterStyleItem {
|
|
73
|
+
if (styleOptions) {
|
|
74
|
+
if (styleOptions instanceof VectorClusterStyleItem) {
|
|
75
|
+
return styleOptions;
|
|
76
|
+
} else {
|
|
77
|
+
return new VectorClusterStyleItem(styleOptions);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return defaultValue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default class VectorClusterGroup extends VcsObject {
|
|
84
|
+
static get className(): string {
|
|
85
|
+
return 'VectorClusterGroup';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static getDefaultOptions(): VectorClusterGroupOptions {
|
|
89
|
+
return {
|
|
90
|
+
type: 'VectorClusterGroup',
|
|
91
|
+
name: '',
|
|
92
|
+
style: undefined,
|
|
93
|
+
highlightStyle: undefined,
|
|
94
|
+
clusterDistance: 40,
|
|
95
|
+
vectorProperties: {
|
|
96
|
+
...VectorProperties.getDefaultOptions(),
|
|
97
|
+
eyeOffset: [0, 0, -100],
|
|
98
|
+
heightAboveGround: 60,
|
|
99
|
+
altitudeMode: 'clampToTerrain',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private _layerListeners = new Map<VectorLayer, (() => void)[]>();
|
|
105
|
+
|
|
106
|
+
private _activeSourceListeners = new Map<VectorLayer, () => void>();
|
|
107
|
+
|
|
108
|
+
private _source = new ClusterEnhancedVectorSource({});
|
|
109
|
+
|
|
110
|
+
private _featureVisibility = new FeatureVisibility();
|
|
111
|
+
|
|
112
|
+
private _style: VectorClusterStyleItem;
|
|
113
|
+
|
|
114
|
+
highlightStyle: VectorClusterStyleItem | undefined;
|
|
115
|
+
|
|
116
|
+
private _styleFunction: StyleFunction;
|
|
117
|
+
|
|
118
|
+
private _activeMaps = new Set<VcsMap>();
|
|
119
|
+
|
|
120
|
+
private _implementations = new Map<
|
|
121
|
+
VcsMap,
|
|
122
|
+
VectorClusterGroupImpl<VcsMap> | undefined
|
|
123
|
+
>();
|
|
124
|
+
|
|
125
|
+
private _globalHider: GlobalHider | undefined;
|
|
126
|
+
|
|
127
|
+
vectorProperties: VectorProperties;
|
|
128
|
+
|
|
129
|
+
clusterDistance: number;
|
|
130
|
+
|
|
131
|
+
constructor(options: VectorClusterGroupOptions) {
|
|
132
|
+
super(options);
|
|
133
|
+
const defaultOptions = VectorClusterGroup.getDefaultOptions();
|
|
134
|
+
|
|
135
|
+
this._style = getStyleOrDefaultStyle(
|
|
136
|
+
options.style,
|
|
137
|
+
getDefaultClusterStyleItem(),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (options.highlightStyle) {
|
|
141
|
+
this.highlightStyle =
|
|
142
|
+
options.highlightStyle instanceof VectorClusterStyleItem
|
|
143
|
+
? options.highlightStyle
|
|
144
|
+
: new VectorClusterStyleItem(options.highlightStyle);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.clusterDistance = parseInteger(
|
|
148
|
+
options.clusterDistance,
|
|
149
|
+
defaultOptions.clusterDistance,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
this.vectorProperties = new VectorProperties(
|
|
153
|
+
options.vectorProperties ?? defaultOptions.vectorProperties!,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
this._styleFunction = this._style.createStyleFunction((layerName) =>
|
|
157
|
+
[...this._layerListeners.keys()].find((l) => l.name === layerName),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get featureVisibility(): FeatureVisibility {
|
|
162
|
+
return this._featureVisibility;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get style(): VectorClusterStyleItem {
|
|
166
|
+
return this._style;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get globalHider(): GlobalHider | undefined {
|
|
170
|
+
return this._globalHider;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setStyle(
|
|
174
|
+
style: VectorClusterStyleItem | VectorClusterStyleItemOptions,
|
|
175
|
+
): void {
|
|
176
|
+
this._style = getStyleOrDefaultStyle(style, this._style);
|
|
177
|
+
this._styleFunction = this._style.createStyleFunction((layerName) =>
|
|
178
|
+
[...this._layerListeners.keys()].find((l) => l.name === layerName),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
this._implementations.forEach((impl) => {
|
|
182
|
+
if (impl) {
|
|
183
|
+
impl.style = this._styleFunction;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
this._source
|
|
187
|
+
.getFeatures()
|
|
188
|
+
.filter((f) => !f.getStyle())
|
|
189
|
+
.forEach((f) => f.changed());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getHighlightStyleForFeature(feature: Feature): StyleLike | void {
|
|
193
|
+
if (this.highlightStyle) {
|
|
194
|
+
return this.highlightStyle.createStyleFunction((layerName) =>
|
|
195
|
+
[...this._layerListeners.keys()].find((l) => l.name === layerName),
|
|
196
|
+
)(feature, 1);
|
|
197
|
+
}
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
addLayer(layer: VectorLayer): void {
|
|
202
|
+
check(layer, VectorLayer);
|
|
203
|
+
|
|
204
|
+
this._layerListeners.set(layer, [
|
|
205
|
+
layer.stateChanged.addEventListener((state) => {
|
|
206
|
+
if (state === LayerState.ACTIVE) {
|
|
207
|
+
this._handleActivation(layer);
|
|
208
|
+
} else if (state === LayerState.INACTIVE) {
|
|
209
|
+
this._handleDeactivation(layer);
|
|
210
|
+
}
|
|
211
|
+
}),
|
|
212
|
+
synchronizeFeatureVisibility(
|
|
213
|
+
layer.featureVisibility,
|
|
214
|
+
this._featureVisibility,
|
|
215
|
+
),
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
if (layer.active) {
|
|
219
|
+
this._handleActivation(layer);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
removeLayer(layer: VectorLayer): void {
|
|
224
|
+
this._handleDeactivation(layer);
|
|
225
|
+
this._layerListeners.get(layer)?.forEach((cb) => cb());
|
|
226
|
+
this._layerListeners.delete(layer);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
getFeatures(): Feature[] {
|
|
230
|
+
return this._source.getFeatures();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* destroys all current implementations and recreates the ones which have an active map.
|
|
235
|
+
* called for instance when the URL for a layer changes
|
|
236
|
+
*/
|
|
237
|
+
async forceRedraw(): Promise<void> {
|
|
238
|
+
const maps = [...this._implementations.keys()];
|
|
239
|
+
|
|
240
|
+
const promises = maps.map((map) => {
|
|
241
|
+
this.removedFromMap(map);
|
|
242
|
+
if (map.active) {
|
|
243
|
+
return this.mapActivated(map);
|
|
244
|
+
}
|
|
245
|
+
return Promise.resolve();
|
|
246
|
+
});
|
|
247
|
+
await Promise.all(promises);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
setGlobalHider(globalHider?: GlobalHider): void {
|
|
251
|
+
check(globalHider, maybe(GlobalHider));
|
|
252
|
+
this._globalHider = globalHider;
|
|
253
|
+
this.forceRedraw().catch((_e) => {
|
|
254
|
+
this.getLogger().error('Failed to redraw after setting global hider');
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private _handleActivation(layer: VectorLayer): void {
|
|
259
|
+
if (!this._activeSourceListeners.has(layer)) {
|
|
260
|
+
const source = layer.getSource();
|
|
261
|
+
|
|
262
|
+
if (source.getState() === 'ready') {
|
|
263
|
+
const features = source
|
|
264
|
+
.getFeatures()
|
|
265
|
+
.filter((f) => featureIsClusterable(f, layer.vectorProperties));
|
|
266
|
+
|
|
267
|
+
this._source.addFeatures(features);
|
|
268
|
+
const listeners = [
|
|
269
|
+
source.on('addfeature', ({ feature }) => {
|
|
270
|
+
if (featureIsClusterable(feature, layer.vectorProperties)) {
|
|
271
|
+
this._source.addFeature(feature);
|
|
272
|
+
}
|
|
273
|
+
}),
|
|
274
|
+
source.on('removefeature', ({ feature }) => {
|
|
275
|
+
this._source.removeFeature(feature!);
|
|
276
|
+
}),
|
|
277
|
+
];
|
|
278
|
+
this._activeSourceListeners.set(layer, () => unByKey(listeners));
|
|
279
|
+
} else {
|
|
280
|
+
source.once('change', this._handleActivation.bind(this, layer));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private _handleDeactivation(layer: VectorLayer): void {
|
|
286
|
+
if (this._activeSourceListeners.has(layer)) {
|
|
287
|
+
const source = layer.getSource();
|
|
288
|
+
source
|
|
289
|
+
.getFeatures()
|
|
290
|
+
.filter((f) => featureIsClusterable(f, layer.vectorProperties))
|
|
291
|
+
.forEach((feat) => {
|
|
292
|
+
this._source.removeFeature(feat, true);
|
|
293
|
+
});
|
|
294
|
+
this._source.changed();
|
|
295
|
+
this._activeSourceListeners.get(layer)!();
|
|
296
|
+
this._activeSourceListeners.delete(layer);
|
|
297
|
+
// previous result item & last feature clicked handling
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// eslint-disable-next-line class-methods-use-this
|
|
302
|
+
isSupported(map: VcsMap): boolean {
|
|
303
|
+
return (
|
|
304
|
+
map.className === 'OpenlayersMap' ||
|
|
305
|
+
map.className === 'CesiumMap' ||
|
|
306
|
+
map.className === 'ObliqueMap'
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
getImplementationOptions(): VectorClusterGroupImplementationOptions {
|
|
311
|
+
return {
|
|
312
|
+
clusterDistance: this.clusterDistance,
|
|
313
|
+
featureVisibility: this._featureVisibility,
|
|
314
|
+
globalHider: this._globalHider,
|
|
315
|
+
maxResolution: 0,
|
|
316
|
+
minResolution: 0,
|
|
317
|
+
name: this.name,
|
|
318
|
+
source: this._source,
|
|
319
|
+
style: this._styleFunction,
|
|
320
|
+
vectorProperties: this.vectorProperties,
|
|
321
|
+
getLayerByName: (layerName: string) =>
|
|
322
|
+
[...this._layerListeners.keys()].find((l) => l.name === layerName),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private _createImplementationForMap(
|
|
327
|
+
map: VcsMap,
|
|
328
|
+
):
|
|
329
|
+
| VectorClusterGroupCesiumImpl
|
|
330
|
+
| VectorClusterGroupOpenlayersImpl
|
|
331
|
+
| VectorClusterGroupObliqueImpl
|
|
332
|
+
| undefined {
|
|
333
|
+
if (map instanceof CesiumMap) {
|
|
334
|
+
return new VectorClusterGroupCesiumImpl(
|
|
335
|
+
map,
|
|
336
|
+
this.getImplementationOptions(),
|
|
337
|
+
);
|
|
338
|
+
} else if (map instanceof OpenlayersMap) {
|
|
339
|
+
return new VectorClusterGroupOpenlayersImpl(
|
|
340
|
+
map,
|
|
341
|
+
this.getImplementationOptions(),
|
|
342
|
+
);
|
|
343
|
+
} else if (map instanceof ObliqueMap) {
|
|
344
|
+
return new VectorClusterGroupObliqueImpl(
|
|
345
|
+
map,
|
|
346
|
+
this.getImplementationOptions(),
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* creates or returns a cached array of cluster group implementations for the given map.
|
|
354
|
+
* @param map initialized Map
|
|
355
|
+
* @returns return the specific implementation
|
|
356
|
+
*/
|
|
357
|
+
getImplementationForMap<T extends VcsMap>(
|
|
358
|
+
map: T,
|
|
359
|
+
): VectorClusterGroupImpl<T> | undefined {
|
|
360
|
+
if (!this._implementations.has(map)) {
|
|
361
|
+
this._implementations.set(map, this._createImplementationForMap(map));
|
|
362
|
+
}
|
|
363
|
+
return this._implementations.get(map) as
|
|
364
|
+
| VectorClusterGroupImpl<T>
|
|
365
|
+
| undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Returns all implementation of this vector cluster group for all maps
|
|
370
|
+
*/
|
|
371
|
+
getImplementations(): VectorClusterGroupImpl<any>[] {
|
|
372
|
+
return [...this._implementations.values()].flat().filter((i) => !!i);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* is called from the map when the map is activated and this vector cluster group belongs to the maps layer collections vector cluster group collection.
|
|
377
|
+
* Will create an implementation if it does not exists and will forward the activation call to the implementation.
|
|
378
|
+
* @param map
|
|
379
|
+
*/
|
|
380
|
+
async mapActivated(map: VcsMap): Promise<void> {
|
|
381
|
+
this._activeMaps.add(map);
|
|
382
|
+
await this._activateImplsForMap(map);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* is called from the map when the map is deactivated, and this vector cluster group belongs to the maps layer collections vector cluster group collection.
|
|
387
|
+
* will forward deactivation call to the map specific implementation
|
|
388
|
+
* @param map
|
|
389
|
+
*/
|
|
390
|
+
mapDeactivated(map: VcsMap): void {
|
|
391
|
+
this._activeMaps.delete(map);
|
|
392
|
+
const impl = this.getImplementationForMap(map);
|
|
393
|
+
impl?.deactivate();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* is called when a vector cluster group is removed from a maps layer collections vector cluster group collection or said map is destroyed.
|
|
398
|
+
* destroys the associated implementation.
|
|
399
|
+
* @param map
|
|
400
|
+
*/
|
|
401
|
+
removedFromMap(map: VcsMap): void {
|
|
402
|
+
this._activeMaps.delete(map);
|
|
403
|
+
const impl = this.getImplementationForMap(map);
|
|
404
|
+
impl?.destroy();
|
|
405
|
+
this._implementations.delete(map);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private async _activateImplsForMap(map: VcsMap): Promise<void> {
|
|
409
|
+
const impl = this.getImplementationForMap(map);
|
|
410
|
+
if (impl) {
|
|
411
|
+
try {
|
|
412
|
+
await impl.activate();
|
|
413
|
+
} catch (err) {
|
|
414
|
+
this.getLogger().error(
|
|
415
|
+
`Layer ${this.name} could not activate impl for map ${map.name}`,
|
|
416
|
+
);
|
|
417
|
+
this.getLogger().error(String(err));
|
|
418
|
+
this._implementations.set(map, undefined);
|
|
419
|
+
impl.destroy();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
toJSON(): VectorClusterGroupOptions {
|
|
425
|
+
const config: Partial<VectorClusterGroupOptions> = super.toJSON();
|
|
426
|
+
const defaultOptions = VectorClusterGroup.getDefaultOptions();
|
|
427
|
+
|
|
428
|
+
if (!this.style.equals(new VectorClusterStyleItem({}))) {
|
|
429
|
+
config.style = this.style.toJSON();
|
|
430
|
+
delete config.style.name;
|
|
431
|
+
delete config.style.type;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (this.highlightStyle) {
|
|
435
|
+
config.highlightStyle = this.highlightStyle.toJSON();
|
|
436
|
+
delete config.highlightStyle.name;
|
|
437
|
+
delete config.highlightStyle.type;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (this.clusterDistance !== defaultOptions.clusterDistance) {
|
|
441
|
+
config.clusterDistance = this.clusterDistance;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const vectorProperties = this.vectorProperties.getVcsMeta(
|
|
445
|
+
defaultOptions.vectorProperties,
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
if (Object.keys(vectorProperties).length > 0) {
|
|
449
|
+
config.vectorProperties = vectorProperties;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return config;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
destroy(): void {
|
|
456
|
+
this._featureVisibility.destroy();
|
|
457
|
+
[...this._layerListeners.values()].forEach((arr) =>
|
|
458
|
+
arr.forEach((cb) => cb()),
|
|
459
|
+
);
|
|
460
|
+
this._layerListeners.clear();
|
|
461
|
+
super.destroy();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Billboard,
|
|
3
|
+
Cartographic,
|
|
4
|
+
CustomDataSource,
|
|
5
|
+
Entity,
|
|
6
|
+
HeightReference,
|
|
7
|
+
Label,
|
|
8
|
+
Math as CesiumMath,
|
|
9
|
+
PointPrimitive,
|
|
10
|
+
VerticalOrigin,
|
|
11
|
+
} from '@vcmap-cesium/engine';
|
|
12
|
+
import Feature from 'ol/Feature.js';
|
|
13
|
+
import Point from 'ol/geom/Point.js';
|
|
14
|
+
import { getStylesArray } from '../util/featureconverter/convert.js';
|
|
15
|
+
import { getBillboardOptions } from '../util/featureconverter/pointToCesium.js';
|
|
16
|
+
import Projection from '../util/projection.js';
|
|
17
|
+
import CesiumMap from '../map/cesiumMap.js';
|
|
18
|
+
import { VectorClusterGroupImplementationOptions } from './vectorClusterGroup.js';
|
|
19
|
+
import VectorClusterCesiumContext from './vectorClusterCesiumContext.js';
|
|
20
|
+
import {
|
|
21
|
+
createSourceVectorContextSync,
|
|
22
|
+
SourceVectorContextSync,
|
|
23
|
+
} from '../layer/cesium/sourceVectorContextSync.js';
|
|
24
|
+
import VectorClusterGroupImpl from './vectorClusterGroupImpl.js';
|
|
25
|
+
import { vectorClusterGroupName } from './vectorClusterSymbols.js';
|
|
26
|
+
import type VectorLayer from '../layer/vectorLayer.js';
|
|
27
|
+
import { vcsLayerName } from '../layer/layerSymbols.js';
|
|
28
|
+
|
|
29
|
+
let scratchCartographic = new Cartographic();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Clusters for vector layers containing point features only
|
|
33
|
+
*/
|
|
34
|
+
export default class VectorClusterGroupCesiumImpl extends VectorClusterGroupImpl<CesiumMap> {
|
|
35
|
+
static get className(): string {
|
|
36
|
+
return 'VectorClusterGroupCesiumImpl';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private _rootCollection: CustomDataSource;
|
|
40
|
+
|
|
41
|
+
private _removeClusterEventListener: (() => void) | undefined;
|
|
42
|
+
|
|
43
|
+
private _context: VectorClusterCesiumContext | undefined;
|
|
44
|
+
|
|
45
|
+
private _sourceVectorContextSync: SourceVectorContextSync | undefined;
|
|
46
|
+
|
|
47
|
+
private _getLayerByName: (name: string) => VectorLayer | undefined;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
map: CesiumMap,
|
|
51
|
+
options: VectorClusterGroupImplementationOptions,
|
|
52
|
+
) {
|
|
53
|
+
super(map, options);
|
|
54
|
+
this._rootCollection = new CustomDataSource(this.name);
|
|
55
|
+
this._rootCollection.clustering.clusterLabels = true;
|
|
56
|
+
this._rootCollection.clustering.clusterPoints = false;
|
|
57
|
+
this._rootCollection.clustering.enabled = true;
|
|
58
|
+
this._rootCollection.clustering.minimumClusterSize = 2;
|
|
59
|
+
this._rootCollection.clustering.pixelRange = options.clusterDistance;
|
|
60
|
+
this._getLayerByName = options.getLayerByName;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private _onCluster(
|
|
64
|
+
entities: Entity[],
|
|
65
|
+
cluster: { billboard: Billboard; label: Label; point: PointPrimitive },
|
|
66
|
+
): void {
|
|
67
|
+
const size = entities.length;
|
|
68
|
+
if (size < 2) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const features = entities.map((e) => e.olFeature);
|
|
73
|
+
const feature = new Feature({ features });
|
|
74
|
+
feature[vectorClusterGroupName] = this.name;
|
|
75
|
+
const style = getStylesArray(this.style, feature, 0)[0];
|
|
76
|
+
|
|
77
|
+
scratchCartographic = Cartographic.fromCartesian(
|
|
78
|
+
cluster.billboard.position,
|
|
79
|
+
undefined,
|
|
80
|
+
scratchCartographic,
|
|
81
|
+
);
|
|
82
|
+
const position = Projection.wgs84ToMercator(
|
|
83
|
+
[
|
|
84
|
+
CesiumMath.toDegrees(scratchCartographic.longitude),
|
|
85
|
+
CesiumMath.toDegrees(scratchCartographic.latitude),
|
|
86
|
+
scratchCartographic.height,
|
|
87
|
+
],
|
|
88
|
+
true,
|
|
89
|
+
);
|
|
90
|
+
feature.setGeometry(new Point(position));
|
|
91
|
+
|
|
92
|
+
const bbOptions = getBillboardOptions(
|
|
93
|
+
feature,
|
|
94
|
+
style,
|
|
95
|
+
this.vectorProperties.getAltitudeMode(feature),
|
|
96
|
+
this.vectorProperties,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (bbOptions) {
|
|
100
|
+
cluster.billboard.image = bbOptions.image as string;
|
|
101
|
+
cluster.billboard.scale = bbOptions.scale as number;
|
|
102
|
+
cluster.billboard.heightReference =
|
|
103
|
+
bbOptions.heightReference as HeightReference;
|
|
104
|
+
cluster.billboard.verticalOrigin =
|
|
105
|
+
bbOptions.verticalOrigin as VerticalOrigin;
|
|
106
|
+
if (bbOptions.eyeOffset) {
|
|
107
|
+
cluster.billboard.eyeOffset = bbOptions.eyeOffset;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (bbOptions.scaleByDistance) {
|
|
111
|
+
cluster.billboard.scaleByDistance = bbOptions.scaleByDistance;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
cluster.billboard.olFeature = feature;
|
|
115
|
+
|
|
116
|
+
cluster.label.show = false;
|
|
117
|
+
cluster.billboard.show = true;
|
|
118
|
+
} else {
|
|
119
|
+
cluster.label.show = false;
|
|
120
|
+
cluster.billboard.show = false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async initialize(): Promise<void> {
|
|
125
|
+
if (!this.initialized) {
|
|
126
|
+
this._context = new VectorClusterCesiumContext(this._rootCollection);
|
|
127
|
+
this._rootCollection[vectorClusterGroupName] = this.name;
|
|
128
|
+
await this.map.addClusterDataSource(this._rootCollection);
|
|
129
|
+
this._sourceVectorContextSync = createSourceVectorContextSync(
|
|
130
|
+
this._source,
|
|
131
|
+
this._context,
|
|
132
|
+
this.map.getScene()!,
|
|
133
|
+
this.style,
|
|
134
|
+
(f) =>
|
|
135
|
+
this._getLayerByName(f[vcsLayerName]!)?.vectorProperties ??
|
|
136
|
+
this.vectorProperties,
|
|
137
|
+
);
|
|
138
|
+
this._removeClusterEventListener =
|
|
139
|
+
this._rootCollection.clustering.clusterEvent.addEventListener(
|
|
140
|
+
(entities, cluster) => {
|
|
141
|
+
this._onCluster(entities, cluster);
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
await super.initialize();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async activate(): Promise<void> {
|
|
149
|
+
if (!this.active) {
|
|
150
|
+
await super.activate();
|
|
151
|
+
if (this.active) {
|
|
152
|
+
this._sourceVectorContextSync?.activate();
|
|
153
|
+
this._rootCollection.show = true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
deactivate(): void {
|
|
159
|
+
this._rootCollection.show = false;
|
|
160
|
+
this._sourceVectorContextSync?.deactivate();
|
|
161
|
+
super.deactivate();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
destroy(): void {
|
|
165
|
+
if (this._removeClusterEventListener) {
|
|
166
|
+
this._removeClusterEventListener();
|
|
167
|
+
}
|
|
168
|
+
if (this.initialized) {
|
|
169
|
+
this._sourceVectorContextSync?.destroy();
|
|
170
|
+
this._context?.destroy();
|
|
171
|
+
this.map.removeClusterDataSource(this._rootCollection);
|
|
172
|
+
}
|
|
173
|
+
this._context = undefined;
|
|
174
|
+
super.destroy();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { check } from '@vcsuite/check';
|
|
2
|
+
import VectorClusterGroup from './vectorClusterGroup.js';
|
|
3
|
+
import Collection from '../util/collection.js';
|
|
4
|
+
import GlobalHider from '../layer/globalHider.js';
|
|
5
|
+
|
|
6
|
+
export default class VectorClusterGroupCollection extends Collection<VectorClusterGroup> {
|
|
7
|
+
/**
|
|
8
|
+
* The global hider for this collection.
|
|
9
|
+
*/
|
|
10
|
+
private _globalHider: GlobalHider;
|
|
11
|
+
|
|
12
|
+
constructor(globalHider: GlobalHider) {
|
|
13
|
+
super();
|
|
14
|
+
this._globalHider = globalHider;
|
|
15
|
+
|
|
16
|
+
this.added.addEventListener((g) => {
|
|
17
|
+
g.setGlobalHider(this._globalHider);
|
|
18
|
+
});
|
|
19
|
+
this.removed.addEventListener((g) => {
|
|
20
|
+
g.setGlobalHider();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The current global hider of these layers
|
|
26
|
+
*/
|
|
27
|
+
get globalHider(): GlobalHider {
|
|
28
|
+
return this._globalHider;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The current global hider of these layers
|
|
33
|
+
* @param globalHider
|
|
34
|
+
*/
|
|
35
|
+
set globalHider(globalHider: GlobalHider) {
|
|
36
|
+
check(globalHider, GlobalHider);
|
|
37
|
+
|
|
38
|
+
this._globalHider = globalHider;
|
|
39
|
+
this._array.forEach((vectorClusterGroup) => {
|
|
40
|
+
vectorClusterGroup.setGlobalHider(this._globalHider);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|