@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,373 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BooleanType,
|
|
3
|
+
newParsingContext,
|
|
4
|
+
StringType,
|
|
5
|
+
NoneType,
|
|
6
|
+
LiteralValue,
|
|
7
|
+
} from 'ol/expr/expression.js';
|
|
8
|
+
import { buildExpression, newEvaluationContext } from 'ol/expr/cpu.js';
|
|
9
|
+
import { is } from '@vcsuite/check';
|
|
10
|
+
|
|
11
|
+
type Block = {
|
|
12
|
+
opening: RegExpExecArray;
|
|
13
|
+
closing: RegExpExecArray;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ConditionalBlock = Block & {
|
|
17
|
+
elseStatement?: RegExpExecArray;
|
|
18
|
+
elseIfs: RegExpExecArray[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} expressionString
|
|
23
|
+
* @param {Record<string, unknown>} data
|
|
24
|
+
* @param {number} evaluationType
|
|
25
|
+
* @returns {*}
|
|
26
|
+
*/
|
|
27
|
+
function evaluateExpression(
|
|
28
|
+
expressionString: string,
|
|
29
|
+
data: Record<string, unknown>,
|
|
30
|
+
evaluationType: number,
|
|
31
|
+
): LiteralValue {
|
|
32
|
+
const parsed = expressionString.startsWith('[')
|
|
33
|
+
? (JSON.parse(expressionString) as any[])
|
|
34
|
+
: [
|
|
35
|
+
'get',
|
|
36
|
+
...expressionString
|
|
37
|
+
.replace(/\[([^\]]+)]/g, '.$1')
|
|
38
|
+
.split('.')
|
|
39
|
+
.filter((f) => f),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const compiledExpression = buildExpression(
|
|
43
|
+
parsed,
|
|
44
|
+
evaluationType,
|
|
45
|
+
newParsingContext(),
|
|
46
|
+
);
|
|
47
|
+
const evaluationContext = newEvaluationContext();
|
|
48
|
+
evaluationContext.properties = data;
|
|
49
|
+
return compiledExpression(evaluationContext);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Replaces template strings by provided attributes, e.g. {{myAttribute}}
|
|
54
|
+
*/
|
|
55
|
+
function replaceAttributes(
|
|
56
|
+
template: string,
|
|
57
|
+
data: Record<string, unknown>,
|
|
58
|
+
): string {
|
|
59
|
+
const pattern = /\{\{([^}]+)}}/g;
|
|
60
|
+
return template.replace(pattern, (_p, value) => {
|
|
61
|
+
return (
|
|
62
|
+
(evaluateExpression(
|
|
63
|
+
(value as string).trim(),
|
|
64
|
+
data,
|
|
65
|
+
StringType,
|
|
66
|
+
) as string) ?? ''
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function regexHits(regexp: RegExp, string: string): RegExpExecArray[] {
|
|
72
|
+
const hits = [];
|
|
73
|
+
let hit;
|
|
74
|
+
// eslint-disable-next-line no-cond-assign
|
|
75
|
+
while ((hit = regexp.exec(string))) {
|
|
76
|
+
hits.push(hit);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return hits;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function findTopLevelBlock(
|
|
83
|
+
openings: RegExpExecArray[],
|
|
84
|
+
closings: RegExpExecArray[],
|
|
85
|
+
accepted: (b: Block) => void,
|
|
86
|
+
rejected: (b: Block) => void,
|
|
87
|
+
): void {
|
|
88
|
+
const localOpenings = openings.slice();
|
|
89
|
+
const localClosings = closings.slice();
|
|
90
|
+
while (localOpenings.length > 0) {
|
|
91
|
+
let matchingClosing;
|
|
92
|
+
let matchingOpening;
|
|
93
|
+
while (!matchingClosing && localClosings.length > 0) {
|
|
94
|
+
const currentClosing = localClosings.shift()!;
|
|
95
|
+
const openingDistances = localOpenings.map(
|
|
96
|
+
(o) => currentClosing.index - o.index,
|
|
97
|
+
);
|
|
98
|
+
const minDistance = openingDistances.reduce((min, currentDistance) => {
|
|
99
|
+
if (currentDistance > 0 && currentDistance < min) {
|
|
100
|
+
return currentDistance;
|
|
101
|
+
}
|
|
102
|
+
return min;
|
|
103
|
+
}, Infinity);
|
|
104
|
+
const matchingOpeningIndex = openingDistances.indexOf(minDistance);
|
|
105
|
+
matchingOpening = localOpenings[matchingOpeningIndex];
|
|
106
|
+
|
|
107
|
+
if (matchingOpeningIndex === 0) {
|
|
108
|
+
matchingClosing = currentClosing;
|
|
109
|
+
} else {
|
|
110
|
+
rejected({ opening: matchingOpening, closing: currentClosing });
|
|
111
|
+
}
|
|
112
|
+
localOpenings.splice(matchingOpeningIndex, 1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (matchingOpening && matchingClosing) {
|
|
116
|
+
accepted({ opening: matchingOpening, closing: matchingClosing });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function tagWithinBlock(tag: RegExpExecArray, block: Block): boolean {
|
|
122
|
+
return tag.index > block.opening.index && tag.index < block.closing.index;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getForEachBlocks(template: string): Block[] {
|
|
126
|
+
const forEachBlocks = [] as Block[];
|
|
127
|
+
const forEachOpenings = regexHits(
|
|
128
|
+
/\s*{{#each\s\(([^.)]+)\)\sin\s([^}]+)}}\s*/g,
|
|
129
|
+
template,
|
|
130
|
+
);
|
|
131
|
+
const forEachClosings = regexHits(/\s*{{\/each}}\s*/g, template);
|
|
132
|
+
|
|
133
|
+
if (forEachClosings.length > forEachOpenings.length) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
'Template failed to render, missing opening tag for each statements',
|
|
136
|
+
);
|
|
137
|
+
} else if (forEachClosings.length < forEachOpenings.length) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Template failed to render, missing closing tag for each statements',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
findTopLevelBlock(
|
|
144
|
+
forEachOpenings,
|
|
145
|
+
forEachClosings,
|
|
146
|
+
(block) => {
|
|
147
|
+
forEachBlocks.push(block);
|
|
148
|
+
},
|
|
149
|
+
() => {},
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return forEachBlocks;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getConditionalBlocks(
|
|
156
|
+
template: string,
|
|
157
|
+
forEachBlocks: Block[],
|
|
158
|
+
): ConditionalBlock[] {
|
|
159
|
+
const conditionalBlocks = [] as ConditionalBlock[];
|
|
160
|
+
let conditionalOpenings = regexHits(/\s*{{#if\s([^}]*)}}\s*/g, template);
|
|
161
|
+
let conditionalClosings = regexHits(/\s*{{\/if}}\s*/g, template);
|
|
162
|
+
let elseIfs = regexHits(/\s*{{elseif\s([^}]*)}}\s*/g, template);
|
|
163
|
+
let elses = regexHits(/\s*{{else}}\s*/g, template);
|
|
164
|
+
|
|
165
|
+
const withinForEachBlock = (tag: RegExpExecArray): Block | undefined =>
|
|
166
|
+
forEachBlocks.find((block) => tagWithinBlock(tag, block));
|
|
167
|
+
|
|
168
|
+
// conditionals within a for each blocks are rendered with the for each block, ignore
|
|
169
|
+
conditionalOpenings = conditionalOpenings.filter(
|
|
170
|
+
(t) => !withinForEachBlock(t),
|
|
171
|
+
);
|
|
172
|
+
conditionalClosings = conditionalClosings.filter(
|
|
173
|
+
(t) => !withinForEachBlock(t),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
if (conditionalClosings.length > conditionalOpenings.length) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
'Template failed to render, missing closing tag for if statements',
|
|
179
|
+
);
|
|
180
|
+
} else if (conditionalClosings.length < conditionalOpenings.length) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
'Template failed to render, missing opening tag for if statements',
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const filterElseIfElse = (block: Block): void => {
|
|
187
|
+
elseIfs = elseIfs.filter((tag) => !tagWithinBlock(tag, block));
|
|
188
|
+
elses = elses.filter((tag) => !tagWithinBlock(tag, block));
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
findTopLevelBlock(
|
|
192
|
+
conditionalOpenings,
|
|
193
|
+
conditionalClosings,
|
|
194
|
+
(block) => {
|
|
195
|
+
const blockElseIfs = elseIfs.filter((tag) => tagWithinBlock(tag, block));
|
|
196
|
+
const elseStatement = elses.find((tag) => tagWithinBlock(tag, block));
|
|
197
|
+
if (
|
|
198
|
+
elseStatement &&
|
|
199
|
+
blockElseIfs.length > 0 &&
|
|
200
|
+
elseStatement.index < blockElseIfs.at(-1)!.index
|
|
201
|
+
) {
|
|
202
|
+
throw new Error('{{else}} must be the last entry in a block');
|
|
203
|
+
}
|
|
204
|
+
conditionalBlocks.push({
|
|
205
|
+
...block,
|
|
206
|
+
elseStatement,
|
|
207
|
+
elseIfs: blockElseIfs,
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
(block) => {
|
|
211
|
+
filterElseIfElse(block);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return conditionalBlocks;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function shouldRemoveWhiteSpace(openingTag: string): boolean {
|
|
219
|
+
return /\n\s*\{/.test(openingTag) && /}\s*\n/.test(openingTag);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* This will extract the block to render separately. This will depend on the white space handling. If the
|
|
224
|
+
* opening is placed on its own line, whitespace after the opening and before the closing blocks will be removed
|
|
225
|
+
* from the sub template, up to the first new line feed.
|
|
226
|
+
*/
|
|
227
|
+
function getSubTemplateForBlock(template: string, block: Block): string {
|
|
228
|
+
const removeWhiteSpace = shouldRemoveWhiteSpace(block.opening[0]);
|
|
229
|
+
let startIndex = block.opening.index + block.opening[0].indexOf('}') + 2;
|
|
230
|
+
let endIndex = block.closing.index + block.closing[0].indexOf('{');
|
|
231
|
+
if (removeWhiteSpace) {
|
|
232
|
+
startIndex += (/}\s*\n/.exec(block.opening[0])?.[0].length ?? 1) - 1;
|
|
233
|
+
endIndex -= (/\n\s*\{/.exec(block.closing[0])?.[0].length ?? 2) - 2;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return template.substring(startIndex, endIndex);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* This will replace a block with a previously extracted blocks rendered template.
|
|
241
|
+
* This will depend on the white space handling. If the opening is placed on its own line,
|
|
242
|
+
* whitespace before the opening and after the closing blocks will be removed up to the first new line feed,
|
|
243
|
+
* from the new template string all together.
|
|
244
|
+
*/
|
|
245
|
+
function replaceBlock(
|
|
246
|
+
template: string,
|
|
247
|
+
block: Block,
|
|
248
|
+
replacement: string,
|
|
249
|
+
): string {
|
|
250
|
+
const removeWhiteSpace = shouldRemoveWhiteSpace(block.opening[0]);
|
|
251
|
+
let startIndex = block.opening.index + block.opening[0].indexOf('{');
|
|
252
|
+
let endIndex = block.closing.index + block.closing[0].indexOf('}') + 2;
|
|
253
|
+
if (removeWhiteSpace) {
|
|
254
|
+
startIndex -= (/\n\s*\{/.exec(block.opening[0])?.[0].length ?? 2) - 2;
|
|
255
|
+
endIndex += (/}\s*\n/.exec(block.closing[0])?.[0].length ?? 1) - 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return `${template.substring(
|
|
259
|
+
0,
|
|
260
|
+
startIndex,
|
|
261
|
+
)}${replacement}${template.substring(endIndex)}`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Replaces {{#if }} blocks
|
|
266
|
+
*/
|
|
267
|
+
function expandConditionalsAndLoops(
|
|
268
|
+
template: string,
|
|
269
|
+
data: Record<string, unknown>,
|
|
270
|
+
): string {
|
|
271
|
+
let renderedTemplate = template;
|
|
272
|
+
const forEachBlocks = getForEachBlocks(template);
|
|
273
|
+
|
|
274
|
+
getConditionalBlocks(template, forEachBlocks)
|
|
275
|
+
.reverse()
|
|
276
|
+
.forEach(
|
|
277
|
+
/** @param {ConditionalBlock} block */ (block) => {
|
|
278
|
+
const partialBlocks = [block.opening];
|
|
279
|
+
if (block.elseIfs) {
|
|
280
|
+
partialBlocks.push(...block.elseIfs);
|
|
281
|
+
}
|
|
282
|
+
let trueStatementIndex = partialBlocks.findIndex((s) =>
|
|
283
|
+
evaluateExpression(s[1].trim(), data, BooleanType),
|
|
284
|
+
);
|
|
285
|
+
if (trueStatementIndex === -1 && block.elseStatement) {
|
|
286
|
+
trueStatementIndex = partialBlocks.length;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let renderedBlock = '';
|
|
290
|
+
if (trueStatementIndex > -1) {
|
|
291
|
+
if (block.elseStatement) {
|
|
292
|
+
partialBlocks.push(block.elseStatement);
|
|
293
|
+
}
|
|
294
|
+
partialBlocks.push(block.closing);
|
|
295
|
+
|
|
296
|
+
const blockTemplate = getSubTemplateForBlock(template, {
|
|
297
|
+
opening: partialBlocks[trueStatementIndex],
|
|
298
|
+
closing: partialBlocks[trueStatementIndex + 1],
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
renderedBlock = expandConditionalsAndLoops(blockTemplate, data);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
renderedTemplate = replaceBlock(renderedTemplate, block, renderedBlock);
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// only iterate over blocks not removed by conditionals
|
|
309
|
+
getForEachBlocks(renderedTemplate)
|
|
310
|
+
.reverse()
|
|
311
|
+
.forEach((block) => {
|
|
312
|
+
const obj = evaluateExpression(block.opening[2].trim(), data, NoneType);
|
|
313
|
+
let keyValuePairs;
|
|
314
|
+
if (is(obj, Object)) {
|
|
315
|
+
keyValuePairs = Object.entries(obj);
|
|
316
|
+
} else if (Array.isArray(obj)) {
|
|
317
|
+
keyValuePairs = obj.entries();
|
|
318
|
+
}
|
|
319
|
+
const renderedBlocks = [];
|
|
320
|
+
if (keyValuePairs) {
|
|
321
|
+
let index = 0;
|
|
322
|
+
const [valueName, keyName, indexName] = block.opening[1]
|
|
323
|
+
.split(',')
|
|
324
|
+
.map((e) => e.trim())
|
|
325
|
+
.slice(0, 3);
|
|
326
|
+
|
|
327
|
+
const blockTemplate = getSubTemplateForBlock(renderedTemplate, block);
|
|
328
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
329
|
+
for (const args of keyValuePairs) {
|
|
330
|
+
const forEachData = structuredClone(data);
|
|
331
|
+
forEachData[valueName] = args[1];
|
|
332
|
+
if (keyName) {
|
|
333
|
+
forEachData[keyName] = args[0];
|
|
334
|
+
}
|
|
335
|
+
if (indexName) {
|
|
336
|
+
forEachData[indexName] = index;
|
|
337
|
+
}
|
|
338
|
+
const currentBlock = expandConditionalsAndLoops(
|
|
339
|
+
blockTemplate,
|
|
340
|
+
forEachData,
|
|
341
|
+
);
|
|
342
|
+
renderedBlocks.push(replaceAttributes(currentBlock, forEachData));
|
|
343
|
+
index += 1;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
renderedTemplate = replaceBlock(
|
|
348
|
+
renderedTemplate,
|
|
349
|
+
block,
|
|
350
|
+
renderedBlocks.join(''),
|
|
351
|
+
);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return renderedTemplate;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Renders a template in these steps. See {@link documentation/vcsTemplate.md} for more information.
|
|
359
|
+
* 1. expand conditional blocks. this will remove any blocks that do not match their expressions and choose from if / elseif / else block which of them to render
|
|
360
|
+
* 2. expand iterations. this will create new templates for each iteration and re-run the rendering for those blocks
|
|
361
|
+
* 3. render attributes. this will add the attributes to all the blocks not within each blocks
|
|
362
|
+
*/
|
|
363
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
364
|
+
export function renderTemplate(
|
|
365
|
+
template: string | string[],
|
|
366
|
+
data: Record<string, unknown>,
|
|
367
|
+
): string {
|
|
368
|
+
const templateString = Array.isArray(template)
|
|
369
|
+
? template.join('\n')
|
|
370
|
+
: template;
|
|
371
|
+
const conditionalTemplate = expandConditionalsAndLoops(templateString, data);
|
|
372
|
+
return replaceAttributes(conditionalTemplate, data);
|
|
373
|
+
}
|
package/src/vcsApp.ts
CHANGED
|
@@ -52,6 +52,12 @@ import FlightInstance, {
|
|
|
52
52
|
} from './util/flight/flightInstance.js';
|
|
53
53
|
import FlightCollection from './util/flight/flightCollection.js';
|
|
54
54
|
import DisplayQuality from './util/displayQuality/displayQuality.js';
|
|
55
|
+
import VectorClusterGroup from './vectorCluster/vectorClusterGroup.js';
|
|
56
|
+
import VectorClusterGroupCollection from './vectorCluster/vectorClusterGroupCollection.js';
|
|
57
|
+
import ClippingPolygonObject, {
|
|
58
|
+
ClippingPolygonObjectOptions,
|
|
59
|
+
} from './util/clipping/clippingPolygonObject.js';
|
|
60
|
+
import ClippingPolygonObjectCollection from './util/clipping/clippingPolygonObjectCollection.js';
|
|
55
61
|
|
|
56
62
|
function getLogger(): Logger {
|
|
57
63
|
return getLoggerByName('init');
|
|
@@ -104,6 +110,11 @@ class VcsApp {
|
|
|
104
110
|
|
|
105
111
|
private _layers: OverrideCollection<Layer, LayerCollection>;
|
|
106
112
|
|
|
113
|
+
private _vectorClusterGroups: OverrideCollection<
|
|
114
|
+
VectorClusterGroup,
|
|
115
|
+
VectorClusterGroupCollection
|
|
116
|
+
>;
|
|
117
|
+
|
|
107
118
|
private _obliqueCollections: OverrideCollection<ObliqueCollection>;
|
|
108
119
|
|
|
109
120
|
private _viewpoints: OverrideCollection<Viewpoint>;
|
|
@@ -116,6 +127,8 @@ class VcsApp {
|
|
|
116
127
|
|
|
117
128
|
private _hiddenObjects: OverrideCollection<HiddenObject>;
|
|
118
129
|
|
|
130
|
+
private _clippingPolygons: OverrideCollection<ClippingPolygonObject>;
|
|
131
|
+
|
|
119
132
|
private _flights: OverrideCollection<FlightInstance, FlightCollection>;
|
|
120
133
|
|
|
121
134
|
private _categoryClassRegistry: OverrideClassRegistry<
|
|
@@ -183,6 +196,14 @@ class VcsApp {
|
|
|
183
196
|
);
|
|
184
197
|
this._layers.locale = this.locale;
|
|
185
198
|
|
|
199
|
+
this._vectorClusterGroups = makeOverrideCollection(
|
|
200
|
+
this._layers.vectorClusterGroups,
|
|
201
|
+
getDynamicModuleId,
|
|
202
|
+
undefined,
|
|
203
|
+
(config) => new VectorClusterGroup(config),
|
|
204
|
+
VectorClusterGroup,
|
|
205
|
+
);
|
|
206
|
+
|
|
186
207
|
this._obliqueCollections = makeOverrideCollection(
|
|
187
208
|
new Collection(),
|
|
188
209
|
getDynamicModuleId,
|
|
@@ -217,6 +238,14 @@ class VcsApp {
|
|
|
217
238
|
this._layers.globalHider,
|
|
218
239
|
);
|
|
219
240
|
|
|
241
|
+
this._clippingPolygons = makeOverrideCollection(
|
|
242
|
+
new ClippingPolygonObjectCollection(this),
|
|
243
|
+
getDynamicModuleId,
|
|
244
|
+
undefined,
|
|
245
|
+
(clippingPolygonOptions: ClippingPolygonObjectOptions) =>
|
|
246
|
+
new ClippingPolygonObject(clippingPolygonOptions),
|
|
247
|
+
);
|
|
248
|
+
|
|
220
249
|
this._flights = makeOverrideCollection(
|
|
221
250
|
new FlightCollection(this),
|
|
222
251
|
getDynamicModuleId,
|
|
@@ -286,6 +315,13 @@ class VcsApp {
|
|
|
286
315
|
return this._layers;
|
|
287
316
|
}
|
|
288
317
|
|
|
318
|
+
get vectorClusterGroups(): OverrideCollection<
|
|
319
|
+
VectorClusterGroup,
|
|
320
|
+
VectorClusterGroupCollection
|
|
321
|
+
> {
|
|
322
|
+
return this._vectorClusterGroups;
|
|
323
|
+
}
|
|
324
|
+
|
|
289
325
|
get obliqueCollections(): OverrideCollection<ObliqueCollection> {
|
|
290
326
|
return this._obliqueCollections;
|
|
291
327
|
}
|
|
@@ -306,6 +342,10 @@ class VcsApp {
|
|
|
306
342
|
return this._hiddenObjects;
|
|
307
343
|
}
|
|
308
344
|
|
|
345
|
+
get clippingPolygons(): OverrideCollection<ClippingPolygonObject> {
|
|
346
|
+
return this._clippingPolygons;
|
|
347
|
+
}
|
|
348
|
+
|
|
309
349
|
get flights(): OverrideCollection<FlightInstance, FlightCollection> {
|
|
310
350
|
return this._flights;
|
|
311
351
|
}
|
|
@@ -383,6 +423,10 @@ class VcsApp {
|
|
|
383
423
|
|
|
384
424
|
await this._styles.parseItems(config.styles, module._id);
|
|
385
425
|
await this._layers.parseItems(config.layers, module._id);
|
|
426
|
+
await this._vectorClusterGroups.parseItems(
|
|
427
|
+
config.vectorClusterGroups,
|
|
428
|
+
module._id,
|
|
429
|
+
);
|
|
386
430
|
// TODO add ade here
|
|
387
431
|
|
|
388
432
|
await this._obliqueCollections.parseItems(
|
|
@@ -392,6 +436,10 @@ class VcsApp {
|
|
|
392
436
|
await this._viewpoints.parseItems(config.viewpoints, module._id);
|
|
393
437
|
await this._maps.parseItems(config.maps, module._id);
|
|
394
438
|
await this._hiddenObjects.parseItems(config.hiddenObjects, module._id);
|
|
439
|
+
await this._clippingPolygons.parseItems(
|
|
440
|
+
config.clippingPolygons,
|
|
441
|
+
module._id,
|
|
442
|
+
);
|
|
395
443
|
await this._flights.parseItems(config.flights, module._id);
|
|
396
444
|
|
|
397
445
|
if (Array.isArray(config.categories)) {
|
|
@@ -418,6 +466,14 @@ class VcsApp {
|
|
|
418
466
|
}
|
|
419
467
|
});
|
|
420
468
|
|
|
469
|
+
[...this._clippingPolygons]
|
|
470
|
+
.filter((c) => c[moduleIdSymbol] === module._id)
|
|
471
|
+
.forEach((c) => {
|
|
472
|
+
if (c.activeOnStartup) {
|
|
473
|
+
c.activate();
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
421
477
|
if (config.startingObliqueCollectionName) {
|
|
422
478
|
const startingObliqueCollection = this._obliqueCollections.getByKey(
|
|
423
479
|
config.startingObliqueCollectionName,
|
|
@@ -516,6 +572,8 @@ class VcsApp {
|
|
|
516
572
|
const config = this._modules.getByKey(moduleId)!.toJSON();
|
|
517
573
|
config.maps = this._maps.serializeModule(moduleId);
|
|
518
574
|
config.layers = this._layers.serializeModule(moduleId);
|
|
575
|
+
config.vectorClusterGroups =
|
|
576
|
+
this._vectorClusterGroups.serializeModule(moduleId);
|
|
519
577
|
config.obliqueCollections =
|
|
520
578
|
this._obliqueCollections.serializeModule(moduleId);
|
|
521
579
|
config.viewpoints = this._viewpoints.serializeModule(moduleId);
|
|
@@ -523,6 +581,9 @@ class VcsApp {
|
|
|
523
581
|
config.hiddenObjects = this._hiddenObjects.serializeModule(
|
|
524
582
|
moduleId,
|
|
525
583
|
) as HiddenObject[];
|
|
584
|
+
config.clippingPolygons = this._clippingPolygons.serializeModule(
|
|
585
|
+
moduleId,
|
|
586
|
+
) as ClippingPolygonObjectOptions[];
|
|
526
587
|
config.flights = this._flights.serializeModule(moduleId);
|
|
527
588
|
config.categories = [...this._categories]
|
|
528
589
|
.map((c) => c.serializeModule(moduleId))
|
|
@@ -562,7 +623,9 @@ class VcsApp {
|
|
|
562
623
|
this._styles.removeModule(moduleId),
|
|
563
624
|
this._obliqueCollections.removeModule(moduleId),
|
|
564
625
|
this._hiddenObjects.removeModule(moduleId),
|
|
626
|
+
this._clippingPolygons.removeModule(moduleId),
|
|
565
627
|
this._flights.removeModule(moduleId),
|
|
628
|
+
this._vectorClusterGroups.removeModule(moduleId),
|
|
566
629
|
]);
|
|
567
630
|
}
|
|
568
631
|
|
|
@@ -603,8 +666,10 @@ class VcsApp {
|
|
|
603
666
|
destroyCollection(this._layers);
|
|
604
667
|
destroyCollection(this._obliqueCollections);
|
|
605
668
|
destroyCollection(this._viewpoints);
|
|
669
|
+
destroyCollection(this._flights);
|
|
606
670
|
destroyCollection(this._styles);
|
|
607
671
|
destroyCollection(this._categories);
|
|
672
|
+
destroyCollection(this._clippingPolygons);
|
|
608
673
|
this._modules.destroy();
|
|
609
674
|
this._hiddenObjects.destroy();
|
|
610
675
|
this._mapClassRegistry.destroy();
|
package/src/vcsModule.ts
CHANGED
|
@@ -7,8 +7,10 @@ import type { ViewpointOptions } from './util/viewpoint.js';
|
|
|
7
7
|
import type { ObliqueCollectionOptions } from './oblique/obliqueCollection.js';
|
|
8
8
|
import type VcsApp from './vcsApp.js';
|
|
9
9
|
import { moduleIdSymbol } from './moduleIdSymbol.js';
|
|
10
|
-
import { HiddenObject } from './util/hiddenObjects.js';
|
|
11
|
-
import { FlightInstanceOptions } from './util/flight/flightInstance.js';
|
|
10
|
+
import type { HiddenObject } from './util/hiddenObjects.js';
|
|
11
|
+
import type { FlightInstanceOptions } from './util/flight/flightInstance.js';
|
|
12
|
+
import type { VectorClusterGroupOptions } from './vectorCluster/vectorClusterGroup.js';
|
|
13
|
+
import type { ClippingPolygonObjectOptions } from './util/clipping/clippingPolygonObject.js';
|
|
12
14
|
|
|
13
15
|
export type VcsModuleConfig = {
|
|
14
16
|
_id?: string;
|
|
@@ -16,6 +18,7 @@ export type VcsModuleConfig = {
|
|
|
16
18
|
description?: string | null;
|
|
17
19
|
properties?: Record<string, unknown>;
|
|
18
20
|
layers?: LayerOptions[];
|
|
21
|
+
vectorClusterGroups?: VectorClusterGroupOptions[];
|
|
19
22
|
maps?: VcsMapOptions[];
|
|
20
23
|
styles?: StyleItemOptions[];
|
|
21
24
|
viewpoints?: ViewpointOptions[];
|
|
@@ -26,6 +29,7 @@ export type VcsModuleConfig = {
|
|
|
26
29
|
obliqueCollections?: ObliqueCollectionOptions[];
|
|
27
30
|
categories?: { name: string; items: object[] }[];
|
|
28
31
|
hiddenObjects?: HiddenObject[];
|
|
32
|
+
clippingPolygons?: ClippingPolygonObjectOptions[];
|
|
29
33
|
flights?: FlightInstanceOptions[];
|
|
30
34
|
};
|
|
31
35
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CustomDataSource,
|
|
3
|
+
EntityCollection,
|
|
4
|
+
Entity,
|
|
5
|
+
Scene,
|
|
6
|
+
} from '@vcmap-cesium/engine';
|
|
7
|
+
import { StyleLike } from 'ol/style/Style.js';
|
|
8
|
+
import type { Feature } from 'ol/index.js';
|
|
9
|
+
import {
|
|
10
|
+
CesiumVectorContext,
|
|
11
|
+
setReferenceForPicking,
|
|
12
|
+
} from '../layer/cesium/vectorContext.js';
|
|
13
|
+
import VectorProperties from '../layer/vectorProperties.js';
|
|
14
|
+
import convert, { ConvertedItem } from '../util/featureconverter/convert.js';
|
|
15
|
+
|
|
16
|
+
class VectorClusterCesiumContext implements CesiumVectorContext {
|
|
17
|
+
entities: EntityCollection;
|
|
18
|
+
|
|
19
|
+
private _featureItems = new Map<Feature, () => void>();
|
|
20
|
+
|
|
21
|
+
private _convertingFeatures: Map<Feature, () => void> = new Map();
|
|
22
|
+
|
|
23
|
+
constructor(dataSource: CustomDataSource) {
|
|
24
|
+
this.entities = dataSource.entities;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private _addConvertedItems(
|
|
28
|
+
feature: Feature,
|
|
29
|
+
allowPicking: boolean,
|
|
30
|
+
items: ConvertedItem[],
|
|
31
|
+
): void {
|
|
32
|
+
let entityOptions: Entity.ConstructorOptions | undefined;
|
|
33
|
+
items.forEach((item) => {
|
|
34
|
+
if (item.type === 'billboard') {
|
|
35
|
+
entityOptions = entityOptions ?? {};
|
|
36
|
+
entityOptions.billboard = {
|
|
37
|
+
...item.item,
|
|
38
|
+
};
|
|
39
|
+
entityOptions.position = entityOptions.position ?? item.item.position;
|
|
40
|
+
} else if (item.type === 'label') {
|
|
41
|
+
entityOptions = entityOptions ?? {};
|
|
42
|
+
entityOptions.label = item.item;
|
|
43
|
+
entityOptions.position = entityOptions.position ?? item.item.position;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (entityOptions) {
|
|
48
|
+
const instance = this.entities.add(entityOptions);
|
|
49
|
+
if (instance) {
|
|
50
|
+
if (allowPicking) {
|
|
51
|
+
setReferenceForPicking(feature, instance);
|
|
52
|
+
}
|
|
53
|
+
this._featureItems.set(feature, (): void => {
|
|
54
|
+
this.entities.remove(instance);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async addFeature(
|
|
61
|
+
feature: Feature,
|
|
62
|
+
style: StyleLike,
|
|
63
|
+
vectorProperties: VectorProperties,
|
|
64
|
+
scene: Scene,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
this._convertingFeatures.get(feature)?.();
|
|
67
|
+
let deleted = false;
|
|
68
|
+
this._convertingFeatures.set(feature, () => {
|
|
69
|
+
deleted = true;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const convertedItems = await convert(
|
|
73
|
+
feature,
|
|
74
|
+
style,
|
|
75
|
+
vectorProperties,
|
|
76
|
+
scene,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
this._featureItems.get(feature)?.();
|
|
80
|
+
|
|
81
|
+
if (deleted) {
|
|
82
|
+
convertedItems.forEach((item) => {
|
|
83
|
+
if (item.type === 'primitive') {
|
|
84
|
+
item.item.destroy();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
this._addConvertedItems(
|
|
89
|
+
feature,
|
|
90
|
+
vectorProperties.getAllowPicking(feature),
|
|
91
|
+
convertedItems,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
hasFeature(feature: Feature): boolean {
|
|
97
|
+
return (
|
|
98
|
+
this._featureItems.has(feature) || this._convertingFeatures.has(feature)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
removeFeature(feature: Feature): void {
|
|
103
|
+
this._convertingFeatures.get(feature)?.();
|
|
104
|
+
this._convertingFeatures.delete(feature);
|
|
105
|
+
this._featureItems.get(feature)?.();
|
|
106
|
+
this._featureItems.delete(feature);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
clear(): void {
|
|
110
|
+
this.entities.removeAll();
|
|
111
|
+
this._featureItems.clear();
|
|
112
|
+
this._convertingFeatures.forEach((destroy) => {
|
|
113
|
+
destroy();
|
|
114
|
+
});
|
|
115
|
+
this._convertingFeatures.clear();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
destroy(): void {
|
|
119
|
+
this.clear();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default VectorClusterCesiumContext;
|