widgets.py 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. """!
  2. @package web_services.widgets
  3. @brief Widgets for web services (WMS, WMTS, NasaOnEarh)
  4. List of classes:
  5. - widgets::WSPanel
  6. - widgets::LayersList
  7. (C) 2012-2013 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Martin Landa <landa.martin gmail.com>
  11. @author Stepan Turek <stepan.turek seznam.cz>
  12. """
  13. import os
  14. import sys
  15. import shutil
  16. from copy import deepcopy
  17. try:
  18. from xml.etree.ElementTree import ParseError
  19. except ImportError: # < Python 2.7
  20. from xml.parsers.expat import ExpatError as ParseError
  21. import wx
  22. import wx.lib.flatnotebook as FN
  23. import wx.lib.colourselect as csel
  24. import wx.lib.mixins.listctrl as listmix
  25. from wx.lib.newevent import NewEvent
  26. from wx.gizmos import TreeListCtrl
  27. from core import globalvar
  28. from core.debug import Debug
  29. from core.gcmd import GWarning, GMessage
  30. from core.gconsole import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT
  31. from core.utils import _
  32. from web_services.cap_interface import WMSCapabilities, WMTSCapabilities, OnEarthCapabilities
  33. from gui_core.widgets import GNotebook
  34. import grass.script as grass
  35. rinwms_path = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms")
  36. if rinwms_path not in sys.path:
  37. sys.path.append(rinwms_path)
  38. from wms_base import WMSDriversInfo
  39. from grass.pydispatch.signal import Signal
  40. class WSPanel(wx.Panel):
  41. def __init__(self, parent, web_service, **kwargs):
  42. """!Show data from capabilities file.
  43. Signal: capParsed - this signal is emitted when capabilities file is downloaded
  44. (after ConnectToServer method was called)
  45. @param parent - parent widget
  46. @param web_service - web service to be panel generated for
  47. """
  48. wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
  49. self.parent = parent
  50. self.ws = web_service
  51. self.capParsed = Signal('WSPanel.capParsed')
  52. # stores widgets, which represents parameters/flags of d.wms
  53. self.params = {}
  54. self.flags = {}
  55. self.o_layer_name = ''
  56. # stores err output from r.in.wms during getting capabilities
  57. self.cmd_err_str = ''
  58. # stores selected layer from layer list
  59. self.sel_layers = []
  60. # downloaded and parsed data from server successfully?
  61. self.is_connected = False
  62. # common part of command for r.in.wms -c and d.wms
  63. self.ws_cmdl = None
  64. # provides information about driver parameters
  65. self.drv_info = WMSDriversInfo()
  66. self.drv_props = self.drv_info.GetDrvProperties(self.ws)
  67. self.ws_drvs = {
  68. 'WMS_1.1.1' : {
  69. 'cmd' : ['wms_version=1.1.1',
  70. 'driver=WMS_GRASS'],
  71. 'cap_parser' : lambda temp_file : WMSCapabilities(temp_file, '1.1.1'),
  72. },
  73. 'WMS_1.3.0' : {
  74. 'cmd' : ['wms_version=1.3.0',
  75. 'driver=WMS_GRASS'],
  76. 'cap_parser' : lambda temp_file : WMSCapabilities(temp_file, '1.3.0'),
  77. },
  78. 'WMTS' : {
  79. 'cmd' : ['driver=WMTS_GRASS'],
  80. 'cap_parser' : WMTSCapabilities,
  81. },
  82. 'OnEarth' : {
  83. 'cmd' : ['driver=OnEarth_GRASS'],
  84. 'cap_parser' : OnEarthCapabilities,
  85. }
  86. }
  87. self.cmdStdErr = GStderr(self)
  88. self.cmd_thread = CmdThread(self)
  89. self.cap_file = grass.tempfile()
  90. self.notebook = GNotebook(parent = self,
  91. style = FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
  92. self._requestPage()
  93. self._advancedSettsPage()
  94. self._layout()
  95. self.Bind(EVT_CMD_DONE, self.OnCapDownloadDone)
  96. self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
  97. def __del__(self):
  98. self.cmd_thread.abort(abortall =True)
  99. grass.try_remove(self.cap_file)
  100. def _layout(self):
  101. reqDataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  102. label = _(" Requested data settings "))
  103. sizer = wx.StaticBoxSizer(reqDataBox, wx.VERTICAL)
  104. sizer.Add(item = self.notebook, proportion = 1,
  105. flag = wx.EXPAND)
  106. self.SetSizer(sizer)
  107. def _requestPage(self):
  108. """!Create request page"""
  109. self.req_page_panel = wx.Panel(parent = self, id = wx.ID_ANY)
  110. self.notebook.AddPage(page = self.req_page_panel,
  111. text=_('Request'),
  112. name = 'request')
  113. # list of layers
  114. self.layersBox = wx.StaticBox(parent = self.req_page_panel, id = wx.ID_ANY,
  115. label=_("List of layers "))
  116. style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT
  117. if self.drv_props['req_multiple_layers']:
  118. style = style | wx.TR_MULTIPLE
  119. if 'WMS' not in self.ws:
  120. style = style | wx.TR_HIDE_ROOT
  121. self.list = LayersList(parent = self.req_page_panel,
  122. web_service = self.ws,
  123. style = style)
  124. self.params['format'] = None
  125. self.params['srs'] = None
  126. if 'srs' not in self.drv_props['ignored_params']:
  127. projText = wx.StaticText(parent = self.req_page_panel, id = wx.ID_ANY, label = _("Source projection:"))
  128. self.params['srs'] = wx.Choice(parent = self.req_page_panel, id = wx.ID_ANY)
  129. self.list.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnListSelChanged)
  130. # layout
  131. self.req_page_sizer = wx.BoxSizer(wx.VERTICAL)
  132. layersSizer = wx.StaticBoxSizer(self.layersBox, wx.HORIZONTAL)
  133. layersSizer.Add(item = self.list, proportion = 1,
  134. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
  135. self.req_page_sizer.Add(item = layersSizer, proportion = 1,
  136. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
  137. self.source_sizer = wx.BoxSizer(wx.HORIZONTAL)
  138. if self.params['format'] is not None:
  139. self.source_sizer.Add(item = self.params['format'],
  140. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  141. if self.params['srs'] is not None:
  142. self.source_sizer.Add(item = projText, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
  143. self.source_sizer.Add(item = self.params['srs'],
  144. flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP | wx.BOTTOM, border = 5)
  145. self.req_page_sizer.Add(item = self.source_sizer,
  146. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
  147. self.req_page_panel.SetSizer(self.req_page_sizer)
  148. def enableButtons(self, enable = True):
  149. """!Enable/disable up, down, buttons
  150. """
  151. self.btnUp.Enable(enable)
  152. self.btnDown.Enable(enable)
  153. def _advancedSettsPage(self):
  154. """!Create advanced settings page
  155. """
  156. #TODO parse maxcol, maxrow, settings from d.wms module?
  157. #TODO OnEarth driver - add selection of time
  158. adv_setts_panel = wx.Panel(parent = self, id = wx.ID_ANY)
  159. self.notebook.AddPage(page = adv_setts_panel,
  160. text=_('Advanced request settings'),
  161. name = 'adv_req_setts')
  162. labels = {}
  163. self.l_odrder_list = None
  164. if 'WMS' in self.ws:
  165. labels['l_order'] = wx.StaticBox(parent = adv_setts_panel, id = wx.ID_ANY,
  166. label = _("Order of layers in raster"))
  167. self.l_odrder_list = wx.ListBox(adv_setts_panel, id = wx.ID_ANY, choices = [],
  168. style = wx.LB_SINGLE|wx.LB_NEEDED_SB)
  169. self.btnUp = wx.Button(adv_setts_panel, id = wx.ID_ANY, label = _("Up"))
  170. self.btnDown = wx.Button(adv_setts_panel, id = wx.ID_ANY, label = _("Down"))
  171. self.btnUp.Bind(wx.EVT_BUTTON, self.OnUp)
  172. self.btnDown.Bind(wx.EVT_BUTTON, self.OnDown)
  173. labels['method'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
  174. label = _("Reprojection method:"))
  175. self.reproj_methods = ['nearest', 'linear', 'cubic', 'cubicspline']
  176. self.params['method'] = wx.Choice(parent = adv_setts_panel, id = wx.ID_ANY,
  177. choices = [_('Nearest neighbor'), _('Linear interpolation'),
  178. _('Cubic interpolation'), _('Cubic spline interpolation')])
  179. labels['maxcols'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
  180. label = _("Maximum columns to request from server at time:"))
  181. self.params['maxcols'] = wx.SpinCtrl(parent = adv_setts_panel, id = wx.ID_ANY, size = (100, -1))
  182. labels['maxrows'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
  183. label = _("Maximum rows to request from server at time:"))
  184. self.params['maxrows'] = wx.SpinCtrl(parent = adv_setts_panel, id = wx.ID_ANY, size = (100, -1))
  185. min = 100
  186. max = 10000
  187. self.params['maxcols'].SetRange(min,max)
  188. self.params['maxrows'].SetRange(min,max)
  189. val = 500
  190. self.params['maxcols'].SetValue(val)
  191. self.params['maxrows'].SetValue(val)
  192. self.flags['o'] = self.params['bgcolor'] = None
  193. if not 'o' in self.drv_props['ignored_flags']:
  194. self.flags['o'] = wx.CheckBox(parent = adv_setts_panel, id = wx.ID_ANY,
  195. label = _("Do not request transparent data"))
  196. self.flags['o'].Bind(wx.EVT_CHECKBOX, self.OnTransparent)
  197. labels['bgcolor'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
  198. label = _("Background color:"))
  199. self.params['bgcolor'] = csel.ColourSelect(parent = adv_setts_panel, id = wx.ID_ANY,
  200. colour = (255, 255, 255),
  201. size = globalvar.DIALOG_COLOR_SIZE)
  202. self.params['bgcolor'].Enable(False)
  203. self.params['urlparams'] = None
  204. if self.params['urlparams'] not in self.drv_props['ignored_params']:
  205. labels['urlparams'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
  206. label = _("Additional query parameters for server:"))
  207. self.params['urlparams'] = wx.TextCtrl(parent = adv_setts_panel, id = wx.ID_ANY)
  208. # layout
  209. border = wx.BoxSizer(wx.VERTICAL)
  210. if 'WMS' in self.ws:
  211. boxSizer = wx.StaticBoxSizer(labels['l_order'], wx.VERTICAL)
  212. gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
  213. gridSizer.Add(self.l_odrder_list,
  214. pos = (0,0),
  215. span = (4, 1),
  216. flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
  217. border = 0)
  218. gridSizer.Add(self.btnUp,
  219. pos = (0,1),
  220. flag = wx.ALIGN_CENTER_VERTICAL,
  221. border = 0)
  222. gridSizer.Add(self.btnDown,
  223. pos = (1,1),
  224. flag = wx.ALIGN_CENTER_VERTICAL,
  225. border = 0)
  226. gridSizer.AddGrowableCol(0)
  227. boxSizer.Add(gridSizer,
  228. flag = wx.EXPAND | wx.ALL,
  229. border = 5)
  230. border.Add(item = boxSizer,
  231. flag = wx.LEFT | wx.RIGHT | wx.UP | wx.EXPAND,
  232. border = 5)
  233. gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
  234. row = 0
  235. for k in ['method', 'maxcols', 'maxrows', 'o', 'bgcolor']:
  236. if self.params.has_key(k):
  237. param = self.params[k]
  238. elif self.flags.has_key(k):
  239. param = self.flags[k]
  240. if param is None:
  241. continue
  242. if labels.has_key(k) or k == 'o':
  243. if k != 'o':
  244. label = labels[k]
  245. else:
  246. label = param
  247. gridSizer.Add(label,
  248. flag = wx.ALIGN_LEFT |
  249. wx.ALIGN_CENTER_VERTICAL,
  250. pos = (row, 0))
  251. if k != 'o':
  252. gridSizer.Add(item = param,
  253. flag = wx.ALIGN_RIGHT |
  254. wx.ALIGN_CENTER_VERTICAL,
  255. pos = (row, 1))
  256. row += 1
  257. gridSizer.AddGrowableCol(0)
  258. border.Add(item = gridSizer,
  259. flag = wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND,
  260. border = 5)
  261. if self.params['urlparams']:
  262. gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
  263. gridSizer.AddGrowableCol(1)
  264. row = 0
  265. gridSizer.Add(labels['urlparams'],
  266. flag = wx.ALIGN_LEFT |
  267. wx.ALIGN_CENTER_VERTICAL,
  268. pos = (row, 0))
  269. gridSizer.Add(item = self.params['urlparams'],
  270. flag = wx.ALIGN_RIGHT |
  271. wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  272. pos = (row, 1))
  273. border.Add(item = gridSizer,
  274. flag = wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND,
  275. border = 5)
  276. adv_setts_panel.SetSizer(border)
  277. def OnUp(self, event):
  278. """!Move selected layer up
  279. """
  280. if self.l_odrder_list.GetSelections():
  281. pos = self.l_odrder_list.GetSelection()
  282. if pos:
  283. self.sel_layers.insert(pos - 1, self.sel_layers.pop(pos))
  284. if pos > 0:
  285. self._updateLayerOrderList(selected = (pos - 1))
  286. else:
  287. self._updateLayerOrderList(selected = 0)
  288. def OnDown(self, event):
  289. """!Move selected to down
  290. """
  291. if self.l_odrder_list.GetSelections():
  292. pos = self.l_odrder_list.GetSelection()
  293. if pos != len(self.sel_layers) - 1:
  294. self.sel_layers.insert(pos + 1, self.sel_layers.pop(pos))
  295. if pos < len(self.sel_layers) -1:
  296. self._updateLayerOrderList(selected = (pos + 1))
  297. else:
  298. self._updateLayerOrderList(selected = len(self.sel_layers) -1)
  299. def _updateLayerOrderList(self, selected = None):
  300. """!Update order in list.
  301. """
  302. def getlayercaption(layer):
  303. if l['title']:
  304. cap = (l['title'])
  305. else:
  306. cap = (l['name'])
  307. if l['style']:
  308. if l['style']['title']:
  309. cap += ' / ' + l['style']['title']
  310. else:
  311. cap += ' / ' + l['style']['name']
  312. return cap
  313. layer_capts = [getlayercaption(l) for l in self.sel_layers]
  314. self.l_odrder_list.Set(layer_capts)
  315. if self.l_odrder_list.IsEmpty():
  316. self.enableButtons(False)
  317. else:
  318. self.enableButtons(True)
  319. if selected is not None:
  320. self.l_odrder_list.SetSelection(selected)
  321. self.l_odrder_list.EnsureVisible(selected)
  322. def OnTransparent(self, event):
  323. checked = event.IsChecked()
  324. if checked:
  325. self.params['bgcolor'].Enable(True)
  326. else:
  327. self.params['bgcolor'].Enable(False)
  328. def ConnectToServer(self, url, username, password):
  329. """!Download and parse data from capabilities file.
  330. @param url - server url
  331. @param username - username for connection
  332. @param password - password for connection
  333. """
  334. self._prepareForNewConn(url, username, password)
  335. cap_cmd = ['r.in.wms', '-c', ('capfile_output=%s' % self.cap_file), '--overwrite'] + self.ws_cmdl
  336. self.currentPid = self.cmd_thread.GetId()
  337. self.cmd_thread.RunCmd(cap_cmd, stderr = self.cmdStdErr)
  338. def OnCmdOutput(self, event):
  339. """!Manage cmd output.
  340. """
  341. if Debug.GetLevel() != 0:
  342. Debug.msg(1, event.text)
  343. elif event.type != 'message' and event.type != 'warning':
  344. self.cmd_err_str += event.text + os.linesep
  345. def _prepareForNewConn(self, url, username, password):
  346. """!Prepare panel for new connection
  347. """
  348. self.is_connected = False
  349. self.sel_layers = []
  350. self.formats_list = []
  351. self.projs_list = []
  352. self.conn = {
  353. 'url' : url,
  354. 'password' : password,
  355. 'username' : username
  356. }
  357. conn_cmd = []
  358. for k, v in self.conn.iteritems():
  359. if v:
  360. conn_cmd.append("%s=%s" % (k,v))
  361. self.ws_cmdl = self.ws_drvs[self.ws]['cmd'] + conn_cmd
  362. def OnCapDownloadDone(self, event):
  363. """!Process donwloaded capabilities file and emits capParsed signal (see class constructor).
  364. """
  365. if event.pid != self.currentPid:
  366. return
  367. if event.returncode != 0:
  368. if self.cmd_err_str:
  369. self.cmd_err_str = _("Unable to download %s capabilities file\nfrom <%s>:\n" % \
  370. (self.ws.replace('_', ' '), self.conn['url'])) + self.cmd_err_str
  371. self._postCapParsedEvt(error_msg = self.cmd_err_str)
  372. self.cmd_err_str = ''
  373. return
  374. self._parseCapFile(self.cap_file)
  375. def _parseCapFile(self, cap_file):
  376. """!Parse capabilities data and emits capParsed signal (see class constructor).
  377. """
  378. try:
  379. self.cap = self.ws_drvs[self.ws]['cap_parser'](cap_file)
  380. except (IOError, ParseError) as error:
  381. error_msg = _("%s web service was not found in fetched capabilities file from <%s>:\n%s\n" % \
  382. (self.ws, self.conn['url'], str(error)))
  383. if Debug.GetLevel() != 0:
  384. Debug.msg(1, error_msg)
  385. self._postCapParsedEvt(None)
  386. else:
  387. self._postCapParsedEvt(error_msg = error_msg)
  388. return
  389. self.is_connected = True
  390. # WMS standard has formats defined for all layers
  391. if 'WMS' in self.ws:
  392. self.formats_list = sorted(self._getFormats())
  393. self._updateFormatRadioBox(self.formats_list)
  394. self._setDefaultFormatVal()
  395. self.list.LoadData(self.cap)
  396. self.OnListSelChanged(event = None)
  397. self._postCapParsedEvt(None)
  398. def ParseCapFile(self, url, username, password, cap_file = None,):
  399. """!Parse capabilities data and emits capParsed signal (see class constructor).
  400. """
  401. self._prepareForNewConn(url, username, password)
  402. if cap_file is None or not url:
  403. self._postCapParsedEvt(None)
  404. return
  405. shutil.copyfile(cap_file, self.cap_file)
  406. self._parseCapFile(self.cap_file)
  407. def UpdateWidgetsByCmd(self, cmd):
  408. """!Update panel widgets accordnig to passed cmd tuple
  409. @param cmd - cmd in tuple
  410. """
  411. dcmd = cmd[1]
  412. layers = []
  413. if dcmd.has_key('layers'):
  414. layers = dcmd['layers']
  415. styles = []
  416. if dcmd.has_key('styles'):
  417. styles = dcmd['styles']
  418. if 'WMS' in self.ws:
  419. layers = layers.split(',')
  420. styles = styles.split(',')
  421. else:
  422. layers = [layers]
  423. styles = [styles]
  424. if len(layers) != len(styles):
  425. styles = [''] * len(layers)
  426. l_st_list = []
  427. for i in range(len(layers)):
  428. l_st_list.append({'style' : styles[i],
  429. 'layer' : layers[i]})
  430. # WMS standard - first layer in params is most bottom...
  431. # therefore layers order need to be reversed
  432. l_st_list = [l for l in reversed(l_st_list)]
  433. self.list.SelectLayers(l_st_list)
  434. params = {}
  435. if dcmd.has_key('format'):
  436. params['format'] = dcmd['format']
  437. if dcmd.has_key('srs'):
  438. params['srs'] = 'EPSG:' + dcmd['srs']
  439. if dcmd.has_key('method'):
  440. params['method'] = dcmd['method']
  441. for p, v in params.iteritems():
  442. if self.params[p]:
  443. self.params[p].SetStringSelection(v)
  444. for p, conv_f in [('urlparams', None), ('maxcols', int), ('maxrows', int)]:
  445. if dcmd.has_key(p):
  446. v = dcmd[p]
  447. if conv_f:
  448. v = conv_f(v)
  449. self.params[p].SetValue(v)
  450. if dcmd.has_key('flags') and \
  451. 'o' in dcmd['flags']:
  452. self.flags['o'].SetValue(1)
  453. self.params['bgcolor'].Enable(True)
  454. if dcmd.has_key('bgcolor') and \
  455. self.params['bgcolor']:
  456. bgcolor = dcmd['bgcolor'].strip().lower()
  457. if len(bgcolor) == 8 and \
  458. '0x' == bgcolor[:2]:
  459. colour= '#' + bgcolor[2:]
  460. self.params['bgcolor'].SetColour(colour)
  461. def IsConnected(self):
  462. """!Was successful in downloading and parsing capabilities data?
  463. """
  464. return self.is_connected
  465. def _postCapParsedEvt(self, error_msg):
  466. """!Helper function
  467. """
  468. self.capParsed.emit(error_msg=error_msg)
  469. def CreateCmd(self):
  470. """!Create d.wms cmd from values of panels widgets
  471. @return cmd list
  472. @return None if required widgets do not have selected/filled values.
  473. """
  474. # check required widgets
  475. if not self._checkImportValues():
  476. return None
  477. # create d.wms command
  478. lcmd = self.ws_cmdl
  479. lcmd = ['d.wms'] + lcmd
  480. layers="layers="
  481. styles='styles='
  482. first = True
  483. # WMS standard - first layer in params is most bottom...
  484. # therefore layers order need to be reversed
  485. for layer in reversed(self.sel_layers):
  486. if not first:
  487. layers += ','
  488. styles += ','
  489. first = False
  490. layers += layer['name']
  491. if layer['style'] is not None:
  492. styles += layer['style']['name']
  493. lcmd.append(layers)
  494. lcmd.append(styles)
  495. if 'format' not in self.drv_props['ignored_params']:
  496. i_format = self.params['format'].GetSelection()
  497. lcmd.append("format=%s" % self.formats_list[i_format])
  498. if 'srs' not in self.drv_props['ignored_params']:
  499. i_srs = self.params['srs'].GetSelection()
  500. epsg_num = int(self.projs_list[i_srs].split(':')[1])
  501. lcmd.append("srs=%s" % epsg_num)
  502. for k in ['maxcols', 'maxrows', 'urlparams']:
  503. lcmd.append(k + '=' + str(self.params[k].GetValue()))
  504. i_method = self.params['method'].GetSelection()
  505. lcmd.append('method=' + self.reproj_methods[i_method])
  506. if not 'o' in self.drv_props['ignored_flags'] and \
  507. self.flags['o'].IsChecked():
  508. lcmd.append('-o')
  509. c = self.params['bgcolor'].GetColour()
  510. hex_color = wx.Colour(c[0], c[1], c[2]).GetAsString(wx.C2S_HTML_SYNTAX)
  511. lcmd.append("bgcolor=" + '0x' + hex_color[1:])
  512. lcmd.append("map=" + self.o_layer_name)
  513. return lcmd
  514. def OnListSelChanged(self, event):
  515. """!Update widgets according to selected layer in list.
  516. """
  517. curr_sel_ls = self.list.GetSelectedLayers()
  518. # update self.sel_layers (selected layer list)
  519. if 'WMS' in self.ws:
  520. for sel_l in self.sel_layers[:]:
  521. if sel_l not in curr_sel_ls:
  522. self.sel_layers.remove(sel_l)
  523. for l in curr_sel_ls:
  524. if l not in self.sel_layers:
  525. self.sel_layers.append(l)
  526. self._updateLayerOrderList()
  527. else:
  528. self.sel_layers = curr_sel_ls
  529. # update projection
  530. self.projs_list = []
  531. projs_list = []
  532. intersect_proj = []
  533. first = True
  534. for l in curr_sel_ls:
  535. layer_projs = l['cap_intf_l'].GetLayerData('srs')
  536. if first:
  537. projs_list = layer_projs
  538. first = False
  539. continue
  540. projs_list = set(projs_list).intersection(layer_projs)
  541. if 'srs' not in self.drv_props['ignored_params']:
  542. for proj in projs_list:
  543. proj_spl = proj.strip().split(':')
  544. if proj_spl[0].strip().lower() in self.drv_info.GetSrs():
  545. try:
  546. int(proj_spl[1])
  547. self.projs_list.append(proj)
  548. except ValueError, IndexError:
  549. continue
  550. cur_sel = self.params['srs'].GetStringSelection()
  551. self.projs_list = sorted(self.projs_list)
  552. self.params['srs'].SetItems(self.projs_list)
  553. if cur_sel:
  554. self.params['srs'].SetStringSelection(cur_sel)
  555. else:
  556. try:
  557. i = self.projs_list.index('EPSG:4326')
  558. self.params['srs'].SetSelection(i)
  559. except ValueError:
  560. if len(self.projs_list) > 0:
  561. self.params['srs'].SetSelection(0)
  562. # update format
  563. if 'WMS' not in self.ws and \
  564. 'format' not in self.drv_props['ignored_params']:
  565. self.formats_list = []
  566. cur_sel = None
  567. if self.params['format'] is not None:
  568. cur_sel = self.params['format'].GetStringSelection()
  569. if len(curr_sel_ls) > 0:
  570. self.formats_list = sorted(self._getFormats(curr_sel_ls[0]['cap_intf_l']))
  571. self._updateFormatRadioBox(self.formats_list)
  572. if cur_sel:
  573. self.params['format'].SetStringSelection(cur_sel)
  574. else:
  575. self._setDefaultFormatVal()
  576. self.Layout()
  577. def _setDefaultFormatVal(self):
  578. """!Set default format value.
  579. """
  580. try:
  581. i = self.formats_list.index('png')
  582. self.params['format'].SetSelection(i)
  583. except ValueError:
  584. pass
  585. def _updateFormatRadioBox(self, formats_list):
  586. """!Helper function
  587. """
  588. if self.params['format'] is not None:
  589. self.req_page_sizer.Detach(self.params['format'])
  590. self.params['format'].Destroy()
  591. if len(self.formats_list) > 0:
  592. self.params['format'] = wx.RadioBox(parent = self.req_page_panel, id = wx.ID_ANY,
  593. label = _("Source image format"), pos = wx.DefaultPosition,
  594. choices = formats_list, majorDimension = 4,
  595. style = wx.RA_SPECIFY_COLS)
  596. self.source_sizer.Insert(item = self.params['format'], before = 2,
  597. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  598. def _getFormats(self, layer = None):
  599. """!Get formats
  600. WMS has formats defined generally for whole cap.
  601. In WMTS and NASA OnEarh formats are defined for layer.
  602. """
  603. formats_label = []
  604. if layer is None:
  605. formats_list = self.cap.GetFormats()
  606. else:
  607. formats_list = layer.GetLayerData('format')
  608. for frmt in formats_list:
  609. frmt = frmt.strip()
  610. label = self.drv_info.GetFormatLabel(frmt)
  611. if label:
  612. formats_label.append(label)
  613. return formats_label
  614. def _checkImportValues(self,):
  615. """!Check if required widgets are selected/filled
  616. """
  617. warning_str = ""
  618. show_war = False
  619. if not self.list or not self.list.GetSelectedLayers():
  620. warning_str += _("Select layer in layer list.\n")
  621. show_war = True
  622. if self.params['format'] is not None and \
  623. self.params['format'].GetSelection() == -1:
  624. warning_str += _("Select source image format.\n")
  625. show_war = True
  626. if self.params['srs'] is not None and \
  627. self.params['srs'].GetSelection() == -1:
  628. warning_str += _("Select source projection.\n")
  629. show_war = True
  630. if not self.o_layer_name:
  631. warning_str += _("Choose output layer name.\n")
  632. show_war = True
  633. if show_war:
  634. GMessage(parent = self.parent,
  635. message = warning_str)
  636. return False
  637. return True
  638. def SetOutputLayerName(self, name):
  639. """!Set name of layer to be added to layer tree
  640. """
  641. self.o_layer_name = name
  642. def GetCapFile(self):
  643. """!Get path to file where capabilities are saved
  644. """
  645. return self.cap_file
  646. def GetWebService(self):
  647. """!Get web service
  648. """
  649. return self.ws
  650. class LayersList(TreeListCtrl, listmix.ListCtrlAutoWidthMixin):
  651. def __init__(self, parent, web_service, style, pos=wx.DefaultPosition):
  652. """!List of layers and styles available in capabilities file
  653. """
  654. self.parent = parent
  655. self.ws = web_service
  656. TreeListCtrl.__init__(self, parent = parent, id = wx.ID_ANY, style = style)
  657. # setup mixins
  658. listmix.ListCtrlAutoWidthMixin.__init__(self)
  659. if self.ws != 'OnEarth':
  660. self.AddColumn(_('Name'))
  661. self.AddColumn(_('Type'))
  662. else:
  663. self.AddColumn(_('Layer name'))
  664. self.SetMainColumn(0) # column with the tree
  665. self.setResizeColumn(0)
  666. self.root = None
  667. self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnListSelChanging)
  668. def LoadData(self, cap = None):
  669. """!Load data into list
  670. """
  671. # detete first all items
  672. self.DeleteAllItems()
  673. if not cap:
  674. return
  675. def AddLayerChildrenToTree(parent_layer, parent_item):
  676. """!Recursive function which adds all capabilities layers/styles to the LayersList.
  677. """
  678. def gettitle(layer):
  679. """!Helper function"""
  680. if layer.GetLayerData('title') is not None:
  681. layer_title = layer.GetLayerData('title')
  682. elif layer.GetLayerData('name') is not None:
  683. layer_title = layer.GetLayerData('name')
  684. else:
  685. layer_title = str(layer.GetId())
  686. return layer_title
  687. def addlayer(layer, item):
  688. if self.ws != 'OnEarth':
  689. self.SetItemText(item, _('layer'), 1)
  690. styles = layer.GetLayerData('styles')
  691. def_st = None
  692. for st in styles:
  693. if st['name']:
  694. style_name = st['name']
  695. else:
  696. continue
  697. if st['title']:
  698. style_name = st['title']
  699. if st['isDefault']:
  700. def_st = st
  701. style_item = self.AppendItem(item, style_name)
  702. if self.ws != 'OnEarth':
  703. self.SetItemText(style_item, _('style'), 1)
  704. self.SetPyData(style_item, {'type' : 'style',
  705. 'layer' : layer, # it is parent layer of style
  706. 'style' : st})
  707. self.SetPyData(item, {'type' : 'layer', # is it layer or style?
  708. 'layer' : layer, # Layer instance from web_services.cap_interface
  709. 'style' : def_st}) # layer can have assigned default style
  710. if parent_layer is None:
  711. parent_layer = cap.GetRootLayer()
  712. layer_title = gettitle(parent_layer)
  713. parent_item = self.AddRoot(layer_title)
  714. addlayer(parent_layer, parent_item)
  715. for layer in parent_layer.GetChildren():
  716. item = self.AppendItem(parent_item, gettitle(layer))
  717. addlayer(layer, item)
  718. AddLayerChildrenToTree(layer, item)
  719. AddLayerChildrenToTree(None, None)
  720. #self.ExpandAll(self.GetRootItem())
  721. def GetSelectedLayers(self):
  722. """!Get selected layers/styles in LayersList
  723. @return dict with these items:
  724. 'name' : layer name used for request
  725. if it is style, it is name of parent layer
  726. 'title' : layer title
  727. 'style' : {'name' : 'style name', title : 'style title'}
  728. 'cap_intf_l' : *Layer instance from web_services.cap_interface
  729. """
  730. sel_layers = self.GetSelections()
  731. sel_layers_dict = []
  732. for s in sel_layers:
  733. try:
  734. layer = self.GetPyData(s)['layer']
  735. except ValueError:
  736. continue
  737. sel_layers_dict.append({
  738. 'name' : layer.GetLayerData('name'),
  739. 'title' : layer.GetLayerData('title'),
  740. 'style' : self.GetPyData(s)['style'],
  741. 'cap_intf_l' : layer
  742. })
  743. return sel_layers_dict
  744. def OnListSelChanging(self, event):
  745. """!Do not allow to select items, which cannot be requested from server.
  746. """
  747. def _selectRequestableChildren(item, list_to_check, items_to_sel):
  748. child_item, cookie = self.GetFirstChild(item)
  749. while child_item.IsOk():
  750. if self.GetPyData(child_item)['layer'].IsRequestable() \
  751. and not self.IsSelected(child_item):
  752. items_to_sel.append(child_item)
  753. elif not self.GetPyData(child_item)['layer'].IsRequestable():
  754. list_to_check.append(child_item)
  755. child_item, cookie = self.GetNextChild(item, cookie)
  756. cur_item = event.GetItem()
  757. if not self.GetPyData(cur_item)['layer'].IsRequestable():
  758. event.Veto()
  759. if not self.HasFlag(wx.TR_MULTIPLE):
  760. return
  761. items_to_chck = []
  762. items_to_sel = []
  763. chck_item = cur_item
  764. while True:
  765. _selectRequestableChildren(chck_item, items_to_chck, items_to_sel)
  766. if items_to_chck:
  767. chck_item = items_to_chck.pop()
  768. else:
  769. break
  770. while items_to_sel:
  771. self.SelectItem(items_to_sel.pop(), unselect_others=False)
  772. def GetItemCount(self):
  773. """!Required for listmix.ListCtrlAutoWidthMixin
  774. """
  775. return 0
  776. def GetCountPerPage(self):
  777. """!Required for listmix.ListCtrlAutoWidthMixin
  778. """
  779. return 0
  780. def SelectLayers(self, l_st_list):
  781. """!Select layers/styles in LayersList
  782. @param l_st_list - [{style : 'style_name', layer : 'layer_name'}, ...]
  783. @return items from l_st_list which were not found
  784. """
  785. def checknext(item, l_st_list, items_to_sel):
  786. def compare(item, l_name, st_name):
  787. it_l_name = self.GetPyData(item)['layer'].GetLayerData('name')
  788. it_st = self.GetPyData(item)['style']
  789. it_type = self.GetPyData(item)['type']
  790. if it_l_name == l_name and \
  791. ( (not it_st and not st_name) or \
  792. (it_st and it_st['name'] == st_name and it_type == 'style')):
  793. return True
  794. return False
  795. for i, l_st in enumerate(l_st_list):
  796. l_name = l_st['layer']
  797. st_name = l_st['style']
  798. if compare(item, l_name, st_name):
  799. items_to_sel[i] = [item, l_st]
  800. break
  801. if len(items_to_sel) == len(l_st_list):
  802. item = self.GetNext(item)
  803. if not item.IsOk():
  804. return
  805. checknext(item, l_st_list, items_to_sel)
  806. self.UnselectAll()
  807. l_st_list = deepcopy(l_st_list)
  808. root_item = self.GetRootItem()
  809. items_to_sel = [None] * len(l_st_list)
  810. checknext(root_item, l_st_list, items_to_sel)
  811. # items are selected according to position in l_st_list
  812. # to be added to Layers order list in right order
  813. for i in items_to_sel:
  814. if not i:
  815. continue
  816. item, l_st = i
  817. un_o = True
  818. if self.HasFlag(wx.TR_MULTIPLE):
  819. un_o = False
  820. self.SelectItem(item, unselect_others=un_o)
  821. l_st_list.remove(l_st)
  822. return l_st_list