@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.
- package/.eslintrc +2 -1
- package/CHANGELOG.md +16 -7
- package/example/config/localGeoJSONPoints.js +2106 -0
- package/example/config/portal.json +47 -1
- package/example/config/services.json +20 -0
- package/example/index.js +50 -7
- package/jest.config.js +1 -1
- package/jest.setup.js +9 -1
- package/package.json +64 -62
- 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/lib/attributeMapper.js +231 -0
- package/src/lib/getValueFromObjectByPath.js +73 -0
- package/src/lib/thousandsSeparator.js +23 -0
- package/src/maps/ol/olMap.js +4 -3
- package/src/renderer/webgl.js +250 -0
- package/src/vectorStyle/createStyle.js +259 -0
- package/src/vectorStyle/lib/colorConvertions.js +97 -0
- package/src/vectorStyle/lib/createLegendInfo.js +28 -0
- package/src/vectorStyle/lib/getGeometryTypeFromService.js +153 -0
- package/src/vectorStyle/lib/getRuleForIndex.js +62 -0
- package/src/vectorStyle/lib/valueOperations.js +172 -0
- package/src/vectorStyle/styleList.js +281 -0
- package/src/vectorStyle/styles/defaultStyles.js +177 -0
- package/src/vectorStyle/styles/point/stylePoint.js +108 -0
- package/src/vectorStyle/styles/point/stylePointCircle.js +29 -0
- package/src/vectorStyle/styles/point/stylePointIcon.js +79 -0
- package/src/vectorStyle/styles/point/stylePointInterval.js +124 -0
- package/src/vectorStyle/styles/point/stylePointNominal.js +232 -0
- package/src/vectorStyle/styles/point/stylePointRegularShape.js +39 -0
- package/src/vectorStyle/styles/polygon/polygonStyleHatch.js +160 -0
- package/src/vectorStyle/styles/polygon/stylePolygon.js +125 -0
- package/src/vectorStyle/styles/style.js +161 -0
- package/src/vectorStyle/styles/styleCesium.js +106 -0
- package/src/vectorStyle/styles/styleLine.js +51 -0
- package/src/vectorStyle/styles/styleText.js +143 -0
- 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/lib/attributeMapper.test.js +290 -0
- package/test/lib/getValueFromObjectByPath.test.js +61 -0
- package/test/lib/thousandsSeparator.test.js +58 -0
- package/test/renderer/webgl.test.js +161 -0
- package/test/vectorStyle/createStyle.test.js +335 -0
- package/test/vectorStyle/lib/colorConvertions.test.js +42 -0
- package/test/vectorStyle/lib/getRuleForIndex.test.js +132 -0
- package/test/vectorStyle/lib/valueOperations.test.js +191 -0
- package/test/vectorStyle/styles/point/stylePoint.test.js +28 -0
- package/test/vectorStyle/styles/point/stylePointCircle.test.js +30 -0
- package/test/vectorStyle/styles/point/stylePointIcon.test.js +83 -0
- package/test/vectorStyle/styles/point/stylePointInterval.test.js +57 -0
- package/test/vectorStyle/styles/point/stylePointNominal.test.js +84 -0
- package/test/vectorStyle/styles/point/stylePointRegularShape.test.js +24 -0
- package/test/vectorStyle/styles/polygon/polygonStyleHatch.test.js +95 -0
- package/test/vectorStyle/styles/polygon/stylePolygon.test.js +61 -0
- package/test/vectorStyle/styles/styleLine.test.js +29 -0
- 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;
|
package/src/maps/ol/olMap.js
CHANGED
|
@@ -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
|
+
}
|