dialogs.py 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. """!
  2. @package web_services.dialogs
  3. @brief Dialogs for web services.
  4. List of classes:
  5. - dialogs::WSDialogBase
  6. - dialogs::AddWSDialog
  7. - dialogs::WSPropertiesDialog
  8. - dialogs::SaveWMSLayerDialog
  9. (C) 2009-2013 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Martin Landa <landa.martin gmail.com>
  13. @author Stepan Turek <stepan.turek seznam.cz>
  14. """
  15. import wx
  16. import os
  17. import sys
  18. import shutil
  19. from copy import deepcopy
  20. import grass.script as grass
  21. from core import globalvar
  22. from core.debug import Debug
  23. from core.gcmd import GMessage, GWarning, GError, RunCommand
  24. from core.utils import GetSettingsPath, CmdToTuple, CmdTupleToList, _
  25. from core.gconsole import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT
  26. from gui_core.gselect import Select
  27. from web_services.widgets import WSPanel, WSManageSettingsWidget
  28. class WSDialogBase(wx.Dialog):
  29. """!Base class for web service dialogs.
  30. """
  31. def __init__(self, parent, id = wx.ID_ANY,
  32. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  33. wx.Dialog.__init__(self, parent, id, style = style, **kwargs)
  34. self.parent = parent
  35. # contains panel for every web service on server
  36. self.ws_panels = {'WMS_1.1.1' : {'panel' : None,
  37. 'label' : 'WMS 1.1.1'},
  38. 'WMS_1.3.0' : {'panel' : None,
  39. 'label' : 'WMS 1.3.0'},
  40. 'WMTS' : {'panel' : None,
  41. 'label' : 'WMTS'},
  42. 'OnEarth' : {'panel' : None,
  43. 'label' : 'OnEarth'},
  44. }
  45. #TODO: should be in file
  46. self.default_servers = { 'OSM-WMS-EUROPE' : ['http://129.206.228.72/cached/osm', '', ''],
  47. 'irs.gis-lab.info (OSM)' : ['http://irs.gis-lab.info', '', ''],
  48. 'NASA OnEarth' : ['http://onearth.jpl.nasa.gov/wms.cgi', '', '']
  49. }
  50. # holds reference to web service panel which is showed
  51. self.active_ws_panel = None
  52. # buttons which are disabled when the dialog is not connected
  53. self.run_btns = []
  54. # stores error messages for GError dialog showed when all web service connections were unsuccessful
  55. self.error_msgs = ''
  56. self._createWidgets()
  57. self._doLayout()
  58. def _createWidgets(self):
  59. settingsFile = os.path.join(GetSettingsPath(), 'wxWS')
  60. self.settsManager = WSManageSettingsWidget(parent=self,
  61. settingsFile=settingsFile,
  62. default_servers=self.default_servers)
  63. self.settingsBox = wx.StaticBox(parent = self,
  64. id = wx.ID_ANY,
  65. label = _(" Server settings "))
  66. self.serverText = wx.StaticText(parent = self,
  67. id = wx.ID_ANY, label = _("Server:"))
  68. self.server = wx.TextCtrl(parent = self, id = wx.ID_ANY)
  69. self.btn_connect = wx.Button(parent = self,
  70. id = wx.ID_ANY, label = _("&Connect"))
  71. self.btn_connect.SetToolTipString(_("Connect to the server"))
  72. if not self.server.GetValue():
  73. self.btn_connect.Enable(False)
  74. self.infoCollapseLabelExp = _('Show advanced connection settings')
  75. self.infoCollapseLabelCol = _('Hide advanced connection settings')
  76. self.adv_conn = wx.CollapsiblePane(parent = self,
  77. label = self.infoCollapseLabelExp,
  78. style = wx.CP_DEFAULT_STYLE |
  79. wx.CP_NO_TLW_RESIZE | wx.EXPAND)
  80. self.MakeAdvConnPane(pane = self.adv_conn.GetPane())
  81. self.adv_conn.Collapse(True)
  82. self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnAdvConnPaneChanged, self.adv_conn)
  83. self.reqDataPanel = wx.Panel(parent = self, id = wx.ID_ANY)
  84. self.layerNameBox = wx.StaticBox(parent = self.reqDataPanel,
  85. id = wx.ID_ANY,
  86. label = _(" Layer Manager Settings "))
  87. self.layerNameText = wx.StaticText(parent = self.reqDataPanel, id = wx.ID_ANY,
  88. label = _("Output layer name:"))
  89. self.layerName = wx.TextCtrl(parent = self.reqDataPanel, id = wx.ID_ANY)
  90. for ws in self.ws_panels.iterkeys():
  91. self.ws_panels[ws]['panel'] = WSPanel(parent = self.reqDataPanel,
  92. web_service = ws)
  93. self.ws_panels[ws]['panel'].capParsed.connect(self.OnPanelCapParsed)
  94. self.ws_panels[ws]['panel'].layerSelected.connect(self.OnLayerSelected)
  95. # buttons
  96. self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
  97. self.btn_close.SetToolTipString(_("Close dialog"))
  98. # statusbar
  99. self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)
  100. # bindings
  101. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  102. self.Bind(wx.EVT_CLOSE, self.OnClose)
  103. self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect)
  104. self.server.Bind(wx.EVT_TEXT, self.OnServer)
  105. self.layerName.Bind(wx.EVT_TEXT, self.OnOutputLayerName)
  106. self.settsManager.settingsChanged.connect(self.OnSettingsChanged)
  107. self.settsManager.settingsSaving.connect(self.OnSettingsSaving)
  108. def OnLayerSelected(self, title):
  109. if not self.layerName.GetValue().strip():
  110. self.layerName.SetValue(title)
  111. def _doLayout(self):
  112. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  113. dialogSizer.Add(item = self.settsManager, proportion = 0,
  114. flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
  115. # connectin settings
  116. settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.VERTICAL)
  117. serverSizer = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
  118. serverSizer.Add(item = self.serverText,
  119. flag = wx.ALIGN_CENTER_VERTICAL)
  120. serverSizer.AddGrowableCol(1)
  121. serverSizer.Add(item = self.server,
  122. flag = wx.EXPAND | wx.ALL)
  123. serverSizer.Add(item = self.btn_connect)
  124. settingsSizer.Add(item = serverSizer, proportion = 0,
  125. flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
  126. settingsSizer.Add(item = self.adv_conn,
  127. flag = wx.ALL | wx.EXPAND, border = 5)
  128. dialogSizer.Add(item = settingsSizer, proportion = 0,
  129. flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
  130. # layer name, parsed capabilites
  131. reqDataSizer = wx.BoxSizer(wx.VERTICAL)
  132. layerNameSizer = wx.StaticBoxSizer(self.layerNameBox, wx.HORIZONTAL)
  133. layerNameSizer.Add(item = self.layerNameText,
  134. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
  135. layerNameSizer.Add(item = self.layerName,
  136. flag = wx.EXPAND, proportion = 1)
  137. reqDataSizer.Add(item = layerNameSizer,
  138. flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
  139. self.ch_ws_sizer = wx.BoxSizer(wx.VERTICAL)
  140. reqDataSizer.Add(item = self.ch_ws_sizer, proportion = 0,
  141. flag = wx.TOP | wx.EXPAND, border = 5)
  142. for ws in self.ws_panels.iterkeys():
  143. reqDataSizer.Add(item = self.ws_panels[ws]['panel'], proportion = 1,
  144. flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
  145. self.ws_panels[ws]['panel'].Hide()
  146. dialogSizer.Add(item = self.reqDataPanel, proportion = 1,
  147. flag = wx.EXPAND)
  148. self.reqDataPanel.SetSizer(reqDataSizer)
  149. self.reqDataPanel.Hide()
  150. # buttons
  151. self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
  152. self.btnsizer.Add(item = self.btn_close, proportion = 0,
  153. flag = wx.ALL | wx.ALIGN_CENTER,
  154. border = 10)
  155. dialogSizer.Add(item = self.btnsizer, proportion = 0,
  156. flag = wx.ALIGN_CENTER)
  157. dialogSizer.Add(item = self.statusbar, proportion = 0)
  158. self.SetSizer(dialogSizer)
  159. self.Layout()
  160. self.SetMinSize((550, -1))
  161. self.SetMaxSize((-1, self.GetBestSize()[1]))
  162. self.Fit()
  163. def MakeAdvConnPane(self, pane):
  164. """!Create advanced connection settings pane
  165. """
  166. self.usernameText = wx.StaticText(parent = pane,
  167. id = wx.ID_ANY, label = _("Username:"))
  168. self.username = wx.TextCtrl(parent = pane, id = wx.ID_ANY)
  169. self.passwText = wx.StaticText(parent = pane,
  170. id = wx.ID_ANY, label = _("Password:"))
  171. self.password = wx.TextCtrl(parent = pane, id = wx.ID_ANY,
  172. style = wx.TE_PASSWORD)
  173. # pane layout
  174. adv_conn_sizer = wx.BoxSizer(wx.VERTICAL)
  175. usernameSizer = wx.BoxSizer(wx.HORIZONTAL)
  176. usernameSizer.Add(item = self.usernameText,
  177. flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
  178. usernameSizer.Add(item = self.username, proportion = 1,
  179. flag = wx.EXPAND, border = 5)
  180. adv_conn_sizer.Add(item = usernameSizer,
  181. flag = wx.ALL | wx.EXPAND, border = 5)
  182. passwSizer = wx.BoxSizer(wx.HORIZONTAL)
  183. passwSizer.Add(item = self.passwText,
  184. flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
  185. passwSizer.Add(item = self.password, proportion = 1,
  186. flag = wx.EXPAND, border = 5)
  187. adv_conn_sizer.Add(item = passwSizer,
  188. flag = wx.ALL | wx.EXPAND, border = 5)
  189. pane.SetSizer(adv_conn_sizer)
  190. adv_conn_sizer.Fit(pane)
  191. pane.SetSizer(adv_conn_sizer)
  192. adv_conn_sizer.Fit(pane)
  193. def OnSettingsSaving(self, name):
  194. """!Check if required data are filled before setting save is performed.
  195. """
  196. server = self.server.GetValue().strip()
  197. if not server:
  198. GMessage(parent = self,
  199. message = _("No data source defined, settings are not saved."))
  200. return
  201. self.settsManager.SetDataToSave((server,
  202. self.username.GetValue(),
  203. self.password.GetValue()))
  204. self.settsManager.SaveSettings(name)
  205. def OnSettingsChanged(self, data):
  206. """!Update widgets according to chosen settings"""
  207. # data list: [server, username, password]
  208. if len < 3:
  209. return
  210. self.server.SetValue(data[0])
  211. self.username.SetValue(data[1])
  212. self.password.SetValue(data[2])
  213. if data[1] or data[2]:
  214. self.adv_conn.Expand()
  215. else:
  216. self.adv_conn.Collapse(True)
  217. def OnClose(self, event):
  218. """!Close the dialog
  219. """
  220. """!Close dialog"""
  221. if not self.IsModal():
  222. self.Destroy()
  223. event.Skip()
  224. def _getCapFiles(self):
  225. ws_cap_files = {}
  226. for v in self.ws_panels.itervalues():
  227. ws_cap_files[v['panel'].GetWebService()] = v['panel'].GetCapFile()
  228. return ws_cap_files
  229. def OnServer(self, event):
  230. """!Server settings edited
  231. """
  232. value = event.GetString()
  233. if value:
  234. self.btn_connect.Enable(True)
  235. else:
  236. self.btn_connect.Enable(False)
  237. def OnOutputLayerName(self, event):
  238. """!Update layer name to web service panel
  239. """
  240. lname = event.GetString()
  241. lname = lname.encode('ascii', 'replace')
  242. for v in self.ws_panels.itervalues():
  243. v['panel'].SetOutputLayerName(lname.strip())
  244. def OnConnect(self, event):
  245. """!Connect to the server
  246. """
  247. server = self.server.GetValue().strip()
  248. self.ch_ws_sizer.Clear(deleteWindows = True)
  249. if self.active_ws_panel is not None:
  250. self.reqDataPanel.Hide()
  251. for btn in self.run_btns:
  252. btn.Enable(False)
  253. self.active_ws_panel = None
  254. self.Layout()
  255. self.Fit()
  256. self.statusbar.SetStatusText(_("Connecting to <%s>..." % self.server.GetValue().strip()))
  257. # number of panels already connected
  258. self.finished_panels_num = 0
  259. for ws in self.ws_panels.iterkeys():
  260. self.ws_panels[ws]['panel'].ConnectToServer(url = server,
  261. username = self.username.GetValue(),
  262. password = self.password.GetValue())
  263. self.ws_panels[ws]['panel'].Hide()
  264. def OnPanelCapParsed(self, error_msg):
  265. """!Called when panel has downloaded and parsed capabilities file.
  266. """
  267. # how many web service panels are finished
  268. self.finished_panels_num += 1
  269. if error_msg:
  270. self.error_msgs += '\n' + error_msg
  271. # if all are finished, show panels, which succeeded in connection
  272. if self.finished_panels_num == len(self.ws_panels):
  273. self.UpdateDialogAfterConnection()
  274. # show error dialog only if connections to all web services were unsuccessful
  275. if not self._getConnectedWS() and self.error_msgs:
  276. GError(self.error_msgs, parent = self)
  277. self.error_msgs = ''
  278. self.Layout()
  279. self.Fit()
  280. def _getConnectedWS(self):
  281. """
  282. @return list of found web services on server (identified as keys in self.ws_panels)
  283. """
  284. conn_ws = []
  285. for ws, data in self.ws_panels.iteritems():
  286. if data['panel'].IsConnected():
  287. conn_ws.append(ws)
  288. return conn_ws
  289. def UpdateDialogAfterConnection(self):
  290. """!Update dialog after all web service panels downloaded and parsed capabilities data.
  291. """
  292. avail_ws = {}
  293. conn_ws = self._getConnectedWS()
  294. for ws in conn_ws:
  295. avail_ws[ws] = self.ws_panels[ws]
  296. self.web_service_sel = []
  297. self.rb_choices = []
  298. # at least one web service found on server
  299. if len(avail_ws) > 0:
  300. self.reqDataPanel.Show()
  301. self.rb_order = ['WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth']
  302. for ws in self.rb_order:
  303. if ws in avail_ws:
  304. self.web_service_sel.append(ws)
  305. self.rb_choices.append(avail_ws[ws]['label'])
  306. self.choose_ws_rb = wx.RadioBox(parent = self.reqDataPanel, id = wx.ID_ANY,
  307. label = _("Available web services"),
  308. pos = wx.DefaultPosition, choices = self.rb_choices,
  309. majorDimension = 1, style = wx.RA_SPECIFY_ROWS)
  310. self.Bind(wx.EVT_RADIOBOX, self.OnChooseWs, self.choose_ws_rb)
  311. self.ch_ws_sizer.Add(item = self.choose_ws_rb,
  312. flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
  313. self._showWsPanel(self.web_service_sel[self.choose_ws_rb.GetSelection()])
  314. self.statusbar.SetStatusText(_("Connected to <%s>" % self.server.GetValue().strip()))
  315. for btn in self.run_btns:
  316. btn.Enable(True)
  317. # no web service found on server
  318. else:
  319. self.statusbar.SetStatusText(_("Unable to connect to <%s>" % self.server.GetValue().strip()))
  320. for btn in self.run_btns:
  321. btn.Enable(False)
  322. self.reqDataPanel.Hide()
  323. self.active_ws_panel = None
  324. def OnChooseWs(self, event):
  325. """!Show panel corresponding to selected web service.
  326. """
  327. choosen_r = event.GetInt()
  328. self._showWsPanel(self.web_service_sel[choosen_r])
  329. def _showWsPanel(self, ws):
  330. """!Helper function
  331. """
  332. if self.active_ws_panel is not None:
  333. self.active_ws_panel.Hide()
  334. self.active_ws_panel = self.ws_panels[ws]['panel']
  335. self.active_ws_panel.Show()
  336. self.SetMaxSize((-1, -1))
  337. self.Layout()
  338. def OnAdvConnPaneChanged(self, event):
  339. """!Collapse search module box
  340. """
  341. if self.adv_conn.IsExpanded():
  342. self.adv_conn.SetLabel(self.infoCollapseLabelCol)
  343. else:
  344. self.adv_conn.SetLabel(self.infoCollapseLabelExp)
  345. self.Layout()
  346. self.SetMaxSize((-1, self.GetBestSize()[1]))
  347. self.SendSizeEvent()
  348. self.Fit()
  349. class AddWSDialog(WSDialogBase):
  350. """!Dialog for adding web service layer."""
  351. def __init__(self, parent, giface, id = wx.ID_ANY,
  352. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  353. WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
  354. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)
  355. self.SetTitle(_("Add web service layer"))
  356. self.parent = parent
  357. self.giface = giface
  358. self.btn_connect.SetDefault()
  359. def _createWidgets(self):
  360. WSDialogBase._createWidgets(self)
  361. self.btn_add = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Add layer"))
  362. self.btn_add.SetToolTipString(_("Add selected web service layers as map layer into layer tree"))
  363. self.btn_add.Enable(False)
  364. self.run_btns.append(self.btn_add)
  365. def _doLayout(self):
  366. WSDialogBase._doLayout(self)
  367. self.btnsizer.Add(item = self.btn_add, proportion = 0,
  368. flag = wx.ALL | wx.ALIGN_CENTER,
  369. border = 10)
  370. # bindings
  371. self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddLayer)
  372. def UpdateDialogAfterConnection(self):
  373. """!Connect to the server
  374. """
  375. WSDialogBase.UpdateDialogAfterConnection(self)
  376. if self._getConnectedWS():
  377. self.btn_add.SetDefault()
  378. else:
  379. self.btn_connect.SetDefault()
  380. def OnAddLayer(self, event):
  381. """!Add web service layer.
  382. """
  383. # add layer
  384. if self.active_ws_panel is None:
  385. return
  386. lcmd = self.active_ws_panel.CreateCmd()
  387. if not lcmd:
  388. return None
  389. # TODO: It is not clear how to do GetOptData in giface
  390. # knowing what GetOptData is doing might help
  391. # (maybe Get... is not the right name)
  392. # please fix giface if you know
  393. # tree -> giface
  394. # GetLayerTree -> GetLayerList
  395. # AddLayer -> AddLayer (but tree ones returns some layer,
  396. # giface ones nothing)
  397. # GetLayerInfo -> Layer object can by used instead
  398. # GetOptData -> unknown
  399. ltree = self.giface.GetLayerTree()
  400. active_ws = self.active_ws_panel.GetWebService()
  401. if 'WMS' not in active_ws:
  402. cap_file = self.active_ws_panel.GetCapFile()
  403. cmd_cap_file = grass.tempfile()
  404. shutil.copyfile(cap_file, cmd_cap_file)
  405. lcmd.append('capfile=' + cmd_cap_file)
  406. layer = ltree.AddLayer(ltype = 'wms',
  407. lname = self.active_ws_panel.GetOutputLayerName(),
  408. lchecked = True, lcmd = lcmd)
  409. ws_cap_files = self._getCapFiles()
  410. # create properties dialog
  411. cmd_list = ltree.GetLayerInfo(layer,'cmd')
  412. cmd = CmdToTuple(cmd_list)
  413. prop_win = WSPropertiesDialog(parent = self.parent,
  414. giface = self.giface,
  415. id = wx.ID_ANY,
  416. layer = layer,
  417. ws_cap_files = ws_cap_files,
  418. cmd = cmd)
  419. prop_win.Hide()
  420. ltree.GetOptData(dcmd = None, layer = layer,
  421. params = None, propwin = prop_win)
  422. class WSPropertiesDialog(WSDialogBase):
  423. """!Dialog for editing web service properties."""
  424. def __init__(self, parent, giface, layer, ws_cap_files, cmd, id = wx.ID_ANY,
  425. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  426. """
  427. @param giface grass interface
  428. @param layer layer tree item
  429. @param ws_cap_files dict web service('WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth') : cap file path
  430. cap files, which will be parsed
  431. @param cmd cmd to which dialog widgets will be initialized if it is possible
  432. (cmp parameters exists in parsed web service cap_file)
  433. """
  434. WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
  435. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)
  436. self.SetTitle(_("Web service layer properties"))
  437. self.layer = layer
  438. self.giface = giface
  439. # after web service panels are connected, set dialog widgets
  440. # according to cmd in this variable (if it is not None)
  441. self.cmd_to_set = None
  442. # store data needed for reverting
  443. self.revert_ws_cap_files = {}
  444. self.revert_cmd = cmd
  445. ws_cap = self._getWSfromCmd(cmd)
  446. for ws in self.ws_panels.iterkeys():
  447. # cap file used in cmd will be deleted, thnaks to the dialogs destructor
  448. if ws == ws_cap and cmd[1].has_key('capfile'):
  449. self.revert_ws_cap_files[ws] = cmd[1]['capfile']
  450. del ws_cap_files[ws]
  451. else:
  452. self.revert_ws_cap_files[ws] = grass.tempfile()
  453. self._setRevertCapFiles(ws_cap_files)
  454. self.LoadCapFiles(ws_cap_files = self.revert_ws_cap_files, cmd = cmd)
  455. self.btn_ok.SetDefault()
  456. def __del__(self):
  457. for f in self.revert_ws_cap_files.itervalues():
  458. grass.try_remove(f)
  459. def _setRevertCapFiles(self, ws_cap_files):
  460. for ws, f in ws_cap_files.iteritems():
  461. if os.path.isfile(ws_cap_files[ws]):
  462. shutil.copyfile(f, self.revert_ws_cap_files[ws])
  463. else:
  464. # delete file content
  465. f_o = open(f, 'w')
  466. f_o.close()
  467. def _createWidgets(self):
  468. WSDialogBase._createWidgets(self)
  469. self.btn_apply = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Apply"))
  470. self.btn_apply.SetToolTipString(_("Apply changes"))
  471. self.btn_apply.Enable(False)
  472. self.run_btns.append(self.btn_apply)
  473. self.btn_ok = wx.Button(parent = self, id = wx.ID_ANY, label = _("&OK"))
  474. self.btn_ok.SetToolTipString(_("Apply changes and close dialog"))
  475. self.btn_ok.Enable(False)
  476. self.run_btns.append(self.btn_ok)
  477. def _doLayout(self):
  478. WSDialogBase._doLayout(self)
  479. self.btnsizer.Add(item = self.btn_apply, proportion = 0,
  480. flag = wx.ALL | wx.ALIGN_CENTER,
  481. border = 10)
  482. self.btnsizer.Add(item = self.btn_ok, proportion = 0,
  483. flag = wx.ALL | wx.ALIGN_CENTER,
  484. border = 10)
  485. # bindings
  486. self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
  487. self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave)
  488. def LoadCapFiles(self, ws_cap_files, cmd):
  489. """!Parse cap files and update dialog.
  490. For parameters description, see the constructor.
  491. """
  492. self.ch_ws_sizer.Clear(deleteWindows = True)
  493. self.cmd_to_set = cmd
  494. self.finished_panels_num = 0
  495. conn = self._getServerConnFromCmd(cmd)
  496. self.server.SetValue(conn['url'])
  497. self.password.SetValue(conn['password'])
  498. self.username.SetValue(conn['username'])
  499. self.layerName.SetValue(cmd[1]['map'])
  500. for ws, data in self.ws_panels.iteritems():
  501. cap_file = None
  502. if ws_cap_files.has_key(ws):
  503. cap_file = ws_cap_files[ws]
  504. data['panel'].ParseCapFile(url = conn['url'],
  505. username = conn['password'],
  506. password = conn['username'],
  507. cap_file = cap_file)
  508. def _getServerConnFromCmd(self, cmd):
  509. """!Get url/server/passwod from cmd tuple
  510. """
  511. conn = { 'url' : '', 'username' : '', 'password' : ''}
  512. for k in conn.iterkeys():
  513. if cmd[1].has_key(k):
  514. conn[k] = cmd[1][k]
  515. return conn
  516. def _apply(self):
  517. """!Apply chosen values from widgets to web service layer."""
  518. lcmd = self.active_ws_panel.CreateCmd()
  519. if not lcmd:
  520. return
  521. active_ws = self.active_ws_panel.GetWebService()
  522. if 'WMS' not in active_ws:
  523. lcmd.append('capfile=' + self.revert_ws_cap_files[active_ws])
  524. self.giface.GetLayerTree().GetOptData(dcmd = lcmd,
  525. layer = self.layer,
  526. params = None,
  527. propwin = self)
  528. #TODO use just list or tuple
  529. cmd = CmdToTuple(lcmd)
  530. self.revert_cmd = cmd
  531. self._setRevertCapFiles(self._getCapFiles())
  532. self.giface.updateMap.emit()
  533. def UpdateDialogAfterConnection(self):
  534. """!Connect to the server
  535. """
  536. WSDialogBase.UpdateDialogAfterConnection(self)
  537. if self._getConnectedWS():
  538. self.btn_ok.SetDefault()
  539. else:
  540. self.btn_connect.SetDefault()
  541. def OnApply(self, event):
  542. self._apply()
  543. def OnSave(self, event):
  544. self._apply()
  545. self._close()
  546. def OnClose(self, event):
  547. """!Close dialog"""
  548. self._close()
  549. def _close(self):
  550. """!Hide dialog"""
  551. self.Hide()
  552. self.LoadCapFiles(cmd = self.revert_cmd,
  553. ws_cap_files = self.revert_ws_cap_files)
  554. def OnPanelCapParsed(self, error_msg):
  555. """!Called when panel has downloaded and parsed capabilities file.
  556. """
  557. WSDialogBase.OnPanelCapParsed(self, error_msg)
  558. if self.finished_panels_num == len(self.ws_panels):
  559. if self.cmd_to_set:
  560. self._updateWsPanelWidgetsByCmd(self.cmd_to_set)
  561. self.cmd_to_set = None
  562. def _updateWsPanelWidgetsByCmd(self, cmd):
  563. """!Set values of widgets according to parameters in cmd.
  564. """
  565. ws = self._getWSfromCmd(cmd)
  566. if self.ws_panels[ws]['panel'].IsConnected():
  567. self.ws_panels[ws]['panel'].UpdateWidgetsByCmd(cmd)
  568. self.choose_ws_rb.SetStringSelection(self.ws_panels[ws]['label'])
  569. self._showWsPanel(ws)
  570. def _getWSfromCmd(self, cmd):
  571. driver = cmd[1]['driver']
  572. ws = driver.split('_')[0]
  573. if ws == 'WMS':
  574. ws += '_' + cmd[1]['wms_version']
  575. return ws
  576. class SaveWMSLayerDialog(wx.Dialog):
  577. """!Dialog for saving web service layer into GRASS vector/raster layer.
  578. @todo Implement saving data in region of map display.
  579. """
  580. def __init__(self, parent, layer, giface):
  581. wx.Dialog.__init__(self, parent = parent, title = ("Save web service layer as raster map"), id = wx.ID_ANY)
  582. self.layer = layer
  583. self._giface = giface
  584. self.cmd = self.layer.GetCmd()
  585. self.thread = CmdThread(self)
  586. self.cmdStdErr = GStderr(self)
  587. self._createWidgets()
  588. def _createWidgets(self):
  589. self.labels = {}
  590. self.params = {}
  591. self.labels['output'] = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name for output raster map:"))
  592. self.params['output'] = Select(parent = self, type = 'rast', mapsets = [grass.gisenv()['MAPSET']],
  593. size = globalvar.DIALOG_GSELECT_SIZE)
  594. self.regionStBoxLabel = wx.StaticBox(parent = self, id = wx.ID_ANY,
  595. label = _("Region"))
  596. self.region_types_order = ['comp', 'named']
  597. self.region_types = {}
  598. #self.region_types['map_display'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Map display', style = wx.RB_GROUP )
  599. self.region_types['comp'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Computational region')
  600. self.region_types['named'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Named region')
  601. self.overwrite = wx.CheckBox(parent = self, id = wx.ID_ANY,
  602. label = _("Overwrite existing raster map"))
  603. self.named_reg_panel = wx.Panel(parent = self, id = wx.ID_ANY)
  604. self.labels['region'] = wx.StaticText(parent = self.named_reg_panel, id = wx.ID_ANY,
  605. label = _("Choose named region:"))
  606. self.params['region'] = Select(parent = self.named_reg_panel, type = 'region',
  607. size = globalvar.DIALOG_GSELECT_SIZE)
  608. # buttons
  609. self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
  610. self.btn_close.SetToolTipString(_("Close dialog"))
  611. self.btn_ok = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Save layer"))
  612. self.btn_ok.SetToolTipString(_("Save web service layer as raster map"))
  613. # statusbar
  614. self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)
  615. self._layout()
  616. def _layout(self):
  617. border = wx.BoxSizer(wx.VERTICAL)
  618. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  619. regionSizer = wx.BoxSizer(wx.HORIZONTAL)
  620. dialogSizer.Add(item = self._addSelectSizer(title = self.labels['output'],
  621. sel = self.params['output']))
  622. dialogSizer.Add(item = self.overwrite)
  623. regionSizer = wx.StaticBoxSizer(self.regionStBoxLabel, wx.VERTICAL)
  624. regionTypeSizer = wx.BoxSizer(wx.HORIZONTAL)
  625. for r_type in self.region_types_order:
  626. regionTypeSizer.Add(item = self.region_types[r_type])
  627. regionSizer.Add(item = regionTypeSizer)
  628. self.named_reg_panel.SetSizer(self._addSelectSizer(title = self.labels['region'],
  629. sel = self.params['region']))
  630. regionSizer.Add(item = self.named_reg_panel)
  631. self.named_reg_panel.Hide()
  632. dialogSizer.Add(item = regionSizer, flag = wx.EXPAND)
  633. # buttons
  634. self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
  635. self.btnsizer.Add(item = self.btn_close, proportion = 0,
  636. flag = wx.ALL | wx.ALIGN_CENTER,
  637. border = 10)
  638. self.btnsizer.Add(item = self.btn_ok, proportion = 0,
  639. flag = wx.ALL | wx.ALIGN_CENTER,
  640. border = 10)
  641. dialogSizer.Add(item = self.btnsizer, proportion = 0,
  642. flag = wx.ALIGN_CENTER)
  643. border.Add(item = dialogSizer, proportion = 0,
  644. flag = wx.ALL, border = 5)
  645. border.Add(item = self.statusbar, proportion = 0)
  646. self.SetSizer(border)
  647. self.Layout()
  648. self.Fit()
  649. # bindings
  650. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  651. self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave)
  652. self.Bind(EVT_CMD_DONE, self.OnCmdDone)
  653. self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
  654. for r_type in self.region_types_order:
  655. self.Bind(wx.EVT_RADIOBUTTON, self.OnRegionType, self.region_types[r_type])
  656. def _addSelectSizer(self, title, sel):
  657. """!Helper layout function.
  658. """
  659. selSizer = wx.BoxSizer(orient = wx.VERTICAL)
  660. selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
  661. selTitleSizer.Add(item = title, proportion = 1,
  662. flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
  663. selSizer.Add(item = selTitleSizer, proportion = 0,
  664. flag = wx.EXPAND)
  665. selSizer.Add(item = sel, proportion = 1,
  666. flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
  667. border = 5)
  668. return selSizer
  669. def OnClose(self, event):
  670. """!Close dialog
  671. """
  672. if not self.IsModal():
  673. self.Destroy()
  674. event.Skip()
  675. def OnRegionType(self, event):
  676. selected = event.GetEventObject()
  677. if selected == self.region_types['named']:
  678. self.named_reg_panel.Show()
  679. else:
  680. self.named_reg_panel.Hide()
  681. self.Layout()
  682. self.Fit()
  683. def OnSave(self, event):
  684. """!Import WMS raster data into GRASS as raster layer.
  685. """
  686. self.thread.abort(abortall = True)
  687. currmapset = grass.gisenv()['MAPSET']
  688. self.output = self.params['output'].GetValue().strip()
  689. l_spl = self.output.strip().split("@")
  690. # check output layer
  691. msg = None
  692. if not self.output:
  693. msg = _('Missing output raster.')
  694. elif len(l_spl) > 1 and \
  695. l_spl[1] != currmapset:
  696. msg = _('Output map can be added only to current mapset.')
  697. elif not self.overwrite.IsChecked() and\
  698. grass.find_file(self.output, 'cell', '.')['fullname']:
  699. msg = _('Output map <%s> already exists' % self.output)
  700. if msg:
  701. GMessage(parent = self,
  702. message = msg)
  703. return
  704. self.output = l_spl[0]
  705. # check region
  706. region = self.params['region'].GetValue().strip()
  707. reg_spl = region.strip().split("@")
  708. reg_mapset = '.'
  709. if len(reg_spl) > 1:
  710. reg_mapset = reg_spl[1]
  711. if self.region_types['comp'].GetValue() == 1:
  712. pass
  713. elif grass.find_file(reg_spl[0], 'region', reg_mapset)['fullname']:
  714. msg = _('Region <%s> does not exists.' % self.params['region'].GetValue())
  715. GWarning(parent = self,
  716. message = msg)
  717. return
  718. # create r.in.wms command
  719. cmd = ('r.in.wms', deepcopy(self.cmd[1]))
  720. if cmd[1].has_key('map'):
  721. del cmd[1]['map']
  722. cmd[1]['output'] = self.output
  723. if self.overwrite.IsChecked():
  724. cmd[1]['overwrite'] = True
  725. if self.region_types['named'].GetValue() == 1:
  726. cmd[1]['region'] = region
  727. cmdList = CmdTupleToList(cmd)
  728. self.currentPid = self.thread.GetId()
  729. self.thread.RunCmd(cmdList, stderr = self.cmdStdErr)
  730. self.statusbar.SetStatusText(_("Downloading data..."))
  731. def OnCmdDone(self, event):
  732. """!When data are fetched.
  733. """
  734. if event.pid != self.currentPid:
  735. return
  736. self._addLayer()
  737. self.statusbar.SetStatusText("")
  738. def _addLayer(self):
  739. """!Add layer into layer tree.
  740. """
  741. llist = self._giface.GetLayerList()
  742. if len(llist.GetLayersByName(self.output)) == 0:
  743. cmd = ['d.rast', 'map=' + self.output]
  744. llist.AddLayer(ltype='raster',
  745. name=self.output,
  746. cmd=cmd,
  747. checked=True)
  748. def OnCmdOutput(self, event):
  749. """!Handle cmd output according to debug level.
  750. """
  751. if Debug.GetLevel() == 0:
  752. if event.type == 'error':
  753. msg = _('Unable to fetch data.\n')
  754. msg += event.text
  755. GWarning(parent = self,
  756. message = msg)
  757. else:
  758. Debug.msg(1, event.text)