dialogs.py 34 KB

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