@masterportal/masterportalapi 2.11.0 → 2.13.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
@@ -3,6 +3,47 @@
3
3
 
4
4
  The [Semantic Versioning](https://semver.org/spec/v2.0.0.html) is used.
5
5
 
6
+ ## Unreleased - in development
7
+ ### Added
8
+ ### Changed
9
+ - GeoJSON now also register a featuresloadend function if the source is a cluster.
10
+ ### Deprecated
11
+ ### Removed
12
+ ### Fixed
13
+
14
+ ---
15
+
16
+ ## 2.13.0 - 2022-02-16
17
+ ### Changed
18
+ - GeoJSON now also register a featuresloadend function if the source is a cluster.
19
+
20
+ ---
21
+
22
+ ## 2.12.0 - 2022-01-24
23
+ ### Added
24
+ - Added support for Node LTS version 18.
25
+ - Add possibility to specify additional layerparams for a wmts layer, like opacity for example.
26
+ - Add possibility to make a 3DTilset layer transparent/opaque.
27
+ - In the gazetteer search the prefix `*` can now be switched off with the attribute `searchStreetBeforeWord`
28
+
29
+ ### Changed
30
+ - The following packages have been updated:
31
+ - dependencies:
32
+ - core-js: 3.24.0 to 3.27.1
33
+ - proj4: 2.8.0 to 2.8.1
34
+ - devDependencies:
35
+ - @babel/core: 7.18.9 to 7.20.12
36
+ - @babel/plugin-transform-modules-commonjs: 7.18.6 to 7.20.11
37
+ - canvas: 2.9.3 to 2.11.0
38
+ - eslint: 8.20.0 to 8.31.0
39
+ - husky: 8.0.1 to 8.0.3
40
+ - jest: 28.1.3 to 29.3.1
41
+ - jest-environment-jsdom: 28.1.3 to 29.3.1
42
+ - jsdoc: 3.6.11 to 4.0.0
43
+ - sass: 1.54.0 to 1.57.1
44
+
45
+ ---
46
+
6
47
  ## 2.11.0 - 2022-11-24
7
48
  ### Added
8
49
  - crossOrigin settings can now be passed to WMSSources (TileWMS, ImageWMS) via config.json or services.json
package/License.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2018-2020 Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung
3
+ Copyright (c) 2018-2023 Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -31,5 +31,5 @@
31
31
  "id": "1001"
32
32
  }
33
33
  ],
34
- "cesiumLib":"https://geoportal-hamburg.de/mastercode/cesium/1_95/Cesium.js"
34
+ "cesiumLib":"https://geoportal-hamburg.de/mastercode/cesium/1_99/Cesium.js"
35
35
  }
package/example/index.js CHANGED
@@ -51,7 +51,7 @@ document.getElementById("enable").addEventListener("click", function () {
51
51
  document.getElementById("layer-visibility").style.display = "none";
52
52
  }
53
53
  else {
54
- const lib = portalConfig.cesiumLib ? portalConfig.cesiumLib : "https://geoportal-hamburg.de/mastercode/cesium/1_95/index.js";
54
+ const lib = portalConfig.cesiumLib ? portalConfig.cesiumLib : "https://geoportal-hamburg.de/mastercode/cesium/1_99/index.js";
55
55
 
56
56
  document.getElementById("layer-visibility").style.display = "block";
57
57
 
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.11.0",
4
+ "version": "2.13.0",
5
5
  "license": "MIT",
6
6
  "description": "Basic functions of the Masterportal as api",
7
7
  "repository": {
@@ -19,28 +19,28 @@
19
19
  "prepare": "husky install"
20
20
  },
21
21
  "dependencies": {
22
- "core-js": "^3.24.0",
22
+ "core-js": "^3.27.1",
23
23
  "ol": "7.1.0",
24
24
  "olcs": "^2.13.1",
25
- "proj4": "^2.8.0",
25
+ "proj4": "^2.8.1",
26
26
  "xml2js": "^0.4.23"
27
27
  },
28
28
  "devDependencies": {
29
- "@babel/core": "^7.18.9",
30
- "@babel/plugin-transform-modules-commonjs": "^7.18.6",
31
- "@parcel/transformer-sass": "^2.6.2",
29
+ "@babel/core": "^7.20.12",
30
+ "@babel/plugin-transform-modules-commonjs": "^7.20.11",
31
+ "@parcel/transformer-sass": "^2.6.0",
32
32
  "buffer": "^6.0.3",
33
- "canvas": "^2.9.3",
34
- "eslint": "^8.20.0",
33
+ "canvas": "^2.11.0",
34
+ "eslint": "^8.31.0",
35
35
  "events": "^3.3.0",
36
- "husky": "^8.0.1",
37
- "jest": "^28.1.3",
36
+ "husky": "^8.0.3",
37
+ "jest": "^29.3.1",
38
38
  "jest-canvas-mock": "^2.4.0",
39
- "jest-environment-jsdom": "^28.1.3",
40
- "jsdoc": "^3.6.11",
41
- "parcel": "^2.6.2",
39
+ "jest-environment-jsdom": "^29.3.1",
40
+ "jsdoc": "^4.0.0",
41
+ "parcel": "^2.6.0",
42
42
  "process": "^0.11.10",
43
- "sass": "^1.54.0",
43
+ "sass": "^1.57.1",
44
44
  "stream-browserify": "^3.0.0",
45
45
  "timers-browserify": "^2.0.12"
46
46
  },
@@ -48,12 +48,11 @@
48
48
  "cesium": "^1.95.0"
49
49
  },
50
50
  "engines": {
51
- "node": "^16.13.2",
51
+ "node": "^16.13.2 || ^18.12.0",
52
52
  "npm": "^8.1.2"
53
53
  },
54
54
  "homepage": "https://bitbucket.org/geowerkstatt-hamburg/masterportalapi#readme",
55
55
  "directories": {
56
- "doc": "docs",
57
56
  "example": "example",
58
57
  "test": "test"
59
58
  },
@@ -22,11 +22,11 @@ export function createLayerSource ({url, features, clusterDistance}, options) {
22
22
 
23
23
  source = createVectorSource(url || features, options.loadingStrategy, format);
24
24
 
25
- if (clusterDistance) {
26
- return createClusterVectorSource(source, clusterDistance, options.clusterGeometryFunction);
25
+ if (typeof clusterDistance === "number") {
26
+ source = createClusterVectorSource(source, clusterDistance, options.clusterGeometryFunction);
27
27
  }
28
28
 
29
- source.once("featuresloadend", event => {
29
+ (typeof clusterDistance === "number" ? source.getSource() : source).once("featuresloadend", event => {
30
30
  if (typeof options.afterLoading === "function") {
31
31
  options.afterLoading(event?.features);
32
32
  }
@@ -15,6 +15,7 @@ function createTileSet (rawLayer) {
15
15
  options.url = url;
16
16
  return new Cesium.Cesium3DTileset(options);
17
17
  }
18
+
18
19
  /**
19
20
  * Creates an terrain layer to use in ol-Cesium map. Sets the id of the rawlayer to tileset.layerReferenceId.
20
21
  * @param {Object} rawLayer - layer specification as in services.json
@@ -34,6 +35,30 @@ export default function Tileset (rawLayer) {
34
35
  this.tileset.layerReferenceId = rawLayer.id;
35
36
  }
36
37
  }
38
+
39
+ /**
40
+ * Sets the opacity to the tilset.
41
+ * @param {Numbe} opacity the opacity
42
+ * @returns {void}
43
+ */
44
+ Tileset.prototype.setOpacity = function (opacity) {
45
+ let color = "rgba(255, 255, 255, " + opacity + ")";
46
+
47
+ if (this.tileset.style !== undefined) {
48
+ const splitValues = this.tileset.style.style.color.split(",");
49
+
50
+ if (!splitValues[0].startsWith("rgba") && splitValues[0].startsWith("rgb")) {
51
+ splitValues[0] = splitValues[0].replace("rgb", "rgba");
52
+ splitValues[2] = splitValues[2].split(")")[0];
53
+ }
54
+
55
+ splitValues[3] = ` ${opacity})`;
56
+ color = splitValues.toString();
57
+ }
58
+
59
+ this.tileset.style = new Cesium.Cesium3DTileStyle({color});
60
+ };
61
+
37
62
  /**
38
63
  *
39
64
  * @param {boolean} value if true, terrainProvider is set to map and terrain will be visible
@@ -53,6 +78,7 @@ Tileset.prototype.setVisible = function (value, map) {
53
78
  }
54
79
  }
55
80
  };
81
+
56
82
  /**
57
83
  * Returns the value for the given key of the rawlayer.
58
84
  * @param {String} key to get the value for
package/src/layer/wmts.js CHANGED
@@ -165,29 +165,30 @@ export function createLayerSourceByCapabilities (attrs, tileLayer) {
165
165
 
166
166
  /**
167
167
  * Creates the WMTSLayer.
168
- * @param {Object} attrs attributes of the layer
168
+ * @param {object} rawLayer - layer specification as in services.json
169
+ * @param {object} layerParams - additional layer params
169
170
  * @returns {void}
170
171
  */
171
- export function createLayer (attrs) {
172
- const tileLayer = new TileLayer({
173
- id: attrs.id,
172
+ export function createLayer (rawLayer, layerParams = {}) {
173
+ const tileLayer = new TileLayer(Object.assign({
174
+ id: rawLayer.id,
174
175
  source: new WMTS({}),
175
- name: attrs.name,
176
- minResolution: attrs.minScale,
177
- maxResolution: attrs.maxScale,
176
+ name: rawLayer.name,
177
+ minResolution: rawLayer.minScale,
178
+ maxResolution: rawLayer.maxScale,
178
179
  supported: ["2D", "3D"],
179
180
  showSettings: true,
180
181
  extent: null,
181
- typ: attrs.typ,
182
- legendURL: attrs.legendURL,
183
- infoFormat: attrs.infoFormat
184
- });
182
+ typ: rawLayer.typ,
183
+ legendURL: rawLayer.legendURL,
184
+ infoFormat: rawLayer.infoFormat
185
+ }, layerParams));
185
186
 
186
- if (attrs.optionsFromCapabilities === undefined) {
187
- createLayerSourceByDefinitions(attrs, tileLayer);
187
+ if (rawLayer.optionsFromCapabilities === undefined) {
188
+ createLayerSourceByDefinitions(rawLayer, tileLayer);
188
189
  }
189
190
  else {
190
- createLayerSourceByCapabilities(attrs, tileLayer);
191
+ createLayerSourceByCapabilities(rawLayer, tileLayer);
191
192
  }
192
193
 
193
194
  return tileLayer;
@@ -118,6 +118,9 @@ class WMSRasterSynchronizer extends olcsAbstractSynchronizer {
118
118
  provider = this.createProviderForStaticImageSource(source);
119
119
  }
120
120
  else if (source instanceof WMTS) {
121
+ if (typeof source.getUrls !== "function" || !Array.isArray(source.getUrls())) {
122
+ return null;
123
+ }
121
124
  provider = new Cesium.WebMapTileServiceImageryProvider({
122
125
  url: source.getUrls()[0],
123
126
  format: source.getFormat(),
@@ -12,12 +12,13 @@ let abortController = null;
12
12
  * @param {(string|string[])} values one or multiple strings, depending on type
13
13
  * @param {function} sortFunction a function with two parameters which can be used to sort all results
14
14
  * @param {function} filterFunction a function which will be executed to filter results
15
+ * @param {boolean} searchStreetBeforeWord whether to put an star in front of the search for streets (full text search)
15
16
  * @returns {Promise<SearchResult[]>} parsed response
16
17
  * @ignore
17
18
  */
18
- function searchAndParse (type, values, sortFunction = (a, b) => a.name.localeCompare(b.name), filterFunction = () => true) {
19
+ function searchAndParse (type, values, sortFunction = (a, b) => a.name.localeCompare(b.name), filterFunction = () => true, searchStreetBeforeWord) {
19
20
  return new Promise((resolve, reject) => {
20
- searchGazetteer(type, values, abortController)
21
+ searchGazetteer(type, values, abortController, searchStreetBeforeWord)
21
22
  .then(results => {
22
23
  parse(type, results).then(parsed => {
23
24
  resolve(parsed.filter(filterFunction)
@@ -33,12 +34,13 @@ function searchAndParse (type, values, sortFunction = (a, b) => a.name.localeCom
33
34
  * Combines streets with their available house numbers before returning.
34
35
  * @param {string} searchstring string to search for
35
36
  * @param {boolean} searchHouseNumbers whether to additionally search for house numbers
37
+ * @param {boolean} searchStreetBeforeWord whether to put an star in front of the search for streets (full text search)
36
38
  * @returns {Promise<SearchResult[]>} parsed response
37
39
  * @ignore
38
40
  */
39
- function searchAndParseStreetAndHouseNumber (searchstring, searchHouseNumbers) {
41
+ function searchAndParseStreetAndHouseNumber (searchstring, searchHouseNumbers, searchStreetBeforeWord) {
40
42
  return new Promise((resolve, reject) => {
41
- searchGazetteer(searchTypes.STREET, searchstring, abortController)
43
+ searchGazetteer(searchTypes.STREET, searchstring, abortController, searchStreetBeforeWord)
42
44
  .then(streetResults => parse(searchTypes.STREET, streetResults))
43
45
  .then(parsedStreetResults => {
44
46
  const allSearches = [],
@@ -48,7 +50,7 @@ function searchAndParseStreetAndHouseNumber (searchstring, searchHouseNumbers) {
48
50
  // put street in front of street's street+hnr as ordering
49
51
  allSearches.push([parsedStreetResults[i]]);
50
52
  if (searchHouseNumbers) {
51
- allSearches.push(searchAndParse(searchTypes.HOUSE_NUMBERS_FOR_STREET, parsedStreetResults[i].name));
53
+ allSearches.push(searchAndParse(searchTypes.HOUSE_NUMBERS_FOR_STREET, parsedStreetResults[i].name, undefined, undefined, searchStreetBeforeWord));
52
54
  }
53
55
  }
54
56
 
@@ -56,7 +58,8 @@ function searchAndParseStreetAndHouseNumber (searchstring, searchHouseNumbers) {
56
58
  allSearches.push(searchAndParse(searchTypes.HOUSE_NUMBERS_FOR_STREET,
57
59
  matches[1],
58
60
  (a, b) => parseInt(a.properties?.hausnummer, 10) - parseInt(b.properties?.hausnummer, 10),
59
- item => item.properties?.hausnummerkomplett?.indexOf(matches[2]) === 0)); // check longer housenumbers and suffixes!
61
+ item => item.properties?.hausnummerkomplett?.indexOf(matches[2]) === 0, // check longer housenumbers and suffixes!,
62
+ searchStreetBeforeWord));
60
63
  }
61
64
 
62
65
  return Promise.all(allSearches);
@@ -98,6 +101,7 @@ export function search (searchstring, params, abortPreviousSearch = false) {
98
101
  zoomToParams,
99
102
  searchAddress = false,
100
103
  searchStreets = false,
104
+ searchStreetBeforeWord = true,
101
105
  searchDistricts = false,
102
106
  searchParcels = false,
103
107
  searchStreetKey = false,
@@ -133,14 +137,23 @@ export function search (searchstring, params, abortPreviousSearch = false) {
133
137
  }
134
138
 
135
139
  if (searchStreets) {
136
- searches.push(searchAndParseStreetAndHouseNumber(searchstring, searchHouseNumbers));
140
+ searches.push(searchAndParseStreetAndHouseNumber(searchstring, searchHouseNumbers, searchStreetBeforeWord));
137
141
  }
138
142
 
139
143
  if (searchAddress) {
140
144
  // assume pattern like "Streetname 41b", split to ["Streetname", "41", "b"]
141
- const values = searchstring.split(/(\d+)/).map(s => s.trim()).filter(x => x),
142
- // if neither two (no affix like b) or three (with affix like b) parts found, not enough (or too many) params for method - don't search
143
- type = [false, false, searchTypes.ADDRESS_UNAFFIXED, searchTypes.ADDRESS_AFFIXED][values.length];
145
+ let values = searchstring.split(/(\d+)/).map(s => s.trim()).filter(x => x);
146
+
147
+ if (values.length > 3) {
148
+ const streetName = values[0],
149
+ houseNum = values[1],
150
+ appendix = searchstring.replace(values[0], "").replace(values[1], "").trim();
151
+
152
+ values = [streetName, houseNum, appendix];
153
+ }
154
+
155
+ // if neither two (no affix like b) or three (with affix like b) parts found, not enough (or too many) params for method - don't search
156
+ const type = [false, false, searchTypes.ADDRESS_UNAFFIXED, searchTypes.ADDRESS_AFFIXED][values.length];
144
157
 
145
158
  if (type) {
146
159
  searches.push(searchAndParse(type, values));
@@ -16,11 +16,12 @@ export function encode (v) {
16
16
  * @param {string} key internal name for query froms searchTypes
17
17
  * @param {(string[]|string)} v string for single-value queries, string[] for multi-value queries, strings in order of appearance in URL
18
18
  * @returns {string} URL query part like "&StoryQuery_ID=queryName&param=value"
19
+ * @param {boolean} searchStreetBeforeWord whether to put an star in front of the search for streets (full text search)
19
20
  * @ignore
20
21
  */
21
- export function getIdQuery (key, v) {
22
+ export function getIdQuery (key, v, searchStreetBeforeWord = true) {
22
23
  return {
23
- [searchTypes.STREET]: encodedValue => `&StoredQuery_ID=findeStrasse&strassenname=*${encodedValue}`,
24
+ [searchTypes.STREET]: encodedValue => searchStreetBeforeWord ? `&StoredQuery_ID=findeStrasse&strassenname=*${encodedValue}` : `&StoredQuery_ID=findeStrasse&strassenname=${encodedValue}`,
24
25
  [searchTypes.DISTRICT]: encodedValue => `&StoredQuery_ID=findeStadtteil&stadtteilname=${encodedValue}`,
25
26
  [searchTypes.PARCEL]: encodedValue => `&StoredQuery_ID=Flurstueck&gemarkung=${encodedValue[0]}&flurstuecksnummer=${encodedValue[1]}`,
26
27
  [searchTypes.STREET_KEY]: encodedValue => `&StoredQuery_ID=findeStrassenSchluessel&strassenschluessel=${encodedValue}`,
@@ -35,12 +36,13 @@ export function getIdQuery (key, v) {
35
36
  * @param {string} key internal name for query froms searchTypes
36
37
  * @param {(string[]|string)} value value to search for
37
38
  * @param {AbortController} abortController the controller to abort the search
39
+ * @param {boolean} searchStreetBeforeWord whether to put an star in front of the search for streets (full text search)
38
40
  * @returns {Promise<string>} xhr response text
39
41
  * @ignore
40
42
  */
41
- export function searchGazetteer (key, value, abortController) {
43
+ export function searchGazetteer (key, value, abortController, searchStreetBeforeWord) {
42
44
  return new Promise((resolve, reject) => {
43
- const url = getGazetteerUrl() + getIdQuery(key, value);
45
+ const url = getGazetteerUrl() + getIdQuery(key, value, searchStreetBeforeWord);
44
46
 
45
47
  fetch(url, {
46
48
  signal: abortController?.signal,
@@ -7,6 +7,7 @@ describe("tileset.js", function () {
7
7
  beforeEach(() => {
8
8
  global.Cesium = {};
9
9
  global.Cesium.Cesium3DTileset = jest.fn();
10
+ global.Cesium.Cesium3DTileStyle = jest.fn(color => color);
10
11
  attr = {
11
12
  id: "4002",
12
13
  name: "Gebäude LoD2",
@@ -68,6 +69,15 @@ describe("tileset.js", function () {
68
69
  expect(cesium3DTilesetSpy).toHaveBeenCalledTimes(2);
69
70
  });
70
71
  });
72
+ describe("setOpacity", function () {
73
+ it("calls setOpacity sets opacity to terrain style", function () {
74
+ const layer = new Tileset(attr, map);
75
+
76
+ layer.setOpacity(0.5);
77
+
78
+ expect(layer.tileset.style).toEqual({color: "rgba(255, 255, 255, 0.5)"});
79
+ });
80
+ });
71
81
  describe("setVisible", function () {
72
82
  it("calls setVisible without map shall not fail", function () {
73
83
  const layer = new Tileset(attr, map);
@@ -33,7 +33,7 @@ jest.mock("../../src/searchAddress/searchGazetteer", () => {
33
33
 
34
34
  import {searchGazetteer as mockedSearchGazetteer} from "../../src/searchAddress/searchGazetteer";
35
35
 
36
- describe("searchAddress", function () {
36
+ describe.only("searchAddress", function () {
37
37
  describe("search", function () {
38
38
  it("rejects if missing configuration of what to search for", function (done) {
39
39
  search("Pflugacker", {}).catch(e => {
@@ -77,15 +77,15 @@ describe("searchAddress", function () {
77
77
  .then(results => {
78
78
  // order of calls may vary, hence using expect.arrayContaining
79
79
  const expected = [
80
- [types.ADDRESS_UNAFFIXED, ["Address", "7"], abortController],
81
- [types.ADDRESS_AFFIXED, ["Address", "7", "b"], abortController],
82
- [types.STREET, "Streetname", abortController],
83
- [types.STREET, "StreetnameAndHnrs", abortController],
84
- [types.HOUSE_NUMBERS_FOR_STREET, "Bargkoppelweg", abortController],
85
- [types.DISTRICT, "Districtname", abortController],
86
- [types.PARCEL, ["1234", "1234"], abortController],
87
- [types.PARCEL, ["1234", "5678"], abortController],
88
- [types.STREET_KEY, "A12345", abortController]
80
+ [types.ADDRESS_UNAFFIXED, ["Address", "7"], abortController, undefined],
81
+ [types.ADDRESS_AFFIXED, ["Address", "7", "b"], abortController, undefined],
82
+ [types.STREET, "Streetname", abortController, true],
83
+ [types.STREET, "StreetnameAndHnrs", abortController, true],
84
+ [types.HOUSE_NUMBERS_FOR_STREET, "Bargkoppelweg", abortController, true],
85
+ [types.DISTRICT, "Districtname", abortController, undefined],
86
+ [types.PARCEL, ["1234", "1234"], abortController, undefined],
87
+ [types.PARCEL, ["1234", "5678"], abortController, undefined],
88
+ [types.STREET_KEY, "A12345", abortController, undefined]
89
89
  ];
90
90
 
91
91
  expect(results[0]).toHaveLength(1);
@@ -15,7 +15,8 @@ describe("searchAddress", function () {
15
15
 
16
16
  describe("getIdQuery", function () {
17
17
  it("encodes given search phrase(s) and builds query fragment from it", function () {
18
- expect(getIdQuery(searchTypes.STREET, "hello world")).toEqual("&StoredQuery_ID=findeStrasse&strassenname=*hello%20world");
18
+ expect(getIdQuery(searchTypes.STREET, "hello world", true)).toEqual("&StoredQuery_ID=findeStrasse&strassenname=*hello%20world");
19
+ expect(getIdQuery(searchTypes.STREET, "hello world", false)).toEqual("&StoredQuery_ID=findeStrasse&strassenname=hello%20world");
19
20
  expect(getIdQuery(searchTypes.DISTRICT, "hello world")).toEqual("&StoredQuery_ID=findeStadtteil&stadtteilname=hello%20world");
20
21
  expect(getIdQuery(searchTypes.PARCEL, ["1 a", "1 b"])).toEqual("&StoredQuery_ID=Flurstueck&gemarkung=1%20a&flurstuecksnummer=1%20b");
21
22
  expect(getIdQuery(searchTypes.STREET_KEY, "A12345")).toEqual("&StoredQuery_ID=findeStrassenSchluessel&strassenschluessel=A12345");