• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 15133426507

20 May 2025 09:04AM UTC coverage: 76.62% (+0.002%) from 76.618%
15133426507

Pull #11110

github

web-flow
Merge 191870fcb into 51ca3d006
Pull Request #11110: c027-2024.01.xx - Fix WMS error and dashboard credentials insertion

30797 of 48242 branches covered (63.84%)

4 of 10 new or added lines in 6 files covered. (40.0%)

1 existing line in 1 file now uncovered.

38507 of 50257 relevant lines covered (76.62%)

32.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.09
/web/client/components/map/openlayers/plugins/WMSLayer.js
1
/*
2
 * Copyright 2017, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import Layers from '../../../../utils/openlayers/Layers';
10
import isNil from 'lodash/isNil';
11
import isEqual from 'lodash/isEqual';
12
import union from 'lodash/union';
13
import isArray from 'lodash/isArray';
14
import assign from 'object-assign';
15
import axios from '../../../../libs/ajax';
16
import CoordinatesUtils from '../../../../utils/CoordinatesUtils';
17
import { getProjection } from '../../../../utils/ProjectionUtils';
18
import { getConfigProp } from '../../../../utils/ConfigUtils';
19

20
import {optionsToVendorParams} from '../../../../utils/VendorParamsUtils';
21
import {addAuthenticationToSLD, addAuthenticationParameter, getAuthenticationHeaders} from '../../../../utils/SecurityUtils';
22

23
import ImageLayer from 'ol/layer/Image';
24
import ImageWMS from 'ol/source/ImageWMS';
25
import {get} from 'ol/proj';
26
import TileLayer from 'ol/layer/Tile';
27
import TileWMS from 'ol/source/TileWMS';
28

29
import VectorTileSource from 'ol/source/VectorTile';
30
import VectorTileLayer from 'ol/layer/VectorTile';
31

32
import { isVectorFormat } from '../../../../utils/VectorTileUtils';
33
import { OL_VECTOR_FORMATS, applyStyle } from '../../../../utils/openlayers/VectorTileUtils';
34

35
import { proxySource, getWMSURLs, wmsToOpenlayersOptions, toOLAttributions, generateTileGrid } from '../../../../utils/openlayers/WMSUtils';
36

37
const loadFunction = (options, headers) => function(image, src) {
48✔
38
    // fixes #3916, see https://gis.stackexchange.com/questions/175057/openlayers-3-wms-styling-using-sld-body-and-post-request
39
    let img = image.getImage();
14✔
40
    let newSrc = proxySource(options.forceProxy, src);
13✔
41

42
    if (typeof window.btoa === 'function' && src.length >= (options.maxLengthUrl || getConfigProp('miscSettings')?.maxURLLength || Infinity)) {
13✔
43
        // GET ALL THE PARAMETERS OUT OF THE SOURCE URL**
44
        let [url, ...dataEntries] = src.split("&");
5✔
45
        url = proxySource(options.forceProxy, url);
5✔
46

47
        // SET THE PROPER HEADERS AND FINALLY SEND THE PARAMETERS
48
        axios.post(url, "&" + dataEntries.join("&"), {
5✔
49
            headers: {
50
                "Content-type": "application/x-www-form-urlencoded;charset=utf-8",
51
                ...headers
52
            },
53
            responseType: 'arraybuffer'
54
        }).then(response => {
55
            if (response.status === 200) {
5!
56
                const uInt8Array = new Uint8Array(response.data);
5✔
57
                let i = uInt8Array.length;
5✔
58
                const binaryString = new Array(i);
5✔
59
                while (i--) {
5✔
60
                    binaryString[i] = String.fromCharCode(uInt8Array[i]);
×
61
                }
62
                const dataImg = binaryString.join('');
5✔
63
                const type = response.headers['content-type'];
5✔
64
                if (type.indexOf('image') === 0) {
5!
65
                    img.src = 'data:' + type + ';base64,' + window.btoa(dataImg);
5✔
66
                }
67
            }
68
        }).catch(e => {
69
            console.error(e);
×
70
        });
71
    } else {
72
        if (headers) {
8✔
73
            axios.get(newSrc, {
4✔
74
                headers,
75
                responseType: 'blob'
76
            }).then(response => {
77
                if (response.status === 200 && response.data && response?.data?.type !== "text/xml") {
4!
78
                    image.getImage().src = URL.createObjectURL(response.data);
4✔
79
                } else {
80
                    console.error("Status code: " + response.status);
×
NEW
81
                    image.getImage().src = null;
×
82
                }
83
            }).catch(e => {
84
                image.getImage().src = null;
4✔
85
                console.error(e);
4✔
86
            });
87
        } else {
88
            img.src = newSrc;
4✔
89
        }
90
    }
91
};
92

93
const createLayer = (options, map, mapId) => {
1✔
94
    // the useForElevation in wms types will be deprecated
95
    // as support for existing configuration
96
    // we can use this fallback
97
    if (options.useForElevation) {
52✔
98
        return Layers.createLayer('elevation', {
4✔
99
            ...options,
100
            provider: 'wms'
101
        }, map, mapId);
102
    }
103
    const urls = getWMSURLs(isArray(options.url) ? options.url : [options.url]);
48✔
104
    const queryParameters = wmsToOpenlayersOptions(options) || {};
48!
105
    urls.forEach(url => addAuthenticationParameter(url, queryParameters, options.securityToken));
50✔
106
    const headers = getAuthenticationHeaders(urls[0], options.securityToken, options.security);
48✔
107
    const vectorFormat = isVectorFormat(options.format);
48✔
108

109
    if (options.singleTile && !vectorFormat) {
48✔
110
        return new ImageLayer({
7✔
111
            msId: options.id,
112
            opacity: options.opacity !== undefined ? options.opacity : 1,
7✔
113
            visible: options.visibility !== false,
114
            zIndex: options.zIndex,
115
            minResolution: options.minResolution,
116
            maxResolution: options.maxResolution,
117
            source: new ImageWMS({
118
                url: urls[0],
119
                crossOrigin: options.crossOrigin,
120
                attributions: toOLAttributions(options.credits),
121
                params: queryParameters,
122
                ratio: options.ratio || 1,
13✔
123
                imageLoadFunction: loadFunction(options, headers)
124
            })
125
        });
126
    }
127
    const sourceOptions = {
41✔
128
        attributions: toOLAttributions(options.credits),
129
        urls: urls,
130
        crossOrigin: options.crossOrigin,
131
        params: queryParameters,
132
        tileGrid: generateTileGrid(options, map),
133
        tileLoadFunction: loadFunction(options, headers)
134
    };
135

136
    const wmsSource = new TileWMS({ ...sourceOptions });
41✔
137
    const layerConfig = {
41✔
138
        msId: options.id,
139
        opacity: options.opacity !== undefined ? options.opacity : 1,
41✔
140
        visible: options.visibility !== false,
141
        zIndex: options.zIndex,
142
        minResolution: options.minResolution,
143
        maxResolution: options.maxResolution
144
    };
145
    let layer;
146
    if (vectorFormat) {
41✔
147
        layer = new VectorTileLayer({
5✔
148
            ...layerConfig,
149
            source: new VectorTileSource({
150
                ...sourceOptions,
151
                format: new OL_VECTOR_FORMATS[options.format]({
152
                    layerName: '_layer_'
153
                }),
154
                tileUrlFunction: (tileCoord, pixelRatio, projection) => wmsSource.tileUrlFunction(tileCoord, pixelRatio, projection)
×
155
            })
156
        });
157
    } else {
158

159
        layer = new TileLayer({
36✔
160
            ...layerConfig,
161
            source: wmsSource
162
        });
163
    }
164
    layer.set('map', map);
41✔
165
    if (vectorFormat) {
41✔
166
        layer.set('wmsSource', wmsSource);
5✔
167
        if (options.vectorStyle) {
5✔
168
            applyStyle(options.vectorStyle, layer, map);
2✔
169
        }
170
    }
171
    return layer;
41✔
172
};
173

174
const mustCreateNewLayer = (oldOptions, newOptions) => {
1✔
175
    return (oldOptions.singleTile !== newOptions.singleTile
17✔
176
        || oldOptions.securityToken !== newOptions.securityToken
177
        || oldOptions.ratio !== newOptions.ratio
178
        // no way to remove attribution when credits are removed, so have re-create the layer is needed. Seems to be solved in OL v5.3.0, due to the ol commit 9b8232f65b391d5d381d7a99a7cd070fc36696e9 (https://github.com/openlayers/openlayers/pull/7329)
179
        || oldOptions.credits !== newOptions.credits && !newOptions.credits
180
        || isVectorFormat(oldOptions.format) !== isVectorFormat(newOptions.format)
181
        || isVectorFormat(oldOptions.format) && isVectorFormat(newOptions.format) && oldOptions.format !== newOptions.format
182
        || oldOptions.localizedLayerStyles !== newOptions.localizedLayerStyles
183
        || oldOptions.tileSize !== newOptions.tileSize
184
        || oldOptions.forceProxy !== newOptions.forceProxy
185
        || oldOptions.url !== newOptions.url
186
        || oldOptions.tileGridStrategy !== newOptions.tileGridStrategy
187
        || !isEqual(oldOptions.tileGrids, newOptions.tileGrids)
188
    );
189
};
190

191
Layers.registerType('wms', {
1✔
192
    create: createLayer,
193
    update: (layer, newOptions, oldOptions, map) => {
194
        const newIsVector = isVectorFormat(newOptions.format);
17✔
195

196
        if (mustCreateNewLayer(oldOptions, newOptions)) {
17✔
197
            // TODO: do we need to clean anything before re-creating stuff from scratch?
198
            return createLayer(newOptions, map);
6✔
199
        }
200
        let needsRefresh = false;
11✔
201
        if (newIsVector && newOptions.vectorStyle && !isEqual(newOptions.vectorStyle, oldOptions.vectorStyle || {})) {
11!
202
            applyStyle(newOptions.vectorStyle, layer, map);
×
203
            needsRefresh = true;
×
204
        }
205

206
        const wmsSource = layer.get('wmsSource') || layer.getSource();
11✔
207
        const vectorSource = newIsVector ? layer.getSource() : null;
11!
208

209
        if (oldOptions.srs !== newOptions.srs) {
11!
210
            const normalizedSrs = CoordinatesUtils.normalizeSRS(newOptions.srs, newOptions.allowedSRS);
×
211
            const extent = get(normalizedSrs).getExtent() || getProjection(normalizedSrs).extent;
×
212
            if (newOptions.singleTile && !newIsVector) {
×
213
                layer.setExtent(extent);
×
214
            } else {
215
                const tileGrid = generateTileGrid(newOptions, map);
×
216
                wmsSource.tileGrid = tileGrid;
×
217
                if (vectorSource) {
×
218
                    vectorSource.tileGrid = tileGrid;
×
219
                }
220
            }
221
            needsRefresh = true;
×
222
        }
223

224
        if (oldOptions.credits !== newOptions.credits && newOptions.credits) {
11✔
225
            wmsSource.setAttributions(toOLAttributions(newOptions.credits));
3✔
226
            needsRefresh = true;
3✔
227
        }
228

229
        let changed = false;
11✔
230
        let oldParams;
231
        let newParams;
232
        if (oldOptions && wmsSource && wmsSource.updateParams) {
11!
233
            if (oldOptions.params && newOptions.params) {
11✔
234
                changed = union(
4✔
235
                    Object.keys(oldOptions.params),
236
                    Object.keys(newOptions.params)
237
                ).reduce((found, param) => {
238
                    if (newOptions.params[param] !== oldOptions.params[param]) {
5✔
239
                        return true;
3✔
240
                    }
241
                    return found;
2✔
242
                }, false);
243
            } else if ((!oldOptions.params && newOptions.params) || (oldOptions.params && !newOptions.params)) {
7!
244
                changed = true;
×
245
            }
246
            oldParams = wmsToOpenlayersOptions(oldOptions);
11✔
247
            newParams = wmsToOpenlayersOptions(newOptions);
11✔
248
            changed = changed || ["LAYERS", "STYLES", "FORMAT", "TRANSPARENT", "TILED", "VERSION", "_v_", "CQL_FILTER", "SLD", "VIEWPARAMS"].reduce((found, param) => {
11✔
249
                if (oldParams[param] !== newParams[param]) {
80✔
250
                    return true;
2✔
251
                }
252
                return found;
78✔
253
            }, false);
254

255
            needsRefresh = needsRefresh || changed;
11✔
256
        }
257

258
        if (oldOptions.minResolution !== newOptions.minResolution) {
11✔
259
            layer.setMinResolution(newOptions.minResolution === undefined ? 0 : newOptions.minResolution);
1!
260
        }
261
        if (oldOptions.maxResolution !== newOptions.maxResolution) {
11✔
262
            layer.setMaxResolution(newOptions.maxResolution === undefined ? Infinity : newOptions.maxResolution);
1!
263
        }
264
        if (!isEqual(oldOptions.security, newOptions.security)) {
11!
265
            const urls = getWMSURLs(isArray(newOptions.url) ? newOptions.url : [newOptions.url]);
×
266
            const headers = getAuthenticationHeaders(urls[0], newOptions.securityToken, newOptions.security);
×
267
            wmsSource.setTileLoadFunction(loadFunction(newOptions, headers));
×
268
            wmsSource.refresh();
×
269
        }
270
        if (needsRefresh) {
11✔
271
            // forces tile cache drop
272
            // this prevents old cached tiles at lower zoom levels to be
273
            // rendered during new params load
274
            wmsSource?.tileCache?.pruneExceptNewestZ?.();
8✔
275
            if (vectorSource) {
8!
276
                vectorSource.clear();
×
277
                vectorSource.refresh();
×
278
            }
279

280
            if (changed) {
8✔
281
                const params = assign(newParams, addAuthenticationToSLD(optionsToVendorParams(newOptions) || {}, newOptions));
5✔
282

283
                wmsSource.updateParams(assign(params, Object.keys(oldParams || {}).reduce((previous, key) => {
5!
284
                    return !isNil(params[key]) ? previous : assign(previous, {
45✔
285
                        [key]: undefined
286
                    });
287
                }, {})));
288
            }
289
        }
290
        return null;
11✔
291
    }
292
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc