@masterportal/masterportalapi 2.62.0 → 2.64.0
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/CHANGELOG.md +47 -0
- package/README.md +8 -0
- package/bitbucket-pipelines.yml +9 -9
- package/example/config/services.json +4 -4
- package/jest.setup.js +4 -0
- package/package.json +17 -17
- package/publiccode.yml +2 -2
- package/src/index.js +5 -1
- package/src/lib/attributeMapper.js +1 -1
- package/src/maps/interactions/drawInteraction.js +172 -15
- package/src/maps/interactions/modifyInteraction.js +15 -2
- package/src/shared/exportItem.js +216 -0
- package/src/shared/kmlExport.js +315 -0
- package/src/shared/utils.js +60 -0
- package/src/vectorStyle/createStyle.js +37 -5
- package/src/vectorStyle/styles/style.js +4 -2
- package/test/lib/attributeMapper.test.js +10 -0
- package/test/maps/interactions/drawInteraction.test.js +159 -3
- package/test/maps/interactions/modifyInteraction.test.js +28 -0
- package/test/shared/exportItem.test.js +265 -0
- package/test/shared/kmlExport.test.js +257 -0
- package/test/shared/utils.test.js +35 -0
- package/test/vectorStyle/styles/style.test.js +70 -0
|
@@ -106,9 +106,12 @@ class StyleClass {
|
|
|
106
106
|
else if (rule?.conditions) {
|
|
107
107
|
let label = "";
|
|
108
108
|
|
|
109
|
-
if (rule.conditions?.properties) {
|
|
109
|
+
if (rule.conditions?.properties && !Array.isArray(rule.conditions.properties)) {
|
|
110
110
|
label = Object.values(rule.conditions.properties).join(", ");
|
|
111
111
|
}
|
|
112
|
+
if (Array.isArray(rule.conditions?.properties)) {
|
|
113
|
+
label = rule.conditions.properties.map(prop => prop.value).join(", ");
|
|
114
|
+
}
|
|
112
115
|
|
|
113
116
|
if (rule.conditions?.sequence
|
|
114
117
|
&& Array.isArray(rule.conditions.sequence)
|
|
@@ -117,7 +120,6 @@ class StyleClass {
|
|
|
117
120
|
&& rule.conditions.sequence[1] >= rule.conditions.sequence[0]) {
|
|
118
121
|
label = label + " (" + rule.conditions.sequence.join("-") + ")";
|
|
119
122
|
}
|
|
120
|
-
|
|
121
123
|
return label;
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -54,6 +54,16 @@ describe("src/utils/attributeMapper.js", () => {
|
|
|
54
54
|
result: 123
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
it("should map object with 0 as valid numeric value", () => {
|
|
58
|
+
const mappingObj = {
|
|
59
|
+
random_int: "count"
|
|
60
|
+
};
|
|
61
|
+
const propsWithZero = Object.assign({}, props, {random_int: 0});
|
|
62
|
+
|
|
63
|
+
expect(mapAttributes(propsWithZero, mappingObj)).toEqual({
|
|
64
|
+
count: 0
|
|
65
|
+
});
|
|
66
|
+
});
|
|
57
67
|
it("should map object with multiple attributes", () => {
|
|
58
68
|
const mappingObj = {
|
|
59
69
|
random_text: "text",
|
|
@@ -60,6 +60,16 @@ describe("src/maps/interactions/drawInteraction.js", () => {
|
|
|
60
60
|
|
|
61
61
|
expect(mapUnitRadius).toEqual(0.0008983152841195215);
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
it("should return the radius unchanged for unknown map units", () => {
|
|
65
|
+
const radius = 100,
|
|
66
|
+
mapProjection = {
|
|
67
|
+
getUnits: () => "ft"
|
|
68
|
+
},
|
|
69
|
+
mapUnitRadius = drawInteractions.transformMapUnitRadius(radius, mapProjection);
|
|
70
|
+
|
|
71
|
+
expect(mapUnitRadius).toEqual(100);
|
|
72
|
+
});
|
|
63
73
|
});
|
|
64
74
|
|
|
65
75
|
describe("drawInnerCircle", () => {
|
|
@@ -87,7 +97,10 @@ describe("src/maps/interactions/drawInteraction.js", () => {
|
|
|
87
97
|
fillTransparency: 0,
|
|
88
98
|
strokeColor: [0, 0, 0],
|
|
89
99
|
strokeWidth: 1,
|
|
90
|
-
polygonDash: [4]
|
|
100
|
+
polygonDash: [4],
|
|
101
|
+
text: "Text",
|
|
102
|
+
textFont: "Arial",
|
|
103
|
+
textFontSize: 10
|
|
91
104
|
};
|
|
92
105
|
|
|
93
106
|
drawInteractions.setStyleObject(currentLayout);
|
|
@@ -111,7 +124,8 @@ describe("src/maps/interactions/drawInteraction.js", () => {
|
|
|
111
124
|
lineStrokeColor: [
|
|
112
125
|
0,
|
|
113
126
|
0,
|
|
114
|
-
0
|
|
127
|
+
0,
|
|
128
|
+
1
|
|
115
129
|
],
|
|
116
130
|
lineStrokeWidth: 1,
|
|
117
131
|
polygonFillColor: [
|
|
@@ -127,7 +141,10 @@ describe("src/maps/interactions/drawInteraction.js", () => {
|
|
|
127
141
|
],
|
|
128
142
|
polygonStrokeDash: [4],
|
|
129
143
|
polygonStrokeDashOffset: undefined,
|
|
130
|
-
polygonStrokeWidth: 1
|
|
144
|
+
polygonStrokeWidth: 1,
|
|
145
|
+
text: "Text",
|
|
146
|
+
textFont: "Arial",
|
|
147
|
+
textFontSize: 10
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
150
|
]
|
|
@@ -155,6 +172,145 @@ describe("src/maps/interactions/drawInteraction.js", () => {
|
|
|
155
172
|
expect(drawInteraction).toBeNull();
|
|
156
173
|
expect(drawInteraction).not.toBeInstanceOf(Draw);
|
|
157
174
|
});
|
|
175
|
+
|
|
176
|
+
it("should create a draw interaction for writeText", () => {
|
|
177
|
+
const source = new VectorSource(),
|
|
178
|
+
drawInteraction = drawInteractions.createDrawInteraction("writeText", source);
|
|
179
|
+
|
|
180
|
+
expect(drawInteraction).not.toBeNull();
|
|
181
|
+
expect(drawInteraction).toBeInstanceOf(Draw);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should create a draw interaction for each supported drawType", () => {
|
|
185
|
+
const source = new VectorSource(),
|
|
186
|
+
supportedTypes = ["box", "circle", "doubleCircle", "line", "pen", "point", "polygon", "writeText", "square"];
|
|
187
|
+
|
|
188
|
+
supportedTypes.forEach(type => {
|
|
189
|
+
const interaction = drawInteractions.createDrawInteraction(type, source);
|
|
190
|
+
|
|
191
|
+
expect(interaction).not.toBeNull();
|
|
192
|
+
expect(interaction).toBeInstanceOf(Draw);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should create a draw interaction for an image symbol", () => {
|
|
197
|
+
const source = new VectorSource(),
|
|
198
|
+
iconList = [{id: "myIcon", type: "image", value: "/icons/test.svg", scale: 1}],
|
|
199
|
+
drawInteraction = drawInteractions.createDrawInteraction("myIcon", source, iconList);
|
|
200
|
+
|
|
201
|
+
expect(drawInteraction).not.toBeNull();
|
|
202
|
+
expect(drawInteraction).toBeInstanceOf(Draw);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should create a draw interaction for a simple_point symbol", () => {
|
|
206
|
+
const source = new VectorSource(),
|
|
207
|
+
iconList = [{id: "myPoint", type: "simple_point", value: "simple_point"}];
|
|
208
|
+
|
|
209
|
+
drawInteractions.setStyleObject({
|
|
210
|
+
fillColor: [255, 0, 0],
|
|
211
|
+
fillTransparency: 0,
|
|
212
|
+
strokeColor: [0, 0, 0],
|
|
213
|
+
strokeWidth: 2
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const drawInteraction = drawInteractions.createDrawInteraction("myPoint", source, iconList);
|
|
217
|
+
|
|
218
|
+
expect(drawInteraction).not.toBeNull();
|
|
219
|
+
expect(drawInteraction).toBeInstanceOf(Draw);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("drawOuterCircle", () => {
|
|
224
|
+
it("should set the outer radius on the feature and add inner circle to source", () => {
|
|
225
|
+
const source = new VectorSource(),
|
|
226
|
+
feature = new Feature({geometry: new Circle([0, 0], 10)}),
|
|
227
|
+
mapProjection = {getUnits: () => "m"},
|
|
228
|
+
options = {innerRadius: 100, outerRadius: 500, unit: "m"};
|
|
229
|
+
|
|
230
|
+
drawInteractions.setStyleObject({
|
|
231
|
+
fillColor: [55, 126, 184],
|
|
232
|
+
fillTransparency: 0,
|
|
233
|
+
strokeColor: [0, 0, 0],
|
|
234
|
+
strokeWidth: 1
|
|
235
|
+
});
|
|
236
|
+
drawInteractions.setStyleObject({
|
|
237
|
+
fillColor: [0, 0, 0],
|
|
238
|
+
fillTransparency: 100,
|
|
239
|
+
strokeColor: [200, 0, 0],
|
|
240
|
+
strokeWidth: 1
|
|
241
|
+
}, true);
|
|
242
|
+
|
|
243
|
+
drawInteractions.drawOuterCircle(feature, mapProjection, source, options);
|
|
244
|
+
|
|
245
|
+
expect(feature.getGeometry().getRadius()).toEqual(500);
|
|
246
|
+
expect(source.getFeatures().length).toEqual(1);
|
|
247
|
+
expect(source.getFeatures()[0].getGeometry().getRadius()).toEqual(100);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("drawCircle", () => {
|
|
252
|
+
it("should register drawstart listener for non-interactive circle", () => {
|
|
253
|
+
const source = new VectorSource(),
|
|
254
|
+
drawInteraction = drawInteractions.createDrawInteraction("circle", source),
|
|
255
|
+
mapProjection = {getUnits: () => "m"},
|
|
256
|
+
options = {innerRadius: 200, interactive: false, outerRadius: 0, unit: "m"};
|
|
257
|
+
|
|
258
|
+
drawInteractions.drawCircle(drawInteraction, "circle", mapProjection, source, options);
|
|
259
|
+
|
|
260
|
+
expect(drawInteraction.getListeners("drawstart").length).toBeGreaterThan(0);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should not register additional listener if innerRadius is 0", () => {
|
|
264
|
+
const source = new VectorSource(),
|
|
265
|
+
drawInteraction = drawInteractions.createDrawInteraction("circle", source),
|
|
266
|
+
mapProjection = {getUnits: () => "m"},
|
|
267
|
+
options = {innerRadius: 0, interactive: false, outerRadius: 0, unit: "m"},
|
|
268
|
+
listenersBefore = drawInteraction.getListeners("drawstart")?.length || 0;
|
|
269
|
+
|
|
270
|
+
drawInteractions.drawCircle(drawInteraction, "circle", mapProjection, source, options);
|
|
271
|
+
|
|
272
|
+
const listenersAfter = drawInteraction.getListeners("drawstart")?.length || 0;
|
|
273
|
+
|
|
274
|
+
expect(listenersAfter).toEqual(listenersBefore);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe("setStyleObject", () => {
|
|
279
|
+
it("should handle fillTransparency correctly", () => {
|
|
280
|
+
drawInteractions.setStyleObject({
|
|
281
|
+
fillColor: [255, 0, 0],
|
|
282
|
+
fillTransparency: 50,
|
|
283
|
+
strokeColor: [0, 0, 0],
|
|
284
|
+
strokeWidth: 2
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
expect(styleObject.rules[0].style.polygonFillColor).toEqual([255, 0, 0, 0.5]);
|
|
288
|
+
expect(styleObject.rules[0].style.lineStrokeColor).toEqual([0, 0, 0, 0.5]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("should use default text values when not provided", () => {
|
|
292
|
+
drawInteractions.setStyleObject({
|
|
293
|
+
fillColor: [0, 0, 0],
|
|
294
|
+
fillTransparency: 0,
|
|
295
|
+
strokeColor: [0, 0, 0],
|
|
296
|
+
strokeWidth: 1
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
expect(styleObject.rules[0].style.text).toEqual("Text");
|
|
300
|
+
expect(styleObject.rules[0].style.textFont).toEqual("Arial");
|
|
301
|
+
expect(styleObject.rules[0].style.textFontSize).toEqual(10);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("should set outer circle style when outerCircle is true", () => {
|
|
305
|
+
drawInteractions.setStyleObject({
|
|
306
|
+
fillColor: [200, 0, 0],
|
|
307
|
+
fillTransparency: 100,
|
|
308
|
+
strokeColor: [200, 0, 0],
|
|
309
|
+
strokeWidth: 2
|
|
310
|
+
}, true);
|
|
311
|
+
|
|
312
|
+
expect(styleObject.rules[0].style.circleStrokeWidth).toEqual(1);
|
|
313
|
+
});
|
|
158
314
|
});
|
|
159
315
|
|
|
160
316
|
});
|
|
@@ -13,6 +13,34 @@ describe("src/maps/interactions/modifyInteraction.js", () => {
|
|
|
13
13
|
expect(modifyInteraction).not.toBeNull();
|
|
14
14
|
expect(modifyInteraction).toBeInstanceOf(Modify);
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
it("should register optional modify callbacks", () => {
|
|
18
|
+
const source = new VectorSource(),
|
|
19
|
+
onModifyStart = jest.fn(),
|
|
20
|
+
onModifyEnd = jest.fn(),
|
|
21
|
+
modifyInteraction = modifyInteractions.createModifyInteraction(source, {
|
|
22
|
+
onModifyStart,
|
|
23
|
+
onModifyEnd
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
modifyInteraction.dispatchEvent("modifystart");
|
|
27
|
+
modifyInteraction.dispatchEvent("modifyend");
|
|
28
|
+
|
|
29
|
+
expect(onModifyStart).toHaveBeenCalledTimes(1);
|
|
30
|
+
expect(onModifyEnd).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should work with only onModifyEnd provided", () => {
|
|
34
|
+
const source = new VectorSource(),
|
|
35
|
+
onModifyEnd = jest.fn(),
|
|
36
|
+
modifyInteraction = modifyInteractions.createModifyInteraction(source, {
|
|
37
|
+
onModifyEnd
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
modifyInteraction.dispatchEvent("modifyend");
|
|
41
|
+
|
|
42
|
+
expect(onModifyEnd).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
16
44
|
});
|
|
17
45
|
|
|
18
46
|
});
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import Feature from "ol/Feature.js";
|
|
2
|
+
import {Point, LineString, Polygon, Circle} from "ol/geom.js";
|
|
3
|
+
import {GeoJSON, GPX} from "ol/format.js";
|
|
4
|
+
import proj4 from "proj4";
|
|
5
|
+
import {register} from "ol/proj/proj4.js";
|
|
6
|
+
import exportItem, {setCsvAttributes} from "../../src/shared/exportItem";
|
|
7
|
+
|
|
8
|
+
// Register projection used in tests
|
|
9
|
+
proj4.defs("EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs");
|
|
10
|
+
register(proj4);
|
|
11
|
+
|
|
12
|
+
describe("src/shared/exportItem.js", () => {
|
|
13
|
+
describe("transform", () => {
|
|
14
|
+
it("transforms line coordinates from source projection to EPSG:4326", () => {
|
|
15
|
+
const coords = [[565874, 5934140], [566000, 5934200]],
|
|
16
|
+
result = exportItem.transform("EPSG:25832", coords, false);
|
|
17
|
+
|
|
18
|
+
expect(result).toHaveLength(2);
|
|
19
|
+
result.forEach(point => {
|
|
20
|
+
expect(point).toHaveLength(2);
|
|
21
|
+
expect(typeof point[0]).toBe("number");
|
|
22
|
+
expect(typeof point[1]).toBe("number");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("transforms polygon coordinates from source projection to EPSG:4326", () => {
|
|
27
|
+
const coords = [[[565874, 5934140], [566000, 5934200], [566100, 5934100], [565874, 5934140]]],
|
|
28
|
+
result = exportItem.transform("EPSG:25832", coords, true);
|
|
29
|
+
|
|
30
|
+
expect(Array.isArray(result)).toBe(true);
|
|
31
|
+
expect(result).toHaveLength(1);
|
|
32
|
+
expect(result[0]).toHaveLength(4);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("handles single-ring polygon correctly", () => {
|
|
36
|
+
const coords = [[[565874, 5934140], [566000, 5934200], [566100, 5934100], [565874, 5934140]]],
|
|
37
|
+
result = exportItem.transform("EPSG:25832", coords, true);
|
|
38
|
+
|
|
39
|
+
expect(Array.isArray(result)).toBe(true);
|
|
40
|
+
expect(result).toHaveLength(1);
|
|
41
|
+
expect(result[0]).toHaveLength(4);
|
|
42
|
+
result[0].forEach(point => {
|
|
43
|
+
expect(point).toHaveLength(2);
|
|
44
|
+
expect(typeof point[0]).toBe("number");
|
|
45
|
+
expect(typeof point[1]).toBe("number");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("returns wrapped array for polygon, flat array for line", () => {
|
|
50
|
+
const lineCoords = [[565874, 5934140], [566000, 5934200]],
|
|
51
|
+
polyCoords = [[[565874, 5934140], [566000, 5934200], [566100, 5934100], [565874, 5934140]]],
|
|
52
|
+
lineResult = exportItem.transform("EPSG:25832", lineCoords, false),
|
|
53
|
+
polyResult = exportItem.transform("EPSG:25832", polyCoords, true);
|
|
54
|
+
|
|
55
|
+
// Line result is a flat array of points
|
|
56
|
+
expect(Array.isArray(lineResult[0])).toBe(true);
|
|
57
|
+
expect(typeof lineResult[0][0]).toBe("number");
|
|
58
|
+
|
|
59
|
+
// Polygon result is wrapped in an extra array
|
|
60
|
+
expect(Array.isArray(polyResult[0])).toBe(true);
|
|
61
|
+
expect(Array.isArray(polyResult[0][0])).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("transformCoordinates", () => {
|
|
66
|
+
it("transforms a Point geometry", () => {
|
|
67
|
+
const geometry = new Point([565874, 5934140]),
|
|
68
|
+
result = exportItem.transformCoordinates(geometry, "EPSG:25832");
|
|
69
|
+
|
|
70
|
+
expect(result).toHaveLength(2);
|
|
71
|
+
expect(typeof result[0]).toBe("number");
|
|
72
|
+
expect(typeof result[1]).toBe("number");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("transforms a LineString geometry", () => {
|
|
76
|
+
const geometry = new LineString([[565874, 5934140], [566000, 5934200]]),
|
|
77
|
+
result = exportItem.transformCoordinates(geometry, "EPSG:25832");
|
|
78
|
+
|
|
79
|
+
expect(result).toHaveLength(2);
|
|
80
|
+
result.forEach(point => {
|
|
81
|
+
expect(point).toHaveLength(2);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("transforms a Polygon geometry", () => {
|
|
86
|
+
const geometry = new Polygon([[[565874, 5934140], [566000, 5934200], [566100, 5934100], [565874, 5934140]]]),
|
|
87
|
+
result = exportItem.transformCoordinates(geometry, "EPSG:25832");
|
|
88
|
+
|
|
89
|
+
expect(Array.isArray(result)).toBe(true);
|
|
90
|
+
expect(result).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("returns an empty array for unsupported geometry types", () => {
|
|
94
|
+
const geometry = {
|
|
95
|
+
getCoordinates: () => [[[[[0, 0]]]]],
|
|
96
|
+
getType: () => "MultiPolygon"
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
expect(exportItem.transformCoordinates(geometry, "EPSG:25832")).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("convertFeatures", () => {
|
|
104
|
+
it("converts features to GeoJSON string", async () => {
|
|
105
|
+
const feature = new Feature({geometry: new Point([565874, 5934140])}),
|
|
106
|
+
format = new GeoJSON(),
|
|
107
|
+
result = await exportItem.convertFeatures([feature], format, "EPSG:25832");
|
|
108
|
+
|
|
109
|
+
expect(typeof result).toBe("string");
|
|
110
|
+
const parsed = JSON.parse(result);
|
|
111
|
+
|
|
112
|
+
expect(parsed.type).toBe("FeatureCollection");
|
|
113
|
+
expect(parsed.features).toHaveLength(1);
|
|
114
|
+
expect(parsed.features[0].geometry.type).toBe("Point");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("converts multiple features", async () => {
|
|
118
|
+
const features = [
|
|
119
|
+
new Feature({geometry: new Point([565874, 5934140])}),
|
|
120
|
+
new Feature({geometry: new LineString([[565874, 5934140], [566000, 5934200]])})
|
|
121
|
+
],
|
|
122
|
+
format = new GeoJSON(),
|
|
123
|
+
result = await exportItem.convertFeatures(features, format, "EPSG:25832"),
|
|
124
|
+
parsed = JSON.parse(result);
|
|
125
|
+
|
|
126
|
+
expect(parsed.features).toHaveLength(2);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("removes trailing zero from 3D point coordinates", async () => {
|
|
130
|
+
const feature = new Feature({geometry: new Point([565874, 5934140, 0])}),
|
|
131
|
+
format = new GeoJSON(),
|
|
132
|
+
result = await exportItem.convertFeatures([feature], format, "EPSG:25832"),
|
|
133
|
+
parsed = JSON.parse(result);
|
|
134
|
+
|
|
135
|
+
expect(parsed.features[0].geometry.coordinates).toHaveLength(2);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("returns an empty FeatureCollection string for empty array", async () => {
|
|
139
|
+
const format = new GeoJSON(),
|
|
140
|
+
result = await exportItem.convertFeatures([], format, "EPSG:25832"),
|
|
141
|
+
parsed = JSON.parse(result);
|
|
142
|
+
|
|
143
|
+
expect(parsed.features).toHaveLength(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("preserves drawState in GPX extensions", async () => {
|
|
147
|
+
const drawState = {
|
|
148
|
+
drawType: {id: "drawLine", geometry: "LineString"},
|
|
149
|
+
strokeWidth: 4,
|
|
150
|
+
colorContour: [12, 34, 56, 1]
|
|
151
|
+
},
|
|
152
|
+
feature = new Feature({
|
|
153
|
+
geometry: new LineString([[565874, 5934140], [566000, 5934200]]),
|
|
154
|
+
masterportal_attributes: {drawState}
|
|
155
|
+
}),
|
|
156
|
+
result = await exportItem.convertFeatures([feature], new GPX(), "EPSG:25832");
|
|
157
|
+
|
|
158
|
+
expect(result).toContain("masterportal:drawState");
|
|
159
|
+
expect(result).toContain(JSON.stringify(drawState));
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("prepareFeatures", () => {
|
|
164
|
+
it("clones features without modifying the originals", () => {
|
|
165
|
+
const original = new Feature({geometry: new Point([565874, 5934140])}),
|
|
166
|
+
result = exportItem.prepareFeatures([original], "EPSG:25832");
|
|
167
|
+
|
|
168
|
+
expect(result).toHaveLength(1);
|
|
169
|
+
expect(result[0]).not.toBe(original);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("converts Circle geometries to Polygons and sets masterportal_attributes", () => {
|
|
173
|
+
const circle = new Feature({geometry: new Circle([565874, 5934140], 100)}),
|
|
174
|
+
result = exportItem.prepareFeatures([circle], "EPSG:25832");
|
|
175
|
+
|
|
176
|
+
expect(result[0].getGeometry().getType()).toBe("Polygon");
|
|
177
|
+
expect(result[0].get("masterportal_attributes")).toBeDefined();
|
|
178
|
+
expect(result[0].get("masterportal_attributes").isGeoCircle).toBe(true);
|
|
179
|
+
expect(result[0].get("masterportal_attributes").geoCircleCenter).toBeDefined();
|
|
180
|
+
expect(typeof result[0].get("masterportal_attributes").geoCircleRadius).toBe("number");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("does not modify non-Circle features", () => {
|
|
184
|
+
const feature = new Feature({geometry: new Point([565874, 5934140])}),
|
|
185
|
+
result = exportItem.prepareFeatures([feature], "EPSG:25832");
|
|
186
|
+
|
|
187
|
+
expect(result[0].getGeometry().getType()).toBe("Point");
|
|
188
|
+
expect(result[0].get("masterportal_attributes")).toBeUndefined();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("preserves existing masterportal_attributes on Circle features", () => {
|
|
192
|
+
const circle = new Feature({geometry: new Circle([565874, 5934140], 100)});
|
|
193
|
+
|
|
194
|
+
circle.set("masterportal_attributes", {existingKey: "existingValue"});
|
|
195
|
+
const result = exportItem.prepareFeatures([circle], "EPSG:25832");
|
|
196
|
+
|
|
197
|
+
expect(result[0].get("masterportal_attributes").existingKey).toBe("existingValue");
|
|
198
|
+
expect(result[0].get("masterportal_attributes").isGeoCircle).toBe(true);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe("setCsvAttributes", () => {
|
|
203
|
+
it("returns false if features is not an array", () => {
|
|
204
|
+
expect(setCsvAttributes(undefined)).toBe(false);
|
|
205
|
+
expect(setCsvAttributes(null)).toBe(false);
|
|
206
|
+
expect(setCsvAttributes("string")).toBe(false);
|
|
207
|
+
expect(setCsvAttributes({})).toBe(false);
|
|
208
|
+
expect(setCsvAttributes(123)).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("sets WKT geometry and EPSG code on csv_attributes", () => {
|
|
212
|
+
const feature = new Feature({geometry: new Point([565874, 5934140])}),
|
|
213
|
+
result = setCsvAttributes([feature], "EPSG:25832");
|
|
214
|
+
|
|
215
|
+
expect(result).toHaveLength(1);
|
|
216
|
+
expect(result[0].get("csv_attributes")).toBeDefined();
|
|
217
|
+
expect(result[0].get("csv_attributes").geometry).toContain("POINT");
|
|
218
|
+
expect(result[0].get("csv_attributes").epsg).toBe("EPSG:25832");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("converts Circle geometries to Polygon WKT", () => {
|
|
222
|
+
const feature = new Feature({geometry: new Circle([565874, 5934140], 100)}),
|
|
223
|
+
result = setCsvAttributes([feature], "EPSG:25832");
|
|
224
|
+
|
|
225
|
+
expect(result[0].get("csv_attributes").geometry).toContain("POLYGON");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("copies non-reserved properties to csv_attributes", () => {
|
|
229
|
+
const feature = new Feature({
|
|
230
|
+
geometry: new Point([565874, 5934140]),
|
|
231
|
+
name: "test feature",
|
|
232
|
+
category: "example"
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
setCsvAttributes([feature], "EPSG:25832");
|
|
236
|
+
expect(feature.get("csv_attributes").name).toBe("test feature");
|
|
237
|
+
expect(feature.get("csv_attributes").category).toBe("example");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("does not copy reserved properties to csv_attributes", () => {
|
|
241
|
+
const feature = new Feature({geometry: new Point([565874, 5934140])});
|
|
242
|
+
|
|
243
|
+
feature.set("masterportal_attributes", {some: "data"});
|
|
244
|
+
setCsvAttributes([feature], "EPSG:25832");
|
|
245
|
+
|
|
246
|
+
expect(feature.get("csv_attributes").masterportal_attributes).toBeUndefined();
|
|
247
|
+
expect(feature.get("csv_attributes").geometry).toContain("POINT");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("skips non-Feature items in the array", () => {
|
|
251
|
+
const feature = new Feature({geometry: new Point([565874, 5934140])}),
|
|
252
|
+
result = setCsvAttributes([feature, "not a feature", null], "EPSG:25832");
|
|
253
|
+
|
|
254
|
+
expect(result).toHaveLength(3);
|
|
255
|
+
expect(result[0].get("csv_attributes")).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("returns the features array on success", () => {
|
|
259
|
+
const features = [new Feature({geometry: new Point([565874, 5934140])})],
|
|
260
|
+
result = setCsvAttributes(features, "EPSG:25832");
|
|
261
|
+
|
|
262
|
+
expect(result).toBe(features);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|