@masterportal/masterportalapi 2.49.0 → 2.50.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 CHANGED
@@ -4,7 +4,6 @@
4
4
  The [Semantic Versioning](https://semver.org/spec/v2.0.0.html) is used.
5
5
 
6
6
  ## Unreleased - in development
7
-
8
7
  ### __Breaking Changes__
9
8
 
10
9
  ### Added
@@ -19,10 +18,28 @@
19
18
 
20
19
  ---
21
20
 
22
- ## 2.49.0 - 2025-04-294
21
+ ## 2.50.0 - 2025-08-214
22
+
23
+ ### Added
24
+ - vectorStyle: added function `initStyleAndAddToList` to styleList. Adds a style contained in style.json to list of styles.
25
+ - A new parameter "doNotLoadInitially" for OAF layer so that the layer can be loaded without features, which is necessary for external filtering.
26
+
27
+ ### Changed
28
+ - The following packages have been updated:
29
+ - dependencies:
30
+ - ol: 10.4.1 to 10.6.0
31
+
32
+ ### Fixed
33
+ - WMTS-Layer: Fixed layers without capabilities.
34
+ - WFTS-Layer: Ensured zIndex is applied correctly to layers using webgl renderer.
35
+
36
+ ---
37
+
38
+ ## 2.49.0 - 2025-04-29
23
39
 
24
40
  ### Added
25
41
  - Add new parameters polygonStrokeDash, polygonStrokeDashOffset in styleObject of drawInteraction.
42
+ - webgl: extended default style for points.
26
43
 
27
44
  ### Changed
28
45
  - The following packages have been updated:
@@ -0,0 +1,43 @@
1
+ image: node:20.12.2
2
+
3
+ clone:
4
+ depth: 5000 # include the last five hundred commits
5
+
6
+ pipelines:
7
+ # Start pipeline with tag to build examples zips and copy them to the Bitbucket download folder
8
+ tags:
9
+ 'v2.*.*':
10
+ - step:
11
+ name: deploy on openCode
12
+ script:
13
+ - echo 'deploy to opencode:'$BITBUCKET_TAG
14
+ - git fetch --all --tags
15
+ - git checkout tags/$BITBUCKET_TAG
16
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git $BITBUCKET_TAG
17
+ - git clone --single-branch --branch master https://geowerkstatt@bitbucket.org/geowerkstatt-hamburg/masterportalapi.git
18
+ - cd masterportalapi
19
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git -f
20
+ custom: # Pipelines that are triggered manually
21
+ npm audit:
22
+ - step:
23
+ name: npm audit (security)
24
+ caches:
25
+ - node
26
+ script:
27
+ - npm install
28
+ - npm audit --json
29
+ deploy last tag on openCode:
30
+ - variables:
31
+ - name: BITBUCKET_TAG
32
+ description: tag to publish docs for
33
+ - step:
34
+ name: deploy last tag on openCode
35
+ script:
36
+ - apt-get update && apt-get install -y curl wput jq
37
+ - git fetch --all --tags
38
+ - echo 'deply to opencode:'$BITBUCKET_TAG
39
+ - git checkout tags/$BITBUCKET_TAG
40
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git $BITBUCKET_TAG
41
+ - git clone --single-branch --branch master https://geowerkstatt@bitbucket.org/geowerkstatt-hamburg/masterportalapi.git
42
+ - cd masterportalapi
43
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git -f
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@masterportal/masterportalapi",
3
3
  "author": "Implementierungspartnerschaft Masterportal <info@masterportal.org> (https://www.masterportal.org)",
4
- "version": "2.49.0",
4
+ "version": "2.50.0",
5
5
  "license": "MIT",
6
6
  "description": "Basic functions of the Masterportal as api",
7
7
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "core-js": "^3.33.1",
23
23
  "dayjs": "^1.11.10",
24
- "ol": "^10.4.0",
24
+ "ol": "^10.6.0",
25
25
  "ol-mapbox-style": "12.5.0",
26
26
  "olcs": "^2.22.1",
27
27
  "proj4": "^2.10.0",
@@ -64,6 +64,7 @@
64
64
  },
65
65
  "homepage": "https://bitbucket.org/geowerkstatt-hamburg/masterportalapi#readme",
66
66
  "directories": {
67
+ "doc": "docs",
67
68
  "example": "example",
68
69
  "test": "test"
69
70
  },
