@masterportal/masterportalapi 2.61.0 → 2.63.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/.editorconfig +1 -1
- package/.eslintrc +0 -2
- package/CHANGELOG.md +42 -0
- package/CODE_OF_CONDUCT.md +84 -0
- package/CONTRIBUTING.md +70 -0
- package/package.json +14 -15
- package/publiccode.yml +2 -2
- package/src/api/ogcApiProcesses.js +1 -1
- package/src/maps/ol/olMap.js +3 -3
- package/src/maps/olcs/3dUtils/wmsRasterSynchronizer.js +1 -1
- package/src/searchAddress/parse.js +85 -27
- package/src/vectorStyle/createStyle.js +37 -5
- package/src/vectorStyle/styles/style.js +4 -2
- package/test/searchAddress/parse.test.js +37 -0
- package/test/vectorStyle/styles/style.test.js +70 -0
package/.editorconfig
CHANGED
package/.eslintrc
CHANGED
|
@@ -104,7 +104,6 @@
|
|
|
104
104
|
"no-void": "error",
|
|
105
105
|
"no-with": "error",
|
|
106
106
|
"radix": "error",
|
|
107
|
-
"vars-on-top": "error",
|
|
108
107
|
"yoda": "error",
|
|
109
108
|
"no-delete-var": "error",
|
|
110
109
|
"no-label-var": "error",
|
|
@@ -159,7 +158,6 @@
|
|
|
159
158
|
"no-whitespace-before-property": "error",
|
|
160
159
|
"object-curly-spacing": "error",
|
|
161
160
|
"object-property-newline": 0,
|
|
162
|
-
"one-var": "error",
|
|
163
161
|
"one-var-declaration-per-line": "error",
|
|
164
162
|
"quotes": "error",
|
|
165
163
|
"require-jsdoc": 0,
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
All important changes in this project are stored in this file.
|
|
3
3
|
|
|
4
4
|
The [Semantic Versioning](https://semver.org/spec/v2.0.0.html) is used.
|
|
5
|
+
|
|
6
|
+
## 2.63.0 - 2026-07-17
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- The following packages have been updated:
|
|
10
|
+
- dependencies:
|
|
11
|
+
- core-js: 3.33.1 to 3.49.0
|
|
12
|
+
- dayjs: 1.11.10 to 1.11.21
|
|
13
|
+
- proj4: 2.10.0 to 2.20.9
|
|
14
|
+
- devDependencies:
|
|
15
|
+
- @parcel/config-default: 2.15.4 to 2.16.4
|
|
16
|
+
- @parcel/transformer-sass: 2.15.4 to 2.16.4
|
|
17
|
+
- canvas: 3.2.0 to 3.2.3
|
|
18
|
+
- jest: 30.1.3 to 30.4.2
|
|
19
|
+
- jest-environment-jsdom: 30.1.2 to 30.4.1
|
|
20
|
+
- jsdoc: 4.0.2 to 4.0.5
|
|
21
|
+
- parcel: 2.15.4 to 2.16.4
|
|
22
|
+
- sass: 1.69.4 to 1.101.0
|
|
23
|
+
- url: 0.11.3 to 0.11.4
|
|
24
|
+
- @cesium/engine: 23.0.0 to 26.0.0
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Issue \#1623: fix label for configuration of style properties as array of objects.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2.62.0 - 2026-06-11
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Allow to override the OpenLayers Map class for 2D maps.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- Linting: Removed one-var and vars-on-top rules.
|
|
38
|
+
- Changed end_of_line to lf in editorconfig file.
|
|
39
|
+
- Issue #17: Replaced package xml2js with native DomParser for parsing xml of search result.
|
|
40
|
+
|
|
41
|
+
### Removed
|
|
42
|
+
- The following packages have been removed:
|
|
43
|
+
- dependencies:
|
|
44
|
+
- xml2js
|
|
45
|
+
|
|
46
|
+
---
|
|
5
47
|
|
|
6
48
|
## 2.61.0 - 2026-04-21
|
|
7
49
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
# Contributor Covenant Code of Conduct
|
|
3
|
+
|
|
4
|
+
## Our Pledge
|
|
5
|
+
|
|
6
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
7
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
9
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
10
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
11
|
+
identity and orientation.
|
|
12
|
+
|
|
13
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
14
|
+
diverse, inclusive, and healthy community.
|
|
15
|
+
|
|
16
|
+
## Our Standards
|
|
17
|
+
|
|
18
|
+
Examples of behavior that contributes to a positive environment for our
|
|
19
|
+
community include:
|
|
20
|
+
|
|
21
|
+
* Demonstrating empathy and kindness toward other people
|
|
22
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
23
|
+
* Giving and gracefully accepting constructive feedback
|
|
24
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
25
|
+
and learning from the experience
|
|
26
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
|
27
|
+
community
|
|
28
|
+
|
|
29
|
+
Examples of unacceptable behavior include:
|
|
30
|
+
|
|
31
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
32
|
+
any kind
|
|
33
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
34
|
+
* Public or private harassment
|
|
35
|
+
* Publishing others' private information, such as a physical or email address,
|
|
36
|
+
without their explicit permission
|
|
37
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
38
|
+
professional setting
|
|
39
|
+
|
|
40
|
+
## Enforcement Responsibilities
|
|
41
|
+
|
|
42
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
43
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
44
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
45
|
+
or harmful.
|
|
46
|
+
|
|
47
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
48
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
49
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
50
|
+
decisions when appropriate.
|
|
51
|
+
|
|
52
|
+
## Scope
|
|
53
|
+
|
|
54
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
55
|
+
an individual is officially representing the community in public spaces.
|
|
56
|
+
Examples of representing our community include using an official e-mail address,
|
|
57
|
+
posting via an official social media account, or acting as an appointed
|
|
58
|
+
representative at an online or offline event.
|
|
59
|
+
|
|
60
|
+
## Enforcement
|
|
61
|
+
|
|
62
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at geodatenanwendungen@gv.hamburg.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Attribution
|
|
68
|
+
|
|
69
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
70
|
+
version 2.1, available at
|
|
71
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
72
|
+
|
|
73
|
+
Community Impact Guidelines were inspired by
|
|
74
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
75
|
+
|
|
76
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
77
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
78
|
+
[https://www.contributor-covenant.org/translations][translations].
|
|
79
|
+
|
|
80
|
+
[homepage]: https://www.contributor-covenant.org
|
|
81
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
82
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
83
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
84
|
+
[translations]: https://www.contributor-covenant.org/translations
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Contributing to masterportalAPI
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to masterportalAPI. This document is the entry point for contributors.
|
|
4
|
+
|
|
5
|
+
If a policy, workflow, or convention is not defined in this repository, the corresponding rules from the [main Masterportal project](https://bitbucket.org/geowerkstatt-hamburg/masterportal/src/dev/) apply.
|
|
6
|
+
|
|
7
|
+
## Code of Conduct
|
|
8
|
+
|
|
9
|
+
All participants are expected to follow our [Code of Conduct](./CODE_OF_CONDUCT.md).
|
|
10
|
+
|
|
11
|
+
## Developer Documentation
|
|
12
|
+
|
|
13
|
+
The main developer documentation for this repository is available in these places:
|
|
14
|
+
|
|
15
|
+
- **[Project overview and usage](./README.md)** — Installation, package usage, error handling, and available scripts
|
|
16
|
+
- **[Example setup](./example/README.md)** — How to use the example application for manual testing
|
|
17
|
+
- **[API documentation generation](./README.md#scripts)** — Generate local JSDoc output with `npm run generate-jsdoc`
|
|
18
|
+
- **[Changelog](./CHANGELOG.md)** — Add your changes under `Unreleased`
|
|
19
|
+
- **[Package metadata](./package.json)** — Supported Node.js and npm versions, scripts, dependencies, and repository metadata
|
|
20
|
+
|
|
21
|
+
If you need guidance that is not documented in this repository, follow the corresponding documentation and contribution rules from the [main Masterportal project](https://bitbucket.org/geowerkstatt-hamburg/masterportal/src/dev/).
|
|
22
|
+
|
|
23
|
+
## Quick Reference
|
|
24
|
+
|
|
25
|
+
| What | Where |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Install dependencies | `npm install` |
|
|
28
|
+
| Start the example dev server | `npm run example` |
|
|
29
|
+
| Run tests | `npm test` |
|
|
30
|
+
| Pre-push check (ESLint + tests) | `npm run prePushHook` |
|
|
31
|
+
| Generate API docs | `npm run generate-jsdoc` |
|
|
32
|
+
|
|
33
|
+
## Asking Questions
|
|
34
|
+
|
|
35
|
+
If you have questions about using or developing with masterportalAPI, start by checking the [README](./README.md).
|
|
36
|
+
|
|
37
|
+
## Getting Familiar with the Code in the Repository
|
|
38
|
+
|
|
39
|
+
The repository is organized into a few main areas:
|
|
40
|
+
|
|
41
|
+
- `src/` contains the library source code
|
|
42
|
+
- `test/` contains the automated test suite
|
|
43
|
+
- `example/` contains a Parcel-based example app for manual testing
|
|
44
|
+
- `public/` contains public assets used by the example and runtime utilities
|
|
45
|
+
|
|
46
|
+
If you are new to the codebase, begin with [README.md](./README.md), then inspect the relevant modules in `src/` and the corresponding test files in `test/`.
|
|
47
|
+
|
|
48
|
+
## How to Contribute
|
|
49
|
+
|
|
50
|
+
1. **Fork the repository** — external developers do not receive write access. See Forks.
|
|
51
|
+
2. **Branch from dev** and follow the Git workflow.
|
|
52
|
+
3. **Write code** that adheres to the coding conventions.
|
|
53
|
+
4. **Add tests** for new or changed functionality.
|
|
54
|
+
5. **Update the changelog** — add an entry under `Unreleased` in [CHANGELOG.md](./CHANGELOG.md).
|
|
55
|
+
6. **Open a Pull Request** against dev. Reviewers will check against the Definition of Done.
|
|
56
|
+
|
|
57
|
+
## Reporting Issues
|
|
58
|
+
|
|
59
|
+
Use the [Bitbucket Issue Tracker](https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/issues). Please include:
|
|
60
|
+
|
|
61
|
+
- A clear description of the problem or feature request
|
|
62
|
+
- Steps to reproduce (for bugs)
|
|
63
|
+
- Expected vs. actual behavior
|
|
64
|
+
- npm and Node.js version
|
|
65
|
+
- Relevant configuration
|
|
66
|
+
- Screenshots, logs if they help explain the problem
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
By contributing, you agree that your contributions will be licensed under the [MIT License](./License.txt).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masterportal/masterportalapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.63.0",
|
|
4
4
|
"description": "Basic functions of the Masterportal as api",
|
|
5
5
|
"homepage": "https://bitbucket.org/geowerkstatt-hamburg/masterportalapi#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -33,43 +33,42 @@
|
|
|
33
33
|
"prepare": "husky install"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"core-js": "^3.
|
|
37
|
-
"dayjs": "^1.11.
|
|
36
|
+
"core-js": "^3.49.0",
|
|
37
|
+
"dayjs": "^1.11.21",
|
|
38
38
|
"ol": "10.9.0",
|
|
39
39
|
"ol-mapbox-style": "13.3.0",
|
|
40
40
|
"olcs": "^2.23.0",
|
|
41
|
-
"proj4": "^2.
|
|
42
|
-
"xml2js": "^0.6.2"
|
|
41
|
+
"proj4": "^2.20.9"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
|
-
"@parcel/config-default": "^2.
|
|
46
|
-
"@parcel/transformer-sass": "^2.
|
|
44
|
+
"@parcel/config-default": "^2.16.4",
|
|
45
|
+
"@parcel/transformer-sass": "^2.16.4",
|
|
47
46
|
"@swc/jest": "^0.2.39",
|
|
48
47
|
"assert": "^2.1.0",
|
|
49
48
|
"browserify-zlib": "^0.2.0",
|
|
50
49
|
"buffer": "^6.0.3",
|
|
51
|
-
"canvas": "3.2.
|
|
50
|
+
"canvas": "3.2.3",
|
|
52
51
|
"eslint": "8.57.1",
|
|
53
52
|
"events": "^3.3.0",
|
|
54
53
|
"https-browserify": "^1.0.0",
|
|
55
54
|
"husky": "8.0.3",
|
|
56
|
-
"jest": "30.
|
|
55
|
+
"jest": "30.4.2",
|
|
57
56
|
"jest-canvas-mock": "2.5.2",
|
|
58
|
-
"jest-environment-jsdom": "30.1
|
|
57
|
+
"jest-environment-jsdom": "30.4.1",
|
|
59
58
|
"jest-fetch-mock": "^3.0.3",
|
|
60
|
-
"jsdoc": "4.0.
|
|
61
|
-
"parcel": "^2.
|
|
59
|
+
"jsdoc": "4.0.5",
|
|
60
|
+
"parcel": "^2.16.4",
|
|
62
61
|
"process": "^0.11.10",
|
|
63
|
-
"sass": "1.
|
|
62
|
+
"sass": "1.101.0",
|
|
64
63
|
"stream-browserify": "^3.0.0",
|
|
65
64
|
"stream-http": "^3.2.0",
|
|
66
65
|
"timers-browserify": "^2.0.12",
|
|
67
|
-
"url": "^0.11.
|
|
66
|
+
"url": "^0.11.4",
|
|
68
67
|
"use-resize-observer": "^9.1.0",
|
|
69
68
|
"util": "^0.12.5"
|
|
70
69
|
},
|
|
71
70
|
"peerDependencies": {
|
|
72
|
-
"@cesium/engine": "^
|
|
71
|
+
"@cesium/engine": "^26.0.0"
|
|
73
72
|
},
|
|
74
73
|
"engines": {
|
|
75
74
|
"node": "^22.19.0 || ^24.11.1",
|
package/publiccode.yml
CHANGED
|
@@ -81,8 +81,8 @@ maintenance:
|
|
|
81
81
|
name: Masterportal
|
|
82
82
|
platforms:
|
|
83
83
|
- web
|
|
84
|
-
releaseDate: '2026-
|
|
84
|
+
releaseDate: '2026-07-17'
|
|
85
85
|
roadmap: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/src/master/'
|
|
86
86
|
softwareType: api
|
|
87
|
-
softwareVersion: 2.
|
|
87
|
+
softwareVersion: 2.63.0
|
|
88
88
|
url: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi.git'
|
|
@@ -202,7 +202,7 @@ async function jobResults (processUrl, jobId, format = "json") {
|
|
|
202
202
|
* @param {number?} [pollParameters.timeout=5000] – how long to try polling in ms
|
|
203
203
|
* @param {number?} [pollParameters.stallFor=100] – how long to wait between retries
|
|
204
204
|
* @returns {object} job results as json
|
|
205
|
-
* @throws
|
|
205
|
+
* @throws {Error}
|
|
206
206
|
*/
|
|
207
207
|
async function pollJobResults (processUrl, jobId, {timeout = 5000, stallFor = 100} = {}) {
|
|
208
208
|
const start = Date.now();
|
package/src/maps/ol/olMap.js
CHANGED
|
@@ -146,16 +146,16 @@ Map.prototype.addLayer = addLayer;
|
|
|
146
146
|
* @param {object} [settings.mapParams] - additional parameter object that is spread into the ol.Map constructor object
|
|
147
147
|
* @param {function} [settings.callback] - optional callback for layer list loading
|
|
148
148
|
* @param {function} [settings.errorCallback] – method called on error events
|
|
149
|
-
* @param {
|
|
149
|
+
* @param {class} [OlMap] - The OpenLayers map class
|
|
150
150
|
* @returns {object} map object from ol
|
|
151
151
|
*/
|
|
152
|
-
export function createMap (config = defaults, {mapParams, callback, errorCallback} = {}) {
|
|
152
|
+
export function createMap (config = defaults, {mapParams, callback, errorCallback} = {}, OlMap = Map) {
|
|
153
153
|
crs.registerProjections(config.namedProjections);
|
|
154
154
|
setBackgroundImage(config);
|
|
155
155
|
setGazetteerUrl(config.gazetteerUrl);
|
|
156
156
|
|
|
157
157
|
const selectedInteractions = Object.assign({}, {dragPan: false, altShiftDragRotate: false, pinchRotate: false}, config.mapInteractions?.interactionModes),
|
|
158
|
-
map = new
|
|
158
|
+
map = new OlMap(Object.assign({
|
|
159
159
|
target: config.target || defaults.target,
|
|
160
160
|
interactions: olDefaultInteractions(selectedInteractions).extend([
|
|
161
161
|
new DragPan({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module olcs.WMSRasterSynchronizer
|
|
3
3
|
*/
|
|
4
4
|
import olLayerGroup from "ol/layer/Group.js";
|
|
5
|
-
import {getUid} from "olcs/
|
|
5
|
+
import {getUid} from "olcs/util.js";
|
|
6
6
|
import TileWMS from "ol/source/TileWMS.js";
|
|
7
7
|
import ImageWMS from "ol/source/ImageWMS.js";
|
|
8
8
|
import WMTS from "ol/source/WMTS.js";
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import xml2js from "xml2js";
|
|
2
|
-
|
|
3
1
|
import {searchTypes} from "./types";
|
|
4
2
|
import {getShowGeographicIdentifier} from "./showGeographicIdentifier";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
|
-
* Flattens
|
|
8
|
-
* NOTE currently keeping special properties ($, _)
|
|
5
|
+
* Flattens result a little to avoid unnecessary one-element arrays.
|
|
6
|
+
* NOTE currently keeping special properties ($, _) to keep attributes.
|
|
9
7
|
* @param {*} a anything
|
|
10
8
|
* @returns {*} a's first element if one-element array, else a
|
|
11
9
|
* @ignore
|
|
@@ -40,7 +38,7 @@ function findKey (o, key) {
|
|
|
40
38
|
|
|
41
39
|
/**
|
|
42
40
|
* Simply copies over all properties it can find.
|
|
43
|
-
* @param {object} entry
|
|
41
|
+
* @param {object} entry parsed result entry
|
|
44
42
|
* @returns {object} copied and flattened properties
|
|
45
43
|
* @ignore
|
|
46
44
|
*/
|
|
@@ -73,7 +71,7 @@ function parseCoordinatesFromWkt (wkt) {
|
|
|
73
71
|
|
|
74
72
|
/**
|
|
75
73
|
* Parses result entries; uses type-specific rules to determine name/geometry.
|
|
76
|
-
* @param {object} entry
|
|
74
|
+
* @param {object} entry parsed result entry
|
|
77
75
|
* @param {string} type type string
|
|
78
76
|
* @returns {SearchResult} parsed search result for districts
|
|
79
77
|
* @ignore
|
|
@@ -162,6 +160,72 @@ function parseEntry (entry, type) {
|
|
|
162
160
|
return searchResult;
|
|
163
161
|
}
|
|
164
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Converts a DOM element to an object structure.
|
|
165
|
+
* Strips namespace prefixes from tag names.
|
|
166
|
+
* @param {Element} node DOM element to convert
|
|
167
|
+
* @returns {object|string} converted object or text content
|
|
168
|
+
* @ignore
|
|
169
|
+
*/
|
|
170
|
+
function domNodeToObject (node) {
|
|
171
|
+
const elementChildren = Array.from(node.children),
|
|
172
|
+
obj = {};
|
|
173
|
+
|
|
174
|
+
// handle attributes
|
|
175
|
+
if (node.attributes.length > 0) {
|
|
176
|
+
obj.$ = {};
|
|
177
|
+
for (const attr of node.attributes) {
|
|
178
|
+
obj.$[attr.localName] = attr.value;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (elementChildren.length === 0) {
|
|
183
|
+
const text = node.textContent?.trim() ?? "";
|
|
184
|
+
|
|
185
|
+
if (obj.$) {
|
|
186
|
+
obj._ = text;
|
|
187
|
+
return obj;
|
|
188
|
+
}
|
|
189
|
+
return text;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
elementChildren.forEach(child => {
|
|
193
|
+
const key = child.localName,
|
|
194
|
+
value = domNodeToObject(child);
|
|
195
|
+
|
|
196
|
+
if (obj[key]) {
|
|
197
|
+
obj[key].push(value);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
obj[key] = [value];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return obj;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Parses an XML string using DOMParser.
|
|
209
|
+
* @param {string} xmlString xml to parse
|
|
210
|
+
* @returns {object} parsed object structure
|
|
211
|
+
* @ignore
|
|
212
|
+
*/
|
|
213
|
+
function parseXmlString (xmlString) {
|
|
214
|
+
const parser = new DOMParser(),
|
|
215
|
+
doc = parser.parseFromString(xmlString, "text/xml"),
|
|
216
|
+
parseError = doc.querySelector("parsererror");
|
|
217
|
+
|
|
218
|
+
if (parseError) {
|
|
219
|
+
throw new Error(`XML parse error: ${parseError.textContent}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const root = doc.documentElement,
|
|
223
|
+
result = {};
|
|
224
|
+
|
|
225
|
+
result[root.localName] = domNodeToObject(root);
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
165
229
|
/**
|
|
166
230
|
* Parses gazetteer xml to search objects.
|
|
167
231
|
* @param {string} key internal name for query froms searchTypes
|
|
@@ -171,26 +235,20 @@ function parseEntry (entry, type) {
|
|
|
171
235
|
*/
|
|
172
236
|
export function parse (key, xmlString) {
|
|
173
237
|
return new Promise((resolve, reject) => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
: [];
|
|
188
|
-
|
|
189
|
-
resolve(searchResults);
|
|
190
|
-
}
|
|
191
|
-
catch (e) {
|
|
192
|
-
reject(e);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
238
|
+
try {
|
|
239
|
+
const source = parseXmlString(xmlString),
|
|
240
|
+
searchResults = source.FeatureCollection?.member
|
|
241
|
+
? source.FeatureCollection.member
|
|
242
|
+
.map(entry => parseEntry(entry, key))
|
|
243
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
244
|
+
: [];
|
|
245
|
+
|
|
246
|
+
resolve(searchResults);
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
reject(e);
|
|
250
|
+
}
|
|
195
251
|
});
|
|
196
252
|
}
|
|
253
|
+
|
|
254
|
+
|
|
@@ -9,6 +9,35 @@ import {getRuleForIndex, getRulesForFeature} from "./lib/getRuleForIndex";
|
|
|
9
9
|
|
|
10
10
|
const legendsOfAllStyles = [];
|
|
11
11
|
let legendInformationLength = 0;
|
|
12
|
+
const warnedLayerIdsWithDefaultStyleFallback = new Set();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Warns once per layer when no styling rule matches and default styles are used.
|
|
16
|
+
* @param {object} [warningContext={}] context for the warning
|
|
17
|
+
* @param {string} [warningContext.layerId] id of the affected layer
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
function warnAboutDefaultStyleFallback (warningContext = {}) {
|
|
21
|
+
const {layerId} = warningContext;
|
|
22
|
+
|
|
23
|
+
if (typeof layerId === "string" && layerId.length > 0) {
|
|
24
|
+
if (warnedLayerIdsWithDefaultStyleFallback.has(layerId)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
warnedLayerIdsWithDefaultStyleFallback.add(layerId);
|
|
28
|
+
console.warn(`No valid styling rule found for layer "${layerId}". Falling back to defaults.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.warn("No valid styling rule found. Falling back to defaults");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Clears cached default-style fallback warnings.
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
function clearDefaultStyleFallbackWarnings () {
|
|
39
|
+
warnedLayerIdsWithDefaultStyleFallback.clear();
|
|
40
|
+
}
|
|
12
41
|
|
|
13
42
|
/**
|
|
14
43
|
* Returns true if feature contains some kind of MultiGeometry
|
|
@@ -152,9 +181,10 @@ function getMultiGeometryStyle (geometryType, feature, rules, isClustered, wfsIm
|
|
|
152
181
|
* @param {object[]} rules styling rules to check. Array can be empty.
|
|
153
182
|
* @param {Boolean} isClustered Flag to show if feature is clustered.
|
|
154
183
|
* @param {String} wfsImgPathFromConfig path to wfsImg from Config
|
|
184
|
+
* @param {object} [warningContext={}] context for warning deduplication
|
|
155
185
|
* @returns {ol/style/Style} style is always returned
|
|
156
186
|
*/
|
|
157
|
-
function getGeometryStyle (feature, rules, isClustered, wfsImgPathFromConfig) {
|
|
187
|
+
function getGeometryStyle (feature, rules, isClustered, wfsImgPathFromConfig, warningContext = {}) {
|
|
158
188
|
const geometryType = feature ? feature.getGeometry().getType() : "cesium";
|
|
159
189
|
|
|
160
190
|
// For simple geometries the first styling rule is used.
|
|
@@ -168,7 +198,7 @@ function getGeometryStyle (feature, rules, isClustered, wfsImgPathFromConfig) {
|
|
|
168
198
|
}
|
|
169
199
|
|
|
170
200
|
// fall back to default styles as configured in geomType specific styles, if no rule is matched
|
|
171
|
-
|
|
201
|
+
warnAboutDefaultStyleFallback(warningContext);
|
|
172
202
|
return isMultiGeometry(geometryType)
|
|
173
203
|
? getMultiGeometryStyle(geometryType, feature, undefined, isClustered, wfsImgPathFromConfig)
|
|
174
204
|
: getSimpleGeometryStyle(geometryType, feature, undefined, isClustered, wfsImgPathFromConfig);
|
|
@@ -224,14 +254,15 @@ function captureLegendFromFeature (styleId, legendInformation) {
|
|
|
224
254
|
* @param {ol/feature} feature the feature to style
|
|
225
255
|
* @param {Boolean} isClustered is feature clustered
|
|
226
256
|
* @param {String} wfsImgPathFromConfig path to wfsImg from Config
|
|
227
|
-
|
|
257
|
+
* @param {object} [warningContext={}] context for warning deduplication
|
|
258
|
+
* @returns {ol/style/Style} style used in layer model
|
|
228
259
|
*/
|
|
229
|
-
function createStyle (styleObject, feature, isClustered, wfsImgPathFromConfig) {
|
|
260
|
+
function createStyle (styleObject, feature, isClustered, wfsImgPathFromConfig, warningContext = {}) {
|
|
230
261
|
const rules = getRulesForFeature(styleObject, feature),
|
|
231
262
|
// Takes first rule in array for labeling so that is giving precedence to the order in the style.json
|
|
232
263
|
style = Array.isArray(rules) && rules.length > 0 ? rules[0].style : null,
|
|
233
264
|
hasLabelField = style?.labelField,
|
|
234
|
-
geometryStyle = getGeometryStyle(feature, rules, isClustered, wfsImgPathFromConfig),
|
|
265
|
+
geometryStyle = getGeometryStyle(feature, rules, isClustered, wfsImgPathFromConfig, warningContext),
|
|
235
266
|
legendInformation = Array.isArray(geometryStyle) ? geometryStyle[0].legendInfos : geometryStyle.legendInfos;
|
|
236
267
|
let styleObjectGeometry = null;
|
|
237
268
|
|
|
@@ -327,5 +358,6 @@ export default {
|
|
|
327
358
|
getGeometryStyle,
|
|
328
359
|
getLabelStyle,
|
|
329
360
|
createStyle,
|
|
361
|
+
clearDefaultStyleFallbackWarnings,
|
|
330
362
|
returnLegendByStyleId
|
|
331
363
|
};
|
|
@@ -106,9 +106,12 @@ class StyleClass {
|
|
|
106
106
|
else if (rule?.conditions) {
|
|
107
107
|
let label = "";
|
|
108
108
|
|
|
109
|
-
if (rule.conditions?.properties) {
|
|
109
|
+
if (rule.conditions?.properties && !Array.isArray(rule.conditions.properties)) {
|
|
110
110
|
label = Object.values(rule.conditions.properties).join(", ");
|
|
111
111
|
}
|
|
112
|
+
if (Array.isArray(rule.conditions?.properties)) {
|
|
113
|
+
label = rule.conditions.properties.map(prop => prop.value).join(", ");
|
|
114
|
+
}
|
|
112
115
|
|
|
113
116
|
if (rule.conditions?.sequence
|
|
114
117
|
&& Array.isArray(rule.conditions.sequence)
|
|
@@ -117,7 +120,6 @@ class StyleClass {
|
|
|
117
120
|
&& rule.conditions.sequence[1] >= rule.conditions.sequence[0]) {
|
|
118
121
|
label = label + " (" + rule.conditions.sequence.join("-") + ")";
|
|
119
122
|
}
|
|
120
|
-
|
|
121
123
|
return label;
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -106,5 +106,42 @@ describe("searchAddress", function () {
|
|
|
106
106
|
done();
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
|
+
|
|
110
|
+
describe("domNodeToObject - strips namespace prefixes from tag names", () => {
|
|
111
|
+
it("should not contain colon in any parsed key", async () => {
|
|
112
|
+
const x = await parse(searchTypes.DISTRICT, gazetteerSearchResults.districtResult);
|
|
113
|
+
// eslint-disable-next-line func-style
|
|
114
|
+
const allKeys = (obj) => {
|
|
115
|
+
if (typeof obj !== "object" || obj === null) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
119
|
+
return Object.keys(obj).flatMap(k => [k, ...allKeys(obj[k])]);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
allKeys(x[0]).forEach(key => {
|
|
123
|
+
expect(key).not.toContain(":");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should parse namespace-prefixed XML and return localName as key", () => {
|
|
128
|
+
const parser = new DOMParser();
|
|
129
|
+
const xml = `
|
|
130
|
+
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"
|
|
131
|
+
xmlns:gml="http://www.opengis.net/gml">
|
|
132
|
+
<wfs:member>
|
|
133
|
+
<gml:Point>
|
|
134
|
+
<gml:pos>10.0 53.5</gml:pos>
|
|
135
|
+
</gml:Point>
|
|
136
|
+
</wfs:member>
|
|
137
|
+
</wfs:FeatureCollection>`;
|
|
138
|
+
const doc = parser.parseFromString(xml, "text/xml");
|
|
139
|
+
|
|
140
|
+
expect(doc.documentElement.localName).toBe("FeatureCollection");
|
|
141
|
+
expect(doc.documentElement.children[0].localName).toBe("member");
|
|
142
|
+
expect(doc.documentElement.children[0].children[0].localName).toBe("Point");
|
|
143
|
+
expect(doc.documentElement.children[0].children[0].children[0].localName).toBe("pos");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
109
146
|
});
|
|
110
147
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import StyleClass from "../../../src/vectorStyle/styles/style";
|
|
2
|
+
|
|
3
|
+
describe("StyleClass", () => {
|
|
4
|
+
describe("createLegendLabel", () => {
|
|
5
|
+
it("returns legendValue with highest precedence", () => {
|
|
6
|
+
const style = new StyleClass(),
|
|
7
|
+
label = style.createLegendLabel({
|
|
8
|
+
conditions: {
|
|
9
|
+
properties: {
|
|
10
|
+
category: "Rule Label"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
attributes: {
|
|
16
|
+
legendValue: 123
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(label).toBe("123");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("creates label from properties configured as array of objects", () => {
|
|
24
|
+
const style = new StyleClass(),
|
|
25
|
+
label = style.createLegendLabel({
|
|
26
|
+
conditions: {
|
|
27
|
+
properties: [
|
|
28
|
+
{
|
|
29
|
+
key: "category",
|
|
30
|
+
value: "A"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: "category",
|
|
34
|
+
value: "B"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
attributes: {}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(label).toBe("A, B");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("appends valid sequence to created label", () => {
|
|
47
|
+
const style = new StyleClass(),
|
|
48
|
+
label = style.createLegendLabel({
|
|
49
|
+
conditions: {
|
|
50
|
+
properties: {
|
|
51
|
+
category: "A"
|
|
52
|
+
},
|
|
53
|
+
sequence: [10, 20]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
attributes: {}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(label).toBe("A (10-20)");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("returns null if no legendValue and no rule conditions exist", () => {
|
|
64
|
+
const style = new StyleClass(),
|
|
65
|
+
label = style.createLegendLabel(undefined, {attributes: {}});
|
|
66
|
+
|
|
67
|
+
expect(label).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|