Coverage for gws-app/gws/base/layer/tree.py: 0%
60 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Structures and utilities for tree layers."""
3from typing import Optional, Callable
5import gws
6import gws.gis.source
7import gws.config.parser
9from . import core
12class FlattenConfig(gws.Config):
13 """Layer hierarchy flattening"""
15 level: int
16 """flatten level"""
17 useGroups: bool = False
18 """use group names (true) or image layer names (false)"""
21class Config(gws.Config):
22 rootLayers: Optional[gws.gis.source.LayerFilter]
23 """source layers to use as roots"""
24 excludeLayers: Optional[gws.gis.source.LayerFilter]
25 """source layers to exclude"""
26 flattenLayers: Optional[FlattenConfig]
27 """flatten the layer hierarchy"""
28 autoLayers: Optional[list[core.AutoLayersOptions]]
29 """custom configurations for automatically created layers"""
32class TreeConfigArgs(gws.Data):
33 root: gws.Root
34 source_layers: list[gws.SourceLayer]
35 roots_slf: gws.gis.source.LayerFilter
36 exclude_slf: gws.gis.source.LayerFilter
37 flatten_config: FlattenConfig
38 auto_layers: list[core.AutoLayersOptions]
39 leaf_layer_maker: Callable
42def layer_configs_from_layer(layer: core.Object, source_layers: list[gws.SourceLayer], leaf_layer_maker: Callable) -> list[gws.Config]:
43 """Generate a config tree from a list of source layers and the main layer config."""
45 return layer_configs_from_args(TreeConfigArgs(
46 root=layer.root,
47 source_layers=source_layers,
48 roots_slf=layer.cfg('rootLayers'),
49 exclude_slf=layer.cfg('excludeLayers'),
50 flatten_config=layer.cfg('flattenLayers'),
51 auto_layers=layer.cfg('autoLayers', default=[]),
52 leaf_layer_maker=leaf_layer_maker
53 ))
56def layer_configs_from_args(tca: TreeConfigArgs) -> list[gws.Config]:
57 """Generate a config tree from a list of source layers."""
59 # by default, take top-level layers as roots
60 roots_slf = tca.roots_slf or gws.gis.source.LayerFilter(level=1)
61 roots = gws.gis.source.filter_layers(tca.source_layers, roots_slf)
63 # make configs...
64 configs = gws.u.compact(_config(tca, sl, 0) for sl in roots)
66 # configs need to be reparsed so that defaults can be injected
67 return [
68 gws.config.parser.parse(
69 tca.root.specs,
70 cfg,
71 'gws.ext.config.layer',
72 read_options={gws.SpecReadOption.acceptExtraProps, gws.SpecReadOption.allowMissing}
73 )
74 for cfg in configs
75 ]
78def _config(tca: TreeConfigArgs, sl: gws.SourceLayer, depth: int):
79 cfg = _base_config(tca, sl, depth)
80 if not cfg:
81 return None
83 cfg = gws.u.merge(gws.u.to_dict(cfg), {
84 'title': sl.title,
85 'clientOptions': {
86 'hidden': not sl.isVisible,
87 'expanded': sl.isExpanded,
88 },
89 'opacity': sl.opacity or 1,
90 })
92 for cc in tca.auto_layers:
93 if gws.gis.source.layer_matches(sl, cc.applyTo):
94 cfg = gws.u.deep_merge(cfg, cc.config)
96 return gws.u.compact(cfg)
99def _base_config(tca: TreeConfigArgs, sl: gws.SourceLayer, depth: int):
100 # source layer excluded by the filter
101 if tca.exclude_slf and gws.gis.source.layer_matches(sl, tca.exclude_slf):
102 return None
104 # leaf layer
105 if not sl.isGroup:
106 return tca.leaf_layer_maker([sl])
108 # flattened group layer
109 # NB use the absolute level to compute flatness, could also use relative (=depth)
110 if tca.flatten_config and sl.aLevel >= tca.flatten_config.level:
112 if tca.flatten_config.useGroups:
113 return tca.leaf_layer_maker([sl])
115 slf = gws.gis.source.LayerFilter(isImage=True)
116 leaves = gws.gis.source.filter_layers([sl], slf)
117 if not leaves:
118 return None
119 return tca.leaf_layer_maker(leaves)
121 # ordinary group layer
122 layer_cfgs = gws.u.compact(_config(tca, sub, depth + 1) for sub in sl.layers)
123 if not layer_cfgs:
124 return None
125 return {
126 'type': 'group',
127 'layers': layer_cfgs,
128 }