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

geosolutions-it / MapStore2 / 14797665772

02 May 2025 02:55PM UTC coverage: 76.933% (-0.06%) from 76.991%
14797665772

Pull #11067

github

web-flow
Merge 99dcd7f92 into d28bf7035
Pull Request #11067: Fix #10966 basic auth for services

30858 of 48053 branches covered (64.22%)

104 of 172 new or added lines in 24 files covered. (60.47%)

3 existing lines in 3 files now uncovered.

38384 of 49893 relevant lines covered (76.93%)

35.93 hits per line

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

90.85
/web/client/components/map/openlayers/Layer.jsx
1
/**
2
 * Copyright 2015, 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 PropTypes from 'prop-types';
10
import React from 'react';
11
import Layers from '../../../utils/openlayers/Layers';
12
import {normalizeSRS, reprojectBbox, getExtentFromNormalized, isBboxCompatible, getPolygonFromExtent} from '../../../utils/CoordinatesUtils';
13
import assign from 'object-assign';
14
import Rx from 'rxjs';
15
import isNumber from 'lodash/isNumber';
16
import isArray from 'lodash/isArray';
17
import omit from 'lodash/omit';
18
import isEqual from 'lodash/isEqual';
19
import isNil from 'lodash/isNil';
20
import { getZoomFromResolution } from '../../../utils/MapUtils';
21

22
export default class OpenlayersLayer extends React.Component {
23
    static propTypes = {
1✔
24
        onWarning: PropTypes.func,
25
        maxExtent: PropTypes.array,
26
        map: PropTypes.object,
27
        mapId: PropTypes.string,
28
        srs: PropTypes.string,
29
        type: PropTypes.string,
30
        options: PropTypes.object,
31
        onLayerLoading: PropTypes.func,
32
        onLayerError: PropTypes.func,
33
        onCreationError: PropTypes.func,
34
        onLayerLoad: PropTypes.func,
35
        position: PropTypes.number,
36
        observables: PropTypes.array,
37
        securityToken: PropTypes.string,
38
        env: PropTypes.array,
39
        resolutions: PropTypes.array
40
    };
41

42
    static defaultProps = {
1✔
43
        observables: [],
44
        onLayerLoading: () => {},
45
        onLayerLoad: () => {},
46
        onLayerError: () => {},
47
        onCreationError: () => {},
48
        onWarning: () => {},
49
        srs: "EPSG:3857"
50
    };
51

52
    componentDidMount() {
53
        this.valid = true;
167✔
54
        this.tilestoload = 0;
167✔
55
        this.imagestoload = 0;
167✔
56
        this.createLayer(
167✔
57
            this.props.type,
58
            this.props.options,
59
            this.props.position,
60
            this.props.securityToken,
61
            this.props.env,
62
            this.props.resolutions
63
        );
64
    }
65

66
    UNSAFE_componentWillReceiveProps(newProps) {
67

68
        this.setLayerVisibility(newProps);
41✔
69

70
        const newOpacity = newProps.options && newProps.options.opacity !== undefined ? newProps.options.opacity : 1.0;
41✔
71
        this.setLayerOpacity(newOpacity);
41✔
72

73
        if (newProps.position !== this.props.position && this.layer && this.layer.setZIndex) {
41✔
74
            this.layer.setZIndex(newProps.position);
1✔
75
        }
76
        if (this.props.options) {
41!
77
            this.updateLayer(newProps, this.props);
41✔
78
        }
79
    }
80

81
    componentWillUnmount() {
82
        if (this.layer && this.props.map) {
73✔
83
            if (this.tileLoadEndStream$) {
72!
84
                this.tileLoadEndStream$.complete();
72✔
85
                this.tileStopStream$.complete();
72✔
86
                this.imageLoadEndStream$.complete();
72✔
87
                this.imageStopStream$.complete();
72✔
88
            }
89
            // detached layers are layers that do not attach directly to the map
90
            // they have their own lifecycle methods instead (e.g. remove)
91
            if (this.layer.detached) {
72!
92
                this.layer.remove();
×
93
            } else {
94
                this.props.map.removeLayer(this.layer);
72✔
95
            }
96
        }
97
        if (this.refreshTimer) {
73!
98
            clearInterval(this.refreshTimer);
×
99
        }
100
        Layers.removeLayer(this.props.type, this.props.options, this.props.map, this.props.mapId, this.layer);
73✔
101
    }
102

103
    render() {
104
        if (this.props.children) {
371✔
105
            const layer = this.layer;
80✔
106
            const children = layer ? React.Children.map(this.props.children, child => {
80✔
107
                return child ? React.cloneElement(child, {container: layer, styleName: this.props.options && this.props.options.styleName}) : null;
19!
108
            }) : null;
109
            return (
80✔
110
                <React.Fragment>
111
                    {children}
112
                </React.Fragment>
113
            );
114
        }
115

116
        return Layers.renderLayer(this.props.type, this.props.options, this.props.map, this.props.mapId, this.layer);
291✔
117
    }
118

119
    setLayerVisibility = (newProps) => {
167✔
120
        const oldVisibility = this.props.options && this.props.options.visibility !== false;
41✔
121
        const newVisibility = newProps.options && newProps.options.visibility !== false;
41✔
122
        if (newVisibility !== oldVisibility && this.layer && this.isValid()) {
41✔
123
            this.layer.setVisible(newVisibility);
1✔
124
        }
125
    };
126

127
    setLayerOpacity = (opacity) => {
167✔
128
        var oldOpacity = this.props.options && this.props.options.opacity !== undefined ? this.props.options.opacity : 1.0;
41✔
129
        if (opacity !== oldOpacity && this.layer) {
41✔
130
            this.layer.setOpacity(opacity);
1✔
131
        }
132
    };
133

134
    generateOpts = (layerOptions, position, srs, securityToken, env, resolutions) => {
167✔
135
        const {
136
            minResolution,
137
            maxResolution,
138
            disableResolutionLimits,
139
            ...otherOptions
140
        } = layerOptions;
219✔
141
        const options = {
219✔
142
            ...otherOptions,
143
            ...(!disableResolutionLimits && {
437✔
144
                minResolution,
145
                maxResolution,
146
                // google layer
147
                minZoom: !isNil(maxResolution) ? getZoomFromResolution(maxResolution, resolutions) : undefined,
218✔
148
                maxZoom: !isNil(minResolution) ? getZoomFromResolution(minResolution, resolutions) : undefined
218✔
149
            })
150
        };
151
        return assign({}, options, isNumber(position) ? {zIndex: position} : null, {
219✔
152
            srs,
153
            onError: () => {
154
                this.props.onCreationError(options);
3✔
155
            },
156
            securityToken,
157
            env
158
        });
159
    };
160

161
    createLayer = (type, options, position, securityToken, env, resolutions) => {
167✔
162
        if (type) {
167✔
163
            const layerOptions = this.generateOpts(options, position, normalizeSRS(this.props.srs), securityToken, env, resolutions);
163✔
164
            this.layer = Layers.createLayer(type, layerOptions, this.props.map, this.props.mapId);
163✔
165
            const compatible = Layers.isCompatible(type, layerOptions);
163✔
166
            // detached layers are layers that do not attach directly to the map
167
            // for this reason addLayer is not called on them
168
            if (this.layer && !this.layer.detached) {
163✔
169
                const parentMap = this.props.map;
154✔
170
                const mapExtent = parentMap && parentMap.getView().getProjection().getExtent();
154✔
171
                const layerExtent = options && options.bbox && options.bbox.bounds;
154✔
172
                const mapBboxPolygon = mapExtent && reprojectBbox(mapExtent, this.props.srs, 'EPSG:4326');
154✔
173
                let layerBboxPolygon = layerExtent && reprojectBbox(
154✔
174
                    getExtentFromNormalized(layerExtent, this.props.srs).extent,
175
                    'EPSG:4326'
176
                );
177
                if (layerBboxPolygon && layerBboxPolygon.length === 2 && isArray(layerBboxPolygon[1])) {
154!
178
                    layerBboxPolygon = layerBboxPolygon[1];
×
179
                }
180

181
                if (mapBboxPolygon && layerBboxPolygon &&
154!
182
                    !isBboxCompatible(getPolygonFromExtent(mapBboxPolygon), getPolygonFromExtent(layerBboxPolygon)) ||
183
                    !compatible) {
184
                    this.props.onWarning({
×
185
                        title: "warning",
186
                        message: "notification.incompatibleDataAndProjection",
187
                        action: {
188
                            label: "close"
189
                        },
190
                        position: "tc",
191
                        uid: "1"
192
                    });
193
                }
194
                this.addLayer(options);
154✔
195
            }
196

197
            this.forceUpdate();
163✔
198
        }
199
    };
200

201
    updateLayer = (newProps, oldProps) => {
167✔
202
        // optimization to avoid to update the layer if not necessary
203
        if (newProps.position === oldProps.position && newProps.srs === oldProps.srs && newProps.securityToken === oldProps.securityToken ) {
41✔
204
            // check if options are the same, except loading
205
            if (newProps.options === oldProps.options) return;
36✔
206
            if (isEqual( omit(newProps.options, ["loading"]), omit(oldProps.options, ["loading"]) ) ) {
23!
UNCOV
207
                return;
×
208
            }
209
        }
210
        const newLayer = Layers.updateLayer(
28✔
211
            this.props.type,
212
            this.layer,
213
            this.generateOpts(newProps.options, newProps.position, newProps.projection, newProps.securityToken, newProps.env, newProps.resolutions),
214
            this.generateOpts(oldProps.options, oldProps.position, oldProps.projection, oldProps.securityToken, oldProps.env, oldProps.resolutions),
215
            this.props.map,
216
            this.props.mapId);
217
        if (newLayer) {
28✔
218
            // detached layers are layers that do not attach directly to the map
219
            // for this reason addLayer /removeLayer should not be called on them
220
            if (!newLayer.detached) {
10!
221
                this.props.map.removeLayer(this.layer);
10✔
222
                this.layer = newLayer;
10✔
223
                this.addLayer(newProps.options);
10✔
224
            } else {
225
                this.layer = newLayer;
×
226
            }
227
        }
228
    };
229

230
    addLayer = (options) => {
167✔
231
        if (this.isValid()) {
164!
232
            this.props.map.addLayer(this.layer);
164✔
233

234
            const tileLoadEndStream$ = new Rx.Subject();
164✔
235
            const tileStopStream$ = new Rx.Subject();
164✔
236

237
            if (options.handleClickOnLayer) {
164✔
238
                this.layer.set("handleClickOnLayer", true);
27✔
239
            }
240
            this.layer.getSource().on('tileloadstart', () => {
164✔
241
                if (this.tilestoload === 0) {
51✔
242
                    this.props.onLayerLoading(options.id);
9✔
243
                    this.tilestoload++;
9✔
244
                } else {
245
                    this.tilestoload++;
42✔
246
                }
247
            });
248
            this.layer.getSource().on('tileloadend', () => {
164✔
249
                tileLoadEndStream$.next({type: 'tileloadend'});
2✔
250
                this.tilestoload--;
2✔
251
                if (this.tilestoload === 0) {
2!
252
                    tileStopStream$.next();
2✔
253
                }
254
            });
255
            this.layer.getSource().on('tileloaderror', (event) => {
164✔
256
                tileLoadEndStream$.next({type: 'tileloaderror', event});
48✔
257
                this.tilestoload--;
48✔
258
                if (this.tilestoload === 0) {
48✔
259
                    tileStopStream$.next();
6✔
260
                }
261
            });
262

263
            tileLoadEndStream$
164✔
264
                .bufferWhen(() => tileStopStream$)
167✔
265
                .subscribe({
266
                    next: (tileEvents) => {
267
                        const errors = tileEvents.filter(e => e.type === 'tileloaderror');
75✔
268
                        if (errors.length > 0 && (options && !options.hideErrors || !options)) {
75!
269
                            this.props.onLayerLoad(options.id, {error: true});
3✔
270
                            this.props.onLayerError(options.id, tileEvents.length, errors.length);
3✔
271
                        } else {
272
                            this.props.onLayerLoad(options.id);
72✔
273
                        }
274
                    }
275
                });
276

277
            this.tileLoadEndStream$ = tileLoadEndStream$;
164✔
278
            this.tileStopStream$ = tileStopStream$;
164✔
279

280
            const imageLoadEndStream$ = new Rx.Subject();
164✔
281
            const imageStopStream$ = new Rx.Subject();
164✔
282

283
            this.layer.getSource().on('imageloadstart', () => {
164✔
284
                if (this.imagestoload === 0) {
5✔
285
                    this.props.onLayerLoading(options.id);
4✔
286
                    this.imagestoload++;
4✔
287
                } else {
288
                    this.imagestoload++;
1✔
289
                }
290
            });
291
            this.layer.getSource().on('imageloadend', () => {
164✔
292
                this.imagestoload--;
×
293
                imageLoadEndStream$.next({type: 'imageloadend'});
×
294
                if (this.imagestoload === 0) {
×
295
                    imageStopStream$.next();
×
296
                }
297
            });
298
            this.layer.getSource().on('imageloaderror', (event) => {
164✔
299
                this.imagestoload--;
2✔
300
                imageLoadEndStream$.next({type: 'imageloaderror', event});
2✔
301
                if (this.imagestoload === 0) {
2!
302
                    imageStopStream$.next();
2✔
303
                }
304
            });
305

306
            imageLoadEndStream$
164✔
307
                .bufferWhen(() => imageStopStream$)
166✔
308
                .subscribe({
309
                    next: (imageEvents) => {
310
                        const errors = imageEvents.filter(e => e.type === 'imageloaderror');
74✔
311
                        if (errors.length > 0) {
74✔
312
                            this.props.onLayerLoad(options.id, {error: true});
2✔
313
                            if (options && !options.hideErrors || !options) {
2!
314
                                this.props.onLayerError(options.id, imageEvents.length, errors.length);
2✔
315
                            }
316
                        } else {
317
                            this.props.onLayerLoad(options.id);
72✔
318
                        }
319
                    }
320
                });
321

322
            this.imageLoadEndStream$ = imageLoadEndStream$;
164✔
323
            this.imageStopStream$ = imageStopStream$;
164✔
324

325
            this.layer.getSource().on('vectorerror', () => {
164✔
326
                this.props.onLayerLoad(options.id, {error: true});
1✔
327
            });
328

329
            if (options.refresh) {
164!
330
                let counter = 0;
×
331
                this.refreshTimer = setInterval(() => {
×
332
                    this.layer.getSource().updateParams(assign({}, options.params, {_refreshCounter: counter++}));
×
333
                }, options.refresh);
334
            }
335
        }
336
    };
337

338
    isValid = () => {
167✔
339
        const valid = Layers.isValid(this.props.type, this.layer);
165✔
340
        this.valid = valid;
165✔
341
        return valid;
165✔
342
    };
343
}
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

© 2025 Coveralls, Inc