@masterportal/masterportalapi 2.13.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.
Files changed (61) hide show
  1. package/.eslintrc +2 -1
  2. package/CHANGELOG.md +16 -7
  3. package/example/config/localGeoJSONPoints.js +2106 -0
  4. package/example/config/portal.json +47 -1
  5. package/example/config/services.json +20 -0
  6. package/example/index.js +50 -7
  7. package/jest.config.js +1 -1
  8. package/jest.setup.js +9 -1
  9. package/package.json +64 -62
  10. package/src/index.js +3 -1
  11. package/src/layer/geojson/index.js +55 -4
  12. package/src/layer/oaf.js +35 -5
  13. package/src/layer/vectorBase.js +20 -1
  14. package/src/layer/wfs.js +38 -7
  15. package/src/lib/attributeMapper.js +231 -0
  16. package/src/lib/getValueFromObjectByPath.js +73 -0
  17. package/src/lib/thousandsSeparator.js +23 -0
  18. package/src/maps/ol/olMap.js +4 -3
  19. package/src/renderer/webgl.js +250 -0
  20. package/src/vectorStyle/createStyle.js +259 -0
  21. package/src/vectorStyle/lib/colorConvertions.js +97 -0
  22. package/src/vectorStyle/lib/createLegendInfo.js +28 -0
  23. package/src/vectorStyle/lib/getGeometryTypeFromService.js +153 -0
  24. package/src/vectorStyle/lib/getRuleForIndex.js +62 -0
  25. package/src/vectorStyle/lib/valueOperations.js +172 -0
  26. package/src/vectorStyle/styleList.js +281 -0
  27. package/src/vectorStyle/styles/defaultStyles.js +177 -0
  28. package/src/vectorStyle/styles/point/stylePoint.js +108 -0
  29. package/src/vectorStyle/styles/point/stylePointCircle.js +29 -0
  30. package/src/vectorStyle/styles/point/stylePointIcon.js +79 -0
  31. package/src/vectorStyle/styles/point/stylePointInterval.js +124 -0
  32. package/src/vectorStyle/styles/point/stylePointNominal.js +232 -0
  33. package/src/vectorStyle/styles/point/stylePointRegularShape.js +39 -0
  34. package/src/vectorStyle/styles/polygon/polygonStyleHatch.js +160 -0
  35. package/src/vectorStyle/styles/polygon/stylePolygon.js +125 -0
  36. package/src/vectorStyle/styles/style.js +161 -0
  37. package/src/vectorStyle/styles/styleCesium.js +106 -0
  38. package/src/vectorStyle/styles/styleLine.js +51 -0
  39. package/src/vectorStyle/styles/styleText.js +143 -0
  40. package/test/layer/geojson/index.test.js +23 -0
  41. package/test/layer/oaf.test.js +30 -0
  42. package/test/layer/vectorBase.test.js +27 -7
  43. package/test/layer/wfs.test.js +30 -0
  44. package/test/lib/attributeMapper.test.js +290 -0
  45. package/test/lib/getValueFromObjectByPath.test.js +61 -0
  46. package/test/lib/thousandsSeparator.test.js +58 -0
  47. package/test/renderer/webgl.test.js +161 -0
  48. package/test/vectorStyle/createStyle.test.js +335 -0
  49. package/test/vectorStyle/lib/colorConvertions.test.js +42 -0
  50. package/test/vectorStyle/lib/getRuleForIndex.test.js +132 -0
  51. package/test/vectorStyle/lib/valueOperations.test.js +191 -0
  52. package/test/vectorStyle/styles/point/stylePoint.test.js +28 -0
  53. package/test/vectorStyle/styles/point/stylePointCircle.test.js +30 -0
  54. package/test/vectorStyle/styles/point/stylePointIcon.test.js +83 -0
  55. package/test/vectorStyle/styles/point/stylePointInterval.test.js +57 -0
  56. package/test/vectorStyle/styles/point/stylePointNominal.test.js +84 -0
  57. package/test/vectorStyle/styles/point/stylePointRegularShape.test.js +24 -0
  58. package/test/vectorStyle/styles/polygon/polygonStyleHatch.test.js +95 -0
  59. package/test/vectorStyle/styles/polygon/stylePolygon.test.js +61 -0
  60. package/test/vectorStyle/styles/styleLine.test.js +29 -0
  61. package/test/vectorStyle/styles/styleText.test.js +49 -0
@@ -0,0 +1,231 @@
1
+ import {getValueFromObjectByPath} from "./getValueFromObjectByPath.js";
2
+ import thousandsSeparator from "./thousandsSeparator";
3
+ /**
4
+ * checks if value starts with special prefix to determine if value is a object path
5
+ * @param {string} value string to check
6
+ * @returns {Boolean} true is value is an object path
7
+ */
8
+ function isObjectPath (value) {
9
+ return typeof value === "string" && value.startsWith("@");
10
+ }
11
+ /**
12
+ * Returns the value of the given key. Also considers, that the key may be an object path.
13
+ * @param {Object} properties properties.
14
+ * @param {String} key Key to derive value from.
15
+ * @returns {*} - Value from key.
16
+ */
17
+ function prepareValue (properties, key) {
18
+ const isPath = isObjectPath(key);
19
+ let value = properties[Object.keys(properties).find(propertiesKey => propertiesKey.toLowerCase() === key.toLowerCase())];
20
+
21
+ if (isPath) {
22
+ value = getValueFromObjectByPath(properties, key);
23
+ }
24
+ return value;
25
+ }
26
+
27
+ /**
28
+ * Parsing the boolean value
29
+ * @param {String} value default value
30
+ * @param {String|Object} format the format of boolean value
31
+ * @returns {String} - original value or parsed value
32
+ */
33
+ function getBooleanValue (value, format) {
34
+ let parsedValue = String(value);
35
+
36
+ if (Object.prototype.hasOwnProperty.call(format, value)) {
37
+ // TODO: check if true -> translation is dismissed because i18next does not exist in masterportalApi
38
+ parsedValue = format[value];
39
+
40
+ }
41
+ return parsedValue;
42
+ }
43
+
44
+ /**
45
+ * Appends a suffix if available.
46
+ * @param {*} value Value to append suffix.
47
+ * @param {String} suffix Suffix
48
+ * @returns {String} - Value with suffix.
49
+ */
50
+ function appendSuffix (value, suffix) {
51
+ let valueWithSuffix = value;
52
+
53
+ if (suffix) {
54
+ valueWithSuffix = String(valueWithSuffix) + " " + suffix;
55
+ }
56
+ return valueWithSuffix;
57
+ }
58
+
59
+ /**
60
+ * Prepend a prefix if available.
61
+ * @param {*} value Value to prepend prefix.
62
+ * @param {String} prefix Prefix
63
+ * @returns {String} - Value with prefix.
64
+ */
65
+ function prependPrefix (value, prefix) {
66
+ let valueWithPrefix = value;
67
+
68
+ if (prefix) {
69
+ valueWithPrefix = prefix + String(valueWithPrefix);
70
+ }
71
+ return valueWithPrefix;
72
+ }
73
+
74
+ /**
75
+ * Derives the value from the given condition.
76
+ * @param {String} key Key.
77
+ * @param {String} condition Condition to filter.
78
+ * @param {Object} properties Properties.
79
+ * @returns {*} - Value that matches the given condition.
80
+ */
81
+ function getValueFromCondition (key, condition, properties) {
82
+ let valueFromCondition,
83
+ match;
84
+
85
+ if (condition === "contains") {
86
+ match = Object.keys(properties).filter(key2 => {
87
+ return key2.includes(key);
88
+ })[0];
89
+ valueFromCondition = properties[match];
90
+ }
91
+ else if (condition === "startsWith") {
92
+ match = Object.keys(properties).filter(key2 => {
93
+ return key2.startsWith(key);
94
+ })[0];
95
+ valueFromCondition = properties[match];
96
+ }
97
+ else if (condition === "endsWith") {
98
+ match = Object.keys(properties).filter(key2 => {
99
+ return key2.endsWith(key);
100
+ })[0];
101
+ valueFromCondition = properties[match];
102
+ }
103
+ else {
104
+ valueFromCondition = properties[key];
105
+ }
106
+
107
+ return valueFromCondition;
108
+
109
+ }
110
+
111
+ /**
112
+ * Derives the gfi value if the value is an object.
113
+ * @param {*} key Key of Attribute.
114
+ * @param {Object} mappingObj Value of attribute.
115
+ * @param {Object} properties object.
116
+ * @returns {*} - Prepared Value
117
+ */
118
+ function prepareValueFromObject (key, mappingObj, properties) {
119
+ const type = mappingObj?.type ? mappingObj.type : "string",
120
+ condition = mappingObj?.condition ? mappingObj.condition : null;
121
+ let preparedValue = prepareValue(properties, key),
122
+ format = mappingObj?.format ? mappingObj.format : "YYYY-MM-DDTHH:mm:ss.SSSZ",
123
+ date;
124
+
125
+ if (condition) {
126
+ preparedValue = getValueFromCondition(key, condition, properties);
127
+ }
128
+ switch (type) {
129
+ case "date": {
130
+ date = new Date(String(preparedValue));
131
+ if (!isNaN(date.getTime())) {
132
+ const year = date.getFullYear(),
133
+ month = ("0" + (date.getMonth() + 1)).slice(-2),
134
+ day = ("0" + date.getDate()).slice(-2);
135
+
136
+ if (format === "YYYY-MM-DDTHH:mm:ss.SSSZ") {
137
+ const offset = date.getTimezoneOffset();
138
+
139
+ let offsetHours = (offset / 60) % 24,
140
+ offsetMinutes = offset % 60;
141
+
142
+ offsetHours = offsetHours < 0 ? "+" + ("0" + Math.abs(offsetHours)).slice(-2) : "-" + ("0" + Math.abs(offsetHours)).slice(-2);
143
+ offsetMinutes = offsetMinutes < 0 ? ":" + ("0" + Math.abs(offsetMinutes)).slice(-2) : ":" + ("0" + offsetMinutes).slice(-2);
144
+ preparedValue = date.toISOString().slice(0, -1) + offsetHours + offsetMinutes;
145
+ }
146
+ else if (format === "YYYY-MM-DD") {
147
+ preparedValue = year + "-" + month + "-" + day;
148
+ }
149
+ else if (format === "DD-MM-YYYY") {
150
+ preparedValue = day + "-" + month + "-" + year;
151
+ }
152
+ else {
153
+ console.error("The format must be 'YYYY-MM-DD' or 'DD-MM-YYYY', if the attribute is missing, ISO 8601 is used.");
154
+ }
155
+ }
156
+ break;
157
+ }
158
+ case "number": {
159
+ preparedValue = thousandsSeparator(preparedValue);
160
+ break;
161
+ }
162
+ case "linechart": {
163
+ preparedValue = Object.assign({
164
+ name: key,
165
+ staObject: preparedValue
166
+ }, mappingObj);
167
+ break;
168
+ }
169
+ case "boolean": {
170
+ format = format === "DD.MM.YYYY HH:mm:ss" ? {true: "true", false: "false"} : format;
171
+ preparedValue = getBooleanValue(preparedValue, format);
172
+ break;
173
+ }
174
+ // default equals to mappingObj.type === "string"
175
+ default: {
176
+ preparedValue = String(preparedValue);
177
+ }
178
+ }
179
+ if (preparedValue && mappingObj.suffix && preparedValue !== "undefined") {
180
+ preparedValue = appendSuffix(preparedValue, mappingObj.suffix);
181
+ }
182
+ if (preparedValue && mappingObj.prefix && preparedValue !== "undefined") {
183
+ preparedValue = prependPrefix(preparedValue, mappingObj.prefix);
184
+ }
185
+ return preparedValue;
186
+ }
187
+
188
+ /**
189
+ * Maps the feature properties by the given object.
190
+ * @param {Object} properties The feature properties.
191
+ * @param {Object} mappingObject Object to me mapped.
192
+ * @param {Boolean} [isNested=true] Flag if Object is nested, like "gfiAttributes".
193
+ * @returns {Object} The mapped properties.
194
+ */
195
+ function mapAttributes (properties, mappingObject, isNested = true) {
196
+ let mappedProperties;
197
+
198
+ if (!mappingObject) {
199
+ return false;
200
+ }
201
+ if (!isNested) {
202
+ if (typeof mappingObject === "string") {
203
+ mappedProperties = prepareValue(properties, mappingObject);
204
+ }
205
+ else {
206
+ mappedProperties = prepareValueFromObject(mappingObject.name, mappingObject, properties);
207
+ }
208
+ }
209
+ else {
210
+ mappedProperties = {};
211
+ Object.keys(mappingObject).forEach(key => {
212
+ let newKey = mappingObject[key],
213
+ value = prepareValue(properties, key);
214
+
215
+ if (typeof newKey === "object") {
216
+ value = prepareValueFromObject(key, newKey, properties);
217
+ newKey = newKey.name;
218
+ }
219
+ if (value && value !== "undefined") {
220
+ mappedProperties[newKey] = value;
221
+ }
222
+ });
223
+ }
224
+ return mappedProperties;
225
+ }
226
+
227
+ export {
228
+ mapAttributes,
229
+ isObjectPath,
230
+ prepareValue
231
+ };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * gets the parts of the given path splitted by ".", ignoring escaped "."
3
+ * @param {String} path the path to split
4
+ * @param {String} [delimitor="."] the delimitor to use
5
+ * @returns {String[]} the resulting parts of the path
6
+ */
7
+ function getPathPartsFromPath (path, delimitor = ".") {
8
+ const len = path.length,
9
+ result = [];
10
+ let letter = "",
11
+ word = "";
12
+
13
+ for (let i = 0; i < len; i++) {
14
+ letter = path[i];
15
+
16
+ if (letter === "\\") {
17
+ i++;
18
+ letter = path[i];
19
+ }
20
+ else if (letter === delimitor) {
21
+ result.push(word);
22
+ word = "";
23
+ continue;
24
+ }
25
+ word += letter;
26
+ }
27
+
28
+ if (word) {
29
+ result.push(word);
30
+ }
31
+ return result;
32
+ }
33
+ /**
34
+ * uses the given path to go into the given object and returns the value found at the end
35
+ * @info arrays can be accessed by using a number as index (e.g. "@Test.0.test")
36
+ * @param {Object} obj an object or array to search through
37
+ * @param {String} path the path to follow through the given object
38
+ * @param {String} [prefix="@"] the prefix to use to recognize the string as path to follow
39
+ * @param {String} [delimitor="."] the delimitor to use
40
+ * @param {Number} [depthBarrier=20] the depth barrier to avoid infinit recurtions
41
+ * @returns {*} any value found at the end or undefined if nothing was found
42
+ */
43
+ function getValueFromObjectByPath (obj, path, prefix = "@", delimitor = ".", depthBarrier = 20) {
44
+ if (
45
+ typeof obj !== "object"
46
+ || obj === null
47
+ || typeof path !== "string"
48
+ || (
49
+ typeof prefix === "string"
50
+ && path.substr(0, prefix.length) !== prefix
51
+ )
52
+ ) {
53
+ return undefined;
54
+ }
55
+ const pathParts = getPathPartsFromPath(typeof prefix === "string" ? path.substring(prefix.length) : path, delimitor),
56
+ len = pathParts.length;
57
+ let value = obj,
58
+ depth = 0;
59
+
60
+ for (let i = 0; i < len; i++) {
61
+ if (typeof value !== "object" || value === null) {
62
+ return undefined;
63
+ }
64
+ value = value[pathParts[i]];
65
+ depth++;
66
+ if (typeof value === "undefined" || depth > depthBarrier) {
67
+ return undefined;
68
+ }
69
+ }
70
+ return value;
71
+ }
72
+
73
+ export {getValueFromObjectByPath, getPathPartsFromPath};
@@ -0,0 +1,23 @@
1
+
2
+ /**
3
+ * adds thousands seperators into a number and changes the decimal point
4
+ * @param {(Number|String)} num the number as number or string
5
+ * @param {String} [delimAbs="."] the letter(s) to use as thousand point
6
+ * @param {String} [delimDec=","] the letter(s) to use as decimal point
7
+ * @returns {String} the given number with thousands seperators or an empty string if any invalid num was given
8
+ */
9
+ function thousandsSeparator (num, delimAbs = ".", delimDec = ",") {
10
+ if (typeof num !== "number" && typeof num !== "string") {
11
+ return "";
12
+ }
13
+
14
+ const value = typeof num !== "string" ? num.toString() : num,
15
+ decPointPos = value.indexOf("."),
16
+ abs = decPointPos > -1 ? value.substring(0, decPointPos) : value,
17
+ result = abs.replace(/\B(?=(\d{3})+(?!\d),?.*)/g, delimAbs),
18
+ dec = decPointPos > -1 ? value.substring(decPointPos + 1) : false;
19
+
20
+ return dec ? result + delimDec + dec : result;
21
+ }
22
+
23
+ export default thousandsSeparator;
@@ -84,12 +84,13 @@ function injectErrorCallback (layer, errorCallback) {
84
84
  * This function is available on all ol/Map instances.
85
85
  * @param {(string|ol/layer/Base)} layerOrId - if of layer to add to map
86
86
  * @param {object} [params] - optional parameter object
87
+ * @param {boolean} [params.layerParams={}] - additional layerParams specified in portalConfig
87
88
  * @param {boolean} [params.visibility=true] - whether added layer is initially visible
88
89
  * @param {Number} [params.transparency=0] - how visible the layer is initially
89
90
  * @param {Function} [params.errorCallback=console.error] - callback for layer source error events
90
91
  * @returns {?ol.Layer} added layer
91
92
  */
92
- function addLayer (layerOrId, params = {visibility: true, transparency: 0, errorCallback: console.error}) {
93
+ function addLayer (layerOrId, params = {layerParams: {}, visibility: true, transparency: 0, errorCallback: console.error}) {
93
94
  const errorCallback = typeof params.errorCallback === "function"
94
95
  ? params.errorCallback
95
96
  : console.error;
@@ -108,7 +109,7 @@ function addLayer (layerOrId, params = {visibility: true, transparency: 0, error
108
109
  console.error("Layer with id '" + layerOrId + "' has unknown type '" + rawLayer.typ + "'. No layer added to map.");
109
110
  return null;
110
111
  }
111
- layer = layerBuilder.createLayer(rawLayer, {}, {map: this});
112
+ layer = layerBuilder.createLayer(rawLayer, {layerParams: params.layerParams}, {map: this});
112
113
  layer.setVisible(typeof params.visibility === "boolean" ? params.visibility : true);
113
114
  layer.setOpacity(typeof params.transparency === "number" ? (100 - params.transparency) / 100 : 1);
114
115
  injectErrorCallback(layer, errorCallback);
@@ -169,7 +170,7 @@ export function createMap (config = defaults, {mapParams, callback, errorCallbac
169
170
  rawLayerList.initializeLayerList(config.layerConf, (param, error) => {
170
171
  getInitialLayers(config)
171
172
  .forEach(layer => {
172
- map.addLayer(layer.id, {errorCallback});
173
+ map.addLayer(layer.id, {layerParams: layer, errorCallback});
173
174
  });
174
175
 
175
176
  if (typeof callback === "function") {
@@ -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
+ }