@masterportal/masterportalapi 2.14.0 → 2.15.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 +7 -1
- package/example/config/localGeoJSONPoints.js +2106 -0
- package/example/config/portal.json +39 -0
- package/example/config/services.json +10 -0
- package/example/index.js +25 -14
- package/package.json +64 -63
- package/src/index.js +3 -1
- package/src/layer/geojson/index.js +55 -4
- package/src/layer/oaf.js +35 -5
- package/src/layer/vectorBase.js +20 -1
- package/src/layer/wfs.js +38 -7
- package/src/maps/ol/olMap.js +4 -3
- package/src/renderer/webgl.js +250 -0
- package/src/vectorStyle/styleList.js +1 -1
- package/test/layer/geojson/index.test.js +23 -0
- package/test/layer/oaf.test.js +30 -0
- package/test/layer/vectorBase.test.js +27 -7
- package/test/layer/wfs.test.js +30 -0
- package/test/renderer/webgl.test.js +161 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import WebGLPointsLayer from "ol/layer/WebGLPoints";
|
|
2
|
+
import WebGLVectorLayerRenderer from "ol/renderer/webgl/VectorLayer";
|
|
3
|
+
import VectorLayer from "ol/layer/Layer";
|
|
4
|
+
import {packColor} from "ol/renderer/webgl/shaders";
|
|
5
|
+
import styleList from "../vectorStyle/styleList";
|
|
6
|
+
import {getRulesForFeature} from "../vectorStyle/lib/getRuleForIndex";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The default style for OpenLayers WebGLPoints class
|
|
10
|
+
* @see https://openlayers.org/en/latest/examples/webgl-points-layer.html
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
const defaultStyle = {
|
|
14
|
+
symbol: {
|
|
15
|
+
symbolType: "circle",
|
|
16
|
+
size: 20,
|
|
17
|
+
color: "#006688",
|
|
18
|
+
rotateWithView: false,
|
|
19
|
+
offset: [0, 0],
|
|
20
|
+
opacity: 0.6
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* parses the styling rules for the renderer
|
|
26
|
+
* @static
|
|
27
|
+
* @private
|
|
28
|
+
* @returns {Object} the style options object with conditional functions
|
|
29
|
+
*/
|
|
30
|
+
export function getRenderFunctions () {
|
|
31
|
+
return {
|
|
32
|
+
/**
|
|
33
|
+
* Used for polygon fills
|
|
34
|
+
* Reads the relevant properties from the Masterportal style object
|
|
35
|
+
* @see https://bitbucket.org/geowerkstatt-hamburg/masterportal/src/dev/doc/style.json.md
|
|
36
|
+
*/
|
|
37
|
+
fill: {
|
|
38
|
+
attributes: {
|
|
39
|
+
color: (feature) => {
|
|
40
|
+
return packColor(feature.styleRule?.style.polygonFillColor || "#006688");
|
|
41
|
+
},
|
|
42
|
+
opacity: (feature) => {
|
|
43
|
+
return typeof feature.styleRule?.style.polygonFillColor?.[3] === "number" ?
|
|
44
|
+
feature.styleRule.style.polygonFillColor[3] : 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
stroke: {
|
|
49
|
+
/**
|
|
50
|
+
* Used for polygon edges and lineStrings
|
|
51
|
+
* Reads the relevant properties from the Masterportal style object
|
|
52
|
+
* @see https://bitbucket.org/geowerkstatt-hamburg/masterportal/src/dev/doc/style.json.md
|
|
53
|
+
*/
|
|
54
|
+
attributes: {
|
|
55
|
+
color: (feature) => {
|
|
56
|
+
return packColor(feature.styleRule?.style.polygonStrokeColor || "#006688");
|
|
57
|
+
},
|
|
58
|
+
width: (feature) => {
|
|
59
|
+
return feature.styleRule?.style.polygonStrokeWidth || 1;
|
|
60
|
+
},
|
|
61
|
+
opacity: (feature) => {
|
|
62
|
+
return typeof feature.styleRule?.style.polygonStrokeColor?.[3] === "number" ?
|
|
63
|
+
feature.styleRule.style.polygonStrokeColor[3] : 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
point: {
|
|
68
|
+
/**
|
|
69
|
+
* As of now, the generic VectorLayerRenderer only supports points rendered as quads
|
|
70
|
+
* available attributes: color, size, opacity
|
|
71
|
+
* Due to that, we use WebGLPoints Layer Class for point geom types
|
|
72
|
+
* Reads the relevant properties from the Masterportal style object
|
|
73
|
+
* @see https://bitbucket.org/geowerkstatt-hamburg/masterportal/src/dev/doc/style.json.md
|
|
74
|
+
*/
|
|
75
|
+
attributes: {
|
|
76
|
+
color: (feature) => {
|
|
77
|
+
return packColor(feature.styleRule?.style.circleFillColor || "#006688");
|
|
78
|
+
},
|
|
79
|
+
size: (feature) => {
|
|
80
|
+
return feature.styleRule?.style.circleRadius || 20;
|
|
81
|
+
},
|
|
82
|
+
opacity: (feature) => {
|
|
83
|
+
return typeof feature.styleRule?.style.circleFillColor?.[3] === "number" ?
|
|
84
|
+
feature.styleRule.style.circleFillColor[3] : 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates a layer object to extend from.
|
|
93
|
+
* @augments VectorLayer
|
|
94
|
+
* @private
|
|
95
|
+
* @implements {WebGLVectorLayerRenderer}
|
|
96
|
+
* @param {Object} attrs attributes of the layer
|
|
97
|
+
* @returns {module:ol/layer/Layer} the LocalWebGLLayer with a custom renderer for WebGL styling
|
|
98
|
+
*/
|
|
99
|
+
export function createVectorLayerRenderer () {
|
|
100
|
+
/**
|
|
101
|
+
* @class LocalWebGLLayer
|
|
102
|
+
* @see https://openlayers.org/en/latest/examples/webgl-vector-layer.html
|
|
103
|
+
* @description the temporary class with a custom renderer to render the vector data with WebGL
|
|
104
|
+
*/
|
|
105
|
+
class LocalWebGLLayer extends VectorLayer {
|
|
106
|
+
/**
|
|
107
|
+
* Creates a new renderer that takes the defined style of the new layer as an input
|
|
108
|
+
* @returns {module:ol/renderer/webgl/WebGLVectorLayerRenderer} the custom renderer
|
|
109
|
+
* @experimental
|
|
110
|
+
*/
|
|
111
|
+
createRenderer () {
|
|
112
|
+
return new WebGLVectorLayerRenderer(this, getRenderFunctions());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return LocalWebGLLayer;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parses the vectorStyle from style.json to the feature
|
|
121
|
+
* to reduce processing on runtime
|
|
122
|
+
* @private
|
|
123
|
+
* @param {module:ol/Feature} feature - the feature to check
|
|
124
|
+
* @param {module:Backbone/Model} [styleObject] - (optional) the style model from StyleList
|
|
125
|
+
* @returns {void}
|
|
126
|
+
*/
|
|
127
|
+
export function formatFeatureStyles (feature, styleObject) {
|
|
128
|
+
if (!styleObject) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// extract first matching rule only
|
|
133
|
+
const rule = getRulesForFeature(styleObject, feature)[0];
|
|
134
|
+
|
|
135
|
+
// don't set on properties to avoid GFI issues
|
|
136
|
+
// undefined if no match
|
|
137
|
+
feature.styleRule = rule;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Layouts the geometry coordinates, removes the Z component
|
|
142
|
+
* @deprecated Will be probably removed in release version
|
|
143
|
+
* @private
|
|
144
|
+
* @param {module:ol/Feature} feature - the feature to format
|
|
145
|
+
* @returns {void}
|
|
146
|
+
*/
|
|
147
|
+
export function formatFeatureGeometry (feature) {
|
|
148
|
+
feature.getGeometry()?.setCoordinates?.(feature.getGeometry().getCoordinates(), "XY");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Cleans the data by automatically parsing data provided as strings to the accurate data type
|
|
153
|
+
* @todo Extend to Date types
|
|
154
|
+
* @private
|
|
155
|
+
* @param {module:ol/Feature} feature - the feature to format
|
|
156
|
+
* @param {String[]} [excludeTypes=["boolean"]] - types that should not be parsed from strings
|
|
157
|
+
* @returns {void}
|
|
158
|
+
*/
|
|
159
|
+
export function formatFeatureData (feature, excludeTypes = ["boolean"]) {
|
|
160
|
+
for (const key in feature.getProperties()) {
|
|
161
|
+
const
|
|
162
|
+
valueAsNumber = parseFloat(feature.get(key)),
|
|
163
|
+
valueIsTrue = typeof feature.get(key) === "string" && feature.get(key).toLowerCase() === "true" ? true : undefined,
|
|
164
|
+
valueIsFalse = typeof feature.get(key) === "string" && feature.get(key).toLowerCase() === "false" ? false : undefined;
|
|
165
|
+
|
|
166
|
+
if (!isNaN(parseFloat(feature.get(key))) && !excludeTypes.includes("number")) {
|
|
167
|
+
feature.set(key, valueAsNumber);
|
|
168
|
+
}
|
|
169
|
+
if (valueIsTrue === true && !excludeTypes.includes("boolean")) {
|
|
170
|
+
feature.set(key, valueIsTrue);
|
|
171
|
+
}
|
|
172
|
+
if (valueIsFalse === false && !excludeTypes.includes("boolean")) {
|
|
173
|
+
feature.set(key, valueIsFalse);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* feature transformations called after loading
|
|
180
|
+
* called by the layer source loader, binds the instance of the layer model
|
|
181
|
+
* should be called on each source refresh
|
|
182
|
+
* @param {module:ol/Feature[]} features - the features loaded by the layer
|
|
183
|
+
* @param {String} styleId - The layer's styleId
|
|
184
|
+
* @param {String[]} excludeTypesFromParsing - types that should not be parsed from strings, only necessary for webgl
|
|
185
|
+
* @returns {void}
|
|
186
|
+
*/
|
|
187
|
+
export function afterLoading (features, styleId, excludeTypesFromParsing) {
|
|
188
|
+
const styleObject = styleList.returnStyleObject(styleId); // load styleModel to extract rules per feature
|
|
189
|
+
|
|
190
|
+
if (Array.isArray(features)) {
|
|
191
|
+
features.forEach(feature => {
|
|
192
|
+
formatFeatureGeometry(feature); /** @deprecated will propbably not be necessary anymore in release version */
|
|
193
|
+
formatFeatureStyles(feature, styleObject); /** @todo needs refactoring in release version */
|
|
194
|
+
formatFeatureData(feature, excludeTypesFromParsing); /** Necessary since the WebGLPoints Style Syntax depends on data types (i.e. numbers not as strings) */
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Creates the OL Layer instance, used to rebuild the layer when shown again after layer has been disposed
|
|
202
|
+
* @param {Object} attrs - the attributes of the layer
|
|
203
|
+
* @public
|
|
204
|
+
* @returns {VectorLayer | WebGLPointsLayer} returns the layer instance
|
|
205
|
+
*/
|
|
206
|
+
export function createLayer (attrs) {
|
|
207
|
+
let LayerConstructor = WebGLPointsLayer;
|
|
208
|
+
const options = {
|
|
209
|
+
id: attrs.id,
|
|
210
|
+
source: attrs.source,
|
|
211
|
+
disableHitDetection: false,
|
|
212
|
+
name: attrs.name,
|
|
213
|
+
typ: attrs.typ,
|
|
214
|
+
gfiAttributes: attrs.gfiAttributes,
|
|
215
|
+
gfiTheme: attrs.gfiTheme,
|
|
216
|
+
hitTolerance: attrs.hitTolerance || 10,
|
|
217
|
+
opacity: attrs.transparency ? (100 - attrs.transparency) / 100 : attrs.opacity,
|
|
218
|
+
renderer: "webgl",
|
|
219
|
+
styleId: attrs.styleId,
|
|
220
|
+
excludeTypesFromParsing: attrs.excludeTypesFromParsing
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* If the layer consists only of points, use WebGLPointsLayer
|
|
225
|
+
* For advanced styling options
|
|
226
|
+
* @see https://openlayers.org/en/latest/examples/webgl-points-layer.html
|
|
227
|
+
*/
|
|
228
|
+
if (attrs.isPointLayer) {
|
|
229
|
+
/**
|
|
230
|
+
* @deprecated
|
|
231
|
+
* @todo will be replaced in the next OL release and incorporated in the WebGLVectorLayerRenderer
|
|
232
|
+
*/
|
|
233
|
+
return new LayerConstructor({
|
|
234
|
+
style: attrs.style || defaultStyle,
|
|
235
|
+
disableHitDetection: false,
|
|
236
|
+
...options,
|
|
237
|
+
isPointLayer: true
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* use ol/renderer/webgl/WebGLVectorLayerRenderer if not point layer
|
|
243
|
+
* Point styling as quads only
|
|
244
|
+
*/
|
|
245
|
+
LayerConstructor = createVectorLayerRenderer(attrs);
|
|
246
|
+
return new LayerConstructor({
|
|
247
|
+
...options,
|
|
248
|
+
isPointLayer: false
|
|
249
|
+
});
|
|
250
|
+
}
|
|
@@ -3,9 +3,17 @@ import VectorSource from "ol/source/Vector.js";
|
|
|
3
3
|
import Feature from "ol/Feature";
|
|
4
4
|
import {Style, Icon} from "ol/style.js";
|
|
5
5
|
import {geojson} from "../../../src";
|
|
6
|
+
import * as webgl from "../../../src/renderer/webgl.js";
|
|
6
7
|
|
|
7
8
|
const features = [new Feature(), new Feature()];
|
|
8
9
|
|
|
10
|
+
jest.mock("../../../src/renderer/webgl.js", () => {
|
|
11
|
+
const original = jest.requireActual("../../../src/renderer/webgl.js");
|
|
12
|
+
|
|
13
|
+
original.afterLoading = jest.fn();
|
|
14
|
+
return original;
|
|
15
|
+
});
|
|
16
|
+
|
|
9
17
|
describe("geojson/index", function () {
|
|
10
18
|
describe("createLayer", function () {
|
|
11
19
|
it("creates an ol/layer/Vector", function () {
|
|
@@ -70,6 +78,12 @@ describe("geojson/index", function () {
|
|
|
70
78
|
expect(layer.get("name")).toEqual("name");
|
|
71
79
|
expect(layer.get("layers")).toEqual("layer1, layer2");
|
|
72
80
|
});
|
|
81
|
+
it("creates a WebGLLayer, with param renderer \"webgl\"", function () {
|
|
82
|
+
const layer = geojson.createLayer({}, {layerParams: {renderer: "webgl"}});
|
|
83
|
+
|
|
84
|
+
expect(layer.constructor.name).toEqual("LocalWebGLLayer");
|
|
85
|
+
expect(layer.getSource()).toBeInstanceOf(VectorSource);
|
|
86
|
+
});
|
|
73
87
|
});
|
|
74
88
|
|
|
75
89
|
describe("createLayerSource", function () {
|
|
@@ -79,6 +93,15 @@ describe("geojson/index", function () {
|
|
|
79
93
|
expect(source.getFormat()).toBeDefined();
|
|
80
94
|
expect(source.getUrl()).toBeDefined();
|
|
81
95
|
});
|
|
96
|
+
it("creates a vectorSource with an additional listener, when renderer is \"webgl\"", () => {
|
|
97
|
+
const
|
|
98
|
+
rawLayer = {url: "example.com/geo.json"},
|
|
99
|
+
source = geojson.createLayerSource({...rawLayer, renderer: "webgl"}, {});
|
|
100
|
+
|
|
101
|
+
expect(source.getListeners("featuresloadend")).toHaveLength(2);
|
|
102
|
+
source.getListeners("featuresloadend")[1]({features: []});
|
|
103
|
+
expect(webgl.afterLoading).toHaveBeenCalled();
|
|
104
|
+
});
|
|
82
105
|
|
|
83
106
|
});
|
|
84
107
|
|
package/test/layer/oaf.test.js
CHANGED
|
@@ -7,6 +7,7 @@ import defaults from "../../src/defaults";
|
|
|
7
7
|
import GeoJSON from "ol/format/GeoJSON.js";
|
|
8
8
|
import * as oaf from "../../src/layer/oaf";
|
|
9
9
|
import {featureCollection} from "./resources/oafFeatures";
|
|
10
|
+
import * as webgl from "../../src/renderer/webgl.js";
|
|
10
11
|
|
|
11
12
|
jest.mock("../../src/rawLayerList.js", () => {
|
|
12
13
|
const original = jest.requireActual("../../src/rawLayerList.js");
|
|
@@ -15,6 +16,13 @@ jest.mock("../../src/rawLayerList.js", () => {
|
|
|
15
16
|
return original;
|
|
16
17
|
});
|
|
17
18
|
|
|
19
|
+
jest.mock("../../src/renderer/webgl.js", () => {
|
|
20
|
+
const original = jest.requireActual("../../src/renderer/webgl.js");
|
|
21
|
+
|
|
22
|
+
original.afterLoading = jest.fn();
|
|
23
|
+
return original;
|
|
24
|
+
});
|
|
25
|
+
|
|
18
26
|
|
|
19
27
|
describe("oaf.js", function () {
|
|
20
28
|
describe("createLayer", function () {
|
|
@@ -107,6 +115,12 @@ describe("oaf.js", function () {
|
|
|
107
115
|
expect(layer.get("name")).toEqual("name");
|
|
108
116
|
expect(layer.get("layers")).toEqual("layer1, layer2");
|
|
109
117
|
});
|
|
118
|
+
it("creates a WebGLLayer, with param renderer \"webgl\"", function () {
|
|
119
|
+
const layer = oaf.createLayer({id: "id"}, {layerParams: {renderer: "webgl"}});
|
|
120
|
+
|
|
121
|
+
expect(layer.constructor.name).toEqual("LocalWebGLLayer");
|
|
122
|
+
expect(layer.getSource()).toBeInstanceOf(VectorSource);
|
|
123
|
+
});
|
|
110
124
|
});
|
|
111
125
|
describe("createLayerSource", function () {
|
|
112
126
|
const consoleError = console.error;
|
|
@@ -241,5 +255,21 @@ describe("oaf.js", function () {
|
|
|
241
255
|
expect(afterLoadingMock.mock.calls.length).toBe(1);
|
|
242
256
|
});
|
|
243
257
|
});
|
|
258
|
+
it("creates a vectorSource with an additional listener, when renderer is \"webgl\"", () => {
|
|
259
|
+
const
|
|
260
|
+
url = "https://url.de",
|
|
261
|
+
rawLayer = {
|
|
262
|
+
id: "id",
|
|
263
|
+
url: url,
|
|
264
|
+
featureNS: "http://www.deegree.org/app",
|
|
265
|
+
featureType: "krankenhaeuser_hh",
|
|
266
|
+
version: "1.0.0"
|
|
267
|
+
},
|
|
268
|
+
source = oaf.createLayerSource({...rawLayer, renderer: "webgl"});
|
|
269
|
+
|
|
270
|
+
expect(source.getListeners("featuresloadend")).toHaveLength(1);
|
|
271
|
+
source.getListeners("featuresloadend")[0]({features: []});
|
|
272
|
+
expect(webgl.afterLoading).toHaveBeenCalled();
|
|
273
|
+
});
|
|
244
274
|
});
|
|
245
275
|
});
|
|
@@ -4,6 +4,14 @@ import Polygon from "ol/geom/Polygon";
|
|
|
4
4
|
import VectorLayer from "ol/layer/Vector";
|
|
5
5
|
import VectorSource from "ol/source/Vector.js";
|
|
6
6
|
import * as vectorBase from "../../src/layer/vectorBase";
|
|
7
|
+
import * as webgl from "../../src/renderer/webgl.js";
|
|
8
|
+
|
|
9
|
+
jest.mock("../../src/renderer/webgl.js", () => {
|
|
10
|
+
const original = jest.requireActual("../../src/renderer/webgl.js");
|
|
11
|
+
|
|
12
|
+
original.afterLoading = jest.fn();
|
|
13
|
+
return original;
|
|
14
|
+
});
|
|
7
15
|
|
|
8
16
|
describe("vectorBase.js", function () {
|
|
9
17
|
|
|
@@ -25,23 +33,35 @@ describe("vectorBase.js", function () {
|
|
|
25
33
|
|
|
26
34
|
expect(source).toBeInstanceOf(VectorSource);
|
|
27
35
|
});
|
|
36
|
+
it("creates a vectorSource with an additional listener, when renderer is \"webgl\"", () => {
|
|
37
|
+
vectorBase.createLayerSource({renderer: "webgl"});
|
|
38
|
+
|
|
39
|
+
expect(webgl.afterLoading).toHaveBeenCalled();
|
|
40
|
+
});
|
|
28
41
|
});
|
|
29
42
|
|
|
30
43
|
describe("createLayer", function () {
|
|
44
|
+
const attr = {
|
|
45
|
+
name: "Albus Percival Wulfric Brian Dumbledore",
|
|
46
|
+
typ: "VectorBase",
|
|
47
|
+
gfiAttributes: "ignore",
|
|
48
|
+
id: "Rawenclaw_01"
|
|
49
|
+
};
|
|
50
|
+
|
|
31
51
|
it("creates a vector Base Layer", function () {
|
|
32
|
-
const
|
|
33
|
-
name: "Albus Percival Wulfric Brian Dumbledore",
|
|
34
|
-
typ: "VectorBase",
|
|
35
|
-
gfiAttributes: "ignore",
|
|
36
|
-
id: "Rawenclaw_01"
|
|
37
|
-
},
|
|
38
|
-
layer = vectorBase.createLayer(attr);
|
|
52
|
+
const layer = vectorBase.createLayer(attr);
|
|
39
53
|
|
|
40
54
|
expect(layer).toBeInstanceOf(VectorLayer);
|
|
41
55
|
expect(layer.get("name")).toEqual("Albus Percival Wulfric Brian Dumbledore");
|
|
42
56
|
expect(layer.get("typ")).toEqual("VectorBase");
|
|
43
57
|
expect(layer.get("id")).toEqual("Rawenclaw_01");
|
|
44
58
|
});
|
|
59
|
+
it("creates a WebGLLayer, with param renderer \"webgl\"", function () {
|
|
60
|
+
const layer = vectorBase.createLayer({...attr, renderer: "webgl"});
|
|
61
|
+
|
|
62
|
+
expect(layer.constructor.name).toEqual("LocalWebGLLayer");
|
|
63
|
+
expect(layer.getSource()).toBeInstanceOf(VectorSource);
|
|
64
|
+
});
|
|
45
65
|
});
|
|
46
66
|
|
|
47
67
|
describe("updateSource", function () {
|
package/test/layer/wfs.test.js
CHANGED
|
@@ -8,6 +8,7 @@ import {WFS} from "ol/format.js";
|
|
|
8
8
|
import * as wfs from "../../src/layer/wfs";
|
|
9
9
|
import {featureCollection} from "./resources/wfsFeatures";
|
|
10
10
|
import {wfsFilterQuery} from "./resources/wfsFilter";
|
|
11
|
+
import * as webgl from "../../src/renderer/webgl.js";
|
|
11
12
|
|
|
12
13
|
jest.mock("../../src/rawLayerList.js", () => {
|
|
13
14
|
const original = jest.requireActual("../../src/rawLayerList.js");
|
|
@@ -16,6 +17,13 @@ jest.mock("../../src/rawLayerList.js", () => {
|
|
|
16
17
|
return original;
|
|
17
18
|
});
|
|
18
19
|
|
|
20
|
+
jest.mock("../../src/renderer/webgl.js", () => {
|
|
21
|
+
const original = jest.requireActual("../../src/renderer/webgl.js");
|
|
22
|
+
|
|
23
|
+
original.afterLoading = jest.fn();
|
|
24
|
+
return original;
|
|
25
|
+
});
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
describe("wfs.js", function () {
|
|
21
29
|
describe("createLayer", function () {
|
|
@@ -107,6 +115,12 @@ describe("wfs.js", function () {
|
|
|
107
115
|
expect(layer.get("name")).toEqual("name");
|
|
108
116
|
expect(layer.get("layers")).toEqual("layer1, layer2");
|
|
109
117
|
});
|
|
118
|
+
it("creates a WebGLLayer, with param renderer \"webgl\"", function () {
|
|
119
|
+
const layer = wfs.createLayer({version: "1.1.0"}, {layerParams: {renderer: "webgl"}});
|
|
120
|
+
|
|
121
|
+
expect(layer.constructor.name).toEqual("LocalWebGLLayer");
|
|
122
|
+
expect(layer.getSource()).toBeInstanceOf(VectorSource);
|
|
123
|
+
});
|
|
110
124
|
});
|
|
111
125
|
describe("createLayerSource", function () {
|
|
112
126
|
const consoleError = console.error;
|
|
@@ -383,6 +397,22 @@ describe("wfs.js", function () {
|
|
|
383
397
|
expect(afterLoadingMock.mock.calls.length).toBe(1);
|
|
384
398
|
});
|
|
385
399
|
});
|
|
400
|
+
it("creates a vectorSource with an additional listener, when renderer is \"webgl\"", () => {
|
|
401
|
+
const
|
|
402
|
+
url = "https://url.de",
|
|
403
|
+
rawLayer = {
|
|
404
|
+
id: "id",
|
|
405
|
+
url: url,
|
|
406
|
+
featureNS: "http://www.deegree.org/app",
|
|
407
|
+
featureType: "krankenhaeuser_hh",
|
|
408
|
+
version: "1.0.0"
|
|
409
|
+
},
|
|
410
|
+
source = wfs.createLayerSource({...rawLayer, renderer: "webgl"});
|
|
411
|
+
|
|
412
|
+
expect(source.getListeners("featuresloadend")).toHaveLength(1);
|
|
413
|
+
source.getListeners("featuresloadend")[0]({features: []});
|
|
414
|
+
expect(webgl.afterLoading).toHaveBeenCalled();
|
|
415
|
+
});
|
|
386
416
|
});
|
|
387
417
|
});
|
|
388
418
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import VectorSource from "ol/source/Vector.js";
|
|
2
|
+
import Feature from "ol/Feature";
|
|
3
|
+
import Polygon from "ol/geom/Polygon";
|
|
4
|
+
import * as webgl from "../../src/renderer/webgl";
|
|
5
|
+
import WebGLPointsLayer from "ol/layer/WebGLPoints";
|
|
6
|
+
import Layer from "ol/layer/Layer";
|
|
7
|
+
import {packColor} from "ol/renderer/webgl/shaders";
|
|
8
|
+
import styleList from "../../src/vectorStyle/styleList";
|
|
9
|
+
|
|
10
|
+
describe("webgl.js", () => {
|
|
11
|
+
let layerParams, rawLayer, styleRule, styleObject;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
layerParams = {
|
|
15
|
+
name: "webglTestLayer",
|
|
16
|
+
id: "123",
|
|
17
|
+
typ: "WFS",
|
|
18
|
+
renderer: "webgl",
|
|
19
|
+
styleId: "123"
|
|
20
|
+
};
|
|
21
|
+
rawLayer = {
|
|
22
|
+
url: "https://url.de",
|
|
23
|
+
name: "wfsSourceLayer",
|
|
24
|
+
id: "123",
|
|
25
|
+
typ: "WFS",
|
|
26
|
+
version: "2.0.0",
|
|
27
|
+
featureNS: "http://www.deegree.org/app",
|
|
28
|
+
featureType: "krankenhaeuser_hh",
|
|
29
|
+
outputFormat: "XML"
|
|
30
|
+
};
|
|
31
|
+
styleRule = {style: {
|
|
32
|
+
polygonFillColor: [255, 0, 0, 0.5],
|
|
33
|
+
polygonStrokeColor: [0, 255, 0, 0.5],
|
|
34
|
+
polygonStrokeWidth: 2,
|
|
35
|
+
circleFillColor: [0, 0, 255, 0.5],
|
|
36
|
+
circleRadius: 6
|
|
37
|
+
}};
|
|
38
|
+
styleObject = {
|
|
39
|
+
rules: [styleRule]
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
jest.spyOn(styleList, "returnStyleObject").mockImplementation(() => styleObject);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
jest.restoreAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("createLayer", () => {
|
|
50
|
+
const source = new VectorSource();
|
|
51
|
+
|
|
52
|
+
it("should return a WebGLPointsLayer", () => {
|
|
53
|
+
layerParams.isPointLayer = true;
|
|
54
|
+
const webglLayer = webgl.createLayer({...rawLayer, ...layerParams, source});
|
|
55
|
+
|
|
56
|
+
expect(webglLayer).toBeInstanceOf(WebGLPointsLayer);
|
|
57
|
+
expect(webglLayer.get("isPointLayer")).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
it("should return a custom Layer", () => {
|
|
60
|
+
layerParams.isPointLayer = false;
|
|
61
|
+
const webglLayer = webgl.createLayer({...rawLayer, ...layerParams, source});
|
|
62
|
+
|
|
63
|
+
expect(webglLayer).toBeInstanceOf(Layer);
|
|
64
|
+
expect(webglLayer).not.toBeInstanceOf(WebGLPointsLayer);
|
|
65
|
+
expect(webglLayer.get("isPointLayer")).toBeFalsy();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("private format and style functions", () => {
|
|
69
|
+
const
|
|
70
|
+
feature = new Feature({
|
|
71
|
+
geometry: new Polygon([
|
|
72
|
+
[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 0]]
|
|
73
|
+
], "XYZ"),
|
|
74
|
+
apple: "42",
|
|
75
|
+
pear: "true",
|
|
76
|
+
pumpkin: "false"
|
|
77
|
+
});
|
|
78
|
+
let $feature;
|
|
79
|
+
|
|
80
|
+
describe("formatFeatureGeometry", () => {
|
|
81
|
+
it("should remove any Z components from the geometry", () => {
|
|
82
|
+
$feature = feature.clone();
|
|
83
|
+
webgl.formatFeatureGeometry($feature);
|
|
84
|
+
/* eslint-disable-next-line max-nested-callbacks */
|
|
85
|
+
expect($feature.getGeometry().getCoordinates()[0].every(coord => coord.length === 2)).toBeTruthy();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("formatFeatureData", () => {
|
|
89
|
+
it("should not format excluded formats 'number' and 'boolean", () => {
|
|
90
|
+
$feature = feature.clone();
|
|
91
|
+
webgl.formatFeatureData($feature, ["number", "boolean"]);
|
|
92
|
+
expect(typeof $feature.get("apple")).toEqual("string");
|
|
93
|
+
expect(typeof $feature.get("pear")).toEqual("string");
|
|
94
|
+
expect(typeof $feature.get("pumpkin")).toEqual("string");
|
|
95
|
+
});
|
|
96
|
+
it("should format numbers and booleans", () => {
|
|
97
|
+
$feature = feature.clone();
|
|
98
|
+
webgl.formatFeatureData($feature, []);
|
|
99
|
+
expect(typeof $feature.get("apple")).toEqual("number");
|
|
100
|
+
expect(typeof $feature.get("pear")).toEqual("boolean");
|
|
101
|
+
expect(typeof $feature.get("pumpkin")).toEqual("boolean");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe("formatFeatureStyles", () => {
|
|
105
|
+
it("bind the styling rule from the styleObject to each feature", () => {
|
|
106
|
+
$feature = feature.clone();
|
|
107
|
+
webgl.formatFeatureStyles($feature, styleObject);
|
|
108
|
+
expect($feature.styleRule).toEqual(styleRule);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("getRenderFunctions", () => {
|
|
112
|
+
it("should read the style from styleRule and pack them to single float", () => {
|
|
113
|
+
const renderFunctions = webgl.getRenderFunctions();
|
|
114
|
+
|
|
115
|
+
$feature = feature.clone();
|
|
116
|
+
webgl.formatFeatureStyles($feature, styleObject);
|
|
117
|
+
expect(renderFunctions.fill.attributes.color($feature)).toEqual(packColor(styleRule.style.polygonFillColor));
|
|
118
|
+
expect(renderFunctions.fill.attributes.opacity($feature)).toEqual(styleRule.style.polygonFillColor[3]);
|
|
119
|
+
expect(renderFunctions.stroke.attributes.color($feature)).toEqual(packColor(styleRule.style.polygonStrokeColor));
|
|
120
|
+
expect(renderFunctions.stroke.attributes.width($feature)).toEqual(styleRule.style.polygonStrokeWidth);
|
|
121
|
+
expect(renderFunctions.stroke.attributes.opacity($feature)).toEqual(styleRule.style.polygonStrokeColor[3]);
|
|
122
|
+
expect(renderFunctions.point.attributes.color($feature)).toEqual(packColor(styleRule.style.circleFillColor));
|
|
123
|
+
expect(renderFunctions.point.attributes.size($feature)).toEqual(styleRule.style.circleRadius);
|
|
124
|
+
expect(renderFunctions.point.attributes.opacity($feature)).toEqual(styleRule.style.circleFillColor[3]);
|
|
125
|
+
});
|
|
126
|
+
it("should return default values if style is empty, i.e. \"default\" style", () => {
|
|
127
|
+
const renderFunctions = webgl.getRenderFunctions();
|
|
128
|
+
|
|
129
|
+
styleObject = {
|
|
130
|
+
rules: [{
|
|
131
|
+
style: {}
|
|
132
|
+
}]
|
|
133
|
+
};
|
|
134
|
+
$feature = feature.clone();
|
|
135
|
+
webgl.formatFeatureStyles($feature, styleObject);
|
|
136
|
+
expect(typeof renderFunctions.fill.attributes.color($feature)).toEqual("number");
|
|
137
|
+
expect(typeof renderFunctions.fill.attributes.opacity($feature)).toEqual("number");
|
|
138
|
+
expect(typeof renderFunctions.stroke.attributes.color($feature)).toEqual("number");
|
|
139
|
+
expect(typeof renderFunctions.stroke.attributes.width($feature)).toEqual("number");
|
|
140
|
+
expect(typeof renderFunctions.stroke.attributes.opacity($feature)).toEqual("number");
|
|
141
|
+
expect(typeof renderFunctions.point.attributes.color($feature)).toEqual("number");
|
|
142
|
+
expect(typeof renderFunctions.point.attributes.size($feature)).toEqual("number");
|
|
143
|
+
expect(typeof renderFunctions.point.attributes.opacity($feature)).toEqual("number");
|
|
144
|
+
});
|
|
145
|
+
it("should return default values if no style rule is found", () => {
|
|
146
|
+
const renderFunctions = webgl.getRenderFunctions();
|
|
147
|
+
|
|
148
|
+
$feature = feature.clone();
|
|
149
|
+
expect(feature.styleRule).toBeUndefined();
|
|
150
|
+
expect(typeof renderFunctions.fill.attributes.color($feature)).toEqual("number");
|
|
151
|
+
expect(typeof renderFunctions.fill.attributes.opacity($feature)).toEqual("number");
|
|
152
|
+
expect(typeof renderFunctions.stroke.attributes.color($feature)).toEqual("number");
|
|
153
|
+
expect(typeof renderFunctions.stroke.attributes.width($feature)).toEqual("number");
|
|
154
|
+
expect(typeof renderFunctions.stroke.attributes.opacity($feature)).toEqual("number");
|
|
155
|
+
expect(typeof renderFunctions.point.attributes.color($feature)).toEqual("number");
|
|
156
|
+
expect(typeof renderFunctions.point.attributes.size($feature)).toEqual("number");
|
|
157
|
+
expect(typeof renderFunctions.point.attributes.opacity($feature)).toEqual("number");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|