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
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Base vector layer."""
3from typing import Optional
5import gws
6import gws.base.model
7import gws.base.template
8import gws.lib.extent
9import gws.lib.style
10import gws.lib.svg
12from . import core
15class Object(core.Object):
16 """Base vector layer.
18 Finds features through the layer's models and renders them as SVG. The
19 client loads the features from the ``mapGetFeatures`` command.
20 """
22 # @TODO rasterize vector layers
23 canRenderBox = False
24 canRenderTile = False
25 canRenderSvg = True
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."""
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 )
40 def render_svg(self, lri):
41 tags = self.render_svg_fragment(lri)
42 if tags:
43 return gws.LayerRenderOutput(tags=tags)
44 #
46 def render_svg_fragment(self, lri: gws.LayerRenderInput):
47 """Render the features in the map view as SVG elements.
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.
53 Args:
54 lri: Render input with the map view, style and user.
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))
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
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)
72 gws.debug.time_start('render_svg:to_svg')
73 tags = []
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))
80 gws.debug.time_end()
82 return tags
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 []
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 []
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
100 return features