1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117 |
- """
- @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 <landa.martin gmail.com>
- @author Stepan Turek <stepan.turek seznam.cz>
- """
- 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)
|