Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 105 additions & 8 deletions gui/wxpython/history/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from icons.icon import MetaIcon

import grass.script as gs
from grass.tools import Tools

from grass.grassdb import history

Expand All @@ -51,6 +52,14 @@
"rows": _("Number of rows:"),
"cols": _("Number of columns:"),
"cells": _("Number of cells:"),
"t": _("Top:"),
"b": _("Bottom:"),
"tbres": _("Top-bottom resolution:"),
"nsres3": _("3D north-south resolution:"),
"ewres3": _("3D east-west resolution:"),
"rows3": _("Number of 3D rows:"),
"cols3": _("Number of 3D cols:"),
"depths": _("Number of depths:"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if you include all values for the comparison of saved and history region (I can see that e.g. 3D cells are missing)

}


Expand Down Expand Up @@ -80,6 +89,7 @@ def __init__(self, parent, giface, title=("Command Info"), style=wx.TAB_TRAVERSA
self.title = title

self.region_settings = None
self.tools = Tools()

self._initImages()

Expand Down Expand Up @@ -141,6 +151,14 @@ def _createRegionSettingsBox(self):
self.region_settings_box, wx.VERTICAL
)

self.sizer_region_name = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_region_settings.Add(
self.sizer_region_name,
proportion=0,
flag=wx.ALL | wx.EXPAND,
border=5,
)

self.sizer_region_settings_match = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_region_settings.Add(
self.sizer_region_settings_match,
Expand All @@ -167,9 +185,6 @@ def _general_info_filter(self, key, value):
filter_keys = ["timestamp", "runtime", "status"]
return key in filter_keys or ((key in {"mask2d", "mask3d"}) and value is True)

def _region_settings_filter(self, key):
return key not in {"projection", "zone", "cells"}

def _updateGeneralInfoBox(self, command_info):
"""Update a static box for displaying general info about the command.

Expand Down Expand Up @@ -216,9 +231,30 @@ def _updateRegionSettingsGrid(self, command_info):
self.sizer_region_settings_grid.Clear(True)

self.region_settings = command_info["region"]

Display_Order = [

@lindakarlovska lindakarlovska Apr 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the Display order could be already part of the TRASLATION_KEYS that could be renamed to REGION_KEYS? Just idea - your current code has basically three different very similar variables here (TRANSLATION_KEYS, Display_Order and valid_keys) so maybe some shortening would be nice. Also what is wrong with region_settings_filter method? As we want to compare mainly numerical values from g.region, I can imagine adding ellipsoid and datum to the filter and get rid of cellls, but otherwise using this filter seems ok for me (maybe I am missing something).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello Linda, apologies for the late response. I think the reason I removed region_settings_filter was that rows3, cols3, and depths caused issues when passed to g.region through the Update current region button. Because of that, I thought we could not use the same filter for both display order and valid keys together. Maybe we can remove valid_keys and use the filter there instead, but I’m not sure how best to handle display order. Do you have a better idea?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@petrasovaa any suggestions here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to whatever solution here if it works.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is minor, but this would look better I think:

Suggested change
Display_Order = [
display_order = [

"n",
"s",
"e",
"w",
"nsres",
"ewres",
"rows",
"cols",
"t",
"b",
"tbres",
"nsres3",
"ewres3",
"rows3",
"cols3",
"depths",
]

idx = 0
for key, value in self.region_settings.items():
if self._region_settings_filter(key):
for key in Display_Order:
if key in self.region_settings:
value = self.region_settings[key]
self.sizer_region_settings_grid.Add(
StaticText(
parent=self.region_settings_box,
Expand All @@ -245,14 +281,61 @@ def _updateRegionSettingsGrid(self, command_info):

self.region_settings_box.Show()

def _get_saved_region_name(self, history_region):
"""Find if history region matches any saved region in the entire project."""
try:
res = self.tools.g_list(type="region", mapset="*", format="json")
region_names = [r["name"] for r in res]
Comment on lines +287 to +288

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely this needs the full name including the mapset:

Suggested change
res = self.tools.g_list(type="region", mapset="*", format="json")
region_names = [r["name"] for r in res]
res = self.tools.g_list(type="region", mapset="*", flags="m", format="json")
region_names = [r["fullname"] for r in res]

except (KeyError, TypeError):
return None

for region_name in region_names:
region_name = region_name.strip()
if region_name:
try:
saved_region = self.tools.g_region(
region=region_name, flags="u3", format="shell"
).keyval

if self._compare_regions(history_region, saved_region):
return region_name
except AttributeError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include ToolError here (from tools).

continue

return None

def _compare_regions(self, r1, r2):
"""Compare two region dictionaries for equality."""
for k, v in r1.items():
try:
if abs(float(v) - float(r2[k])) > 1e-8:
return False
except (KeyError, ValueError, TypeError):
return False
return True

def _updateRegionSettingsMatch(self):
"""Update text, icon and button dealing with region update"""
self.sizer_region_name.Clear(True)
self.sizer_region_settings_match.Clear(True)

# Region condition
history_region = self.region_settings
current_region = self._get_current_region()
region_matches = history_region == current_region
region_matches = self._compare_regions(history_region, current_region)
saved_name = self._get_saved_region_name(history_region)

if saved_name:
self.sizer_region_name.Add(
StaticText(
parent=self.region_settings_box,
id=wx.ID_ANY,
label=_("Region name: {}").format(saved_name),
),
proportion=0,
flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
border=10,
)

# Icon and button according to the condition
if region_matches:
Expand Down Expand Up @@ -322,14 +405,28 @@ def hideCommandInfo(self):

def _get_current_region(self):
"""Get current computational region settings."""
return gs.region()
return gs.region(region3d=True)

def _get_history_region(self):
"""Get computational region settings of executed command."""
valid_keys = {
"n",
"s",
"e",
"w",
"nsres",
"ewres",
"t",
"b",
"tbres",
"nsres3",
"ewres3",
}

return {
key: value
for key, value in self.region_settings.items()
if self._region_settings_filter(key)
if key in valid_keys
}

def OnUpdateRegion(self, event):
Expand Down
2 changes: 1 addition & 1 deletion python/grass/grassdb/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def get_initial_command_info(env_run):
mask3d_name = f"RASTER3D_MASK@{env['MAPSET']}"

# Computational region settings
region_settings = gs.region(env=env_run)
region_settings = gs.region(region3d=True, env=env_run)

# Finalize the command info dictionary
return {
Expand Down
Loading