@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,184 @@
1
+ import {zoomToSearchResult} from "../lib/zoomTo";
2
+
3
+ import {parse} from "./parse";
4
+ import {searchTypes} from "./types";
5
+ import {searchGazetteer} from "./searchGazetteer";
6
+
7
+ let abortController = null;
8
+
9
+ /**
10
+ * Chains gazetteer request and response parser.
11
+ * @param {string} type from searchTypes
12
+ * @param {(string|string[])} values one or multiple strings, depending on type
13
+ * @returns {Promise<SearchResult[]>} parsed response
14
+ * @ignore
15
+ */
16
+ function searchAndParse (type, values) {
17
+ return new Promise((resolve, reject) => {
18
+ searchGazetteer(type, values, abortController)
19
+ .then(results => {
20
+ const parsed = parse(type, results);
21
+
22
+ resolve(parsed);
23
+ })
24
+ .catch(e => reject(e));
25
+ });
26
+ }
27
+
28
+ /**
29
+ * Chains gazetteer request and response parser for street and house number.
30
+ * Combines streets with their available house numbers before returning.
31
+ * @param {string} searchstring string to search for
32
+ * @param {boolean} searchHouseNumbers whether to additionally search for house numbers
33
+ * @returns {Promise<SearchResult[]>} parsed response
34
+ * @ignore
35
+ */
36
+ function searchAndParseStreetAndHouseNumber (searchstring, searchHouseNumbers) {
37
+ return new Promise((resolve, reject) => {
38
+ searchGazetteer(searchTypes.STREET, searchstring, abortController)
39
+ .then(streetResults => parse(searchTypes.STREET, streetResults))
40
+ .then(parsedStreetResults => {
41
+ const allSearches = [];
42
+
43
+ for (let i = 0; i < parsedStreetResults.length; i++) {
44
+ // put street in front of street's street+hnr as ordering
45
+ allSearches.push([parsedStreetResults[i]]);
46
+ if (searchHouseNumbers) {
47
+ allSearches.push(searchAndParse(searchTypes.HOUSE_NUMBERS_FOR_STREET, parsedStreetResults[i].name));
48
+ }
49
+ }
50
+
51
+ return Promise.all(allSearches);
52
+ })
53
+ .then(allResults => resolve([].concat(...allResults)))
54
+ .catch(e => reject(e));
55
+ });
56
+ }
57
+
58
+ /**
59
+ * The search function uses the configured gazetteer to retrieve geospatial information
60
+ * regarding a search string. Use the parameters to decide what to search for. At least one
61
+ * searchX parameter must be true to start a search, or the search will be rejected.
62
+ * @param {String} searchstring search string
63
+ * @param {object} params parameter object
64
+ * @param {boolean} [params.zoom = false] whether to zoom to the result if it's a single hit
65
+ * @param {boolean} [params.zoomToParams] parameter object forwarded to ol/View.fit function {@link https://openlayers.org/en/latest/apidoc/module-ol_View.html#~FitOptions}
66
+ * @param {ol/Map} [params.map] map object must be given if zoomTo is true
67
+ * @param {boolean} [params.searchAddress = false] set true to search for a whole address
68
+ * @param {boolean} [params.searchStreets = false] set true to search for streets
69
+ * @param {boolean} [params.searchHouseNumbers = false] set true to search for house numbers; only works if searchStreets is true
70
+ * @param {boolean} [params.searchDistricts = false] set true to search for districts
71
+ * @param {boolean} [params.searchParcels = false] set true to search for parcels
72
+ * @param {boolean} [params.searchStreetKey = false] set true to search for street keys
73
+ * @param {boolean} [params.minCharacters = 3] minimum length of searchstring
74
+ * @param {boolean} [abortPreviousSearch = false] if true the previous search is aborted
75
+ * @returns {Promise<SearchResult[]>} resolves array of search results; rejects without value if search was canceled internally
76
+ */
77
+ export function search (searchstring, params, abortPreviousSearch = false) {
78
+ if (abortPreviousSearch && abortController !== null) {
79
+ abortController.abort();
80
+ }
81
+ abortController = new AbortController();
82
+
83
+ return new Promise((resolve, reject) => {
84
+ const {
85
+ map,
86
+ zoom = false,
87
+ zoomToParams,
88
+ searchAddress = false,
89
+ searchStreets = false,
90
+ searchDistricts = false,
91
+ searchParcels = false,
92
+ searchStreetKey = false,
93
+ minCharacters = 3
94
+ } = params,
95
+ // promises array
96
+ searches = [];
97
+ let {
98
+ searchHouseNumbers = false
99
+ } = params;
100
+
101
+ // stop search if search string too short
102
+ if (searchstring.length < minCharacters) {
103
+ reject({error: "Search string too short."});
104
+ return;
105
+ }
106
+
107
+ // warn if zooming will not be possible
108
+ if (zoom && !map) {
109
+ console.warn("Instructed to zoom, but required map object was not given. Zooming will be skipped.");
110
+ }
111
+
112
+ // warn if supposed to search for house numbers, but not street - set searchHouseNumbers false for next check
113
+ if (!searchStreets && searchHouseNumbers) {
114
+ console.warn(`Search for '${searchstring}' supposed to retrieve house numbers, but not streets. Invalid search configuration. House numbers will not be searched for as a result.`);
115
+ searchHouseNumbers = false;
116
+ }
117
+
118
+ // stop search if no search to be done
119
+ if (!(searchAddress || searchStreets || searchHouseNumbers || searchDistricts || searchParcels || searchStreetKey)) {
120
+ reject({error: `Search for '${searchstring}' received no indication what to search for. Search is canceled.`});
121
+ return;
122
+ }
123
+
124
+ if (searchStreets) {
125
+ searches.push(searchAndParseStreetAndHouseNumber(searchstring, searchHouseNumbers));
126
+ }
127
+
128
+ if (searchAddress) {
129
+ // assume pattern like "Streetname 41b", split to ["Streetname", "41", "b"]
130
+ const values = searchstring.split(/(\d+)/).map(s => s.trim()).filter(x => x),
131
+ // 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
132
+ type = [false, false, searchTypes.ADDRESS_UNAFFIXED, searchTypes.ADDRESS_AFFIXED][values.length];
133
+
134
+ if (type) {
135
+ searches.push(searchAndParse(type, values));
136
+ }
137
+ }
138
+
139
+ // needs pattern that looks like a name
140
+ if (searchDistricts && (/^[a-z-üäöß]+$/i).test(searchstring)) {
141
+ searches.push(searchAndParse(searchTypes.DISTRICT, searchstring));
142
+ }
143
+
144
+ // needs pattern like "A12345"
145
+ if (searchStreetKey && (/^[a-z]{1}[0-9]{1,5}$/i).test(searchstring)) {
146
+ searches.push(searchAndParse(searchTypes.STREET_KEY, searchstring));
147
+ }
148
+
149
+ if (searchParcels) {
150
+ let values;
151
+
152
+ // assume pattern like "1234/1...", "1234 1...", ...
153
+ if ((/^[0-9]{4}[\s|/][0-9]*$/).test(searchstring)) {
154
+ values = searchstring.split(/[\s|/]/);
155
+ }
156
+ // ... or "12345...", where separation is after fourth character
157
+ else if ((/^[0-9]{5,}$/).test(searchstring)) {
158
+ values = [searchstring.slice(0, 4), searchstring.slice(4)];
159
+ }
160
+
161
+ // if searchstring didn't match a pattern, don't search
162
+ if (values) {
163
+ searches.push(searchAndParse(searchTypes.PARCEL, values));
164
+ }
165
+ }
166
+
167
+ Promise.all(searches)
168
+ .then(arr => {
169
+ const flattened = [].concat(...arr);
170
+
171
+ if (zoom && map && flattened.length === 1) {
172
+ try {
173
+ zoomToSearchResult(map, flattened[0], zoomToParams);
174
+ }
175
+ catch (e) {
176
+ console.error("Zooming to element from gazetteer failed.");
177
+ console.error(e);
178
+ }
179
+ }
180
+ resolve(flattened);
181
+ })
182
+ .catch(e => reject(e));
183
+ });
184
+ }
@@ -0,0 +1,52 @@
1
+ import {getGazetteerUrl} from "./gazetteerUrl";
2
+ import {searchTypes} from "./types";
3
+
4
+ /**
5
+ * Encodes given string(s) to be usable as URI component.
6
+ * @param {(string[]|string)} v value(s) to encode
7
+ * @returns {(string[]|string)} encoded value(s)
8
+ * @ignore
9
+ */
10
+ export function encode (v) {
11
+ return Array.isArray(v) ? v.map(encodeURIComponent) : encodeURIComponent(v);
12
+ }
13
+
14
+ /**
15
+ * Builds the part of the url query where a stored query is addressed by id.
16
+ * @param {string} key internal name for query froms searchTypes
17
+ * @param {(string[]|string)} v string for single-value queries, string[] for multi-value queries, strings in order of appearance in URL
18
+ * @returns {string} URL query part like "&StoryQuery_ID=queryName&param=value"
19
+ * @ignore
20
+ */
21
+ export function getIdQuery (key, v) {
22
+ return {
23
+ [searchTypes.STREET]: encodedValue => `&StoredQuery_ID=findeStrasse&strassenname=*${encodedValue}`,
24
+ [searchTypes.DISTRICT]: encodedValue => `&StoredQuery_ID=findeStadtteil&stadtteilname=${encodedValue}`,
25
+ [searchTypes.PARCEL]: encodedValue => `&StoredQuery_ID=Flurstueck&gemarkung=${encodedValue[0]}&flurstuecksnummer=${encodedValue[1]}`,
26
+ [searchTypes.STREET_KEY]: encodedValue => `&StoredQuery_ID=findeStrassenSchluessel&strassenschluessel=${encodedValue}`,
27
+ [searchTypes.ADDRESS_AFFIXED]: encodedValue => `&StoredQuery_ID=AdresseMitZusatz&strassenname=${encodedValue[0]}&hausnummer=${encodedValue[1]}&zusatz=${encodedValue[2]}`,
28
+ [searchTypes.ADDRESS_UNAFFIXED]: encodedValue => `&StoredQuery_ID=AdresseOhneZusatz&strassenname=${encodedValue[0]}&hausnummer=${encodedValue[1]}`,
29
+ [searchTypes.HOUSE_NUMBERS_FOR_STREET]: encodedValue => `&StoredQuery_ID=HausnummernZuStrasse&strassenname=${encodedValue}`
30
+ }[key](encode(v));
31
+ }
32
+
33
+ /**
34
+ * Retrieves xml text for a gazetteer search.
35
+ * @param {string} key internal name for query froms searchTypes
36
+ * @param {(string[]|string)} value value to search for
37
+ * @param {AbortController} abortController the controller to abort the search
38
+ * @returns {Promise<string>} xhr response text
39
+ * @ignore
40
+ */
41
+ export function searchGazetteer (key, value, abortController) {
42
+ return new Promise((resolve, reject) => {
43
+ const url = getGazetteerUrl() + getIdQuery(key, value);
44
+
45
+ fetch(url, {
46
+ signal: abortController?.signal,
47
+ timeout: 6000
48
+ })
49
+ .then(response => resolve(response.text()))
50
+ .catch(error => reject(error));
51
+ });
52
+ }
@@ -0,0 +1,20 @@
1
+ import defaults from "../defaults";
2
+
3
+ let showGeographicIdentifier = defaults.showGeographicIdentifier;
4
+
5
+ /**
6
+ * Sets whether geographicIdentifier should be used as name of a search result.
7
+ * @param {boolean} show geographicIdentifier should be used as name of a search result
8
+ * @returns {void}
9
+ */
10
+ export function setShowGeographicIdentifier (show) {
11
+ show ? showGeographicIdentifier = show : null;
12
+ }
13
+
14
+ /**
15
+ * Retrieves active gazetteer URL.
16
+ * @returns {boolean} geographicIdentifier should be used as name of a search result
17
+ */
18
+ export function getShowGeographicIdentifier () {
19
+ return showGeographicIdentifier;
20
+ }
@@ -0,0 +1,17 @@
1
+ // enum-like object to avoid typos
2
+ export const searchTypes = {
3
+ STREET: "street",
4
+ DISTRICT: "district",
5
+ PARCEL: "parcel",
6
+ STREET_KEY: "streetKey",
7
+ ADDRESS_AFFIXED: "addressAffixed",
8
+ ADDRESS_UNAFFIXED: "addressUnaffixed",
9
+ HOUSE_NUMBERS_FOR_STREET: "houseNumbersForStreet"
10
+ };
11
+
12
+ /**
13
+ * @typedef {Object} SearchResult
14
+ * @property {String} type which kind of hit this is
15
+ * @property {object} properties contents of the hit as parsed by xml2js - e.g. contains $ keys for xml attributes
16
+ * @property {object} geometry may be a point, a bbox, a polygon, ...
17
+ */
package/test/.eslintrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "jest": true,
5
+ "browser": true
6
+ }
7
+ }
@@ -0,0 +1,109 @@
1
+ import {Map, View} from "ol";
2
+ import proj4 from "proj4";
3
+ import * as Proj from "ol/proj.js";
4
+ import * as crs from "../src/crs";
5
+
6
+ const namedProjections = [
7
+ ["EPSG:31467", "+title=Bessel/Gauß-Krüger 3 +proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs"],
8
+ ["EPSG:25832", "+title=ETRS89/UTM 32N +proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"],
9
+ ["EPSG:8395", "+title=ETRS89/Gauß-Krüger 3 +proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=GRS80 +datum=GRS80 +units=m +no_defs"],
10
+ ["EPSG:4326", "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"]
11
+ ];
12
+
13
+ describe("crs.js", function () {
14
+ beforeAll(() => crs.registerProjections(namedProjections));
15
+
16
+ describe("registerProjections", function () {
17
+ it("registers the projections", function () {
18
+ // registerProjection was called in beforeAll
19
+ namedProjections.forEach(namedProjection => {
20
+ const proj4Return = proj4.defs(namedProjection[0]),
21
+ olReturn = Proj.get(namedProjection[0]);
22
+
23
+ expect(typeof proj4Return).toBe("object");
24
+ expect(typeof olReturn).toBe("object");
25
+ });
26
+ });
27
+ });
28
+
29
+ describe("getProjection", function () {
30
+ it("returns the proj4 projection", function () {
31
+ expect(crs.getProjection("EPSG:25832")).toEqual(proj4.defs("EPSG:25832"));
32
+ });
33
+ });
34
+
35
+ describe("getProjections", function () {
36
+ it("returns all known projections", function () {
37
+ const projections = crs.getProjections(),
38
+ projectionNames = projections.map(entry => entry.name),
39
+ knownProjectionNames = namedProjections.map(entry => entry[0]);
40
+
41
+ // proj4 may come with default projections, hence >=
42
+ expect(projectionNames.length >= knownProjectionNames.length).toBe(true);
43
+ projections.forEach(projection => expect(typeof projection).toBe("object"));
44
+ knownProjectionNames.forEach(name => expect(projectionNames).toContain(name.replace("EPSG:", "http://www.opengis.net/gml/srs/epsg.xml#")));
45
+ });
46
+ });
47
+
48
+ describe("transform", function () {
49
+ it("transforms based on projection names", function () {
50
+ const resultA = crs.transform("EPSG:25832", "EPSG:4326", [0, 0]),
51
+ resultB = crs.transform("EPSG:4326", "EPSG:25832", resultA);
52
+
53
+ expect(resultA[0]).toBeCloseTo(4.511256115);
54
+ expect(resultA[1]).toBe(0);
55
+ expect(resultB[0]).toBeCloseTo(0);
56
+ expect(resultB[1]).toBe(0);
57
+ });
58
+
59
+ it("transforms based on projections", function () {
60
+ const resultA = crs.transform(proj4.defs("EPSG:25832"), proj4.defs("EPSG:4326"), [0, 0]),
61
+ resultB = crs.transform(proj4.defs("EPSG:4326"), proj4.defs("EPSG:25832"), resultA);
62
+
63
+ expect(resultA[0]).toBeCloseTo(4.511256115);
64
+ expect(resultA[1]).toBe(0);
65
+ expect(resultB[0]).toBeCloseTo(0);
66
+ expect(resultB[1]).toBe(0);
67
+ });
68
+
69
+ it("returns undefined and logs error if transformation can not be performed", function () {
70
+ const consoleError = console.error,
71
+ mockError = jest.fn();
72
+ let result = null;
73
+
74
+ console.error = mockError;
75
+ result = crs.transform(proj4.defs("EPSG:WHOOPS_TYPO"), proj4.defs("EPSG:4326"), [0, 0]);
76
+ expect(result).toBeUndefined();
77
+ expect(mockError.mock.calls.length).toBe(1);
78
+ console.error = consoleError;
79
+ });
80
+ });
81
+
82
+ describe("transformToMapProjection", function () {
83
+ it("transforms point to map's current projection", function () {
84
+ const map = new Map({
85
+ view: new View({
86
+ projection: "EPSG:4326"
87
+ })
88
+ }),
89
+ result = crs.transformToMapProjection(map, "EPSG:25832", [0, 0]);
90
+
91
+ expect(result[0]).toBeCloseTo(4.511256115);
92
+ expect(result[1]).toBe(0);
93
+ });
94
+ });
95
+
96
+ describe("transformFromMapProjection", function () {
97
+ it("transforms point from map's current projection", function () {
98
+ const map = new Map({
99
+ view: new View({
100
+ projection: "EPSG:25832"
101
+ })
102
+ }),
103
+ result = crs.transformFromMapProjection(map, "EPSG:4326", [0, 0]);
104
+
105
+ expect(result[0]).toBeCloseTo(4.511256115);
106
+ expect(result[1]).toBe(0);
107
+ });
108
+ });
109
+ });
@@ -0,0 +1,133 @@
1
+ import VectorLayer from "ol/layer/Vector";
2
+ import VectorSource from "ol/source/Vector.js";
3
+ import Feature from "ol/Feature";
4
+ import {Style, Icon} from "ol/style.js";
5
+ import {geojson} from "../../../src";
6
+
7
+ const features = [new Feature(), new Feature()];
8
+
9
+ describe("geojson/index", function () {
10
+ describe("createLayer", function () {
11
+ it("creates an ol/layer/Vector", function () {
12
+ const layer = geojson.createLayer({});
13
+
14
+ expect(layer).toBeInstanceOf(VectorLayer);
15
+ });
16
+
17
+ it("uses default layer style, unless layerStyle is explicitly given", function () {
18
+ function styleFunction () {
19
+ return null;
20
+ }
21
+ const styledLayer = geojson.createLayer({id: "id", style: styleFunction});
22
+
23
+ expect(styledLayer).toBeInstanceOf(VectorLayer);
24
+ expect(styledLayer.get("id")).toEqual("id");
25
+ expect(styledLayer.getSource()).toBeInstanceOf(VectorSource);
26
+ expect(styledLayer.getStyleFunction()).toBeDefined();
27
+ expect(styledLayer.getStyleFunction()).toEqual(styleFunction);
28
+ });
29
+ });
30
+ describe("createLayer with additional params and options", function () {
31
+ it("creates a VectorLayer with layerParams", function () {
32
+ const layerParams = {
33
+ name: "name",
34
+ layers: "layer1, layer2"
35
+ },
36
+ layer = geojson.createLayer({id: "id"}, {layerParams});
37
+
38
+ expect(layer).toBeInstanceOf(VectorLayer);
39
+ expect(layer.get("id")).toEqual("id");
40
+ expect(layer.getSource()).toBeInstanceOf(VectorSource);
41
+ expect(layer.get("name")).toEqual("name");
42
+ expect(layer.get("layers")).toEqual("layer1, layer2");
43
+ });
44
+ it("creates a VectorLayer with style in options", function () {
45
+ function styleFunction () {
46
+ const icon = new Style({
47
+ image: new Icon({
48
+ src: "https://building.png",
49
+ scale: 0.5,
50
+ opacity: 1
51
+ })
52
+ });
53
+
54
+ return [icon];
55
+ }
56
+ const options = {
57
+ style: styleFunction
58
+ },
59
+ layerParams = {
60
+ name: "name",
61
+ layers: "layer1, layer2"
62
+ },
63
+ layer = geojson.createLayer({id: "id"}, {layerParams, options});
64
+
65
+ expect(layer).toBeInstanceOf(VectorLayer);
66
+ expect(layer.get("id")).toEqual("id");
67
+ expect(layer.getSource()).toBeInstanceOf(VectorSource);
68
+ expect(layer.getStyleFunction()).toBeDefined();
69
+ expect(layer.getStyleFunction()).toEqual(options.style);
70
+ expect(layer.get("name")).toEqual("name");
71
+ expect(layer.get("layers")).toEqual("layer1, layer2");
72
+ });
73
+ });
74
+
75
+ describe("createLayerSource", function () {
76
+ it("sets format and url for remote geojson", function () {
77
+ const source = geojson.createLayerSource({url: "example.com/geo.json"}, {loadingStrategy: {}});
78
+
79
+ expect(source.getFormat()).toBeDefined();
80
+ expect(source.getUrl()).toBeDefined();
81
+ });
82
+
83
+ });
84
+
85
+ describe("setFeatureStyle", function () {
86
+ it("sets a given style to all given features", function () {
87
+ function styleFunction () {
88
+ return null;
89
+ }
90
+
91
+ geojson.setFeatureStyle(features, styleFunction);
92
+ expect(features[0].getStyle()).toBe(styleFunction);
93
+ expect(features[1].getStyle()).toBe(styleFunction);
94
+ });
95
+ });
96
+
97
+ describe("hideAllFeatures", function () {
98
+ it("sets all features' styles of a layer to the null constant function", function () {
99
+ const layer = geojson.createLayer({id: "id"});
100
+
101
+ layer.getSource().addFeatures(features);
102
+
103
+ geojson.hideAllFeatures(layer);
104
+ layer.getSource().getFeatures().forEach(feature => expect(feature.getStyle()()).toBeNull());
105
+ });
106
+ });
107
+ describe("showAllFeatures", function () {
108
+ it("sets all features' styles of a layer to undefined", function () {
109
+ const layer = geojson.createLayer({id: "id"});
110
+
111
+ layer.getSource().addFeatures(features);
112
+ geojson.hideAllFeatures(layer);
113
+ geojson.showAllFeatures(layer);
114
+ layer.getSource().getFeatures().forEach(feature => expect(feature.getStyle()).toBeUndefined());
115
+ });
116
+ });
117
+ describe("showFeaturesById", function () {
118
+ it("sets features with id visible, and all others invisible; unknown ids are ignored", () => {
119
+ const layer = geojson.createLayer({id: "id"});
120
+
121
+ layer.getSource().addFeatures(features);
122
+
123
+ geojson.showFeaturesById(layer, []);
124
+ expect(features[0].getStyle()()).toBeNull();
125
+ expect(features[1].getStyle()()).toBeNull();
126
+
127
+ geojson.showFeaturesById(layer, ["2", "9"]);
128
+
129
+ expect(features[0].getStyle()()).toBeNull();
130
+ expect(features[1].getStyle()()).toBeNull();
131
+ });
132
+ });
133
+ });
@@ -0,0 +1,46 @@
1
+ import {Style} from "ol/style.js";
2
+
3
+ import style, {setCustomStyles} from "../../../src/layer/geojson/style";
4
+
5
+ const types = [
6
+ "Point",
7
+ "LineString",
8
+ "MultiLineString",
9
+ "MultiPoint",
10
+ "MultiPolygon",
11
+ "Polygon",
12
+ "GeometryCollection",
13
+ "Circle"
14
+ ];
15
+
16
+ function mockFeature (type) {
17
+ return {
18
+ getGeometry: () => ({
19
+ getType: () => type
20
+ })
21
+ };
22
+ }
23
+
24
+ describe("geojson/style", function () {
25
+ // reset module in case another test changed it
26
+ beforeEach(() => setCustomStyles({}));
27
+
28
+ describe("style", function () {
29
+ it("returns default styling for all openlayers feature types", function () {
30
+ types.forEach(type => expect(style(mockFeature(type))).toBeInstanceOf(Style));
31
+ });
32
+
33
+ it("uses custom styles first, if set", function () {
34
+ const custom = {
35
+ Point: 1,
36
+ Polygon: 1
37
+ };
38
+
39
+ setCustomStyles(custom);
40
+
41
+ types.forEach(type => custom[type]
42
+ ? expect(typeof style(mockFeature(type))).toBe("number")
43
+ : expect(style(mockFeature(type))).toBeInstanceOf(Style));
44
+ });
45
+ });
46
+ });
@@ -0,0 +1,83 @@
1
+ import * as lib from "../../src/layer/lib";
2
+
3
+ describe("wms.js", function () {
4
+ describe("isLayerVisibleInResolution", function () {
5
+ it("returns true if layer is visible from x to y and resolution r is x<=r<=y", function () {
6
+ const mockLayer = {
7
+ getMaxResolution: () => 5,
8
+ getMinResolution: () => 2
9
+ };
10
+
11
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 1})).toBe(false);
12
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 2})).toBe(true);
13
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 3})).toBe(true);
14
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 4})).toBe(true);
15
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 5})).toBe(true);
16
+ expect(lib.isLayerVisibleInResolution(mockLayer, {resolution: 6})).toBe(false);
17
+ });
18
+ });
19
+
20
+ describe("getLegendURLs", function () {
21
+ const expectedBaseString = "example.com?SERVICE=WMS&REQUEST=GetLegendGraphic&FORMAT=image/png&VERSION=0.0.1&LAYER=";
22
+
23
+ it("returns the defined legendURL if given via services.json", function () {
24
+ const legendURL = lib.getLegendURLs({
25
+ legendURL: "example.com/legend.png",
26
+ layers: "a,b",
27
+ url: "example.com",
28
+ version: "0.0.1",
29
+ typ: "WMS"
30
+ });
31
+
32
+ expect(legendURL).toEqual(["example.com/legend.png"]);
33
+ });
34
+
35
+ it("returns an empty array if legendURL is defined as 'ignore' services.json", function () {
36
+ const legendURL = lib.getLegendURLs({
37
+ legendURL: "ignore",
38
+ layers: "a,b",
39
+ url: "example.com",
40
+ version: "0.0.1",
41
+ typ: "WMS"
42
+ });
43
+
44
+ expect(legendURL).toEqual([]);
45
+ });
46
+
47
+ it("returns an empty array if no layers were requested", function () {
48
+ const legendURLs = lib.getLegendURLs({
49
+ layers: "",
50
+ url: "example.com",
51
+ version: "0.0.1",
52
+ typ: "WMS"
53
+ });
54
+
55
+ expect(legendURLs.length).toBe(0);
56
+ });
57
+
58
+ it("returns one correct legend URL if one layer was requested", function () {
59
+ const legendURLs = lib.getLegendURLs({
60
+ layers: "a",
61
+ url: "example.com",
62
+ version: "0.0.1",
63
+ typ: "WMS"
64
+ });
65
+
66
+ expect(legendURLs.length).toBe(1);
67
+ expect(legendURLs[0]).toEqual(expectedBaseString + "a");
68
+ });
69
+
70
+ it("returns multiple correct legend URLs if multiple layers were requested", function () {
71
+ const legendURLs = lib.getLegendURLs({
72
+ layers: "a,b",
73
+ url: "example.com",
74
+ version: "0.0.1",
75
+ typ: "WMS"
76
+ });
77
+
78
+ expect(legendURLs.length).toBe(2);
79
+ expect(legendURLs[0]).toEqual(expectedBaseString + "a");
80
+ expect(legendURLs[1]).toEqual(expectedBaseString + "b");
81
+ });
82
+ });
83
+ });