diff --git a/python/grass/jupyter/display.py b/python/grass/jupyter/display.py index 27250f0418a..f747a398d7b 100644 --- a/python/grass/jupyter/display.py +++ b/python/grass/jupyter/display.py @@ -47,8 +47,8 @@ class GrassRenderer: def __init__( self, - height=400, - width=600, + height=None, + width=None, filename=None, env=None, font="sans", @@ -82,8 +82,8 @@ def __init__( else: self._env = os.environ.copy() # Environment Settings - self._env["GRASS_RENDER_WIDTH"] = str(width) - self._env["GRASS_RENDER_HEIGHT"] = str(height) + self._env["GRASS_RENDER_WIDTH"] = str(width) if width else "600" + self._env["GRASS_RENDER_HEIGHT"] = str(height) if height else "400" self._env["GRASS_FONT"] = font self._env["GRASS_RENDER_TEXT_SIZE"] = str(text_size) self._env["GRASS_RENDER_IMMEDIATE"] = renderer @@ -117,7 +117,13 @@ def cleanup(tmpdir): self._env["GRASS_LEGEND_FILE"] = str(self._legend_file) # rendering region setting - self._region_manager = RegionManagerFor2D(use_region, saved_region, self._env) + self._region_manager = RegionManagerFor2D( + use_region=use_region, + saved_region=saved_region, + width=width, + height=height, + env=self._env, + ) @property def filename(self): @@ -146,6 +152,7 @@ def run(self, module, **kwargs): # Check module is from display library then run if module[0] == "d": self._region_manager.set_region_from_command(module, **kwargs) + self._region_manager.adjust_rendering_size_from_region() gs.run_command(module, env=self._env, **kwargs) else: raise ValueError("Module must begin with letter 'd'.") diff --git a/python/grass/jupyter/region.py b/python/grass/jupyter/region.py index afd198edb44..7b33e94a823 100644 --- a/python/grass/jupyter/region.py +++ b/python/grass/jupyter/region.py @@ -20,6 +20,7 @@ get_map_name_from_d_command, get_region, reproject_region, + get_rendering_size, ) @@ -110,20 +111,25 @@ def _set_bbox(self, env): class RegionManagerFor2D: """Region manager for 2D displays (gets region from display commands)""" - def __init__(self, use_region, saved_region, env): + def __init__(self, use_region, saved_region, width, height, env): """Manages region during rendering. :param use_region: if True, use either current or provided saved region, else derive region from rendered layers :param saved_region: if name of saved_region is provided, this region is then used for rendering + :param width: rendering width + :param height: rendering height :param env: environment for rendering """ self._env = env + self._width = width + self._height = height self._use_region = use_region self._saved_region = saved_region self._extent_set = False self._resolution_set = False + self._size_set = False def set_region_from_env(self, env): """Copies GRASS_REGION from provided environment @@ -131,6 +137,20 @@ def set_region_from_env(self, env): if "GRASS_REGION" in env: self._env["GRASS_REGION"] = env["GRASS_REGION"] + def adjust_rendering_size_from_region(self): + """Sets the environmental render width and height variables + based on the region dimensions. Only first call of this + method sets the variables, subsequent calls do not adjust them. + """ + if not self._size_set: + region = gs.region(env=self._env) + width, height = get_rendering_size(region, self._width, self._height) + self._env["GRASS_RENDER_WIDTH"] = str(round(width)) + self._env["GRASS_RENDER_HEIGHT"] = str(round(height)) + # only when extent is set you can disable future size setting + if self._extent_set: + self._size_set = True + def set_region_from_command(self, module, **kwargs): """Sets computational region for rendering. diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index c03e8ccb139..fd0754433e2 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -150,3 +150,33 @@ def get_map_name_from_d_command(module, **kwargs): special = {"d.his": "hue", "d.legend": "raster", "d.rgb": "red", "d.shade": "shade"} parameter = special.get(module, "map") return kwargs.get(parameter, "") + + +def get_rendering_size(region, width, height, default_width=600, default_height=400): + """Returns the rendering width and height based + on the region aspect ratio. + + :param dict region: region dictionary + :param integer width: rendering width (can be None) + :param integer height: rendering height (can be None) + :param integer default_width: default rendering width (can be None) + :param integer default_height: default rendering height (can be None) + + :return tuple (width, height): adjusted width and height + + When both width and height are provided, values are returned without + adjustment. When one value is provided, the other is computed + based on the region aspect ratio. When no dimension is given, + the default width or height is used and the other dimension computed. + """ + if width and height: + return (width, height) + region_width = region["e"] - region["w"] + region_height = region["n"] - region["s"] + if width: + return (width, round(width * region_height / region_width)) + if height: + return (round(height * region_width / region_height), height) + if region_height > region_width: + return (round(default_height * region_width / region_height), default_height) + return (default_width, round(default_width * region_height / region_width))