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

compassinformatics / cpsi-mapview / 12318249496

13 Dec 2024 03:16PM UTC coverage: 26.211% (-0.02%) from 26.23%
12318249496

Pull #687

github

web-flow
Merge 0eb3b0e2e into a53d12225
Pull Request #687: WIP: Update to GeoExt7, ExtJS7 and OL10

487 of 2328 branches covered (20.92%)

Branch coverage included in aggregate %.

1 of 35 new or added lines in 6 files covered. (2.86%)

4 existing lines in 2 files now uncovered.

1445 of 5043 relevant lines covered (28.65%)

1.16 hits per line

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

7.56
/app/controller/LayerTreeController.js
1
/**
2
 * The controller for the {@link CpsiMapview.view.LayerTree}
3
 *
4
 * @class CpsiMapview.controller.LayerTreeController
5
 */
6
Ext.define('CpsiMapview.controller.LayerTreeController', {
1✔
7
    extend: 'Ext.app.ViewController',
8

9
    alias: 'controller.cmv_layertree',
10

11
    requires: [
12
        'BasiGX.util.Map',
13
        'BasiGX.util.Layer',
14
        'CpsiMapview.data.model.LayerTreeNode',
15
        'CpsiMapview.view.main.Map',
16
        'CpsiMapview.util.Layer'
17
    ],
18

19
    statics: {
20
        /**
21
         * Detects the original tree node config from tree.json file for a
22
         * layer identified by the given layerKey.
23
         *
24
         * @param {String} layerKey Identifier of the layer to get node conf for
25
         * @param {Object} childNodeConf Conf to start with (root if not given)
26
         */
27
        getTreeNodeConf: function (layerKey, childNodeConf) {
28
            var nodeConf = null;
×
29
            var staticMe = CpsiMapview.controller.LayerTreeController;
×
30
            if (!childNodeConf) {
×
31
                childNodeConf = CpsiMapview.treeConfig;
×
32
            }
33

34
            if (childNodeConf.id === layerKey) {
×
35
                return childNodeConf;
×
36
            } else {
37
                // exit for leafs
38
                if (childNodeConf.leaf === true) {
×
39
                    return null;
×
40
                }
41
                // iterate recursively over all child nodes
42
                for (var i = 0; i < childNodeConf.children.length; i += 1) {
×
43
                    var currentChild = childNodeConf.children[i];
×
44
                    // Search in the current child
45
                    nodeConf = staticMe.getTreeNodeConf(layerKey, currentChild);
×
46

47
                    // Return result immediately if the node conf has been found
48
                    if (nodeConf !== null) {
×
49
                        return nodeConf;
×
50
                    }
51
                }
52
            }
53

54
            return nodeConf;
×
55
        }
56
    },
57

58

59
    /**
60
     * Mode in order to steer how the layers in tree will be structured.
61
     * At the moment 'BASELAYER_OVERLAY' will divide the layers in 2 folders
62
     * 'Base Layers' and 'Overlays' (depending on their property 'isBaseLayer').
63
     * All other settings will result in a flat layer list.
64
     *
65
     * @cfg {String}
66
     */
67
    structureMode: null,
68

69
    /**
70
     * Shows if the event 'cmv-init-layersadded' has been fired or not.
71
     *
72
     * @property {Boolean}
73
     * @private
74
     */
75
    initLayersAdded: false,
76

77
    /**
78
     * Holds the default values for tree nodes of the config file tree.json.
79
     * Will be set in #makeLayerStore function.
80
     *
81
     * @private
82
     * @readonly
83
     */
84
    treeConfDefaults: {},
85

86
    constructor: function () {
87
        var me = this;
1✔
88

89
        var mapPanel = CpsiMapview.view.main.Map.guess();
1✔
90

91
        mapPanel.on('cmv-init-layersadded', function () {
1✔
92
            me.initLayersAdded = true;
×
93
            me.autoConnectToMap(); // connect after all the layers have been loaded to the map
×
94
        });
95

96
        Ext.GlobalEvents.on('login', function () {
1✔
97
            me.filterLayersByRole();
2✔
98
        });
99

100
        Ext.GlobalEvents.on('logout', function () {
1✔
101
            me.filterLayersByRole();
×
102
        });
103
    },
104

105
    /**
106
     * Guesses the mapcomponent and assigns the appropriate layers store, if one
107
     * could be guessed.
108
     */
109
    autoConnectToMap: function () {
110

111
        var me = this;
×
112

113
        var mapComp = BasiGX.util.Map.getMapComponent();
×
114
        me.map = mapComp && mapComp.getMap();
×
115

116
        if (me.map) {
×
117
            me.makeLayerStore();
×
118
        }
119
    },
120

121
    /**
122
     * This method assigns an instance of the GeoExt class
123
     * `GeoExt.data.store.LayersTree` based on the connected OL #map to the view. The layers
124
     * of the `ol.Map` are restructured and divided into groups based on the
125
     * JSON tree structure loaded in #loadTreeStructure. This assures that
126
     * the layers will appear in different folders in this TreePanel
127
     * (as defined in the tree structure JSON).
128
     */
129
    makeLayerStore: function () {
130
        var me = this;
×
131

132
        var treeJsonPromise = me.loadTreeStructure();
×
133
        treeJsonPromise.then(function (treeJson) {
×
134
            // save defaults for tree nodes from config
135
            me.treeConfDefaults = treeJson.defaults || {};
×
136

137
            // get the root layer group holding the grouped map layers
138
            var rootLayerGroup = me.getGroupedLayers(treeJson.treeConfig);
×
139

140
            me.map.set('layerTreeRoot', rootLayerGroup);
×
141
            me.map.getLayers().insertAt(0, rootLayerGroup);
×
142

143
            // create a new LayerStore from the grouped layers
144
            var groupedLayerTreeStore = Ext.create('GeoExt.data.store.LayersTree', {
×
145
                model: 'CpsiMapview.data.model.LayerTreeNode',
146
                layerGroup: rootLayerGroup,
147
                // filters are applied from bottom to top
148
                // necessary for filtering empty layer groups
149
                filterer: 'bottomup'
150
            });
151
            me.getView().setStore(groupedLayerTreeStore);
×
152

153
            // update possible switchlayers when collapsing their parent folder
154
            // otherwise the node text would be wrong/empty
155
            me.getView().getRootNode().on(
×
156
                'beforeexpand',
157
                me.updateSwitchLayerNodes,
158
                me
159
            );
160

161
            me.getView().getRootNode().cascade(function (node) {
×
162
                // apply properties for tree node from corresponding tree-conf
163
                if (node.getOlLayer()) {
×
164
                    var origTreeNodeConf = node.getOlLayer().get('_origTreeConf') || {};
×
165
                    me.applyTreeConfigsToNode(node, origTreeNodeConf);
×
166

167
                    // We are creating the gridWindow here already, to ensure that
168
                    // any preset filter will be applied directly, without having to
169
                    // open the gridWindow first.
170
                    var gridWindow = CpsiMapview.util.Grid.getGridWindow(node.getOlLayer());
×
171
                    if (gridWindow) {
×
172
                        var grid = gridWindow.down('grid');
×
173
                        if (grid) {
×
174
                            grid.fireEvent('applypresetfilters');
×
175
                        }
176
                    }
177
                }
178
            });
179

180
            // preserve tree config to access later on
181
            CpsiMapview.treeConfig = treeJson.treeConfig;
×
182

183
            // inform subscribers that LayerTree is ready
184
            me.getView().fireEvent('cmv-init-layertree', me);
×
185
        });
186

187
        // fallback in case loading the JSON tree structure failed:
188
        // create a flat store holding all map layers at one hierarchy
189
        treeJsonPromise.catch(function () {
×
190
            Ext.Logger.warn('Loading of JSON structure for LayerTree failed' +
×
191
                '- creating flat layer hierarchy as fallback');
192

193
            var layerTreeStore = Ext.create('GeoExt.data.store.LayersTree', {
×
194
                layerGroup: me.map.get('layerTreeRoot') || me.map.getLayerGroup()
×
195
            });
196

197
            me.getView().setStore(layerTreeStore);
×
198

199
            // inform subscribers that LayerTree is ready
200
            me.getView().fireEvent('cmv-init-layertree', me);
×
201
        });
202
    },
203

204
    /**
205
     * Loads the JSON tree structure from 'resources/data/layers/tree.json'.
206
     *
207
     * @return {Ext.Promise} Promise resolving once the JSON is loaded
208
     */
209
    loadTreeStructure: function () {
210
        var app = Ext.getApplication ? Ext.getApplication() : Ext.app.Application.instance;
×
211
        return app.getResourcePaths().then(function (resourcePaths) {
×
212
            return new Ext.Promise(function (resolve, reject) {
×
213
                Ext.Ajax.request({
×
214
                    url: resourcePaths.treeConfig,
215
                    method: 'GET',
216
                    success: function (response) {
217
                        var respJson = Ext.decode(response.responseText);
×
218
                        resolve(respJson);
×
219
                    },
220
                    failure: function (response) {
221
                        reject(response.status);
×
222
                    }
223
                });
224
            });
225
        });
226
    },
227

228

229
    /**
230
     * Ensures tree and map only contain layers for which
231
     * the user has the required roles to see.
232
     */
233
    filterLayersByRole: function () {
234
        var me = this;
2✔
235
        var store = me.getView().getStore();
2✔
236
        if (!store){
2!
237
            return;
×
238
        }
239
        store.filterBy(function(record){
2✔
240
            var requiredRoles = record.get('requiredRoles');
3✔
241
            if (requiredRoles && Ext.isArray(requiredRoles) && requiredRoles.length){
3!
242
                var showNode = CpsiMapview.util.RoleManager.hasAtLeastOneRequiredRole(requiredRoles);
×
243
                return showNode;
×
244
            }
245
            return true;
3✔
246
        });
247
    },
248

249
    /**
250
     * Re-groups the layers of the #map, so they are put into a folder hierarchy
251
     * based on the given tree structure loaded in #loadTreeStructure.
252
     * For each folder an OL layer group is created and gets aggregated in a
253
     * root layer group.
254
     *
255
     * @param  {Object} treeJson LayerTree structure
256
     * @return {ol.layer.Group}  Root layer group
257
     */
258
    getGroupedLayers: function (treeConfJson) {
259
        var me = this;
×
260

261
        // wrapping all under the 'root' node aggregating all together
262
        var rootLayerGroup = new ol.layer.Group({
×
263
            name: 'root',
264
            layers: []
265
        });
266
        // recursively create the OL layer group by the given tree structure
267
        me.createOlLayerGroups(treeConfJson.children, rootLayerGroup);
×
268

269
        return rootLayerGroup;
×
270
    },
271

272
    /**
273
     * Creates recursively the OL layer groups for the given tree structure and
274
     * puts them all together in the given parent group so they get folders in the LayerTree.
275
     * Layers are directly put to the given parent group so they appear as "leafs" in the LayerTree.
276
     *
277
     * @param  {Object} treeNodesJson Child section of the LayerTree structure
278
     * @param  {ol.layer.Group} parentGroup The parent group to put children (another groups / layers) into
279
     */
280
    createOlLayerGroups: function (treeNodeChilds, parentGroup) {
281
        var me = this;
×
282
        // go over all passed in tree child nodes
283
        Ext.each(treeNodeChilds, function (child) {
×
284
            // apply defaults for tree nodes from config
285
            var generalDefaults = me.treeConfDefaults.general || {};
×
286
            Ext.applyIf(child, generalDefaults);
×
287

288
            // respect "isLeaf" for legacy reasons (but recommended using "leaf")
289
            var isLeaf = Ext.isDefined(child.isLeaf) ? child.isLeaf : child.leaf;
×
290
            // layer groups --> folders in tree
291
            if (isLeaf !== true) {
×
292
                // create empty layer group for this level
293
                var layerGroup = new ol.layer.Group({
×
294
                    name: child.title,
295
                    layers: [],
296
                });
297

298
                // preserve the original tree JSON config to re-use it later on
299
                layerGroup.set('_origTreeConf', child);
×
300

301
                var parentLayers = parentGroup.getLayers();
×
302
                parentLayers.insertAt(0, layerGroup);
×
303

304
                // recursion
305
                me.createOlLayerGroups(child.children, layerGroup);
×
306
            } else {
307
                // layers --> leafs in tree
308
                var mapLyr = BasiGX.util.Layer.getLayerBy('layerKey', child.id);
×
309

310
                if (mapLyr) {
×
311
                    // apply tree config to OL layer
312
                    // needed since the LayerTreeNode model derives them from OL layer
313
                    me.applyTreeConfigsToOlLayer(mapLyr, child);
×
314

315
                    // preserve the original tree JSON config to re-use it later on
316
                    mapLyr.set('_origTreeConf', child);
×
317

318
                    // add OL layer to parent OL LayerGroup
319
                    me.map.removeLayer(mapLyr);
×
320
                    parentGroup.getLayers().insertAt(0, mapLyr);
×
321

322
                } else {
323
                    //<debug>
324

325
                    // get the layers config object
326
                    var app = Ext.getApplication ? Ext.getApplication() : Ext.app.Application.instance;
×
327
                    var layerJson = app.layerJson;
×
328

329

330
                    // any switch layers not in the resolution when the app is loaded will be missing
331
                    // so we check for layer keys in the config JSON
332

333
                    var childLayers = Ext.Array.pluck(layerJson.layers, 'layers').filter(Boolean);
×
334
                    var allLayers = Ext.Array.merge(Ext.Array.flatten(childLayers), layerJson.layers);
×
335
                    var layerKeys = Ext.Array.pluck(allLayers, 'layerKey');
×
336

337
                    if (!Ext.Array.indexOf(layerKeys, child.id) === -1) {
×
338
                        Ext.Logger.warn('Layer with layerKey ' + child.id + ' not found in map layers');
×
339
                    }
340
                    //</debug>
341
                }
342
            }
343
        });
344
    },
345

346
    /**
347
     * Applies the values from the tree layer config to OL the given
348
     * OL layer.
349
     *
350
     * @param {ol.layer.Base} olLayer The OL layer to apply tree conf values to
351
     * @param {Object} treeNodeConf The tree node layer config JSON
352
     */
353
    applyTreeConfigsToOlLayer: function (olLayer, treeNodeConf) {
354
        // name gets transformed to text on the layer tree node
355
        olLayer.set('name', treeNodeConf.text);
×
356
        // description gets transformed to qtip on the layer tree node
357
        olLayer.set('description', treeNodeConf.qtip);
×
358
        // descTitle gets transformed to qtitle on the layer tree node
359
        olLayer.set('descTitle', treeNodeConf.text);
×
360
        // changes the icon in the layer tree leaf
361
        olLayer.set('iconCls', treeNodeConf.iconCls);
×
362
    },
363

364
    /**
365
     * Applies the values from the tree layer config to the given
366
     * tree node instance.
367
     *
368
     * @param {Ext.data.NodeInterface} node The tree node to apply tree conf values to
369
     * @param {Object} treeNodeConf The tree node layer config JSON
370
     */
371
    applyTreeConfigsToNode: function (node, treeNodeConf) {
372
        node.set('cls', treeNodeConf.cls);
×
373
        node.set('expandable', Ext.isDefined(treeNodeConf.expandable) ? treeNodeConf.expandable : true);
×
374
        node.set('glyph', treeNodeConf.glyph);
×
375
        node.set('icon', treeNodeConf.icon);
×
376
        node.set('qshowDelay', treeNodeConf.qshowDelay);
×
377

378
        node.set('text', treeNodeConf.text);
×
379
        node.set('requiredRoles', treeNodeConf.requiredRoles);
×
380

381
        // expand configured folders in this tree
382
        node.set('expanded', treeNodeConf.expanded);
×
383

384
        // hide checkbox on tree node if configured
385
        // setting checked to undefined has no effect since GeoExt.data.model.LayerTreeNode
386
        // overwrites this with the layer's visibility.
387
        if (treeNodeConf.checked === false) {
×
388
            node.addCls('cpsi-tree-no-checkbox');
×
389
        }
390
    },
391

392
    /**
393
     * Adds configuration to updated switch layer nodes.
394
     *
395
     * Ensures all layer information is transfered to the new
396
     * switch layer after the switch event
397
     *
398
     * @param {Ext.data.TreeModel} groupNode The folder node that is expanded
399
     */
400
    updateSwitchLayerNodes: function(groupNode) {
401
        var me = this;
×
402
        groupNode.cascade(function(child){
×
NEW
403
            var layer = child.getOlLayer();
×
404
            if (!layer || !layer.get('isSwitchLayer')) {
×
405
                // we only care about switch layers
406
                return;
×
407
            }
408
            // the configuration for the tree node got lost during
409
            // the switch. We read it again from the layer and apply
410
            // it to the node.
411
            var origTreeNodeConf = child.getOlLayer().get('_origTreeConf') || {};
×
412
            me.applyTreeConfigsToNode(child, origTreeNodeConf);
×
413
        });
414
    },
415

416
    /**
417
     * This reacts on toggling the add wms Button in the layertree. It shows a window with an AddWmsForm.
418
     * @param {Ext.button.Button} button
419
     * @param {boolean} pressed
420
     */
421
    onAddWmsToggle: function (button, pressed) {
422
        var me = this;
×
423

424
        if (pressed) {
×
425
            if (!this.addWmsWindow) {
×
426
                this.addWmsWindow = Ext.create(me.getView().addWmsWindowConfig);
×
427
                this.addWmsWindow.on('close', function () {
×
428
                    button.setPressed(false);
×
429
                });
430
            }
431
            this.addWmsWindow.show();
×
432

433
        } else if (this.addWmsWindow) {
×
434
            this.addWmsWindow.close();
×
435
        }
436
    },
437

438
    /**
439
     * This reacts on toggling the add arcgisrest Button in the layertree. It shows a window with an AddArcGISRestForm.
440
     * @param {Ext.button.Button} button
441
     * @param {boolean} pressed
442
     */
443
    onAddArcGISRestToggle: function (button, pressed) {
444
        var me = this;
×
445

446
        if (pressed) {
×
447
            if (!this.addArcGISRestWindow) {
×
448
                this.addArcGISRestWindow = Ext.create(me.getView().addArcGISRestWindowConfig);
×
449
                this.addArcGISRestWindow.on('close', function () {
×
450
                    button.setPressed(false);
×
451
                });
452
            }
453
            this.addArcGISRestWindow.show();
×
454

455
        } else if (this.addArcGISRestWindow) {
×
456
            this.addArcGISRestWindow.close();
×
457
        }
458
    },
459

460
    /**
461
    * Destroy any associated windows when this component gets destroyed
462
    */
463
    onBeforeDestroy: function () {
464
        if (this.addWmsWindow) {
×
465
            this.addWmsWindow.destroy();
×
466
            this.addWmsWindow = null;
×
467
        }
468
        if (this.addArcGISRestWindow) {
×
469
            this.addArcGISRestWindow.destroy();
×
470
            this.addArcGISRestWindow = null;
×
471
        }
472
    },
473

474
    /**
475
    * Updates the UI of childNodes of an expanded item
476
    * @param {CpsiMapview.data.model.LayerTreeNode} LayerTreeNode
477
    */
478
    updateExpandedItemChildNodesUI: function (layerTreeNode) {
479
        Ext.each(layerTreeNode.childNodes, function (node) {
×
480
            var layer =  node.getOlLayer();
×
481
            if (layer.get('isWms') || layer.get('isWfs') || layer.get('isVt')) {
×
482
                CpsiMapview.util.Layer.updateLayerNodeUI(layer, false);
×
483
            }
484
        });
485
    }
486
});
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