@masterportal/masterportalapi 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +6 -0
  3. package/.eslintrc +184 -0
  4. package/.husky/pre-push +4 -0
  5. package/CHANGELOG.md +217 -0
  6. package/License.txt +21 -0
  7. package/README.md +24 -0
  8. package/babel.config.json +6 -0
  9. package/example/README.md +7 -0
  10. package/example/config/localGeoJSON.js +956 -0
  11. package/example/config/portal.json +35 -0
  12. package/example/config/services.json +79 -0
  13. package/example/index.html +27 -0
  14. package/example/index.js +210 -0
  15. package/example/style.scss +59 -0
  16. package/jest.config.js +18 -0
  17. package/jest.setup.js +5 -0
  18. package/jsdoc.json +15 -0
  19. package/package.json +62 -0
  20. package/public/marker.svg +1 -0
  21. package/public/stringMarker.js +5 -0
  22. package/src/crs.js +122 -0
  23. package/src/defaults.js +38 -0
  24. package/src/index.js +30 -0
  25. package/src/layer/geojson/index.js +109 -0
  26. package/src/layer/geojson/style.js +61 -0
  27. package/src/layer/lib.js +32 -0
  28. package/src/layer/oaf.js +207 -0
  29. package/src/layer/terrain.js +72 -0
  30. package/src/layer/tileset.js +71 -0
  31. package/src/layer/vector.js +48 -0
  32. package/src/layer/vectorBase.js +39 -0
  33. package/src/layer/wfs.js +218 -0
  34. package/src/layer/wms.js +185 -0
  35. package/src/lib/coordsToPairs.js +15 -0
  36. package/src/lib/getInitialLayers.js +22 -0
  37. package/src/lib/load3DScript.js +22 -0
  38. package/src/lib/oafUtil.js +22 -0
  39. package/src/lib/setBackgroundImage.js +23 -0
  40. package/src/lib/wfsUtil.js +75 -0
  41. package/src/lib/zoomTo.js +40 -0
  42. package/src/maps/api.js +5 -0
  43. package/src/maps/map.js +35 -0
  44. package/src/maps/mapView.js +54 -0
  45. package/src/maps/ol/olMap.js +124 -0
  46. package/src/maps/olcs/3dUtils/fixedOverlaySynchronizer.js +39 -0
  47. package/src/maps/olcs/3dUtils/wmsRasterSynchronizer.js +348 -0
  48. package/src/maps/olcs/olcsMap.js +212 -0
  49. package/src/rawLayerList.js +88 -0
  50. package/src/searchAddress/gazetteerUrl.js +22 -0
  51. package/src/searchAddress/index.js +13 -0
  52. package/src/searchAddress/parse.js +196 -0
  53. package/src/searchAddress/search.js +184 -0
  54. package/src/searchAddress/searchGazetteer.js +52 -0
  55. package/src/searchAddress/showGeographicIdentifier.js +20 -0
  56. package/src/searchAddress/types.js +17 -0
  57. package/test/.eslintrc +7 -0
  58. package/test/crs.test.js +109 -0
  59. package/test/layer/geojson/index.test.js +133 -0
  60. package/test/layer/geojson/style.test.js +46 -0
  61. package/test/layer/lib.test.js +83 -0
  62. package/test/layer/oaf.test.js +245 -0
  63. package/test/layer/resources/oafFeatures.js +105 -0
  64. package/test/layer/resources/wfsFeatures.js +26 -0
  65. package/test/layer/resources/wfsFilter.js +17 -0
  66. package/test/layer/terrain.test.js +106 -0
  67. package/test/layer/tileset.test.js +116 -0
  68. package/test/layer/vector.test.js +76 -0
  69. package/test/layer/vectorBase.test.js +48 -0
  70. package/test/layer/wfs.test.js +388 -0
  71. package/test/layer/wms.test.js +256 -0
  72. package/test/lib/coordsToPairs.test.js +9 -0
  73. package/test/lib/getInitialLayers.test.js +28 -0
  74. package/test/lib/oafUtil.test.js +24 -0
  75. package/test/lib/setBackgroundImage.test.js +49 -0
  76. package/test/lib/wfsUtil.test.js +28 -0
  77. package/test/lib/zoomTo.test.js +74 -0
  78. package/test/map.test.js +258 -0
  79. package/test/mapView.test.js +59 -0
  80. package/test/rawLayerList.test.js +48 -0
  81. package/test/searchAddress/gazetteerSearchResults/address.js +175 -0
  82. package/test/searchAddress/gazetteerSearchResults/district.js +71 -0
  83. package/test/searchAddress/gazetteerSearchResults/houseNumber.js +32 -0
  84. package/test/searchAddress/gazetteerSearchResults/index.js +19 -0
  85. package/test/searchAddress/gazetteerSearchResults/parcel.js +44 -0
  86. package/test/searchAddress/gazetteerSearchResults/street.js +46 -0
  87. package/test/searchAddress/gazetteerSearchResults/streetKey.js +51 -0
  88. package/test/searchAddress/gazetteerUrl.test.js +22 -0
  89. package/test/searchAddress/parse.test.js +110 -0
  90. package/test/searchAddress/search.test.js +150 -0
  91. package/test/searchAddress/searchGazetteer.test.js +58 -0
  92. package/test/searchAddress/showGeographicIdentifier.test.js +22 -0
