Coverage for gws-app/gws/base/layer/vector.py: 57%

54 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""Base vector layer.""" 

2 

3from typing import Optional 

4 

5import gws 

6import gws.base.model 

7import gws.base.template 

8import gws.lib.extent 

9import gws.lib.style 

10import gws.lib.svg 

11 

12from . import core 

13 

14 

15class Object(core.Object): 

16 """Base vector layer. 

17 

18 Finds features through the layer's models and renders them as SVG. The 

19 client loads the features from the ``mapGetFeatures`` command. 

20 """ 

21 

22 # @TODO rasterize vector layers 

23 canRenderBox = False 

24 canRenderTile = False 

25 canRenderSvg = True 

26 

27 geometryType: Optional[gws.GeometryType] = None 

28 """Geometry type of the layer's features.""" 

29 geometryCrs: Optional[gws.Crs] = None 

30 """CRS of the layer's feature geometries.""" 

31 

32 def props(self, user): 

33 return gws.u.merge( 

34 super().props(user), 

35 type='vector', 

36 url=self.url_path_for('features'), 

37 geometryType=self.geometryType 

38 ) 

39 

40 def render_svg(self, lri): 

41 tags = self.render_svg_fragment(lri) 

42 if tags: 

43 return gws.LayerRenderOutput(tags=tags) 

44 # 

45 

46 def render_svg_fragment(self, lri: gws.LayerRenderInput): 

47 """Render the features in the map view as SVG elements. 

48 

49 Labels are rendered from the ``feature.label`` template. For a rotated 

50 view, features are searched in the circumscribed square of the view 

51 extent. 

52 

53 Args: 

54 lri: Render input with the map view, style and user. 

55 

56 Returns: 

57 A list of SVG elements, or ``None`` if no features are found. 

58 """ 

59 bounds = lri.view.bounds 

60 if lri.view.rotation: 

61 bounds = gws.Bounds(crs=lri.view.bounds.crs, extent=gws.lib.extent.circumsquare(bounds.extent)) 

62 

63 search = gws.SearchQuery(bounds=bounds) 

64 features = self.find_features(search, lri.user) 

65 if not features: 

66 gws.log.debug(f'render {self}: no features found') 

67 return 

68 

69 # @TODO should pick a project template too, cmp. map/action/get_features 

70 tpl = self.root.app.templateMgr.find_template(f'feature.label', where=[self], user=lri.user) 

71 

72 gws.debug.time_start('render_svg:to_svg') 

73 tags = [] 

74 

75 for feature in features: 

76 if tpl: 

77 feature.render_views([tpl], layer=self, user=lri.user) 

78 tags.extend(feature.to_svg(lri.view, feature.views.get('label', ''), lri.style)) 

79 

80 gws.debug.time_end() 

81 

82 return tags 

83 

84 def find_features(self, search, user): 

85 model = self.root.app.modelMgr.find_model(self, user=user, access=gws.Access.read) 

86 if not model: 

87 return [] 

88 

89 mc = gws.ModelContext(op=gws.ModelOperation.read, target=gws.ModelReadTarget.map, user=user) 

90 features = model.find_features(search, mc) 

91 if not features: 

92 return [] 

93 

94 for feature in features: 

95 if search.bounds: 

96 feature.transform_to(search.bounds.crs) 

97 if not feature.category: 

98 feature.category = self.title 

99 

100 return features