""" @package web_services.dialogs @brief Dialogs for web services. List of classes: - dialogs::WSDialogBase - dialogs::AddWSDialog - dialogs::WSPropertiesDialog - dialogs::SaveWMSLayerDialog (C) 2009-2021 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa @author Stepan Turek """ import wx import os import six import shutil from copy import deepcopy import grass.script as grass from grass.script.task import cmdlist_to_tuple, cmdtuple_to_list from core import globalvar from core.debug import Debug from core.gcmd import GMessage, GWarning, GError from core.utils import GetSettingsPath from core.gconsole import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT from gui_core.gselect import Select from gui_core.wrap import Button, StaticText, StaticBox, TextCtrl, RadioButton from web_services.widgets import WSPanel, WSManageSettingsWidget class WSDialogBase(wx.Dialog): """Base class for web service dialogs.""" def __init__( self, parent, id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs, ): wx.Dialog.__init__(self, parent, id, style=style, **kwargs) self.parent = parent # contains panel for every web service on server self.ws_panels = { "WMS_1.1.1": {"panel": None, "label": "WMS 1.1.1"}, "WMS_1.3.0": {"panel": None, "label": "WMS 1.3.0"}, "WMTS": {"panel": None, "label": "WMTS"}, "OnEarth": {"panel": None, "label": "OnEarth"}, } # TODO: should be in file self.default_servers = { "OSM-WMS": [ "https://ows.terrestris.de/osm/service?", "", "", ], "NASA GIBS WMTS": [ "http://gibs.earthdata.nasa.gov/wmts/epsg4326/best/wmts.cgi", "", "", ], "tiles.maps.eox.at (Sentinel-2)": [ "https://tiles.maps.eox.at/wms", "", "", ], } # holds reference to web service panel which is showed self.active_ws_panel = None # buttons which are disabled when the dialog is not connected self.run_btns = [] # stores error messages for GError dialog showed when all web service # connections were unsuccessful self.error_msgs = "" self._createWidgets() self._doLayout() def _createWidgets(self): settingsFile = os.path.join(GetSettingsPath(), "wxWS") self.settsManager = WSManageSettingsWidget( parent=self, settingsFile=settingsFile, default_servers=self.default_servers ) self.settingsBox = StaticBox( parent=self, id=wx.ID_ANY, label=_(" Server settings ") ) self.serverText = StaticText(parent=self, id=wx.ID_ANY, label=_("Server:")) self.server = TextCtrl(parent=self, id=wx.ID_ANY) self.btn_connect = Button(parent=self, id=wx.ID_ANY, label=_("&Connect")) self.btn_connect.SetToolTip(_("Connect to the server")) if not self.server.GetValue(): self.btn_connect.Enable(False) self.infoCollapseLabelExp = _("Show advanced connection settings") self.infoCollapseLabelCol = _("Hide advanced connection settings") self.adv_conn = wx.CollapsiblePane( parent=self, label=self.infoCollapseLabelExp, style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND, ) self.MakeAdvConnPane(pane=self.adv_conn.GetPane()) self.adv_conn.Collapse(True) self.Bind( wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnAdvConnPaneChanged, self.adv_conn ) self.reqDataPanel = wx.Panel(parent=self, id=wx.ID_ANY) self.layerNameBox = StaticBox( parent=self.reqDataPanel, id=wx.ID_ANY, label=_(" Layer Manager Settings ") ) self.layerNameText = StaticText( parent=self.reqDataPanel, id=wx.ID_ANY, label=_("Output layer name:") ) self.layerName = TextCtrl(parent=self.reqDataPanel, id=wx.ID_ANY) for ws in six.iterkeys(self.ws_panels): # set class WSPanel argument layerNameTxtCtrl self.ws_panels[ws]["panel"] = WSPanel( parent=self.reqDataPanel, web_service=ws ) self.ws_panels[ws]["panel"].capParsed.connect(self.OnPanelCapParsed) self.ws_panels[ws]["panel"].layerSelected.connect(self.OnLayerSelected) # buttons self.btn_close = Button(parent=self, id=wx.ID_CLOSE) self.btn_close.SetToolTip(_("Close dialog")) # statusbar self.statusbar = wx.StatusBar(parent=self, id=wx.ID_ANY) # bindings self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose) self.Bind(wx.EVT_CLOSE, self.OnClose) self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect) self.server.Bind(wx.EVT_TEXT, self.OnServer) self.layerName.Bind(wx.EVT_TEXT, self.OnOutputLayerName) self.settsManager.settingsChanged.connect(self.OnSettingsChanged) self.settsManager.settingsSaving.connect(self.OnSettingsSaving) def OnLayerSelected(self, title): self.layerName.SetValue(title) def _doLayout(self): dialogSizer = wx.BoxSizer(wx.VERTICAL) dialogSizer.Add( self.settsManager, proportion=0, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5, ) # connectin settings settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.VERTICAL) serverSizer = wx.FlexGridSizer(cols=3, vgap=5, hgap=5) serverSizer.Add(self.serverText, flag=wx.ALIGN_CENTER_VERTICAL) serverSizer.AddGrowableCol(1) serverSizer.Add(self.server, flag=wx.EXPAND | wx.ALL) serverSizer.Add(self.btn_connect) settingsSizer.Add( serverSizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5 ) settingsSizer.Add(self.adv_conn, flag=wx.ALL | wx.EXPAND, border=5) dialogSizer.Add( settingsSizer, proportion=0, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5, ) # layer name, parsed capabilities reqDataSizer = wx.BoxSizer(wx.VERTICAL) layerNameSizer = wx.StaticBoxSizer(self.layerNameBox, wx.HORIZONTAL) layerNameSizer.Add( self.layerNameText, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5 ) layerNameSizer.Add(self.layerName, flag=wx.EXPAND, proportion=1) reqDataSizer.Add( layerNameSizer, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=5 ) self.ch_ws_sizer = wx.BoxSizer(wx.VERTICAL) reqDataSizer.Add( self.ch_ws_sizer, proportion=0, flag=wx.TOP | wx.EXPAND, border=5 ) for ws in six.iterkeys(self.ws_panels): reqDataSizer.Add( self.ws_panels[ws]["panel"], proportion=1, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=5, ) self.ws_panels[ws]["panel"].Hide() dialogSizer.Add(self.reqDataPanel, proportion=1, flag=wx.EXPAND) self.reqDataPanel.SetSizer(reqDataSizer) self.reqDataPanel.Hide() # buttons self.btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL) self.btnsizer.Add( self.btn_close, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) dialogSizer.Add(self.btnsizer, proportion=0, flag=wx.ALIGN_CENTER) # expand wxWidget wx.StatusBar statusbarSizer = wx.BoxSizer(wx.HORIZONTAL) statusbarSizer.Add(self.statusbar, proportion=1, flag=wx.EXPAND) dialogSizer.Add(statusbarSizer, proportion=0, flag=wx.EXPAND) self.SetSizer(dialogSizer) self.Layout() self.SetMinSize((550, -1)) self.SetMaxSize((-1, self.GetBestSize()[1])) self.Fit() def MakeAdvConnPane(self, pane): """Create advanced connection settings pane""" self.usernameText = StaticText(parent=pane, id=wx.ID_ANY, label=_("Username:")) self.username = TextCtrl(parent=pane, id=wx.ID_ANY) self.passwText = StaticText(parent=pane, id=wx.ID_ANY, label=_("Password:")) self.password = TextCtrl(parent=pane, id=wx.ID_ANY, style=wx.TE_PASSWORD) # pane layout adv_conn_sizer = wx.BoxSizer(wx.VERTICAL) usernameSizer = wx.BoxSizer(wx.HORIZONTAL) usernameSizer.Add(self.usernameText, flag=wx.ALIGN_CENTER_VERTICAL, border=5) usernameSizer.Add(self.username, proportion=1, flag=wx.EXPAND, border=5) adv_conn_sizer.Add(usernameSizer, flag=wx.ALL | wx.EXPAND, border=5) passwSizer = wx.BoxSizer(wx.HORIZONTAL) passwSizer.Add(self.passwText, flag=wx.ALIGN_CENTER_VERTICAL, border=5) passwSizer.Add(self.password, proportion=1, flag=wx.EXPAND, border=5) adv_conn_sizer.Add(passwSizer, flag=wx.ALL | wx.EXPAND, border=5) pane.SetSizer(adv_conn_sizer) adv_conn_sizer.Fit(pane) pane.SetSizer(adv_conn_sizer) adv_conn_sizer.Fit(pane) def OnSettingsSaving(self, name): """Check if required data are filled before setting save is performed.""" server = self.server.GetValue().strip() if not server: GMessage( parent=self, message=_("No data source defined, settings are not saved."), ) return self.settsManager.SetDataToSave( (server, self.username.GetValue(), self.password.GetValue()) ) self.settsManager.SaveSettings(name) def OnSettingsChanged(self, data): """Update widgets according to chosen settings""" # data list: [server, username, password] if len(data) < 3: return self.server.SetValue(data[0]) self.username.SetValue(data[1]) self.password.SetValue(data[2]) if data[1] or data[2]: self.adv_conn.Expand() else: self.adv_conn.Collapse(True) # clear content of the wxWidget wx.TextCtrl (Output layer # name:), based on changing default server selection in the # wxWidget wx.Choice if len(self.layerName.GetValue()) > 0: self.layerName.Clear() def OnClose(self, event): """Close the dialog""" """Close dialog""" if not self.IsModal(): self.Destroy() event.Skip() def _getCapFiles(self): ws_cap_files = {} for v in six.itervalues(self.ws_panels): ws_cap_files[v["panel"].GetWebService()] = v["panel"].GetCapFile() return ws_cap_files def OnServer(self, event): """Server settings edited""" value = event.GetString() if value: self.btn_connect.Enable(True) else: self.btn_connect.Enable(False) # clear content of the wxWidget wx.TextCtrl (Output Layer # name:), based on changing content of the wxWidget # wx.TextCtrl (Server:) self.layerName.Clear() def OnOutputLayerName(self, event): """Update layer name to web service panel""" lname = event.GetString() for v in six.itervalues(self.ws_panels): v["panel"].SetOutputLayerName(lname.strip()) def OnConnect(self, event): """Connect to the server""" server = self.server.GetValue().strip() self.ch_ws_sizer.Clear(True) if self.active_ws_panel is not None: self.reqDataPanel.Hide() for btn in self.run_btns: btn.Enable(False) self.active_ws_panel = None self.Layout() self.Fit() self.statusbar.SetStatusText( _("Connecting to <%s>..." % self.server.GetValue().strip()) ) # number of panels already connected self.finished_panels_num = 0 for ws in six.iterkeys(self.ws_panels): self.ws_panels[ws]["panel"].ConnectToServer( url=server, username=self.username.GetValue(), password=self.password.GetValue(), ) self.ws_panels[ws]["panel"].Hide() def OnPanelCapParsed(self, error_msg): """Called when panel has downloaded and parsed capabilities file.""" # how many web service panels are finished self.finished_panels_num += 1 if error_msg: self.error_msgs += "\n" + error_msg # if all are finished, show panels, which succeeded in connection if self.finished_panels_num == len(self.ws_panels): self.UpdateDialogAfterConnection() # show error dialog only if connections to all web services were # unsuccessful if not self._getConnectedWS() and self.error_msgs: GError(self.error_msgs, parent=self) self.error_msgs = "" self.Layout() self.Fit() def _getConnectedWS(self): """ :return: list of found web services on server (identified as keys in self.ws_panels) """ conn_ws = [] for ws, data in six.iteritems(self.ws_panels): if data["panel"].IsConnected(): conn_ws.append(ws) return conn_ws def UpdateDialogAfterConnection(self): """Update dialog after all web service panels downloaded and parsed capabilities data.""" avail_ws = {} conn_ws = self._getConnectedWS() for ws in conn_ws: avail_ws[ws] = self.ws_panels[ws] self.web_service_sel = [] self.rb_choices = [] # at least one web service found on server if len(avail_ws) > 0: self.reqDataPanel.Show() self.rb_order = ["WMS_1.1.1", "WMS_1.3.0", "WMTS", "OnEarth"] for ws in self.rb_order: if ws in avail_ws: self.web_service_sel.append(ws) self.rb_choices.append(avail_ws[ws]["label"]) self.choose_ws_rb = wx.RadioBox( parent=self.reqDataPanel, id=wx.ID_ANY, label=_("Available web services"), pos=wx.DefaultPosition, choices=self.rb_choices, majorDimension=1, style=wx.RA_SPECIFY_ROWS, ) self.Bind(wx.EVT_RADIOBOX, self.OnChooseWs, self.choose_ws_rb) self.ch_ws_sizer.Add( self.choose_ws_rb, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border=5, ) self._showWsPanel(self.web_service_sel[self.choose_ws_rb.GetSelection()]) self.statusbar.SetStatusText( _("Connected to <%s>" % self.server.GetValue().strip()) ) for btn in self.run_btns: btn.Enable(True) # no web service found on server else: self.statusbar.SetStatusText( _("Unable to connect to <%s>" % self.server.GetValue().strip()) ) for btn in self.run_btns: btn.Enable(False) self.reqDataPanel.Hide() self.active_ws_panel = None def OnChooseWs(self, event): """Show panel corresponding to selected web service.""" chosen_r = event.GetInt() self._showWsPanel(self.web_service_sel[chosen_r]) def _showWsPanel(self, ws): """Helper function""" if self.active_ws_panel is not None: self.active_ws_panel.Hide() self.active_ws_panel = self.ws_panels[ws]["panel"] if not self.active_ws_panel.IsShown(): self.active_ws_panel.Show() self.SetMaxSize((-1, -1)) self.active_ws_panel.GetContainingSizer().Layout() def OnAdvConnPaneChanged(self, event): """Collapse search module box""" if self.adv_conn.IsExpanded(): self.adv_conn.SetLabel(self.infoCollapseLabelCol) else: self.adv_conn.SetLabel(self.infoCollapseLabelExp) self.Layout() self.SetMaxSize((-1, self.GetBestSize()[1])) self.SendSizeEvent() self.Fit() class AddWSDialog(WSDialogBase): """Dialog for adding web service layer.""" def __init__( self, parent, giface, id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs, ): WSDialogBase.__init__( self, parent, id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs, ) self.SetTitle(_("Add web service layer")) self.parent = parent self.giface = giface self.btn_connect.SetDefault() def _createWidgets(self): WSDialogBase._createWidgets(self) self.btn_add = Button(parent=self, id=wx.ID_ANY, label=_("&Add layer")) self.btn_add.SetToolTip( _("Add selected web service layers as map layer into layer tree") ) self.btn_add.Enable(False) self.run_btns.append(self.btn_add) def _doLayout(self): WSDialogBase._doLayout(self) self.btnsizer.Add( self.btn_add, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) # bindings self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddLayer) def UpdateDialogAfterConnection(self): """Connect to the server""" WSDialogBase.UpdateDialogAfterConnection(self) if self._getConnectedWS(): self.btn_add.SetDefault() else: self.btn_connect.SetDefault() def OnAddLayer(self, event): """Add web service layer.""" # add layer if self.active_ws_panel is None: return lcmd = self.active_ws_panel.CreateCmd() if not lcmd: return None # TODO: It is not clear how to do GetOptData in giface # knowing what GetOptData is doing might help # (maybe Get... is not the right name) # please fix giface if you know # tree -> giface # GetLayerTree -> GetLayerList # AddLayer -> AddLayer (but tree ones returns some layer, # giface ones nothing) # GetLayerInfo -> Layer object can by used instead # GetOptData -> unknown ltree = self.giface.GetLayerTree() active_ws = self.active_ws_panel.GetWebService() if "WMS" not in active_ws: cap_file = self.active_ws_panel.GetCapFile() cmd_cap_file = grass.tempfile() shutil.copyfile(cap_file, cmd_cap_file) lcmd.append("capfile=" + cmd_cap_file) layer = ltree.AddLayer( ltype="wms", lname=self.active_ws_panel.GetOutputLayerName(), lchecked=True, lcmd=lcmd, ) ws_cap_files = self._getCapFiles() # create properties dialog cmd_list = ltree.GetLayerInfo(layer, "cmd") cmd = cmdlist_to_tuple(cmd_list) prop_win = WSPropertiesDialog( parent=self.parent, giface=self.giface, id=wx.ID_ANY, layer=layer, ws_cap_files=ws_cap_files, cmd=cmd, ) prop_win.Hide() ltree.GetOptData(dcmd=None, layer=layer, params=None, propwin=prop_win) class WSPropertiesDialog(WSDialogBase): """Dialog for editing web service properties.""" def __init__( self, parent, giface, layer, ws_cap_files, cmd, id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs, ): """ :param giface: grass interface :param layer: layer tree item :param ws_cap_files: dict web service('WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth') : cap file path cap files, which will be parsed :param cmd: cmd to which dialog widgets will be initialized if it is possible (cmp parameters exists in parsed web service cap_file) """ WSDialogBase.__init__( self, parent, id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs, ) self.SetTitle(_("Web service layer properties")) self.layer = layer self.giface = giface # after web service panels are connected, set dialog widgets # according to cmd in this variable (if it is not None) self.cmd_to_set = None # store data needed for reverting self.revert_ws_cap_files = {} self.revert_cmd = cmd ws_cap = self._getWSfromCmd(cmd) for ws in six.iterkeys(self.ws_panels): # cap file used in cmd will be deleted, thnaks to the dialogs # destructor if ws == ws_cap and "capfile" in cmd[1]: self.revert_ws_cap_files[ws] = cmd[1]["capfile"] del ws_cap_files[ws] else: self.revert_ws_cap_files[ws] = grass.tempfile() self._setRevertCapFiles(ws_cap_files) self.LoadCapFiles(ws_cap_files=self.revert_ws_cap_files, cmd=cmd) self.btn_ok.SetDefault() def __del__(self): for f in six.itervalues(self.revert_ws_cap_files): grass.try_remove(f) def _setRevertCapFiles(self, ws_cap_files): for ws, f in six.iteritems(ws_cap_files): if os.path.isfile(ws_cap_files[ws]): shutil.copyfile(f, self.revert_ws_cap_files[ws]) else: # delete file content f_o = open(f, "w") f_o.close() def _createWidgets(self): WSDialogBase._createWidgets(self) self.btn_apply = Button(parent=self, id=wx.ID_ANY, label=_("&Apply")) self.btn_apply.SetToolTip(_("Apply changes")) self.btn_apply.Enable(False) self.run_btns.append(self.btn_apply) self.btn_ok = Button(parent=self, id=wx.ID_ANY, label=_("&OK")) self.btn_ok.SetToolTip(_("Apply changes and close dialog")) self.btn_ok.Enable(False) self.run_btns.append(self.btn_ok) def _doLayout(self): WSDialogBase._doLayout(self) self.btnsizer.Add( self.btn_apply, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) self.btnsizer.Add( self.btn_ok, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) # bindings self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply) self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave) def LoadCapFiles(self, ws_cap_files, cmd): """Parse cap files and update dialog. For parameters description, see the constructor. """ self.ch_ws_sizer.Clear(True) self.cmd_to_set = cmd self.finished_panels_num = 0 conn = self._getServerConnFromCmd(cmd) self.server.SetValue(conn["url"]) self.password.SetValue(conn["password"]) self.username.SetValue(conn["username"]) self.layerName.SetValue(cmd[1]["map"]) for ws, data in six.iteritems(self.ws_panels): cap_file = None if ws in ws_cap_files: cap_file = ws_cap_files[ws] data["panel"].ParseCapFile( url=conn["url"], username=conn["password"], password=conn["username"], cap_file=cap_file, ) def _getServerConnFromCmd(self, cmd): """Get url/server/passwod from cmd tuple""" conn = {"url": "", "username": "", "password": ""} for k in six.iterkeys(conn): if k in cmd[1]: conn[k] = cmd[1][k] return conn def _apply(self): """Apply chosen values from widgets to web service layer.""" lcmd = self.active_ws_panel.CreateCmd() if not lcmd: return active_ws = self.active_ws_panel.GetWebService() if "WMS" not in active_ws: lcmd.append("capfile=" + self.revert_ws_cap_files[active_ws]) self.giface.GetLayerTree().GetOptData( dcmd=lcmd, layer=self.layer, params=True, propwin=self ) # TODO use just list or tuple cmd = cmdlist_to_tuple(lcmd) self.revert_cmd = cmd self._setRevertCapFiles(self._getCapFiles()) self.giface.updateMap.emit() def UpdateDialogAfterConnection(self): """Connect to the server""" WSDialogBase.UpdateDialogAfterConnection(self) if self._getConnectedWS(): self.btn_ok.SetDefault() else: self.btn_connect.SetDefault() def OnApply(self, event): self._apply() def OnSave(self, event): self._apply() self._close() def OnClose(self, event): """Close dialog""" self._close() def _close(self): """Hide dialog""" self.Hide() self.LoadCapFiles(cmd=self.revert_cmd, ws_cap_files=self.revert_ws_cap_files) def OnPanelCapParsed(self, error_msg): """Called when panel has downloaded and parsed capabilities file.""" WSDialogBase.OnPanelCapParsed(self, error_msg) if self.finished_panels_num == len(self.ws_panels): if self.cmd_to_set: self._updateWsPanelWidgetsByCmd(self.cmd_to_set) self.cmd_to_set = None def _updateWsPanelWidgetsByCmd(self, cmd): """Set values of widgets according to parameters in cmd.""" ws = self._getWSfromCmd(cmd) if self.ws_panels[ws]["panel"].IsConnected(): self.choose_ws_rb.SetStringSelection(self.ws_panels[ws]["label"]) self._showWsPanel(ws) self.ws_panels[ws]["panel"].UpdateWidgetsByCmd(cmd) def _getWSfromCmd(self, cmd): driver = cmd[1]["driver"] ws = driver.split("_")[0] if ws == "WMS": ws += "_" + cmd[1]["wms_version"] return ws class SaveWMSLayerDialog(wx.Dialog): """Dialog for saving web service layer into GRASS vector/raster layer. .. todo:: Implement saving data in region of map display. """ def __init__(self, parent, layer, giface): wx.Dialog.__init__( self, parent=parent, title=("Save web service layer as raster map"), id=wx.ID_ANY, ) self.layer = layer self._giface = giface self.cmd = self.layer.GetCmd() self.thread = CmdThread(self) self.cmdStdErr = GStderr(self) self._createWidgets() def _createWidgets(self): self.labels = {} self.params = {} self.labels["output"] = StaticText( parent=self, id=wx.ID_ANY, label=_("Name for output raster map:") ) self.params["output"] = Select( parent=self, type="raster", mapsets=[grass.gisenv()["MAPSET"]], size=globalvar.DIALOG_GSELECT_SIZE, ) self.regionStBoxLabel = StaticBox( parent=self, id=wx.ID_ANY, label=" %s " % _("Export region") ) self.region_types_order = ["display", "comp", "named"] self.region_types = {} self.region_types["display"] = RadioButton( parent=self, label=_("Map display"), style=wx.RB_GROUP ) self.region_types["comp"] = RadioButton( parent=self, label=_("Computational region") ) self.region_types["named"] = RadioButton(parent=self, label=_("Named region")) self.region_types["display"].SetToolTip( _("Extent and resolution" " are based on Map Display geometry.") ) self.region_types["comp"].SetToolTip( _("Extent and resolution" " are based on computational region.") ) self.region_types["named"].SetToolTip( _("Extent and resolution" " are based on named region.") ) self.region_types["display"].SetValue(True) # set default as map display self.overwrite = wx.CheckBox( parent=self, id=wx.ID_ANY, label=_("Overwrite existing raster map") ) self.named_reg_panel = wx.Panel(parent=self, id=wx.ID_ANY) self.labels["region"] = StaticText( parent=self.named_reg_panel, id=wx.ID_ANY, label=_("Choose named region:") ) self.params["region"] = Select( parent=self.named_reg_panel, type="region", size=globalvar.DIALOG_GSELECT_SIZE, ) # buttons self.btn_close = Button(parent=self, id=wx.ID_CLOSE) self.SetEscapeId(self.btn_close.GetId()) self.btn_close.SetToolTip(_("Close dialog")) self.btn_ok = Button(parent=self, label=_("&Save layer")) self.btn_ok.SetToolTip(_("Save web service layer as raster map")) # statusbar self.statusbar = wx.StatusBar(parent=self, id=wx.ID_ANY) self._layout() def _layout(self): self._border = wx.BoxSizer(wx.VERTICAL) dialogSizer = wx.BoxSizer(wx.VERTICAL) regionSizer = wx.BoxSizer(wx.HORIZONTAL) dialogSizer.Add( self._addSelectSizer(title=self.labels["output"], sel=self.params["output"]) ) regionSizer = wx.StaticBoxSizer(self.regionStBoxLabel, wx.VERTICAL) regionTypeSizer = wx.BoxSizer(wx.HORIZONTAL) for r_type in self.region_types_order: regionTypeSizer.Add(self.region_types[r_type], flag=wx.RIGHT, border=8) regionSizer.Add(regionTypeSizer) self.named_reg_panel.SetSizer( self._addSelectSizer(title=self.labels["region"], sel=self.params["region"]) ) regionSizer.Add(self.named_reg_panel) self.named_reg_panel.Hide() dialogSizer.Add(regionSizer, flag=wx.EXPAND) dialogSizer.Add(self.overwrite, flag=wx.TOP, border=10) # buttons self.btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL) self.btnsizer.Add( self.btn_close, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) self.btnsizer.Add( self.btn_ok, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10 ) dialogSizer.Add(self.btnsizer, proportion=0, flag=wx.ALIGN_CENTER) self._border.Add(dialogSizer, proportion=0, flag=wx.ALL, border=5) self._border.Add(self.statusbar, proportion=0) self.SetSizer(self._border) self.Layout() self.Fit() # bindings self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave) self.Bind(EVT_CMD_DONE, self.OnCmdDone) self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) for r_type in self.region_types_order: self.Bind(wx.EVT_RADIOBUTTON, self.OnRegionType, self.region_types[r_type]) def _addSelectSizer(self, title, sel): """Helper layout function.""" selSizer = wx.BoxSizer(orient=wx.VERTICAL) selTitleSizer = wx.BoxSizer(wx.HORIZONTAL) selTitleSizer.Add( title, proportion=1, flag=wx.LEFT | wx.TOP | wx.EXPAND, border=5 ) selSizer.Add(selTitleSizer, proportion=0, flag=wx.EXPAND) selSizer.Add( sel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5, ) return selSizer def OnRegionType(self, event): selected = event.GetEventObject() if selected == self.region_types["named"]: self.named_reg_panel.Show() else: self.named_reg_panel.Hide() self._border.Layout() self.Fit() def OnSave(self, event): """Import WMS raster data into GRASS as raster layer.""" self.thread.abort(abortall=True) currmapset = grass.gisenv()["MAPSET"] self.output = self.params["output"].GetValue().strip() l_spl = self.output.strip().split("@") # check output layer msg = None if not self.output: msg = _("Missing output raster.") elif len(l_spl) > 1 and l_spl[1] != currmapset: msg = _("Output map can be added only to current mapset.") elif ( not self.overwrite.IsChecked() and grass.find_file(self.output, "cell", ".")["fullname"] ): msg = _("Output map <%s> already exists" % self.output) if msg: GMessage(parent=self, message=msg) return self.output = l_spl[0] # check region region = self.params["region"].GetValue().strip() reg_spl = region.strip().split("@") reg_mapset = "." if len(reg_spl) > 1: reg_mapset = reg_spl[1] if self.region_types["named"].GetValue(): if not grass.find_file(reg_spl[0], "windows", reg_mapset)["fullname"]: msg = _( "Region <%s> does not exist." % self.params["region"].GetValue() ) GWarning(parent=self, message=msg) return # create r.in.wms command cmd = ("r.in.wms", deepcopy(self.cmd[1])) if "map" in cmd[1]: del cmd[1]["map"] cmd[1]["output"] = self.output if self.overwrite.IsChecked(): cmd[1]["overwrite"] = True env = os.environ.copy() if self.region_types["named"].GetValue(): cmd[1]["region"] = region elif self.region_types["display"].GetValue(): region = self._giface.GetMapWindow().GetMap().SetRegion() env["GRASS_REGION"] = region cmdList = cmdtuple_to_list(cmd) self.currentPid = self.thread.GetId() self.thread.RunCmd(cmdList, env=env, stderr=self.cmdStdErr) self.statusbar.SetStatusText(_("Downloading data...")) def OnCmdDone(self, event): """When data are fetched.""" if event.pid != self.currentPid: return self._addLayer() self.statusbar.SetStatusText("") def _addLayer(self): """Add layer into layer tree.""" llist = self._giface.GetLayerList() if len(llist.GetLayersByName(self.output)) == 0: cmd = ["d.rast", "map=" + self.output] llist.AddLayer(ltype="raster", name=self.output, cmd=cmd, checked=True) def OnCmdOutput(self, event): """Handle cmd output according to debug level.""" if Debug.GetLevel() == 0: if event.type == "error": msg = _("Unable to fetch data.\n") msg += event.text GWarning(parent=self, message=msg) else: Debug.msg(1, event.text)