package/publiccode.yml ADDED
@@ -0,0 +1,88 @@
1
+ # This repository adheres to the publiccode.yml standard by including this
2
+ # metadata file that makes public software easily discoverable.
3
+ # More info at https://github.com/italia/publiccode.yml
4
+
5
+ publiccodeYmlVersion: '0.3'
6
+ categories:
7
+ - geographic-information-systems, api
8
+ description:
9
+ de:
10
+ apiDocumentation: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/src/master/README.md'
11
+ documentation: 'https://www.masterportal.org/dokumentation'
12
+ features:
13
+ - OGC-konform
14
+ - Konfigurierbar
15
+ - OpenSource
16
+ - Adminfreundlich
17
+ - Modular
18
+ genericName: 'MasterportalAPI'
19
+ localisedName: MasterportalAPI
20
+ longDescription: |2
21
+ Die MasterportalAPI ist eine API zum Einbinden und Anzeigen von Karteninhalten auf Ihrer Webseite. Sie basiert auf OpenLayers und wurde um Funktionen erweitert, die die einfache Nutzung von Konfigurations- und Infrastrukturdateien des Masterportals, einer funktionsreichen Mapping-App der Geowerkstatt Hamburg, ermöglichen. Sie ermöglicht außerdem die Erstellung einer 3D-Karte mithilfe von OLCS und Cäsium. Weitere Informationen finden Sie auf masterportal.org. Die MasterportalAPI ist Open-Source-Software. Es ist unter der [++MIT-Lizenz++](https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/src/master/License.txt) veröffentlicht.
22
+ shortDescription: 'Die MasterportalAPI ist eine auf OpenLayers basierende API zum Einbinden und Anzeigen von Karteninhalten auf Ihrer Webseite'
23
+ developmentStatus: stable
24
+ intendedAudience:
25
+ countries:
26
+ - de
27
+ - en
28
+ - es
29
+ - it
30
+ - pt
31
+ - ru
32
+ - tr
33
+ - ua
34
+ - nl
35
+ scope:
36
+ - welfare
37
+ - agriculture
38
+ - culture
39
+ - defence
40
+ - education
41
+ - emergency-services
42
+ - employment
43
+ - energy
44
+ - environment
45
+ - finance-and-economic-development
46
+ - foreign-affairs
47
+ - government
48
+ - healthcare
49
+ - infrastructures
50
+ - local-authorities
51
+ - manufacturing
52
+ - research
53
+ - justice
54
+ - science-and-technology
55
+ - security
56
+ - society
57
+ - sport
58
+ - tourism
59
+ - transportation
60
+ landingURL: 'https://www.masterportal.org'
61
+ legal:
62
+ license: MIT
63
+ mainCopyrightOwner: 'Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung'
64
+ repoOwner: 'Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung'
65
+ localisation:
66
+ availableLanguages:
67
+ - de
68
+ - en
69
+ localisationReady: true
70
+ logo: |-
71
+ https://www.masterportal.org/fileadmin/_processed_/7/d/csm_news_masterportal_ce24f3cc3d.jpg
72
+ maintenance:
73
+ contacts:
74
+ - name: Betrieb Webanwendungen
75
+ affiliation: Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung
76
+ contractors:
77
+ - name: Landesbetrieb Geoinformation und Vermessung
78
+ until: '2025-12-01'
79
+ website: 'https://www.hamburg.de/bsw/landesbetrieb-geoinformation-und-vermessung/'
80
+ type: community
81
+ name: Masterportal
82
+ platforms:
83
+ - web
84
+ releaseDate: '2025-04-29'
85
+ roadmap: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/src/master/'
86
+ softwareType: api
87
+ softwareVersion: 2.49.0
88
+ url: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi.git'
package/src/layer/oaf.js CHANGED
@@ -23,6 +23,7 @@ import * as webgl from "../renderer/webgl";
23
23
  * @property {function} beforeLoading - function called before loading
24
24
  * @property {function} afterLoading - function called after loading, gets parameter features
25
25
  * @property {function} featuresFilter - function called after loading to filter features, gets parameter features
26
+ * @property {function} doNotLoadInitially - default false, if set to true the layer will be initialized without network call, an empty array of features is promoted - this is useful to add features later on (e.g. by filtering) to a full functional but initially empty layer
26
27
  * @property {function} clusterGeometryFunction - used in cluster source. Returns the geometry of the cluster, gets parameter feature
