Coverage for gws-app/gws/base/legend/core.py: 89%
54 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
1from typing import Optional
3import gws
4import gws.lib.image
5import gws.lib.mime
8class Props(gws.Props):
9 type: str
12class Config(gws.ConfigWithAccess):
13 """Layer legend confuguration."""
15 cacheMaxAge: gws.Duration = '1d'
16 """Max cache age for remote legends."""
17 options: Optional[dict]
18 """Provider-dependent legend options."""
21class Object(gws.Legend):
22 """Generic legend object."""
24 cacheMaxAge: int
25 options: dict
27 def configure(self):
28 self.options = self.cfg('options', default={})
29 self.cacheMaxAge = self.cfg('cacheMaxAge', default=3600 * 24)
32def output_to_bytes(lro: gws.LegendRenderOutput) -> Optional[bytes]:
33 """Convert a LegendRenderOutput to raw image bytes.
35 Args:
36 lro: The legend render output object.
38 Returns:
39 The image encoded as bytes if available, otherwise None.
40 """
41 img = output_to_image(lro)
42 return img.to_bytes() if img else None
45def output_to_image(lro: gws.LegendRenderOutput) -> Optional[gws.Image]:
46 """Extract an image object from a LegendRenderOutput.
48 Args:
49 lro: The legend render output object.
51 Returns:
52 The image object if available, otherwise None (e.g. when only HTML is set).
53 """
54 if lro.image:
55 return lro.image
56 if lro.image_path:
57 return gws.lib.image.from_path(lro.image_path)
58 if lro.html:
59 return None
62def output_to_image_path(lro: gws.LegendRenderOutput) -> Optional[str]:
63 """Resolve the file path to the image of a LegendRenderOutput.
65 Args:
66 lro: The legend render output object.
68 Returns:
69 Path to the image file if available, otherwise None.
70 """
71 if lro.image:
72 img_path = gws.u.ephemeral_path('legend.png')
73 return lro.image.to_path(img_path, gws.lib.mime.PNG)
74 if lro.image_path:
75 return lro.image_path
76 if lro.html:
77 return None
80def combine_outputs(lros: list[gws.LegendRenderOutput], options: dict = None) -> Optional[gws.LegendRenderOutput]:
81 """Combine multiple LegendRenderOutputs into a single output.
83 Args:
84 lros: A list of legend render outputs to combine.
85 options: Optional combination settings (currently unused).
87 Returns:
88 A new LegendRenderOutput containing the combined image,
89 or None if no images were provided.
90 """
91 imgs = gws.u.compact(output_to_image(lro) for lro in lros)
92 img = _combine_images(imgs, options)
93 if not img:
94 return None
95 return gws.LegendRenderOutput(image=img, size=img.size())
98def _combine_images(images: list[gws.Image], options: dict = None) -> Optional[gws.Image]:
99 if not images:
100 return None
101 # @TODO other combination options
102 return _combine_vertically(images)
105def _combine_vertically(images: list[gws.Image]):
106 ws = [img.size()[0] for img in images]
107 hs = [img.size()[1] for img in images]
109 comp = gws.lib.image.from_size((max(ws), sum(hs)))
110 y = 0
111 for img in images:
112 comp.paste(img, (0, y))
113 y += img.size()[1]
115 return comp