~ubuntu-branches/ubuntu/wily/grass/wily

« back to all changes in this revision

Viewing changes to gui/wxpython/web_services/dialogs.py

Tags: 7.0.0~rc1+ds1-1~exp1
* New upstream release candidate.
* Repack upstream tarball, remove precompiled Python objects.
* Add upstream metadata.
* Update gbp.conf and Vcs-Git URL to use the experimental branch.
* Update watch file for GRASS 7.0.
* Drop build dependencies for Tcl/Tk, add build dependencies:
  python-numpy, libnetcdf-dev, netcdf-bin, libblas-dev, liblapack-dev
* Update Vcs-Browser URL to use cgit instead of gitweb.
* Update paths to use grass70.
* Add configure options: --with-netcdf, --with-blas, --with-lapack,
  remove --with-tcltk-includes.
* Update patches for GRASS 7.
* Update copyright file, changes:
  - Update copyright years
  - Group files by license
  - Remove unused license sections
* Add patches for various typos.
* Fix desktop file with patch instead of d/rules.
* Use minimal dh rules.
* Bump Standards-Version to 3.9.6, no changes.
* Use dpkg-maintscript-helper to replace directories with symlinks.
  (closes: #776349)
* Update my email to use @debian.org address.

Show diffs side-by-side

added added

removed removed

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