@@ -0,0 +1,212 @@
1
+ import OLCesium from "olcs/OLCesium.js";
2
+ import VectorSynchronizer from "olcs/VectorSynchronizer.js";
3
+ import FixedOverlaySynchronizer from "./3dUtils/fixedOverlaySynchronizer.js";
4
+ import WMSRasterSynchronizer from "./3dUtils/wmsRasterSynchronizer.js";
5
+ import {transform, get} from "ol/proj.js";
6
+
7
+ let mapIdCounter = 0;
8
+
9
+ /**
10
+ * Sets the cesium scene defaults from the config.js
11
+ * @param {Cesium.scene} scene Cesium scene.
12
+ * @param {Object} config optional configuration parameters.
13
+ * @returns {void}
14
+ */
15
+ function setCesiumSceneDefaults (scene, config) {
16
+ let params;
17
+
18
+ if (Object.prototype.hasOwnProperty.call(config, "cesiumParameter")) {
19
+ params = config.cesiumParameter;
20
+ if (params?.fog) {
21
+ scene.fog.enabled = params.fog?.enabled ? params.fog.enabled : scene.fog.enabled;
22
+ scene.fog.density = params.fog?.density ? parseFloat(params.fog.density) : scene.fog.density;
23
+ scene.fog.screenSpaceErrorFactor = params.fog?.screenSpaceErrorFactor ? parseFloat(params.fog.screenSpaceErrorFactor) : scene.fog.screenSpaceErrorFactor;
24
+ }
25
+
26
+ scene.globe.tileCacheSize = params?.tileCacheSize ? parseInt(params.tileCacheSize, 10) : scene.globe.tileCacheSize;
27
+ scene.globe.maximumScreenSpaceError = params?.maximumScreenSpaceError ? params.maximumScreenSpaceError : scene.globe.maximumScreenSpaceError;
28
+ scene.shadowMap.maximumDistance = 5000.0;
29
+ scene.shadowMap.darkness = 0.6;
30
+ scene.shadowMap.size = 2048;
31
+ scene.fxaa = params?.fxaa ? params.fxaa : scene.fxaa;
32
+ scene.globe.enableLighting = params?.enableLighting ? params.enableLighting : scene.globe.enableLighting;
33
+ scene.globe.depthTestAgainstTerrain = true;
34
+ scene.highDynamicRange = false;
35
+ scene.pickTranslucentDepth = true;
36
+ scene.camera.enableTerrainAdjustmentWhenLoading = true;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Sets the camera parameters either from config or from an trigger.
42
+ * @param {Object} params Params.
43
+ * @param {Object} map3D olcs map.
44
+ * @param {Cesium} cesium cesium.
45
+ * @returns {void}
46
+ */
47
+ export function setCameraParameter (params, map3D, cesium) {
48
+ let camera,
49
+ destination,
50
+ orientation;
51
+
52
+ // if the cameraPosition is given, directly set the cesium camera position, otherwise use olcesium Camera
53
+ if (map3D && params.cameraPosition) {
54
+ camera = map3D.getCesiumScene().camera;
55
+ destination = cesium.Cartesian3.fromDegrees(params.cameraPosition[0], params.cameraPosition[1], params.cameraPosition[2]);
56
+ orientation = {
57
+ heading: cesium.Math.toRadians(parseFloat(params.heading)),
58
+ pitch: cesium.Math.toRadians(parseFloat(params.pitch)),
59
+ roll: cesium.Math.toRadians(parseFloat(params.roll))
60
+ };
61
+
62
+ camera.setView({
63
+ destination,
64
+ orientation
65
+ });
66
+ }
67
+ else if (map3D !== undefined && params !== null) {
68
+ camera = map3D.getCamera();
69
+ if (params?.tilt) {
70
+ camera.setTilt(parseFloat(params.tilt));
71
+ }
72
+ if (params?.heading) {
73
+ camera.setHeading(parseFloat(params.heading));
74
+ }
75
+ if (params?.altitude) {
76
+ camera.setAltitude(parseFloat(params.altitude));
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Reacts to 3D click event in cesium scene.
83
+ * @param {Event} event The cesium event.
84
+ * @returns {Function} Returns the callbackfunction with click informations.
85
+ */
86
+ function reactTo3DClickEvent (event) {
87
+ const scene = this.scene,
88
+ ray = scene.camera.getPickRay(event.position),
89
+ cartesian = scene.globe.pick(ray, scene),
90
+ mapProjection = this.map3D.getOlView().getProjection();
91
+ let height,
92
+ coords,
93
+ cartographic,
94
+ distance,
95
+ resolution,
96
+ transformedCoords,
97
+ transformedPickedPosition,
98
+ pickedPositionCartesian,
99
+ cartographicPickedPosition,
100
+ clickObject;
101
+
102
+ if (cartesian) {
103
+ if (document.querySelector(".nav li")?.classList.contains("open")) {
104
+ document.querySelector(".nav li").classList.remove("open");
105
+ }
106
+ cartographic = scene.globe.ellipsoid.cartesianToCartographic(cartesian);
107
+ coords = [window.Cesium.Math.toDegrees(cartographic.longitude), window.Cesium.Math.toDegrees(cartographic.latitude)];
108
+ height = scene.globe.getHeight(cartographic);
109
+ if (height) {
110
+ coords = coords.concat([height]);
111
+ }
112
+
113
+ distance = window.Cesium.Cartesian3.distance(cartesian, scene.camera.position);
114
+ resolution = this.map3D.getCamera().calcResolutionForDistance(distance, cartographic.latitude);
115
+ transformedCoords = transform(coords, get("EPSG:4326"), mapProjection);
116
+ transformedPickedPosition = null;
117
+
118
+ if (scene.pickPositionSupported) {
119
+ pickedPositionCartesian = scene.pickPosition(event.position);
120
+ if (pickedPositionCartesian) {
121
+ cartographicPickedPosition = scene.globe.ellipsoid.cartesianToCartographic(pickedPositionCartesian);
122
+ transformedPickedPosition = transform([window.Cesium.Math.toDegrees(cartographicPickedPosition.longitude), window.Cesium.Math.toDegrees(cartographicPickedPosition.latitude)], get("EPSG:4326"), mapProjection);
123
+ transformedPickedPosition.push(cartographicPickedPosition.height);
124
+ }
125
+ }
126
+ clickObject = {
127
+ map3D: this,
128
+ position: event.position,
129
+ pickedPosition: transformedPickedPosition,
130
+ coordinate: transformedCoords,
131
+ latitude: coords[0],
132
+ longitude: coords[1],
133
+ resolution: resolution,
134
+ originalEvent: event
135
+ };
136
+ }
137
+ return this.callback(clickObject);
138
+ }
139
+
140
+ /**
141
+ * Logic to listen to click events in 3D mode.
142
+ * @param {Object} map3DObject Contains the scene, 3D map and a callback function.
143
+ * @returns {void}
144
+ */
145
+ function handle3DEvents (map3DObject) {
146
+ let eventHandler;
147
+
148
+ if (window.Cesium) {
149
+ eventHandler = new window.Cesium.ScreenSpaceEventHandler(map3DObject.scene.canvas);
150
+ eventHandler.setInputAction(reactTo3DClickEvent.bind(map3DObject), window.Cesium.ScreenSpaceEventType.LEFT_CLICK);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Creates a 3D-map.
156
+ * @param {object} [settings] The settings for the 3D-map.
157
+ * @param {module:ol/PluggableMap~PluggableMap} [settings.map2D] The 2D-Map
158
+ * @param {Cesium.JulianDate} [settings.shadowTime] The shadow time in julian date format if undefined olcs default is Cesium.JulianDate.now().
159
+ * @returns {module:OLCesium} the 3d-map
160
+ */
161
+ export function createMap (settings) {
162
+ const map3D = new OLCesium({
163
+ map: settings.map2D,
164
+ time: settings.shadowTime ? settings.shadowTime : undefined,
165
+ sceneOptions: {
166
+ shadows: false
167
+ },
168
+ stopOpenLayersEventsPropagation: true,
169
+ createSynchronizers: (olMap, scene) => {
170
+ return [new WMSRasterSynchronizer(olMap, scene), new VectorSynchronizer(olMap, scene), new FixedOverlaySynchronizer(olMap, scene)];
171
+ }
172
+ });
173
+
174
+ map3D.id = `map3D_${mapIdCounter++}`;
175
+ map3D.mapMode = "3D";
176
+
177
+ return map3D;
178
+ }
179
+
180
+ /**
181
+ * Prepares the camera and listens to camera changed events.
182
+ * @param {Cesium.scene} scene Cesium scene.
183
+ * @param {Object} urlParams optional urlParams for camera.
184
+ * @param {Object} map3D olcs map.
185
+ * @param {Object} config optional configuration parameters.
186
+ * @param {Cesium} cesium optional cesium.
187
+ * @returns {Cesium.scene.camera} Cesium camera.
188
+ */
189
+ export function prepareCamera (scene, urlParams, map3D, config, cesium) {
190
+ const camera = scene.camera;
191
+ let cameraParameter = Object.prototype.hasOwnProperty.call(config, "cameraParameter") ? config.cameraParameter : {};
192
+
193
+ cameraParameter = urlParams?.altitude ? Object.assign(cameraParameter || {}, {altitude: urlParams?.altitude}) : cameraParameter;
194
+ cameraParameter = urlParams?.heading ? Object.assign(cameraParameter || {}, {heading: urlParams?.heading}) : cameraParameter;
195
+ cameraParameter = urlParams?.tilt ? Object.assign(cameraParameter || {}, {tilt: urlParams?.tilt}) : cameraParameter;
196
+
197
+ if (Object.keys(cameraParameter).length > 0) {
198
+ setCameraParameter(cameraParameter, map3D, cesium);
199
+ }
200
+ return camera;
201
+ }
202
+
203
+ /**
204
+ * Prepares the cesium scene.
205
+ * @param {Object} map3DObject Contains the scene, 3D map and a callback function.
206
+ * @param {Object} config optional configuration parameters.
207
+ * @returns {void}
208
+ */
209
+ export function prepareScene (map3DObject, config) {
210
+ handle3DEvents(map3DObject);
211
+ setCesiumSceneDefaults(map3DObject.scene, config);
212
+ }
@@ -0,0 +1,88 @@
1
+ import defaults from "./defaults";
2
+
3
+ /** layerList that contains all known services.
4
+ * @type{Array}
5
+ * @ignore
6
+ */
7
+ let layerList = [];
8
+
9
+ /**
10
+ * Initializes the layer list with either an object or an URL. May be used again to override the layer list.
11
+ * createMap will call this for you, but won't notify you of when it's done. Use this function manually with a
12
+ * callback to know when layers can be added programmatically.
13
+ * @param {(string|object)} [layerConf="https://geoportal-hamburg.de/lgv-config/services-internet.json"] - either the URL to fetch the services from, or the object containing the services
14
+ * @param {function} [callback] - called with services after loaded; called with false and error on error
15
+ * @returns {undefined} nothing, add callback to receive layerList
16
+ */
17
+ export function initializeLayerList (layerConf = defaults.layerConf, callback) {
18
+ if (Array.isArray(layerConf)) {
19
+ // case: parameter was services.json contents
20
+ layerList = layerConf;
21
+ if (typeof callback === "function") {
22
+ callback(layerList);
23
+ return;
24
+ }
25
+ return;
26
+ }
27
+
28
+ // case: parameter is URL
29
+ const Http = new XMLHttpRequest();
30
+
31
+ Http.open("GET", layerConf);
32
+ Http.timeout = 10000;
33
+ Http.send();
34
+ Http.onload = function () {
35
+ try {
36
+ layerList = JSON.parse(Http.responseText);
37
+ }
38
+ catch (error) {
39
+ console.error("An error occured when parsing the response after loading '" + layerConf + "':", error);
40
+ return callback(false, error);
41
+ }
42
+ if (typeof callback === "function") {
43
+ return callback(layerList);
44
+ }
45
+ return true;
46
+ };
47
+ Http.onerror = function (e) {
48
+ console.error("An error occured when trying to fetch services from '" + layerConf + "':", e);
49
+ callback(false, e);
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Returns the first entry in layerList matching the given searchAttributes.
55
+ * @param {object} searchAttributes - key/value-pairs to be searched for, e.g. { typ: "WMS" } to get the first WMS
56
+ * @returns {?object} first layer matching the searchAttributes or null if none was found
57
+ */
58
+ export function getLayerWhere (searchAttributes) {
59
+ const keys = Object.keys(searchAttributes);
60
+
61
+ return layerList.find(entry => keys.every(key => entry[key] === searchAttributes[key])) || null;
62
+ }
63
+
64
+ /** @returns {object[]} complete layerList as initialized */
65
+ export function getLayerList () {
66
+ return layerList;
67
+ }
68
+
69
+ /**
70
+ * Returns display names map for a layer, or display name for a specific attribute.
71
+ * @param {string} layerId - if of layer to fetch display names for
72
+ * @param {string} [featureAttribute] - if given, only one entry of map is returned
73
+ * @returns {?(object|string)} - map of originalName->displayName or name of featureAttribute if specified; if layer or featureAttribute not found, null
74
+ */
75
+ export function getDisplayNamesOfFeatureAttributes (layerId, featureAttribute) {
76
+ const attributes = getLayerWhere({id: layerId});
77
+
78
+ if (attributes && typeof featureAttribute === "string") {
79
+ const displayName = attributes.gfiAttributes && attributes.gfiAttributes[featureAttribute];
80
+
81
+ return typeof displayName === "string" ? displayName : null;
82
+ }
83
+ else if (attributes) {
84
+ return attributes.gfiAttributes || null;
85
+ }
86
+
87
+ return null;
88
+ }
@@ -0,0 +1,22 @@
1
+ import defaults from "../defaults";
2
+
3
+ // Holds URL of gazetteer to use in search function
4
+ let gazetteerUrl = defaults.gazetteerUrl;
5
+
6
+ /**
7
+ * Sets the gazetteerUrl to be used by the search function.
8
+ * If parameter is falsy, url from defaults.js is kept.
9
+ * @param {String} url url to use for searchAddress
10
+ * @returns {void}
11
+ */
12
+ export function setGazetteerUrl (url) {
13
+ url ? gazetteerUrl = url : null;
14
+ }
15
+
16
+ /**
17
+ * Retrieves active gazetteer URL.
18
+ * @returns {string} currently set gazetteer URL
19
+ */
20
+ export function getGazetteerUrl () {
21
+ return gazetteerUrl;
22
+ }
@@ -0,0 +1,13 @@
1
+ import {search} from "./search";
2
+ import {searchTypes} from "./types";
3
+ import {setGazetteerUrl, getGazetteerUrl} from "./gazetteerUrl";
4
+ import {setShowGeographicIdentifier, getShowGeographicIdentifier} from "./showGeographicIdentifier";
5
+
6
+ export {
7
+ search,
8
+ searchTypes,
9
+ setGazetteerUrl,
10
+ getGazetteerUrl,
11
+ setShowGeographicIdentifier,
12
+ getShowGeographicIdentifier
13
+ };
@@ -0,0 +1,196 @@
1
+ import xml2js from "xml2js";
2
+
3
+ import {searchTypes} from "./types";
4
+ import {getShowGeographicIdentifier} from "./showGeographicIdentifier";
5
+
6
+ /**
7
+ * Flattens xml2js result a little to avoid unnecessary one-element arrays.
8
+ * NOTE currently keeping special properties ($, _) of xml2js to keep attributes.
9
+ * @param {*} a anything
10
+ * @returns {*} a's first element if one-element array, else a
11
+ * @ignore
12
+ */
13
+ function flatten (a) {
14
+ if (Array.isArray(a) && a.length === 1) {
15
+ return flatten(a[0]);
16
+ }
17
+ return a;
18
+ }
19
+
20
+ /**
21
+ * Recursively searches an object for a key.
22
+ * Since parsed XML tends to contain extra arrays, those are stepped into.
23
+ * @param {(object|Array)} o object to deep-search for key
24
+ * @param {string} key key to search for
25
+ * @returns {*} value behind key
26
+ * @ignore
27
+ */
28
+ function findKey (o, key) {
29
+ if (Array.isArray(o)) {
30
+ return o.map(x => findKey(x, key)).filter(x => x)[0];
31
+ }
32
+ if (typeof o !== "object") {
33
+ return null;
34
+ }
35
+ if (typeof o[key] !== "undefined") {
36
+ return o[key];
37
+ }
38
+ return Object.keys(o).map(oKey => findKey(o[oKey], key)).filter(x => x)[0];
39
+ }
40
+
41
+ /**
42
+ * Simply copies over all properties it can find.
43
+ * @param {object} entry xml2js-parsed result entry
44
+ * @returns {object} copied and flattened properties
45
+ * @ignore
46
+ */
47
+ function getAllPropertiesFlat (entry) {
48
+ const properties = {
49
+ objectType: Object.keys(entry)[0]
50
+ };
51
+
52
+ Object.keys(entry[properties.objectType][0]).forEach(propertyName => {
53
+ properties[propertyName] = flatten(entry[properties.objectType][0][propertyName]);
54
+ });
55
+
56
+ return properties;
57
+ }
58
+
59
+ /**
60
+ * Parses coordinates from wkt string.
61
+ * @param {string} wkt position as wkt
62
+ * @returns {string} position as coordinates
63
+ */
64
+ function parseCoordinatesFromWkt (wkt) {
65
+ let pos = [];
66
+
67
+ if (wkt.startsWith("POINT")) {
68
+ pos = wkt.split(")")[0]?.split("(")[1];
69
+ }
70
+
71
+ return pos;
72
+ }
73
+
74
+ /**
75
+ * Parses result entries; uses type-specific rules to determine name/geometry.
76
+ * @param {object} entry xml2js-parsed result entry
77
+ * @param {string} type type string
78
+ * @returns {SearchResult} parsed search result for districts
79
+ * @ignore
80
+ */
81
+ function parseEntry (entry, type) {
82
+ const searchResult = {
83
+ type,
84
+ properties: getAllPropertiesFlat(entry)
85
+ };
86
+ let pos = searchResult.properties?.pos ? parseCoordinatesFromWkt(searchResult.properties.pos) : findKey(searchResult.properties, "pos")[0],
87
+ hausnummer = "",
88
+ hausnummernzusatz = "",
89
+ strassenname = "";
90
+
91
+ // setting name
92
+ switch (type) {
93
+ case searchTypes.DISTRICT:
94
+ searchResult.name = searchResult.properties.geographicIdentifier?._ ? searchResult.properties.geographicIdentifier._ : searchResult.properties.geographicIdentifier;
95
+ break;
96
+ case searchTypes.STREET_KEY:
97
+ case searchTypes.STREET:
98
+ if (searchResult.properties?.position_strassenachse) {
99
+ pos = findKey(searchResult.properties?.position_strassenachse, "pos")[0];
100
+ }
101
+ searchResult.name = getShowGeographicIdentifier() ? searchResult.properties.geographicIdentifier._ : searchResult.properties.strassenname;
102
+ break;
103
+ case searchTypes.PARCEL:
104
+ searchResult.name = `${searchResult.properties.gemarkung}/${searchResult.properties.flurstuecksnummer}`;
105
+ break;
106
+ case searchTypes.HOUSE_NUMBERS_FOR_STREET:
107
+ // house number like "11a" as name is not very telling - use "Streetname 11a" and save "11a" separately in properties
108
+ hausnummer = searchResult.properties.hausnummer?._ ? searchResult.properties.hausnummer._ : searchResult.properties.hausnummer;
109
+
110
+ if (searchResult.properties.hausnummernzusatz) {
111
+ hausnummernzusatz = searchResult.properties.hausnummernzusatz?._ ? searchResult.properties.hausnummernzusatz._ : searchResult.properties.hausnummernzusatz;
112
+ }
113
+
114
+ searchResult.properties.hausnummerkomplett = `${hausnummer}${hausnummernzusatz}`;
115
+ searchResult.name =
116
+ getShowGeographicIdentifier() ? searchResult.properties.geographicIdentifier._ :
117
+ (searchResult.properties?.geographicIdentifier ? searchResult.properties.geographicIdentifier._.split(searchResult.properties.hausnummerkomplett)[0] : searchResult.properties.strassenname + " ") +
118
+ searchResult.properties.hausnummerkomplett;
119
+ break;
120
+ case searchTypes.ADDRESS_AFFIXED:
121
+ strassenname = searchResult.properties.strassenname?._ ? searchResult.properties.strassenname._ : searchResult.properties.strassenname;
122
+ hausnummer = searchResult.properties.hausnummer?._ ? searchResult.properties.hausnummer._ : searchResult.properties.hausnummer;
123
+ hausnummernzusatz = searchResult.properties.hausnummernzusatz?._ ? searchResult.properties.hausnummernzusatz._ : searchResult.properties.hausnummernzusatz;
124
+
125
+ searchResult.name = getShowGeographicIdentifier() ? searchResult.properties.geographicIdentifier._ : `${strassenname} ${hausnummer}${hausnummernzusatz}`;
126
+ break;
127
+ case searchTypes.ADDRESS_UNAFFIXED:
128
+ strassenname = searchResult.properties.strassenname?._ ? searchResult.properties.strassenname._ : searchResult.properties.strassenname;
129
+ hausnummer = searchResult.properties.hausnummer?._ ? searchResult.properties.hausnummer._ : searchResult.properties.hausnummer;
130
+
131
+ searchResult.name =
132
+ getShowGeographicIdentifier() ? searchResult.properties.geographicIdentifier._ :
133
+ (searchResult.properties?.geographicIdentifier ? searchResult.properties.geographicIdentifier._.split(hausnummer)[0] : strassenname + " ") + hausnummer;
134
+ break;
135
+ default:
136
+ searchResult.name = null;
137
+ console.error(`Unknown type in searchAddress.parse: '${type}'. Could not set name.`);
138
+ break;
139
+ }
140
+
141
+ // setting geometry
142
+ switch (type) {
143
+ case searchTypes.DISTRICT:
144
+ case searchTypes.STREET_KEY:
145
+ case searchTypes.STREET:
146
+ case searchTypes.PARCEL:
147
+ case searchTypes.HOUSE_NUMBERS_FOR_STREET:
148
+ case searchTypes.ADDRESS_AFFIXED:
149
+ case searchTypes.ADDRESS_UNAFFIXED:
150
+ // for these, all polygon points are identical - using point is more clear
151
+ searchResult.geometry = {
152
+ type: "Point",
153
+ coordinates: pos.split(" ")
154
+ };
155
+ break;
156
+ default:
157
+ searchResult.geometry = null;
158
+ console.error(`Unknown type in searchAddress.parse: '${type}'. Could not set geometry.`);
159
+ break;
160
+ }
161
+
162
+ return searchResult;
163
+ }
164
+
165
+ /**
166
+ * Parses gazetteer xml to search objects.
167
+ * @param {string} key internal name for query froms searchTypes
168
+ * @param {string} xmlString gazetteer answer as xml string
169
+ * @returns {Promise<SearchResult[]>} parsed response
170
+ * @ignore
171
+ */
172
+ export function parse (key, xmlString) {
173
+ return new Promise((resolve, reject) => {
174
+ // tag processor "stripPrefix" removes e.g. "dog:" prefix on properties
175
+ xml2js.parseString(xmlString, {tagNameProcessors: [xml2js.processors.stripPrefix]},
176
+ (err, source) => {
177
+ if (err) {
178
+ reject(err);
179
+ return;
180
+ }
181
+
182
+ try {
183
+ const searchResults = source.FeatureCollection?.member
184
+ ? source.FeatureCollection.member
185
+ .map(entry => parseEntry(entry, key))
186
+ .sort((a, b) => a.name.localeCompare(b.name))
187
+ : [];
188
+
189
+ resolve(searchResults);
190
+ }
191
+ catch (e) {
192
+ reject(e);
193
+ }
194
+ });
195
+ });
196
+ }