From ca584d34463fbd3eb2a6d10c08ccf8a62421a426 Mon Sep 17 00:00:00 2001 From: Saket Kumar Mall Date: Mon, 6 Apr 2026 21:31:24 +0530 Subject: [PATCH 1/3] Add show projection and show region options --- gui/wxpython/datacatalog/tree.py | 36 ++++++++++++++++ gui/wxpython/lmgr/statusbar.py | 70 ++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 8dd96fb62e0..acb59831238 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -2019,6 +2019,34 @@ def done(event): # temp gisrc file must be deleted onDone self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) + def OnShowProjection(self, event): + """Show projection of selected location""" + + def done(event): + gs.try_remove(event.userData) + + cmd = ["g.proj", "-p"] + gisrc, env = gs.create_environment( + self.selected_grassdb[0].data["name"], + self.selected_location[0].data["name"], + "PERMANENT", + ) + self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) + + def OnShowRegion(self, event): + """Show region of selected mapset""" + + def done(event): + gs.try_remove(event.userData) + + cmd = ["g.region", "-p3"] + gisrc, env = gs.create_environment( + self.selected_grassdb[0].data["name"], + self.selected_location[0].data["name"], + self.selected_mapset[0].data["name"], + ) + self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) + def OnCopyName(self, event): """Copy layer name to clipboard""" if wx.TheClipboard.Open(): @@ -2264,6 +2292,10 @@ def _popupMenuMapset(self): if self._restricted: item.Enable(False) + item = wx.MenuItem(menu, wx.ID_ANY, _("Show region")) + menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnShowRegion, item) + item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMapset, item) @@ -2295,6 +2327,10 @@ def _popupMenuLocation(self): if self._restricted: item.Enable(False) + item = wx.MenuItem(menu, wx.ID_ANY, _("Show projection")) + menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnShowProjection, item) + item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename project")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameLocation, item) diff --git a/gui/wxpython/lmgr/statusbar.py b/gui/wxpython/lmgr/statusbar.py index c30fde67d5c..f8b7b77fad6 100644 --- a/gui/wxpython/lmgr/statusbar.py +++ b/gui/wxpython/lmgr/statusbar.py @@ -21,6 +21,7 @@ import grass.script as gs +import json from core.gcmd import RunCommand from core.watchdog import watchdog_used from gui_core.wrap import Button @@ -34,9 +35,10 @@ def __init__(self, parent, giface): self.giface = giface self.widget = wx.StatusBar(self.parent, id=wx.ID_ANY) self.widget.SetMinHeight(24) - self.widget.SetFieldsCount(2) - self.widget.SetStatusWidths([-1, 100]) + self.widget.SetFieldsCount(3) + self.widget.SetStatusWidths([-1, 100, 100]) self.mask = SbMask(self.widget, self.giface) + self.crs = SbCRS(self.widget, self.giface) self.widget.Bind(wx.EVT_SIZE, self.OnSize) self._repositionStatusbar() @@ -54,9 +56,15 @@ def _repositionStatusbar(self): rect1.y += 1 self.mask.GetWidget().SetRect(rect1) + rect2 = self.GetWidget().GetFieldRect(2) + rect2.x += 1 + rect2.y += 1 + self.crs.GetWidget().SetRect(rect2) + def Refresh(self): - """Refresh statusbar. So far it refreshes just a mask.""" + """Refresh statusbar""" self.mask.Refresh() + self.crs.Refresh() def OnSize(self, event): """Adjust main window statusbar on changing size""" @@ -155,3 +163,59 @@ def OnRemoveMask(self, event): action="delete", element="raster", ) + + +class SbCRS: + """Button to show the EPSG code if available, otherwise 'Custom CRS'. + Clicking it prints Coordinate Reference System (CRS) information for + the current project to the console. + """ + + def __init__(self, parent, giface): + self.name = "crs" + self.giface = giface + self.widget = Button( + parent=parent, id=wx.ID_ANY, label=_("Custom CRS"), style=wx.NO_BORDER + ) + self.widget.Bind(wx.EVT_BUTTON, self.OnShowProjection) + self.widget.SetToolTip( + tip=_("Left mouse click to print detailed projection info") + ) + + self.giface.currentMapsetChanged.connect(self.Refresh) + self.Refresh() + + def Show(self): + self.widget.Show() + + def Hide(self): + self.widget.Hide() + + def GetWidget(self): + """Returns underlying widget. + + :return: widget or None if doesn't exist + """ + return self.widget + + def Refresh(self): + """Fetch the EPSG code, or display 'Custom CRS' if not found""" + + label = _("Custom CRS") + + try: + proj_str = gs.read_command( + "g.proj", flags="p", format="projjson", quiet=True + ) + if proj_str: + proj_info = json.loads(proj_str) + if "id" in proj_info and proj_info["id"].get("authority") == "EPSG": + label = f"EPSG: {proj_info['id']['code']}" + except (TypeError, KeyError, json.JSONDecodeError): + pass + + self.widget.SetLabel(label) + self.Show() + + def OnShowProjection(self, event): + self.giface.RunCmd(["g.proj", "-p"]) From 9644ae8f601471b0359042afa5225334827734e1 Mon Sep 17 00:00:00 2001 From: Saket Kumar Mall Date: Mon, 20 Apr 2026 16:46:40 +0530 Subject: [PATCH 2/3] Add minor changes --- gui/wxpython/datacatalog/tree.py | 18 ++++++++++-------- gui/wxpython/lmgr/statusbar.py | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index acb59831238..5302acc64b1 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -2292,10 +2292,6 @@ def _popupMenuMapset(self): if self._restricted: item.Enable(False) - item = wx.MenuItem(menu, wx.ID_ANY, _("Show region")) - menu.AppendItem(item) - self.Bind(wx.EVT_MENU, self.OnShowRegion, item) - item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMapset, item) @@ -2306,6 +2302,11 @@ def _popupMenuMapset(self): menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnReloadMapset, item) + menu.AppendSeparator() + item = wx.MenuItem(menu, wx.ID_ANY, _("Show computational region")) + menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnShowRegion, item) + item = wx.MenuItem(menu, wx.ID_ANY, _("&Copy path")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyMapsetPath, item) @@ -2327,10 +2328,6 @@ def _popupMenuLocation(self): if self._restricted: item.Enable(False) - item = wx.MenuItem(menu, wx.ID_ANY, _("Show projection")) - menu.AppendItem(item) - self.Bind(wx.EVT_MENU, self.OnShowProjection, item) - item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename project")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameLocation, item) @@ -2341,6 +2338,11 @@ def _popupMenuLocation(self): menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnReloadLocation, item) + menu.AppendSeparator() + item = wx.MenuItem(menu, wx.ID_ANY, _("Show projection info")) + menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnShowProjection, item) + item = wx.MenuItem(menu, wx.ID_ANY, _("Copy path")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyLocationPath, item) diff --git a/gui/wxpython/lmgr/statusbar.py b/gui/wxpython/lmgr/statusbar.py index f8b7b77fad6..420c92f96f4 100644 --- a/gui/wxpython/lmgr/statusbar.py +++ b/gui/wxpython/lmgr/statusbar.py @@ -209,8 +209,9 @@ def Refresh(self): ) if proj_str: proj_info = json.loads(proj_str) - if "id" in proj_info and proj_info["id"].get("authority") == "EPSG": - label = f"EPSG: {proj_info['id']['code']}" + proj_id = proj_info.get("id", {}) + if proj_id.get("authority") == "EPSG" and proj_id.get("code"): + label = f"EPSG: {proj_id['code']}" except (TypeError, KeyError, json.JSONDecodeError): pass From 5159c654b62a55033c91cf707799eb20d45382ed Mon Sep 17 00:00:00 2001 From: saket0187 Date: Mon, 20 Apr 2026 19:58:39 +0530 Subject: [PATCH 3/3] Update docstring --- gui/wxpython/datacatalog/tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 5302acc64b1..2135965f5bf 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -2020,7 +2020,7 @@ def done(event): self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) def OnShowProjection(self, event): - """Show projection of selected location""" + """Show projection info of selected project (location)""" def done(event): gs.try_remove(event.userData) @@ -2034,7 +2034,7 @@ def done(event): self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) def OnShowRegion(self, event): - """Show region of selected mapset""" + """Show computational region of selected mapset""" def done(event): gs.try_remove(event.userData)