27
28
  * @property {function} style - style function to style the layer
28
29
  * @property {object} loadingParams - added as params to url
@@ -173,6 +174,10 @@ function getLayerSource (rawLayer, options) {
173
174
  let source = null;
174
175
 
175
176
  function loader (extent, _, __, success, failure) {
177
+ if (options.doNotLoadInitially) {
178
+ onLoad(source, [], onError, success, failure, options);
179
+ return;
180
+ }
176
181
  const bboxParam = options.loadingStrategy === bbox ? extent : options.loadingParams.bbox,
177
182
  url = createUrl(rawLayer, {
178
183
  ...options.loadingParams,
package/src/layer/wmts.js CHANGED
@@ -186,7 +186,7 @@ function createLayer (rawLayer, layerParams = {}) {
186
186
  infoFormat: rawLayer.infoFormat
187
187
  }, layerParams));
188
188
 
189
- if (rawLayer.optionsFromCapabilities === undefined || rawLayer.capabilitiesUrl === undefined) {
189
+ if (!rawLayer.optionsFromCapabilities || !rawLayer.capabilitiesUrl) {
190
190
  createLayerSourceByDefinitions(rawLayer, tileLayer);
191
191
  }
192
192
  else {
@@ -1,27 +1,38 @@
1
- import WebGLPointsLayer from "ol/layer/WebGLVector";
2
1
  import WebGLVectorLayerRenderer from "ol/renderer/webgl/VectorLayer";
3
2
  import WebGLVectorTileLayerRenderer from "ol/renderer/webgl/VectorTileLayer.js";
4
3
  import VectorTile from "ol/layer/VectorTile.js";
5
4
  import VectorLayer from "ol/layer/Layer";
5
+ import WebGLVectorLayer from "ol/layer/WebGLVector.js";
6
6
  import styleList from "../vectorStyle/styleList";
7
7
  import {getRulesForFeature} from "../vectorStyle/lib/getRuleForIndex";
8
8
  import {returnColor} from "../vectorStyle/lib/colorConvertions";
9
- import {asArray} from "ol/color.js";
10
- import {packColor, parseLiteralStyle} from "ol/webgl/styleparser.js";
11
9
 
12
10
  /**
13
11
  * The default style for OpenLayers WebGLPoints class
14
12
  * @see https://openlayers.org/en/latest/examples/webgl-points-layer.html
15
13
  * @private
16
14
  */
17
- const defaultPointStyle = {
18
- "circle-radius": 10,
19
- "circle-fill-color": "#006688",
20
- "circle-rotate-with-view": false,
21
- "circle-displacement": [0, 0],
22
- "circle-opacity": 0.6
23
- },
24
-
15
+ const defaultPointStyle = [
16
+ {
17
+ style: {
18
+ "circle-fill-color": ["match", ["get", "selected"], 1, "#0c2026", "#006688"],
19
+ "circle-radius": [
20
+ "interpolate",
21
+ ["exponential", 1.5],
22
+ ["zoom"],
23
+ 3, 6,
24
+ 6, 10,
25
+ 9, 20,
26
+ 12, 35,
27
+ 15, 60,
28
+ 18, 110
29
+ ],
30
+ "circle-rotate-with-view": false,
31
+ "circle-displacement": [0, 0],
32
+ "circle-opacity": 0.6
33
+ }
34
+ }
35
+ ],
25
36
  defaultPolygonStyle = {
26
37
  fillColor: "#006688",
27
38
  strokeColor: "#006688",
@@ -52,6 +63,11 @@ export function createVectorLayerRenderer () {
52
63
  * @description the temporary class with a custom renderer to render the vector data with WebGL
53
64
  */
54
65
  class LocalWebGLLayer extends VectorLayer {
66
+ constructor (options) {
67
+ super(options);
68
+ // Initialize with any default style variables
69
+ this.styleVariables = options.styleVariables || {};
70
+ }
55
71
  /**
56
72
  * Creates a new renderer that takes the defined style of the new layer as an input.
57
73
  * @returns {module:ol/renderer/webgl/WebGLVectorLayerRenderer} the custom renderer
@@ -62,6 +78,13 @@ export function createVectorLayerRenderer () {
62
78
  style
63
79
  });
64
80
  }
81
+ /**
82
+ * Get the current style variables.
83
+ * @returns {Object} - The current style variables.
84
+ */
85
+ getStyleVariables () {
86
+ return this.styleVariables;
87
+ }
65
88
  }
66
89
 
67
90
  return LocalWebGLLayer;
@@ -72,9 +95,10 @@ export function createVectorLayerRenderer () {
72
95
  * @augments VectorTile
73
96
  * @private
74
97
  * @implements {WebGLVectorTileLayerRenderer}
98
+ * @param {Object} attrs - the attributes of the layer
75
99
  * @returns {module:ol/layer/VectorTile} the LocalWebGLLayer with a custom renderer for WebGL styling of vectorTile layers.
76
100
  */
77
- export function createVectorTileLayerRenderer () {
101
+ export function createVectorTileLayerRenderer (attrs) {
78
102
  /**
79
103
  * @class LocalWebGLLayer
80
104
  * @see https://openlayers.org/en/latest/examples/webgl-vector-tiles.html
@@ -88,70 +112,8 @@ export function createVectorTileLayerRenderer () {
88
112
  * @experimental
89
113
  */
90
114
  createRenderer () {
91
- const result = parseLiteralStyle(style),
92
- invisible = [255, 255, 255, 0],
93
- grey = [119, 119, 119, 1];
94
-
95
-
96
115
  return new WebGLVectorTileLayerRenderer(this, {
97
- style: {
98
- fill: {
99
- fragment: result.builder.getFillFragmentShader(),
100
- vertex: result.builder.getFillVertexShader()
101
- },
102
- stroke: {
103
- fragment: result.builder.getStrokeFragmentShader(),
104
- vertex: result.builder.getStrokeVertexShader()
105
- },
106
- symbol: {
107
- fragment: result.builder.getSymbolFragmentShader(),
108
- vertex: result.builder.getSymbolVertexShader()
109
- },
110
- builder: result.builder,
111
- attributes: {
112
- fillColor: {
113
- size: 2,
114
- callback: (feature) => {
115
- const featureStyle = this.getStyle()(feature, 1);
116
-
117
- if (featureStyle && featureStyle[0]) {
118
- const color = featureStyle[0]?.getFill()?.getColor();
119
-
120
- return packColor(color && typeof color === "string" ? asArray(color) : invisible);
121
- }
122
- return packColor(grey);
123
- }
124
- },
125
- strokeColor: {
126
- size: 2,
127
- callback: (feature) => {
128
- const featureStyle = this.getStyle()(feature, 1);
129
-
130
- if (featureStyle && featureStyle[0]) {
131
- const color = featureStyle[0]?.getStroke()?.getColor();
132
-
133
- return packColor(color ? asArray(color) : invisible);
134
- }
135
- return packColor(grey);
136
- }
137
- },
138
- strokeWidth: {
139
- size: 1,
140
- callback: (feature) => {
141
- const featureStyle = this.getStyle()(feature, 1);
142
-
143
- if (featureStyle && featureStyle[0]) {
144
- const width = featureStyle[0]?.getStroke()?.getWidth();
145
-
146
- if (width !== undefined) {
147
- return width > 10 ? width / 10 : 1;/** @todo needs refactoring in later versions */
148
- }
149
- }
150
- return 1;
151
- }
152
- }
153
- }
154
- }
116
+ style: attrs.style ? attrs.style : style
155
117
  });
156
118
  }
157
119
  }
@@ -275,10 +237,10 @@ export function afterLoading (features, styleId, excludeTypesFromParsing, isPoin
275
237
  * Creates the OL Layer instance, used to rebuild the layer when shown again after layer has been disposed
276
238
  * @param {Object} attrs - the attributes of the layer
277
239
  * @public
278
- * @returns {VectorLayer | WebGLPointsLayer} returns the layer instance
240
+ * @returns {VectorLayer | WebGLVectorLayer} returns the layer instance
279
241
  */
280
242
  export function createLayer (attrs) {
281
- let LayerConstructor = WebGLPointsLayer;
243
+ let LayerConstructor = WebGLVectorLayer;
282
244
  const options = {
283
245
  id: attrs.id,
284
246
  source: attrs.source,
@@ -292,7 +254,8 @@ export function createLayer (attrs) {
292
254
  renderer: "webgl",
293
255
  styleId: attrs.styleId,
294
256
  excludeTypesFromParsing: attrs.excludeTypesFromParsing,
295
- extent: attrs.extent
257
+ extent: attrs.extent,
258
+ zIndex: attrs.zIndex
296
259
  };
297
260
 
298
261
  /**
@@ -274,6 +274,30 @@ function addToStyleList (jsonStyles) {
274
274
  });
275
275
  }
276
276
 
277
+ /**
278
+ * Fetches the style.json file and adds the style for the styleId to the style list, if contained in style.json.
279
+ * @param {object} [Config] - the Config.js object
280
+ * @param {string} [styleId] - the styleId to add
281
+ * @returns {object} the style object for the styleId
282
+ */
283
+ async function initStyleAndAddToList (Config, styleId) {
284
+ let style;
285
+
286
+ await fetch(Config.styleConf)
287
+ .then(response => response.json())
288
+ .then(data => {
289
+ style = data.find(aStyle => aStyle.styleId === styleId);
290
+
291
+ if (style) {
292
+ styleList.push(style);
293
+ }
294
+ })
295
+ .catch(error => {
296
+ console.error(error);
297
+ });
298
+ return style;
299
+ }
300
+
277
301
  /**
278
302
  * Returns style object by styleId or by layerId
279
303
  * @param {string} layerId layerId
@@ -285,6 +309,7 @@ function returnStyleObject (layerId) {
285
309
 
286
310
  export default {
287
311
  initializeStyleList,
312
+ initStyleAndAddToList,
288
313
  addToStyleList,
289
314
  returnStyleObject
290
315
  };
@@ -52,6 +52,16 @@ describe("oaf.js", function () {
52
52
  expect(layer.get("id")).toEqual("id");
53
53
  expect(layer.getSource()).toBeInstanceOf(VectorSource);
54
54
  });
55
+ it("creates empty layer if doNotLoadInitially is true", function () {
56
+ const options = {
57
+ doNotLoadInitially: true
58
+ },
59
+ layer = oaf.createLayer({id: "id"}, {options});
60
+
61
+ expect(layer).toBeInstanceOf(VectorLayer);
62
+ expect(layer.getSource()).toBeInstanceOf(VectorSource);
63
+ expect(layer.getSource().getFeatures().length).toEqual(0);
64
+ });
55
65
  it("creates a VectorLayer with style in rawLayer", function () {
56
66
  function styleFunction () {
57
67
  const icon = new Style({
@@ -5,7 +5,7 @@ import Feature from "ol/Feature";
5
5
  import Polygon from "ol/geom/Polygon";
6
6
  import MultiPoint from "ol/geom/MultiPoint";
7
7
  import * as webgl from "../../src/renderer/webgl";
8
- import WebGLPointsLayer from "ol/layer/WebGLVector";
8
+ import WebGLVectorLayer from "ol/layer/WebGLVector.js";
9
9
  import Layer from "ol/layer/Layer";
10
10
  import {returnColor} from "../../src/vectorStyle/lib/colorConvertions";
11
11
 
@@ -18,7 +18,7 @@ jest.mock("../../src/vectorStyle/styleList.js", () => {
18
18
  });
19
19
 
20
20
  describe("webgl.js", () => {
21
- let layerParams, layerParamsVectorTile, rawLayer, rawLayerVectorTile, styleRule, styleObject, defaultStyle;
21
+ let layerParams, layerParamsVectorTile, rawLayer, rawLayerVectorTile, styleRule, styleObject, defaultPointStyle;
22
22
 
23
23
  beforeEach(() => {
24
24
  layerParams = {
@@ -68,23 +68,30 @@ describe("webgl.js", () => {
68
68
  styleObject = {
69
69
  rules: [styleRule]
70
70
  };
71
- defaultStyle = {
71
+ defaultPointStyle = {
72
72
  fillColor: "#006688",
73
73
  strokeColor: "#006688",
74
74
  strokeWidth: 1,
75
75
  opacity: 1,
76
- size: 20
76
+ zoom: [
77
+ 3, 6,
78
+ 6, 10,
79
+ 9, 20,
80
+ 12, 35,
81
+ 15, 60,
82
+ 18, 110
83
+ ]
77
84
  };
78
85
  });
79
86
 
80
87
  describe("createLayer", () => {
81
88
  let source = new VectorSource();
82
89
 
83
- it("should return a WebGLPointsLayer", () => {
90
+ it("should return a WebGLVectorLayer", () => {
84
91
  layerParams.isPointLayer = true;
85
92
  const webglLayer = webgl.createLayer({...rawLayer, ...layerParams, source});
86
93
 
87
- expect(webglLayer).toBeInstanceOf(WebGLPointsLayer);
94
+ expect(webglLayer).toBeInstanceOf(WebGLVectorLayer);
88
95
  expect(webglLayer.get("isPointLayer")).toBeTruthy();
89
96
  });
90
97
  it("should return a custom Layer", () => {
@@ -92,7 +99,7 @@ describe("webgl.js", () => {
92
99
  const webglLayer = webgl.createLayer({...rawLayer, ...layerParams, source});
93
100
 
94
101
  expect(webglLayer).toBeInstanceOf(Layer);
95
- expect(webglLayer).not.toBeInstanceOf(WebGLPointsLayer);
102
+ expect(webglLayer).not.toBeInstanceOf(WebGLVectorLayer);
96
103
  expect(webglLayer.get("isPointLayer")).toBeFalsy();
97
104
  });
98
105
  it("vectorTile: should return a VectorTile layer", () => {
@@ -102,7 +109,7 @@ describe("webgl.js", () => {
102
109
 
103
110
  expect(webglLayer).toBeInstanceOf(Layer);
104
111
  expect(webglLayer).toBeInstanceOf(VectorTile);
105
- expect(webglLayer).not.toBeInstanceOf(WebGLPointsLayer);
112
+ expect(webglLayer).not.toBeInstanceOf(WebGLVectorLayer);
106
113
  expect(webglLayer.get("isPointLayer")).toBeFalsy();
107
114
  });
108
115
  });
@@ -187,12 +194,46 @@ describe("webgl.js", () => {
187
194
  rules: [styleRule]
188
195
  };
189
196
  webgl.formatFeatureStyles($feature, styleObject);
190
- expect($feature.get("fillColor")).toEqual(defaultStyle.fillColor);
191
- expect($feature.get("strokeColor")).toEqual(defaultStyle.strokeColor);
192
- expect($feature.get("strokeWidth")).toEqual(defaultStyle.strokeWidth);
193
- expect($feature.get("opacity")).toEqual(defaultStyle.opacity);
194
- expect($feature.get("circleSize")).toEqual(defaultStyle.size);
197
+ expect($feature.get("fillColor")).toEqual(defaultPointStyle.fillColor);
198
+ expect($feature.get("strokeColor")).toEqual(defaultPointStyle.strokeColor);
199
+ expect($feature.get("strokeWidth")).toEqual(defaultPointStyle.strokeWidth);
200
+ expect($feature.get("opacity")).toEqual(defaultPointStyle.opacity);
195
201
  });
202
+
203
+ it("should calculate circle-radius based on zoom level", () => {
204
+ const zoomLevels = [3, 6, 9, 12, 15, 18],
205
+ expectedRadii = [6, 10, 20, 35, 60, 110];
206
+
207
+ // eslint-disable-next-line no-use-before-define, max-nested-callbacks
208
+ zoomLevels.forEach((zoom, index) => testZoomLevel(zoom, index, expectedRadii));
209
+ });
210
+
211
+ function interpolateCircleRadius (zoom, zoomArray) {
212
+ for (let i = 0; i < zoomArray.length; i += 2) {
213
+ const z = zoomArray[i],
214
+ r = zoomArray[i + 1];
215
+
216
+ if (zoom <= z) {
217
+ if (i === 0) {
218
+ return r;
219
+ }
220
+ const zPrev = zoomArray[i - 2],
221
+ rPrev = zoomArray[i - 1],
222
+ t = (zoom - zPrev) / (z - zPrev);
223
+
224
+ return rPrev + (t * (r - rPrev));
225
+ }
226
+ }
227
+ return zoomArray[zoomArray.length - 1];
228
+ }
229
+
230
+ function testZoomLevel (zoom, index, expectedRadii) {
231
+ const expectedRadius = expectedRadii[index],
232
+ actualRadius = interpolateCircleRadius(zoom, defaultPointStyle.zoom);
233
+
234
+ expect(actualRadius).toEqual(expectedRadius);
235
+ }
236
+
196
237
  });
197
238
  });
198
239
  });