widgets.py 35 KB

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