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

1"""Structures and utilities for tree layers.""" 

2 

3from typing import Optional, Callable 

4 

5import gws 

6import gws.gis.source 

7import gws.config.parser 

8 

9from . import core 

10 

11 

12class FlattenConfig(gws.Config): 

13 """Layer hierarchy flattening""" 

14 

15 level: int 

16 """flatten level""" 

17 useGroups: bool = False 

18 """use group names (true) or image layer names (false)""" 

19 

20 

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""" 

30 

31 

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 

40 

41 

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.""" 

44 

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 )) 

54 

55 

56def layer_configs_from_args(tca: TreeConfigArgs) -> list[gws.Config]: 

57 """Generate a config tree from a list of source layers.""" 

58 

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) 

62 

63 # make configs... 

64 configs = gws.u.compact(_config(tca, sl, 0) for sl in roots) 

65 

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 ] 

76 

77 

78def _config(tca: TreeConfigArgs, sl: gws.SourceLayer, depth: int): 

79 cfg = _base_config(tca, sl, depth) 

80 if not cfg: 

81 return None 

82 

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 }) 

91 

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) 

95 

96 return gws.u.compact(cfg) 

97 

98 

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 

103 

104 # leaf layer 

105 if not sl.isGroup: 

106 return tca.leaf_layer_maker([sl]) 

107 

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: 

111 

112 if tca.flatten_config.useGroups: 

113 return tca.leaf_layer_maker([sl]) 

114 

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) 

120 

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 }