@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.
- package/.editorconfig +15 -0
- package/.eslintignore +6 -0
- package/.eslintrc +184 -0
- package/.husky/pre-push +4 -0
- package/CHANGELOG.md +217 -0
- package/License.txt +21 -0
- package/README.md +24 -0
- package/babel.config.json +6 -0
- package/example/README.md +7 -0
- package/example/config/localGeoJSON.js +956 -0
- package/example/config/portal.json +35 -0
- package/example/config/services.json +79 -0
- package/example/index.html +27 -0
- package/example/index.js +210 -0
- package/example/style.scss +59 -0
- package/jest.config.js +18 -0
- package/jest.setup.js +5 -0
- package/jsdoc.json +15 -0
- package/package.json +62 -0
- package/public/marker.svg +1 -0
- package/public/stringMarker.js +5 -0
- package/src/crs.js +122 -0
- package/src/defaults.js +38 -0
- package/src/index.js +30 -0
- package/src/layer/geojson/index.js +109 -0
- package/src/layer/geojson/style.js +61 -0
- package/src/layer/lib.js +32 -0
- package/src/layer/oaf.js +207 -0
- package/src/layer/terrain.js +72 -0
- package/src/layer/tileset.js +71 -0
- package/src/layer/vector.js +48 -0
- package/src/layer/vectorBase.js +39 -0
- package/src/layer/wfs.js +218 -0
- package/src/layer/wms.js +185 -0
- package/src/lib/coordsToPairs.js +15 -0
- package/src/lib/getInitialLayers.js +22 -0
- package/src/lib/load3DScript.js +22 -0
- package/src/lib/oafUtil.js +22 -0
- package/src/lib/setBackgroundImage.js +23 -0
- package/src/lib/wfsUtil.js +75 -0
- package/src/lib/zoomTo.js +40 -0
- package/src/maps/api.js +5 -0
- package/src/maps/map.js +35 -0
- package/src/maps/mapView.js +54 -0
- package/src/maps/ol/olMap.js +124 -0
- package/src/maps/olcs/3dUtils/fixedOverlaySynchronizer.js +39 -0
- package/src/maps/olcs/3dUtils/wmsRasterSynchronizer.js +348 -0
- package/src/maps/olcs/olcsMap.js +212 -0
- package/src/rawLayerList.js +88 -0
- package/src/searchAddress/gazetteerUrl.js +22 -0
- package/src/searchAddress/index.js +13 -0
- package/src/searchAddress/parse.js +196 -0
- package/src/searchAddress/search.js +184 -0
- package/src/searchAddress/searchGazetteer.js +52 -0
- package/src/searchAddress/showGeographicIdentifier.js +20 -0
- package/src/searchAddress/types.js +17 -0
- package/test/.eslintrc +7 -0
- package/test/crs.test.js +109 -0
- package/test/layer/geojson/index.test.js +133 -0
- package/test/layer/geojson/style.test.js +46 -0
- package/test/layer/lib.test.js +83 -0
- package/test/layer/oaf.test.js +245 -0
- package/test/layer/resources/oafFeatures.js +105 -0
- package/test/layer/resources/wfsFeatures.js +26 -0
- package/test/layer/resources/wfsFilter.js +17 -0
- package/test/layer/terrain.test.js +106 -0
- package/test/layer/tileset.test.js +116 -0
- package/test/layer/vector.test.js +76 -0
- package/test/layer/vectorBase.test.js +48 -0
- package/test/layer/wfs.test.js +388 -0
- package/test/layer/wms.test.js +256 -0
- package/test/lib/coordsToPairs.test.js +9 -0
- package/test/lib/getInitialLayers.test.js +28 -0
- package/test/lib/oafUtil.test.js +24 -0
- package/test/lib/setBackgroundImage.test.js +49 -0
- package/test/lib/wfsUtil.test.js +28 -0
- package/test/lib/zoomTo.test.js +74 -0
- package/test/map.test.js +258 -0
- package/test/mapView.test.js +59 -0
- package/test/rawLayerList.test.js +48 -0
- package/test/searchAddress/gazetteerSearchResults/address.js +175 -0
- package/test/searchAddress/gazetteerSearchResults/district.js +71 -0
- package/test/searchAddress/gazetteerSearchResults/houseNumber.js +32 -0
- package/test/searchAddress/gazetteerSearchResults/index.js +19 -0
- package/test/searchAddress/gazetteerSearchResults/parcel.js +44 -0
- package/test/searchAddress/gazetteerSearchResults/street.js +46 -0
- package/test/searchAddress/gazetteerSearchResults/streetKey.js +51 -0
- package/test/searchAddress/gazetteerUrl.test.js +22 -0
- package/test/searchAddress/parse.test.js +110 -0
- package/test/searchAddress/search.test.js +150 -0
- package/test/searchAddress/searchGazetteer.test.js +58 -0
- 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¶m=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
package/test/crs.test.js
ADDED
|
@@ -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
|
+
});
|