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

« back to all changes in this revision

Viewing changes to gui/wxpython/mapdisp/frame.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 mapdisp.frame
 
3
 
 
4
@brief Map display with toolbar for various display management
 
5
functions, and additional toolbars (vector digitizer, 3d view).
 
6
 
 
7
Can be used either from Layer Manager or as d.mon backend.
 
8
 
 
9
Classes:
 
10
 - mapdisp::MapFrame
 
11
 
 
12
(C) 2006-2014 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 Michael Barton
 
18
@author Jachym Cepicky
 
19
@author Martin Landa <landa.martin gmail.com>
 
20
@author Vaclav Petras <wenzeslaus gmail.com> (SingleMapFrame, handlers support)
 
21
@author Anna Kratochvilova <kratochanna gmail.com> (SingleMapFrame)
 
22
@author Stepan Turek <stepan.turek seznam.cz> (handlers support)
 
23
"""
 
24
 
 
25
import os
 
26
import sys
 
27
import copy
 
28
 
 
29
from core import globalvar
 
30
import wx
 
31
import wx.aui
 
32
 
 
33
from core.render        import Map
 
34
from vdigit.toolbars    import VDigitToolbar
 
35
from mapdisp.toolbars   import MapToolbar, NvizIcons
 
36
from mapdisp.gprint     import PrintOptions
 
37
from core.gcmd          import GError, GMessage, RunCommand
 
38
from dbmgr.dialogs      import DisplayAttributesDialog
 
39
from core.utils         import ListOfCatsToRange, GetLayerNameFromCmd, _
 
40
from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
 
41
from core.debug         import Debug
 
42
from core.settings      import UserSettings
 
43
from gui_core.mapdisp   import SingleMapFrame
 
44
from mapwin.base import MapWindowProperties
 
45
from gui_core.query     import QueryDialog, PrepareQueryResults
 
46
from mapwin.buffered import BufferedMapWindow
 
47
from mapwin.decorations import TextLayerDialog, \
 
48
    LegendController, BarscaleController, ArrowController
 
49
from modules.histogram  import HistogramFrame
 
50
from wxplot.histogram   import HistogramPlotFrame
 
51
from wxplot.profile     import ProfileFrame
 
52
from wxplot.scatter     import ScatterFrame
 
53
from mapwin.analysis import ProfileController, MeasureDistanceController, \
 
54
    MeasureAreaController
 
55
from gui_core.forms import GUI
 
56
from core.giface import Notification
 
57
 
 
58
from mapdisp import statusbar as sb
 
59
 
 
60
import grass.script as grass
 
61
 
 
62
from grass.pydispatch.signal import Signal
 
63
 
 
64
 
 
65
class MapFrame(SingleMapFrame):
 
66
    """Main frame for map display window. Drawing takes place in
 
67
    child double buffered drawing window.
 
68
    """
 
69
    def __init__(self, parent, giface, title = _("GRASS GIS - Map display"),
 
70
                 toolbars = ["map"], tree = None, notebook = None, lmgr = None,
 
71
                 page = None, Map = Map(), auimgr = None, name = 'MapWindow', **kwargs):
 
72
        """Main map display window with toolbars, statusbar and
 
73
        2D map window, 3D map window and digitizer.
 
74
        
 
75
        :param toolbars: array of activated toolbars, e.g. ['map', 'digit']
 
76
        :param tree: reference to layer tree
 
77
        :param notebook: control book ID in Layer Manager
 
78
        :param lmgr: Layer Manager
 
79
        :param page: notebook page with layer tree
 
80
        :param map: instance of render.Map
 
81
        :param auimgs: AUI manager
 
82
        :param name: frame name
 
83
        :param kwargs: wx.Frame attributes
 
84
        """
 
85
        SingleMapFrame.__init__(self, parent = parent, title = title,
 
86
                              Map = Map, auimgr = auimgr, name = name, **kwargs)
 
87
        
 
88
        self._giface = giface
 
89
        # Layer Manager object
 
90
        # need by GLWindow (a lot), VDigitWindow (a little bit)
 
91
        self._layerManager = lmgr
 
92
        # Layer Manager layer tree object
 
93
        # used for VDigit toolbar and window and GLWindow
 
94
        self.tree = tree
 
95
        # Notebook page holding the layer tree
 
96
        # used only in OnCloseWindow
 
97
        self.page = page
 
98
        # Layer Manager layer tree notebook
 
99
        # used only in OnCloseWindow
 
100
        self.layerbook = notebook
 
101
 
 
102
        # Emitted when starting (switching to) 3D mode.
 
103
        # Parameter firstTime specifies if 3D was already actived.
 
104
        self.starting3dMode = Signal("MapFrame.starting3dMode")
 
105
 
 
106
        # Emitted when ending (switching from) 3D mode.
 
107
        self.ending3dMode = Signal("MapFrame.ending3dMode")
 
108
 
 
109
        # properties are shared in other objects, so defining here
 
110
        self.mapWindowProperties = MapWindowProperties()
 
111
        self.mapWindowProperties.setValuesFromUserSettings()
 
112
 
 
113
        #
 
114
        # Add toolbars
 
115
        #
 
116
        for toolb in toolbars:
 
117
            self.AddToolbar(toolb)
 
118
 
 
119
        #
 
120
        # Add statusbar
 
121
        #
 
122
        
 
123
        # items for choice
 
124
        self.statusbarItems = [sb.SbCoordinates,
 
125
                               sb.SbRegionExtent,
 
126
                               sb.SbCompRegionExtent,
 
127
                               sb.SbShowRegion,
 
128
                               sb.SbAlignExtent,
 
129
                               sb.SbResolution,
 
130
                               sb.SbDisplayGeometry,
 
131
                               sb.SbMapScale,
 
132
                               sb.SbGoTo,
 
133
                               sb.SbProjection]
 
134
                            
 
135
        self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
 
136
                                           sb.SbDisplayGeometry,
 
137
                                           sb.SbShowRegion,
 
138
                                           sb.SbResolution,
 
139
                                           sb.SbMapScale)
 
140
        
 
141
        # create statusbar and its manager
 
142
        statusbar = self.CreateStatusBar(number = 4, style = 0)
 
143
        if globalvar.wxPython3:
 
144
            statusbar.SetMinHeight(24)
 
145
        statusbar.SetStatusWidths([-5, -2, -1, -1])
 
146
        self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
 
147
        
 
148
        # fill statusbar manager
 
149
        self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
 
150
        self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
 
151
        sbRender = sb.SbRender(self, statusbar = statusbar, position = 3)
 
152
        self.statusbarManager.AddStatusbarItem(sbRender)
 
153
        
 
154
        self.statusbarManager.Update()
 
155
        
 
156
        #
 
157
        self.Map.updateProgress.connect(self.statusbarManager.SetProgress)
 
158
 
 
159
        # init decoration objects
 
160
        self.decorations = {}
 
161
        self.legend = LegendController(self.Map, self._giface)
 
162
        self.barscale = BarscaleController(self.Map, self._giface)
 
163
        self.arrow = ArrowController(self.Map, self._giface)
 
164
        self.decorations[self.legend.id] = self.legend
 
165
        self.decorations[self.barscale.id] = self.barscale
 
166
        self.decorations[self.arrow.id] = self.arrow
 
167
 
 
168
        self.mapWindowProperties.autoRenderChanged.connect(
 
169
            lambda value:
 
170
            self.OnRender(None) if value else None)
 
171
 
 
172
        #
 
173
        # Init map display (buffered DC & set default cursor)
 
174
        #
 
175
        self.MapWindow2D = BufferedMapWindow(self, giface = self._giface,
 
176
                                             Map=self.Map,
 
177
                                             properties=self.mapWindowProperties,
 
178
                                             overlays=self.decorations)
 
179
        self.MapWindow2D.mapQueried.connect(self.Query)
 
180
        self.MapWindow2D.overlayActivated.connect(self._activateOverlay)
 
181
        self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
 
182
        self.MapWindow2D.overlayHidden.connect(self._hideOverlay)
 
183
        for overlay in (self.legend, self.barscale, self.arrow):
 
184
            overlay.overlayChanged.connect(lambda: self.MapWindow2D.UpdateMap(render=False, renderVector=False))
 
185
        self._setUpMapWindow(self.MapWindow2D)
 
186
 
 
187
        self.MapWindow2D.mouseHandlerUnregistered.connect(self.ResetPointer)
 
188
 
 
189
        self.MapWindow2D.InitZoomHistory()
 
190
        self.MapWindow2D.zoomChanged.connect(self.StatusbarUpdate)
 
191
 
 
192
        self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
 
193
        # default is 2D display mode
 
194
        self.MapWindow = self.MapWindow2D
 
195
        self.MapWindow.SetNamedCursor('default')
 
196
        # used by vector digitizer
 
197
        self.MapWindowVDigit = None
 
198
        # used by Nviz (3D display mode)
 
199
        self.MapWindow3D = None 
 
200
 
 
201
        #
 
202
        # initialize region values
 
203
        #
 
204
        self._initMap(Map = self.Map) 
 
205
 
 
206
        self.toolbars['map'].SelectDefault()
 
207
        #
 
208
        # Bind various events
 
209
        #
 
210
        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
 
211
        self.Bind(wx.EVT_SIZE,     self.OnSize)
 
212
        
 
213
        #
 
214
        # Update fancy gui style
 
215
        #
 
216
        self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
 
217
                          Dockable(False).BestSize((-1,-1)).Name('2d').
 
218
                          CloseButton(False).DestroyOnClose(True).
 
219
                          Layer(0))
 
220
        self._mgr.Update()
 
221
 
 
222
        #
 
223
        # Init print module and classes
 
224
        #
 
225
        self.printopt = PrintOptions(self, self.MapWindow)
 
226
 
 
227
        #
 
228
        # Re-use dialogs
 
229
        #
 
230
        self.dialogs = {}
 
231
        self.dialogs['attributes'] = None
 
232
        self.dialogs['category'] = None
 
233
        self.dialogs['vnet'] = None
 
234
        self.dialogs['query'] = None
 
235
 
 
236
        # initialize layers to query (d.what.vect/rast)
 
237
        self._vectQueryLayers = []
 
238
        self._rastQueryLayers = []
 
239
 
 
240
        self.measureController = None
 
241
 
 
242
    def GetMapWindow(self):
 
243
        return self.MapWindow
 
244
 
 
245
    def SetTitleNumber(self, displayId=1):
 
246
        """Set map display title"""
 
247
        try:
 
248
            grassVersion = grass.version()['version']
 
249
        except KeyError:
 
250
            sys.stderr.write(_("Unable to get GRASS version\n"))
 
251
            grassVersion = "?"
 
252
 
 
253
        gisenv = grass.gisenv()
 
254
        title = _("GRASS GIS %(version)s Map Display: %(id)s  - Location: %(loc)s@%(mapset)s") % \
 
255
            {'version': grassVersion,
 
256
             'id': str(displayId),
 
257
             'loc': gisenv["LOCATION_NAME"],
 
258
             'mapset': gisenv["MAPSET"]}
 
259
 
 
260
        self.SetTitle(title)
 
261
 
 
262
    def _addToolbarVDigit(self):
 
263
        """Add vector digitizer toolbar
 
264
        """
 
265
        from vdigit.main import haveVDigit, VDigit
 
266
        
 
267
        if not haveVDigit:
 
268
            from vdigit import errorMsg
 
269
            
 
270
            self.toolbars['map'].combo.SetValue(_("2D view"))
 
271
            
 
272
            GError(_("Unable to start wxGUI vector digitizer.\n"
 
273
                     "Details: %s") % errorMsg, parent = self)
 
274
            return
 
275
 
 
276
        if not self.MapWindowVDigit:
 
277
            from vdigit.mapwindow import VDigitWindow
 
278
            self.MapWindowVDigit = VDigitWindow(parent=self, giface=self._giface,
 
279
                                                properties=self.mapWindowProperties,
 
280
                                                Map=self.Map, tree=self.tree,
 
281
                                                lmgr=self._layerManager,
 
282
                                                overlays=self.decorations)
 
283
            self._setUpMapWindow(self.MapWindowVDigit)
 
284
            self.MapWindowVDigit.digitizingInfo.connect(
 
285
                lambda text:
 
286
                self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(text))
 
287
            self.MapWindowVDigit.digitizingInfoUnavailable.connect(
 
288
                lambda:
 
289
                self.statusbarManager.statusbarItems['coordinates'].SetAdditionalInfo(None))
 
290
            self.MapWindowVDigit.Show()
 
291
            self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
 
292
                          Dockable(False).BestSize((-1,-1)).Name('vdigit').
 
293
                          CloseButton(False).DestroyOnClose(True).
 
294
                          Layer(0))
 
295
        
 
296
        self._switchMapWindow(self.MapWindowVDigit)
 
297
        
 
298
        if self._mgr.GetPane('2d').IsShown():
 
299
            self._mgr.GetPane('2d').Hide()
 
300
        elif self._mgr.GetPane('3d').IsShown():
 
301
            self._mgr.GetPane('3d').Hide()
 
302
        self._mgr.GetPane('vdigit').Show()
 
303
        self.toolbars['vdigit'] = VDigitToolbar(parent=self, toolSwitcher=self._toolSwitcher,
 
304
                                                MapWindow = self.MapWindow,
 
305
                                                digitClass=VDigit, giface=self._giface)
 
306
        self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
 
307
        
 
308
        self._mgr.AddPane(self.toolbars['vdigit'],
 
309
                          wx.aui.AuiPaneInfo().
 
310
                          Name("vdigittoolbar").Caption(_("Vector Digitizer Toolbar")).
 
311
                          ToolbarPane().Top().Row(1).
 
312
                          LeftDockable(False).RightDockable(False).
 
313
                          BottomDockable(False).TopDockable(True).
 
314
                          CloseButton(False).Layer(2).
 
315
                          BestSize((self.toolbars['vdigit'].GetBestSize())))
 
316
        # change mouse to draw digitized line
 
317
        self.MapWindow.mouse['box'] = "point"
 
318
        self.MapWindow.zoomtype     = 0
 
319
        self.MapWindow.pen          = wx.Pen(colour = 'red',   width = 2, style = wx.SOLID)
 
320
        self.MapWindow.polypen      = wx.Pen(colour = 'green', width = 2, style = wx.SOLID)
 
321
 
 
322
    def AddNviz(self):
 
323
        """Add 3D view mode window
 
324
        """
 
325
        from nviz.main import haveNviz, GLWindow, errorMsg
 
326
        
 
327
        # check for GLCanvas and OpenGL
 
328
        if not haveNviz:
 
329
            self.toolbars['map'].combo.SetValue(_("2D view"))
 
330
            GError(parent = self,
 
331
                   message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
 
332
                               "was not found or loaded properly.\n"
 
333
                               "Switching back to 2D display mode.\n\nDetails: %s" % errorMsg))
 
334
            return
 
335
 
 
336
        # here was disabling 3D for other displays, now done on starting3dMode
 
337
 
 
338
        self.toolbars['map'].Enable2D(False)
 
339
        # add rotate tool to map toolbar
 
340
        self.toolbars['map'].InsertTool((('rotate', NvizIcons['rotate'],
 
341
                                          self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
 
342
        self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
 
343
                                          tool=self.toolbars['map'].rotate)
 
344
        self.toolbars['map'].InsertTool((('flyThrough', NvizIcons['flyThrough'],
 
345
                                          self.OnFlyThrough, wx.ITEM_CHECK, 8),))
 
346
        self._toolSwitcher.AddToolToGroup(group='mouseUse', toolbar=self.toolbars['map'],
 
347
                                          tool=self.toolbars['map'].flyThrough)
 
348
        self.toolbars['map'].ChangeToolsDesc(mode2d = False)
 
349
        # update status bar
 
350
        
 
351
        self.statusbarManager.HideStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
 
352
        self.statusbarManager.SetMode(0)
 
353
        
 
354
        # erase map window
 
355
        self.MapWindow.EraseMap()
 
356
        
 
357
        self._giface.WriteCmdLog(_("Starting 3D view mode..."), notification=Notification.HIGHLIGHT)
 
358
        self.SetStatusText(_("Please wait, loading data..."), 0)
 
359
        
 
360
        # create GL window
 
361
        if not self.MapWindow3D:
 
362
            self.MapWindow3D = GLWindow(self, giface = self._giface, id = wx.ID_ANY, frame = self,
 
363
                                        Map = self.Map, tree = self.tree, lmgr = self._layerManager)
 
364
            self._setUpMapWindow(self.MapWindow3D)
 
365
            self.MapWindow3D.mapQueried.connect(self.Query)
 
366
            self._switchMapWindow(self.MapWindow3D)
 
367
            self.MapWindow.SetNamedCursor('default')
 
368
 
 
369
            # here was AddNvizTools in lmgr
 
370
            self.starting3dMode.emit(firstTime=True)
 
371
 
 
372
            # switch from MapWindow to MapWindowGL
 
373
            self._mgr.GetPane('2d').Hide()
 
374
            self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
 
375
                              Dockable(False).BestSize((-1,-1)).Name('3d').
 
376
                              CloseButton(False).DestroyOnClose(True).
 
377
                              Layer(0))
 
378
            
 
379
            self.MapWindow3D.Show()
 
380
            self.MapWindow3D.ResetViewHistory()            
 
381
            self.MapWindow3D.UpdateView(None)
 
382
            self.MapWindow3D.overlayActivated.connect(self._activateOverlay)
 
383
            self.MapWindow3D.overlayHidden.connect(self._hideOverlay)
 
384
            self.legend.overlayChanged.connect(self.MapWindow3D.UpdateOverlays)
 
385
        else:
 
386
            self._switchMapWindow(self.MapWindow3D)
 
387
            os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True, windres3 = True)
 
388
            self.MapWindow3D.GetDisplay().Init()
 
389
            del os.environ['GRASS_REGION']
 
390
            
 
391
            # switch from MapWindow to MapWindowGL
 
392
            self._mgr.GetPane('2d').Hide()
 
393
            self._mgr.GetPane('3d').Show()
 
394
 
 
395
            # here was AddNvizTools in lmgr and updating of pages
 
396
            self.starting3dMode.emit(firstTime=False)
 
397
 
 
398
            self.MapWindow3D.ResetViewHistory()
 
399
 
 
400
        self._giface.updateMap.disconnect(self.MapWindow2D.UpdateMap)
 
401
        self._giface.updateMap.connect(self.MapWindow3D.UpdateMap)
 
402
        self.MapWindow3D.overlays = self.MapWindow2D.overlays
 
403
        self.MapWindow3D.textdict = self.MapWindow2D.textdict
 
404
        # update overlays needs to be called after because getClientSize
 
405
        # is called during update and it must give reasonable values
 
406
        wx.CallAfter(self.MapWindow3D.UpdateOverlays)
 
407
        
 
408
        self.SetStatusText("", 0)
 
409
        self._mgr.Update()
 
410
 
 
411
    def Disable3dMode(self):
 
412
        """Disables 3D mode (NVIZ) in user interface."""
 
413
        # TODO: this is broken since item is removed but switch is drived by index
 
414
        if '3D' in self.toolbars['map'].combo.GetString(1):
 
415
            self.toolbars['map'].combo.Delete(1)
 
416
 
 
417
    def RemoveNviz(self):
 
418
        """Restore 2D view"""
 
419
        try:
 
420
            self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
 
421
            self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
 
422
        except AttributeError:
 
423
            pass
 
424
        
 
425
        # update status bar
 
426
        self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
 
427
        self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
 
428
                                                       key = 'statusbarMode',
 
429
                                                       subkey = 'selection'))
 
430
        self.SetStatusText(_("Please wait, unloading data..."), 0)
 
431
        # unloading messages from library cause highlight anyway
 
432
        self._giface.WriteCmdLog(_("Switching back to 2D view mode..."),
 
433
                                 notification=Notification.NO_NOTIFICATION)
 
434
        if self.MapWindow3D:
 
435
            self.MapWindow3D.OnClose(event = None)
 
436
        # switch from MapWindowGL to MapWindow
 
437
        self._mgr.GetPane('2d').Show()
 
438
        self._mgr.GetPane('3d').Hide()
 
439
 
 
440
        self._switchMapWindow(self.MapWindow2D)
 
441
        # here was RemoveNvizTools form lmgr
 
442
        self.ending3dMode.emit()
 
443
        try:
 
444
            self.MapWindow2D.overlays = self.MapWindow3D.overlays
 
445
            self.MapWindow2D.textdict = self.MapWindow3D.textdict
 
446
        except AttributeError:
 
447
            pass
 
448
        # TODO: here we end because self.MapWindow3D is None for a while
 
449
        self._giface.updateMap.disconnect(self.MapWindow3D.UpdateMap)
 
450
        self._giface.updateMap.connect(self.MapWindow2D.UpdateMap)
 
451
        self.legend.overlayChanged.disconnect(self.MapWindow3D.UpdateOverlays)
 
452
 
 
453
        self.MapWindow.UpdateMap()
 
454
        self._mgr.Update()
 
455
        self.GetMapToolbar().SelectDefault()
 
456
        
 
457
    def AddToolbar(self, name, fixed = False):
 
458
        """Add defined toolbar to the window
 
459
 
 
460
        Currently recognized toolbars are:
 
461
         - 'map'     - basic map toolbar
 
462
         - 'vdigit'  - vector digitizer
 
463
 
 
464
        :param name: toolbar to add
 
465
        :param fixed: fixed toolbar
 
466
        """
 
467
        # default toolbar
 
468
        if name == "map":
 
469
            self.toolbars['map'] = MapToolbar(self, toolSwitcher=self._toolSwitcher)
 
470
            
 
471
            self._mgr.AddPane(self.toolbars['map'],
 
472
                              wx.aui.AuiPaneInfo().
 
473
                              Name("maptoolbar").Caption(_("Map Toolbar")).
 
474
                              ToolbarPane().Top().Name('mapToolbar').
 
475
                              LeftDockable(False).RightDockable(False).
 
476
                              BottomDockable(False).TopDockable(True).
 
477
                              CloseButton(False).Layer(2).
 
478
                              BestSize((self.toolbars['map'].GetBestSize())))
 
479
            
 
480
        # vector digitizer
 
481
        elif name == "vdigit":
 
482
            self.toolbars['map'].combo.SetValue(_("Digitize"))
 
483
            self._addToolbarVDigit()
 
484
        
 
485
        if fixed:
 
486
            self.toolbars['map'].combo.Disable()
 
487
         
 
488
        self._mgr.Update()
 
489
        
 
490
    def RemoveToolbar (self, name):
 
491
        """Removes defined toolbar from the window
 
492
 
 
493
        .. todo::
 
494
            Only hide, activate by calling AddToolbar()
 
495
        """
 
496
        # cannot hide main toolbar
 
497
        if name == "map":
 
498
            return
 
499
        
 
500
        self._mgr.DetachPane(self.toolbars[name])
 
501
        self._toolSwitcher.RemoveToolbarFromGroup('mouseUse', self.toolbars[name])
 
502
        self.toolbars[name].Destroy()
 
503
        self.toolbars.pop(name)
 
504
        
 
505
        if name == 'vdigit':
 
506
            self._mgr.GetPane('vdigit').Hide()
 
507
            self._mgr.GetPane('2d').Show()
 
508
            self._switchMapWindow(self.MapWindow2D)
 
509
            
 
510
        self.toolbars['map'].combo.SetValue(_("2D view"))
 
511
        self.toolbars['map'].Enable2D(True)
 
512
        
 
513
        self._mgr.Update()
 
514
    
 
515
    def IsPaneShown(self, name):
 
516
        """Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
 
517
        if self._mgr.GetPane(name).IsOk():
 
518
            return self._mgr.GetPane(name).IsShown()
 
519
        return False
 
520
 
 
521
    def RemoveQueryLayer(self):
 
522
        """Removes temporary map layers (queries)"""
 
523
        qlayer = self.GetMap().GetListOfLayers(name = globalvar.QUERYLAYER)
 
524
        for layer in qlayer:
 
525
            self.GetMap().DeleteLayer(layer)
 
526
 
 
527
    def OnRender(self, event):
 
528
        """Re-render map composition (each map layer)
 
529
        """
 
530
        self.RemoveQueryLayer()
 
531
        
 
532
        # deselect features in vdigit
 
533
        if self.GetToolbar('vdigit'):
 
534
            if self.MapWindow.digit:
 
535
                self.MapWindow.digit.GetDisplay().SetSelected([])
 
536
            self.MapWindow.UpdateMap(render = True, renderVector = True)
 
537
        else:
 
538
            self.MapWindow.UpdateMap(render = True)
 
539
        
 
540
        # update statusbar
 
541
        self.StatusbarUpdate()
 
542
 
 
543
    def OnPointer(self, event):
 
544
        """Pointer button clicked
 
545
        """      
 
546
        self.MapWindow.SetModePointer()
 
547
 
 
548
        if self.GetToolbar('vdigit'):
 
549
            self.toolbars['vdigit'].action['id'] = -1
 
550
            self.toolbars['vdigit'].action['desc']=''
 
551
 
 
552
    def OnRotate(self, event):
 
553
        """Rotate 3D view
 
554
        """
 
555
        self.MapWindow.mouse['use'] = "rotate"
 
556
        
 
557
        # change the cursor
 
558
        self.MapWindow.SetNamedCursor('hand')
 
559
 
 
560
    def OnFlyThrough(self, event):
 
561
        """Fly-through mode
 
562
        """
 
563
        self.MapWindow.mouse['use'] = "fly"
 
564
        
 
565
        # change the cursor
 
566
        self.MapWindow.SetNamedCursor('hand')
 
567
        self.MapWindow.SetFocus()
 
568
 
 
569
    def SaveToFile(self, event):
 
570
        """Save map to image
 
571
        """
 
572
        filetype, ltype = self._prepareSaveToFile()
 
573
        if not ltype:
 
574
            return
 
575
        
 
576
        # get size
 
577
        dlg = ImageSizeDialog(self)
 
578
        dlg.CentreOnParent()
 
579
        if dlg.ShowModal() != wx.ID_OK:
 
580
            dlg.Destroy()
 
581
            return
 
582
        width, height = dlg.GetValues()
 
583
        dlg.Destroy()
 
584
        
 
585
        # get filename
 
586
        dlg = wx.FileDialog(parent = self,
 
587
                            message = _("Choose a file name to save the image "
 
588
                                        "(no need to add extension)"),
 
589
                            wildcard = filetype,
 
590
                            style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
 
591
        
 
592
        if dlg.ShowModal() == wx.ID_OK:
 
593
            path = dlg.GetPath()
 
594
            if not path:
 
595
                dlg.Destroy()
 
596
                return
 
597
            
 
598
            base, ext = os.path.splitext(path)
 
599
            fileType = ltype[dlg.GetFilterIndex()]['type']
 
600
            extType  = ltype[dlg.GetFilterIndex()]['ext']
 
601
            if ext != extType:
 
602
                path = base + '.' + extType
 
603
            
 
604
            self.MapWindow.SaveToFile(path, fileType,
 
605
                                      width, height)
 
606
            
 
607
        dlg.Destroy()
 
608
 
 
609
    def DOutFile(self, command):
 
610
        """Saves map to image by running d.out.file from gui or d.mon.
 
611
        Command is expected to be validated by parser.        
 
612
        """
 
613
        filetype, ltype = self._prepareSaveToFile()
 
614
        if not ltype:
 
615
            return
 
616
        width, height = self.MapWindow.GetClientSize()
 
617
        for param in command[1:]:
 
618
            p, val = param.split('=')
 
619
            if p == 'format':  # must be there
 
620
                if self.IsPaneShown('3d'):
 
621
                    extType = 'ppm'
 
622
                else:
 
623
                    extType = val
 
624
            if p == 'output':  # must be there
 
625
                name = val
 
626
            elif p == 'size':
 
627
                width, height = val.split(',')
 
628
 
 
629
        base, ext = os.path.splitext(name)
 
630
        if not ext:
 
631
            name = base + '.' + extType
 
632
        elif ext[1:] != extType:
 
633
            extType = ext[1:]
 
634
 
 
635
        if self.IsPaneShown('3d'):
 
636
            bitmapType = 'ppm'
 
637
        else:
 
638
            bitmapType = wx.BITMAP_TYPE_PNG  # default type
 
639
        for each in ltype:
 
640
            if each['ext'] == extType:
 
641
                bitmapType = each['type']
 
642
                break
 
643
        self.MapWindow.SaveToFile(name, bitmapType, int(width), int(height))
 
644
 
 
645
    def DOutFileOptData(self, dcmd, layer, params, propwin):
 
646
        """Dummy function which is called when d.out.file is called
 
647
        and returns parsed and validated command which is then passed
 
648
        to DOutFile method."""
 
649
        if not dcmd:
 
650
            return
 
651
 
 
652
        self.DOutFile(dcmd)
 
653
 
 
654
    def DToRast(self, command):
 
655
        """Saves currently loaded composition of layers as a raster map.
 
656
        """
 
657
        if self.IsPaneShown('3d'):
 
658
            self._giface.WriteError(_('d.to.rast can be used only in 2D mode.'))
 
659
            return
 
660
        outputRaster = None
 
661
        overwrite = False
 
662
        for param in command[1:]:
 
663
            try:
 
664
                p, val = param.split('=')
 
665
                if p == 'output':
 
666
                    outputRaster = val
 
667
            except ValueError:
 
668
                if param.startswith('--overwrite'):
 
669
                    overwrite = True
 
670
 
 
671
        if not outputRaster:
 
672
            return
 
673
        # output file as PNG
 
674
        tmpName = 'd_to_rast_tmp'
 
675
        pngFile = grass.tempfile(create=False) + '.png'
 
676
        dOutFileCmd = ['d.out.file', 'output=' + pngFile, 'format=png']
 
677
        self.DOutFile(dOutFileCmd)
 
678
        # import back as red, green, blue rasters
 
679
        returncode, messages = RunCommand('r.in.gdal', flags='o', input=pngFile, output=tmpName,
 
680
                                          quiet=True, overwrite=overwrite, getErrorMsg=True)
 
681
        if not returncode == 0:
 
682
            self._giface.WriteError(_('Failed to run d.to.rast:\n') + messages)
 
683
            return
 
684
        # set region for composite
 
685
        grass.use_temp_region()
 
686
        returncode, messages = RunCommand('g.region', raster=tmpName + '.red',
 
687
                                          quiet=True, getErrorMsg=True)
 
688
        if not returncode == 0:
 
689
            grass.del_temp_region()
 
690
            self._giface.WriteError(_('Failed to run d.to.rast:\n') + messages)
 
691
            return
 
692
        # composite
 
693
        returncode, messages = RunCommand('r.composite', red=tmpName + '.red',
 
694
                                          green=tmpName + '.green', blue=tmpName + '.blue',
 
695
                                          output=outputRaster, quiet=True,
 
696
                                          overwrite=overwrite, getErrorMsg=True)
 
697
        grass.del_temp_region()
 
698
        RunCommand('g.remove', type='raster', flags='f', quiet=True,
 
699
                   name=[tmpName + '.red', tmpName + '.green', tmpName + '.blue'])
 
700
        if not returncode == 0:
 
701
            self._giface.WriteError(_('Failed to run d.to.rast:\n') + messages)
 
702
            grass.try_remove(pngFile)
 
703
            return
 
704
 
 
705
        # alignExtent changes only region variable
 
706
        oldRegion = self.GetMap().GetCurrentRegion().copy()
 
707
        self.GetMap().AlignExtentFromDisplay()
 
708
        region = self.GetMap().GetCurrentRegion().copy()
 
709
        self.GetMap().region.update(oldRegion)
 
710
        RunCommand('r.region', map=outputRaster, n=region['n'], s=region['s'],
 
711
                   e=region['e'], w=region['w'], quiet=True)
 
712
 
 
713
        grass.try_remove(pngFile)
 
714
 
 
715
    def DToRastOptData(self, dcmd, layer, params, propwin):
 
716
        """Dummy function which is called when d.to.rast is called
 
717
        and returns parsed and validated command which is then passed
 
718
        to DToRast method."""
 
719
        if not dcmd:
 
720
            return
 
721
 
 
722
        self.DToRast(dcmd)
 
723
 
 
724
    def _prepareSaveToFile(self):
 
725
        """Get wildcards and format extensions."""
 
726
        if self.IsPaneShown('3d'):
 
727
            filetype = "TIF file (*.tif)|*.tif|PPM file (*.ppm)|*.ppm"
 
728
            ltype = [{ 'ext' : 'tif', 'type' : 'tif' },
 
729
                     { 'ext' : 'ppm', 'type' : 'ppm' }]
 
730
        else:
 
731
            img = self.MapWindow.img
 
732
            if not img:
 
733
                GMessage(parent = self,
 
734
                         message = _("Nothing to render (empty map). Operation canceled."))
 
735
                return None, None
 
736
            filetype, ltype = GetImageHandlers(img)
 
737
        return filetype, ltype
 
738
 
 
739
    def PrintMenu(self, event):
 
740
        """
 
741
        Print options and output menu for map display
 
742
        """
 
743
        printmenu = wx.Menu()
 
744
        # Add items to the menu
 
745
        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
 
746
        printmenu.AppendItem(setup)
 
747
        self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
 
748
 
 
749
        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
 
750
        printmenu.AppendItem(preview)
 
751
        self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
 
752
 
 
753
        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
 
754
        printmenu.AppendItem(doprint)
 
755
        self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
 
756
 
 
757
        # Popup the menu.  If an item is selected then its handler
 
758
        # will be called before PopupMenu returns.
 
759
        self.PopupMenu(printmenu)
 
760
        printmenu.Destroy()
 
761
 
 
762
    def OnCloseWindow(self, event):
 
763
        """Window closed.
 
764
        Also close associated layer tree page
 
765
        """
 
766
        Debug.msg(2, "MapFrame.OnCloseWindow(): function starts")
 
767
        pgnum = None
 
768
        self.Map.Clean()
 
769
        
 
770
        # close edited map and 3D tools properly
 
771
        if self.GetToolbar('vdigit'):
 
772
            maplayer = self.toolbars['vdigit'].GetLayer()
 
773
            if maplayer:
 
774
                self.toolbars['vdigit'].OnExit()
 
775
        if self.IsPaneShown('3d'):
 
776
            self.RemoveNviz()
 
777
        
 
778
        if not self._layerManager:
 
779
            self.Destroy()
 
780
        elif self.page:
 
781
            pgnum = self.layerbook.GetPageIndex(self.page)
 
782
            if pgnum > -1:
 
783
                self.layerbook.DeletePage(pgnum)
 
784
        Debug.msg(2, "MapFrame.OnCloseWindow(): function ends")
 
785
 
 
786
    def Query(self, x, y):
 
787
        """Query selected layers. 
 
788
 
 
789
        :param x,y: coordinates
 
790
        """
 
791
        if self._vectQueryLayers or self._rastQueryLayers:
 
792
            rast = self._rastQueryLayers
 
793
            vect = self._vectQueryLayers
 
794
        else:
 
795
            layers = self._giface.GetLayerList().GetSelectedLayers(checkedOnly=False)
 
796
            rast = []
 
797
            vect = []
 
798
            for layer in layers:
 
799
                name, found = GetLayerNameFromCmd(layer.cmd)
 
800
                if not found:
 
801
                    continue
 
802
                ltype = layer.maplayer.GetType()
 
803
                if ltype == 'raster':
 
804
                    rast.append(name)
 
805
                elif ltype in ('rgb', 'his'):
 
806
                    for iname in name.split('\n'):
 
807
                        rast.append(iname)
 
808
                elif ltype in ('vector', 'thememap', 'themechart'):
 
809
                    vect.append(name)
 
810
            if vect:
 
811
                # check for vector maps open to be edited
 
812
                digitToolbar = self.GetToolbar('vdigit')
 
813
                if digitToolbar:
 
814
                    lmap = digitToolbar.GetLayer().GetName()
 
815
                    for name in vect:
 
816
                        if lmap == name:
 
817
                            self._giface.WriteWarning(_("Vector map <%s> "
 
818
                                                                      "opened for editing - skipped.") % lmap)
 
819
                            vect.remove(name)
 
820
 
 
821
        if not (rast + vect):
 
822
            GMessage(parent = self,
 
823
                     message = _('No raster or vector map layer selected for querying.'))
 
824
            return
 
825
 
 
826
        # set query snap distance for v.what at map unit equivalent of 10 pixels
 
827
        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
 
828
 
 
829
        # TODO: replace returning None by exception or so
 
830
        try:
 
831
            east, north = self.MapWindow.Pixel2Cell((x, y))
 
832
        except TypeError:
 
833
            return
 
834
 
 
835
        if not self.IsPaneShown('3d'):
 
836
            self.QueryMap(east, north, qdist, rast, vect)
 
837
        else:
 
838
            if rast:
 
839
                self.MapWindow.QuerySurface(x, y)
 
840
            if vect:
 
841
                self.QueryMap(east, north, qdist, rast = [], vect = vect)
 
842
 
 
843
    def SetQueryLayersAndActivate(self, ltype, maps):
 
844
        """Activate query mode and set layers to query.
 
845
        This method is used for querying in d.mon using d.what.rast/vect"""
 
846
        self.toolbars['map'].SelectTool(self.toolbars['map'].query)
 
847
        if ltype == 'vector':
 
848
            self._vectQueryLayers = maps
 
849
        elif ltype == 'raster':
 
850
            self._rastQueryLayers = maps
 
851
 
 
852
    def QueryMap(self, east, north, qdist, rast, vect):
 
853
        """Query raster or vector map layers by r/v.what
 
854
        
 
855
        :param east,north: coordinates
 
856
        :param qdist: query distance
 
857
        :param rast: raster map names
 
858
        :param vect: vector map names
 
859
        """
 
860
        Debug.msg(1, "QueryMap(): raster=%s vector=%s" % (','.join(rast),
 
861
                                                          ','.join(vect)))
 
862
 
 
863
        # use display region settings instead of computation region settings
 
864
        self.tmpreg = os.getenv("GRASS_REGION")
 
865
        os.environ["GRASS_REGION"] = self.Map.SetRegion(windres = False)
 
866
 
 
867
        rastQuery = []
 
868
        vectQuery = []
 
869
        if rast:
 
870
            rastQuery = grass.raster_what(map=rast, coord=(east, north))
 
871
        if vect:
 
872
            encoding = UserSettings.Get(group='atm', key='encoding', subkey='value')
 
873
            try:
 
874
                vectQuery = grass.vector_what(map=vect, coord=(east, north), distance=qdist,
 
875
                                              encoding=encoding)
 
876
            except grass.ScriptError:
 
877
                GError(parent=self,
 
878
                       message=_("Failed to query vector map(s) <{maps}>. "
 
879
                                 "Check database settings and topology.").format(maps=','.join(vect)))
 
880
        self._QueryMapDone()
 
881
        if 'Id' in vectQuery:
 
882
            self._queryHighlight(vectQuery)
 
883
 
 
884
        result = rastQuery + vectQuery
 
885
        result = PrepareQueryResults(coordinates = (east, north), result = result)
 
886
        if self.dialogs['query']:
 
887
            self.dialogs['query'].Raise()
 
888
            self.dialogs['query'].SetData(result)
 
889
        else:
 
890
            self.dialogs['query'] = QueryDialog(parent = self, data = result)
 
891
            self.dialogs['query'].Bind(wx.EVT_CLOSE, self._oncloseQueryDialog)
 
892
            self.dialogs['query'].redirectOutput.connect(self._onRedirectQueryOutput)
 
893
            self.dialogs['query'].Show()
 
894
 
 
895
    def _oncloseQueryDialog(self, event):
 
896
        self.dialogs['query'] = None
 
897
        self._vectQueryLayers = []
 
898
        self._rastQueryLayers = []
 
899
        event.Skip()
 
900
 
 
901
    def _onRedirectQueryOutput(self, output, style='log'):
 
902
        """Writes query output into console"""
 
903
        if style == 'log':
 
904
            self._giface.WriteLog(output, notification=Notification.MAKE_VISIBLE)
 
905
        elif style == 'cmd':
 
906
            self._giface.WriteCmdLog(output)
 
907
 
 
908
    def _queryHighlight(self, vectQuery):
 
909
        """Highlight category from query."""
 
910
        cats = name = None
 
911
        for res in vectQuery:
 
912
            cats = {res['Layer']: [res['Category']]}
 
913
            name = res['Map']
 
914
        try:
 
915
            qlayer = self.Map.GetListOfLayers(name = globalvar.QUERYLAYER)[0]
 
916
        except IndexError:
 
917
            qlayer = None
 
918
 
 
919
        if not (cats and name):
 
920
            if qlayer:
 
921
                self.Map.DeleteLayer(qlayer)
 
922
                self.MapWindow.UpdateMap(render = False, renderVector = False)
 
923
            return
 
924
 
 
925
        if not self.IsPaneShown('3d') and self.IsAutoRendered():
 
926
            # highlight feature & re-draw map
 
927
            if qlayer:
 
928
                qlayer.SetCmd(self.AddTmpVectorMapLayer(name, cats,
 
929
                                                        useId = False,
 
930
                                                        addLayer = False))
 
931
            else:
 
932
                qlayer = self.AddTmpVectorMapLayer(name, cats, useId = False)
 
933
            
 
934
            # set opacity based on queried layer
 
935
            # TODO fix
 
936
            # opacity = layer.maplayer.GetOpacity(float = True)
 
937
            # qlayer.SetOpacity(opacity)
 
938
            
 
939
            self.MapWindow.UpdateMap(render = False, renderVector = False)
 
940
 
 
941
    def _QueryMapDone(self):
 
942
        """Restore settings after querying (restore GRASS_REGION)
 
943
        """
 
944
        if hasattr(self, "tmpreg"):
 
945
            if self.tmpreg:
 
946
                os.environ["GRASS_REGION"] = self.tmpreg
 
947
            elif 'GRASS_REGION' in os.environ:
 
948
                del os.environ["GRASS_REGION"]
 
949
        elif 'GRASS_REGION' in os.environ:
 
950
            del os.environ["GRASS_REGION"]
 
951
        
 
952
        if hasattr(self, "tmpreg"):
 
953
            del self.tmpreg
 
954
        
 
955
    def OnQuery(self, event):
 
956
        """Query tools menu"""
 
957
        self.MapWindow.mouse['use'] = "query"
 
958
        self.MapWindow.mouse['box'] = "point"
 
959
        self.MapWindow.zoomtype = 0
 
960
        
 
961
        # change the cursor
 
962
        self.MapWindow.SetNamedCursor('cross')
 
963
        
 
964
    def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
 
965
        """Add temporal vector map layer to map composition
 
966
 
 
967
        :param name: name of map layer
 
968
        :param useId: use feature id instead of category 
 
969
        """
 
970
        # color settings from ATM
 
971
        color = UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'color')
 
972
        colorStr = str(color[0]) + ":" + \
 
973
            str(color[1]) + ":" + \
 
974
            str(color[2])
 
975
 
 
976
        # icon used in vector display and its size
 
977
        icon = ''
 
978
        size = 0
 
979
        # here we know that there is one selected layer and it is vector
 
980
        vparam = self._giface.GetLayerList().GetSelectedLayers()[0].cmd
 
981
        for p in vparam:
 
982
            if '=' in p:
 
983
                parg,pval = p.split('=', 1)
 
984
                if parg == 'icon': icon = pval
 
985
                elif parg == 'size': size = float(pval)
 
986
 
 
987
        pattern = ["d.vect",
 
988
                   "map=%s" % name,
 
989
                   "color=%s" % colorStr,
 
990
                   "fill_color=%s" % colorStr,
 
991
                   "width=%d"  % UserSettings.Get(group = 'atm', key = 'highlight', subkey = 'width')]
 
992
        if icon != '':
 
993
            pattern.append('icon=%s' % icon)
 
994
        if size > 0:
 
995
            pattern.append('size=%i' % size)
 
996
        
 
997
        if useId:
 
998
            cmd = pattern
 
999
            cmd.append('-i')
 
1000
            cmd.append('cats=%s' % str(cats))
 
1001
        else:
 
1002
            cmd = []
 
1003
            for layer in cats.keys():
 
1004
                cmd.append(copy.copy(pattern))
 
1005
                lcats = cats[layer]
 
1006
                cmd[-1].append("layer=%d" % layer)
 
1007
                cmd[-1].append("cats=%s" % ListOfCatsToRange(lcats))
 
1008
 
 
1009
        if addLayer:
 
1010
            if useId:
 
1011
                return self.Map.AddLayer(ltype = 'vector', name = globalvar.QUERYLAYER, command = cmd,
 
1012
                                         active = True, hidden = True, opacity = 1.0)
 
1013
            else:
 
1014
                return self.Map.AddLayer(ltype = 'command', name = globalvar.QUERYLAYER, command = cmd,
 
1015
                                         active = True, hidden = True, opacity = 1.0)
 
1016
        else:
 
1017
            return cmd
 
1018
 
 
1019
    def OnMeasureDistance(self, event):
 
1020
        self._onMeasure(MeasureDistanceController)
 
1021
 
 
1022
    def OnMeasureArea(self, event):
 
1023
        self._onMeasure(MeasureAreaController)
 
1024
 
 
1025
    def _onMeasure(self, controller):
 
1026
        """Starts measurement mode.
 
1027
 
 
1028
        :param controller: measurement class (MeasureDistanceController, MeasureAreaController)
 
1029
        """
 
1030
        self.measureController = controller(self._giface, mapWindow=self.GetMapWindow())
 
1031
        # assure that the mode is ended and lines are cleared whenever other tool is selected
 
1032
        self._toolSwitcher.toggleToolChanged.connect(lambda: self.measureController.Stop())
 
1033
        self.measureController.Start()
 
1034
 
 
1035
    def OnProfile(self, event):
 
1036
        """Launch profile tool
 
1037
        """
 
1038
        rasters = []
 
1039
        layers = self._giface.GetLayerList().GetSelectedLayers()
 
1040
        for layer in layers:
 
1041
            if layer.type == 'raster':
 
1042
                rasters.append(layer.maplayer.name)
 
1043
        self.Profile(rasters=rasters)
 
1044
 
 
1045
    def Profile(self, rasters=None):
 
1046
        """Launch profile tool"""
 
1047
        self.profileController = ProfileController(self._giface,
 
1048
                                                   mapWindow=self.GetMapWindow())
 
1049
        win = ProfileFrame(parent=self, rasterList=rasters,
 
1050
                           units=self.Map.projinfo['units'],
 
1051
                           controller=self.profileController)
 
1052
        win.Show()
 
1053
        # Open raster select dialog to make sure that a raster (and
 
1054
        # the desired raster) is selected to be profiled
 
1055
        win.OnSelectRaster(None)
 
1056
 
 
1057
    def OnHistogramPyPlot(self, event):
 
1058
        """Init PyPlot histogram display canvas and tools
 
1059
        """
 
1060
        raster = []
 
1061
 
 
1062
        for layer in self._giface.GetLayerList().GetSelectedLayers():
 
1063
            if layer.maplayer.GetType() == 'raster':
 
1064
                raster.append(layer.maplayer.GetName())
 
1065
 
 
1066
        win = HistogramPlotFrame(parent = self, rasterList = raster)
 
1067
        win.CentreOnParent()
 
1068
        win.Show()
 
1069
        
 
1070
    def OnScatterplot(self, event):
 
1071
        """Init PyPlot scatterplot display canvas and tools
 
1072
        """
 
1073
        raster = []
 
1074
 
 
1075
        for layer in self._giface.GetLayerList().GetSelectedLayers():
 
1076
            if layer.maplayer.GetType() == 'raster':
 
1077
                raster.append(layer.maplayer.GetName())
 
1078
 
 
1079
        win = ScatterFrame(parent = self, rasterList = raster)
 
1080
        
 
1081
        win.CentreOnParent()
 
1082
        win.Show()
 
1083
        # Open raster select dialog to make sure that at least 2 rasters (and the desired rasters)
 
1084
        # are selected to be plotted
 
1085
        win.OnSelectRaster(None)
 
1086
 
 
1087
    def OnHistogram(self, event):
 
1088
        """Init histogram display canvas and tools
 
1089
        """
 
1090
        win = HistogramFrame(self, giface=self._giface)
 
1091
        
 
1092
        win.CentreOnParent()
 
1093
        win.Show()
 
1094
        win.Refresh()
 
1095
        win.Update()
 
1096
 
 
1097
    def _activateOverlay(self, overlayId):
 
1098
        """Launch decoration dialog according to overlay id.
 
1099
 
 
1100
        :param overlayId: id of overlay        
 
1101
        """
 
1102
        if overlayId > 100:
 
1103
            self.OnAddText(None)
 
1104
        elif overlayId == 0:
 
1105
            self.AddLegend(cmd=self.legend.cmd, showDialog=True)
 
1106
        elif overlayId == 1:
 
1107
            self.AddBarscale(showDialog=True)
 
1108
        elif overlayId == 2:
 
1109
            self.AddArrow(showDialog=True)
 
1110
 
 
1111
    def _hideOverlay(self, overlayId):
 
1112
        """Hide overlay.
 
1113
 
 
1114
        :param overlayId: id of overlay        
 
1115
        """
 
1116
        self.decorations[overlayId].Hide()
 
1117
 
 
1118
    def AddBarscale(self, cmd=None, showDialog=None):
 
1119
        """Handler for scale bar map decoration menu selection."""
 
1120
        if self.IsPaneShown('3d'):
 
1121
            self.MapWindow3D.SetDrawScalebar((70, 70))
 
1122
            return
 
1123
 
 
1124
        if self.barscale.IsShown() and showDialog is None:
 
1125
            self.barscale.Hide()
 
1126
            return
 
1127
 
 
1128
        if cmd:
 
1129
            self.barscale.cmd = cmd
 
1130
 
 
1131
        if not showDialog:
 
1132
            self.barscale.Show()
 
1133
            return
 
1134
 
 
1135
        # Decoration overlay control dialog
 
1136
        if self.barscale.dialog:
 
1137
            if self.barscale.dialog.IsShown():
 
1138
                self.barscale.dialog.SetFocus()
 
1139
                self.barscale.dialog.Raise()
 
1140
            else:
 
1141
                self.barscale.dialog.Show()
 
1142
        else:
 
1143
            # If location is latlon, only display north arrow (scale won't work)
 
1144
            #        proj = self.Map.projinfo['proj']
 
1145
            #        if proj == 'll':
 
1146
            #            barcmd = 'd.barscale -n'
 
1147
            #        else:
 
1148
            #            barcmd = 'd.barscale'
 
1149
 
 
1150
            # decoration overlay control dialog
 
1151
            GUI(parent=self, giface=self._giface, show=True,
 
1152
                modal=False).ParseCommand(self.barscale.cmd,
 
1153
                                          completed=(self.barscale.GetOptData, None, None))
 
1154
 
 
1155
        self.MapWindow.mouse['use'] = 'pointer'
 
1156
 
 
1157
    def AddLegend(self, cmd=None, showDialog=None):
 
1158
        """Handler for legend map decoration menu selection."""
 
1159
        if self.legend.IsShown() and showDialog is None:
 
1160
            self.legend.Hide()
 
1161
            return
 
1162
        if cmd:
 
1163
            self.legend.cmd = cmd
 
1164
        else:
 
1165
            layers = self._giface.GetLayerList().GetSelectedLayers()
 
1166
            for layer in layers:
 
1167
                if layer.type == 'raster':
 
1168
                    isMap = False
 
1169
                    # replace map
 
1170
                    for i, legendParam in enumerate(self.legend.cmd[1:]):
 
1171
                        idx = i + 1
 
1172
                        param_val = legendParam.split('=')
 
1173
                        if len(param_val) != 2:
 
1174
                            continue
 
1175
                        param, val = param_val
 
1176
                        if param == 'raster':
 
1177
                            self.legend.cmd[idx] = 'raster={rast}'.format(rast=layer.maplayer.name)
 
1178
                            isMap = True
 
1179
                        elif param in ('use', 'range'):
 
1180
                            # clear range or use to avoid problems
 
1181
                            del self.legend.cmd[idx]
 
1182
 
 
1183
                    if not isMap:  # for the first time
 
1184
                        self.legend.cmd.append('raster=%s' % layer.maplayer.name)
 
1185
                    break
 
1186
 
 
1187
        if not showDialog and self.legend.CmdIsValid():
 
1188
            self.legend.Show()
 
1189
            return
 
1190
 
 
1191
        # Decoration overlay control dialog
 
1192
        # always create new one to avoid problem when switching between maps
 
1193
        if self.legend.dialog:
 
1194
            if self.legend.dialog.IsShown():
 
1195
                self.legend.dialog.SetFocus()
 
1196
                self.legend.dialog.Raise()
 
1197
            else:
 
1198
                self.legend.dialog.Destroy()
 
1199
                self.legend.dialog = None
 
1200
        if not self.legend.dialog:
 
1201
            GUI(parent=self, giface=self._giface, show=True,
 
1202
                modal=False).ParseCommand(self.legend.cmd,
 
1203
                                          completed=(self.legend.GetOptData, None, None))
 
1204
 
 
1205
        self.MapWindow.mouse['use'] = 'pointer'
 
1206
 
 
1207
    def AddArrow(self, cmd=None, showDialog=None):
 
1208
        """Handler for north arrow menu selection."""
 
1209
        if self.IsPaneShown('3d'):
 
1210
            # here was opening of appearance page of nviz notebook
 
1211
            # but now moved to MapWindow3D where are other problematic nviz calls
 
1212
            self.MapWindow3D.SetDrawArrow((70, 70))
 
1213
            return
 
1214
 
 
1215
        if self.arrow.IsShown() and showDialog is None:
 
1216
            self.arrow.Hide()
 
1217
            return
 
1218
        if cmd:
 
1219
            self.arrow.cmd = cmd
 
1220
 
 
1221
        if not showDialog:
 
1222
            self.arrow.Show()
 
1223
            return
 
1224
 
 
1225
        # Decoration overlay control dialog
 
1226
        if self.arrow.dialog:
 
1227
            if self.arrow.dialog.IsShown():
 
1228
                self.arrow.dialog.SetFocus()
 
1229
                self.arrow.dialog.Raise()
 
1230
            else:
 
1231
                self.arrow.dialog.Show()
 
1232
        else:
 
1233
            GUI(parent=self, giface=self._giface, show=True,
 
1234
                modal=False).ParseCommand(self.arrow.cmd,
 
1235
                                          completed=(self.arrow.GetOptData, None, None))
 
1236
 
 
1237
        self.MapWindow.mouse['use'] = 'pointer'
 
1238
 
 
1239
    def OnAddText(self, event):
 
1240
        """Handler for text decoration menu selection.
 
1241
        """
 
1242
        if self.MapWindow.dragid > -1:
 
1243
            id = self.MapWindow.dragid
 
1244
            self.MapWindow.dragid = -1
 
1245
        else:
 
1246
            # index for overlay layer in render
 
1247
            if len(self.MapWindow.textdict.keys()) > 0:
 
1248
                id = max(self.MapWindow.textdict.keys()) + 1
 
1249
            else:
 
1250
                id = 101
 
1251
        
 
1252
        self.dialogs['text'] = TextLayerDialog(parent = self, ovlId = id, 
 
1253
                                               title = _('Add text layer'),
 
1254
                                               size = (400, 200))
 
1255
        self.dialogs['text'].CenterOnParent()
 
1256
 
 
1257
        # If OK button pressed in decoration control dialog
 
1258
        if self.dialogs['text'].ShowModal() == wx.ID_OK:
 
1259
            text = self.dialogs['text'].GetValues()['text']
 
1260
            active = self.dialogs['text'].GetValues()['active']
 
1261
        
 
1262
            # delete object if it has no text or is not active
 
1263
            if text == '' or active == False:
 
1264
                try:
 
1265
                    self.MapWindow2D.pdc.ClearId(id)
 
1266
                    self.MapWindow2D.pdc.RemoveId(id)
 
1267
                    del self.MapWindow.textdict[id]
 
1268
                    if self.IsPaneShown('3d'):
 
1269
                        self.MapWindow3D.UpdateOverlays()
 
1270
                        self.MapWindow.UpdateMap()
 
1271
                    else:
 
1272
                        self.MapWindow2D.UpdateMap(render = False, renderVector = False)
 
1273
                except:
 
1274
                    pass
 
1275
                return
 
1276
 
 
1277
            
 
1278
            self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
 
1279
            
 
1280
            if self.IsPaneShown('3d'):
 
1281
                self.MapWindow3D.UpdateOverlays()
 
1282
                self.MapWindow3D.UpdateMap()
 
1283
            else:
 
1284
                self.MapWindow2D.pdc.ClearId(id)
 
1285
                self.MapWindow2D.pdc.SetId(id)
 
1286
                self.MapWindow2D.UpdateMap(render = False, renderVector = False)
 
1287
            
 
1288
        self.MapWindow.mouse['use'] = 'pointer'
 
1289
        
 
1290
    def GetOptData(self, dcmd, type, params, propwin):
 
1291
        """Callback method for decoration overlay command generated by
 
1292
        dialog created in menuform.py
 
1293
        """
 
1294
        # Reset comand and rendering options in render.Map. Always render decoration.
 
1295
        # Showing/hiding handled by PseudoDC
 
1296
        self.Map.ChangeOverlay(ovltype = type, type = 'overlay', name = '', command = dcmd,
 
1297
                               active = True, render = False)
 
1298
        self.params[type] = params
 
1299
        self.propwin[type] = propwin
 
1300
 
 
1301
    def OnZoomToMap(self, event):
 
1302
        """Set display extents to match selected raster (including
 
1303
        NULLs) or vector map.
 
1304
        """
 
1305
        Debug.msg(3, "MapFrame.OnZoomToMap()")
 
1306
        layers = None
 
1307
        if self.IsStandalone():
 
1308
            layers = self.MapWindow.GetMap().GetListOfLayers(active = False)
 
1309
        
 
1310
        self.MapWindow.ZoomToMap(layers = layers)
 
1311
 
 
1312
    def OnZoomToRaster(self, event):
 
1313
        """Set display extents to match selected raster map (ignore NULLs)
 
1314
        """
 
1315
        self.MapWindow.ZoomToMap(ignoreNulls = True)
 
1316
        
 
1317
    def OnZoomToSaved(self, event):
 
1318
        """Set display geometry to match extents in
 
1319
        saved region file
 
1320
        """
 
1321
        self.MapWindow.SetRegion(zoomOnly=True)
 
1322
        
 
1323
    def OnSetDisplayToWind(self, event):
 
1324
        """Set computational region (WIND file) to match display
 
1325
        extents
 
1326
        """
 
1327
        self.MapWindow.DisplayToWind()
 
1328
 
 
1329
    def OnSetWindToRegion(self, event):
 
1330
        """Set computational region (WIND file) from named region
 
1331
        file
 
1332
        """
 
1333
        self.MapWindow.SetRegion(zoomOnly=False)
 
1334
 
 
1335
    def OnSetExtentToWind(self, event):
 
1336
        """Set compulational region extent interactively"""
 
1337
        self.MapWindow.SetModeDrawRegion()
 
1338
 
 
1339
    def OnSaveDisplayRegion(self, event):
 
1340
        """Save display extents to named region file.
 
1341
        """
 
1342
        self.MapWindow.SaveRegion(display = True)
 
1343
 
 
1344
    def OnSaveWindRegion(self, event):
 
1345
        """Save computational region to named region file.
 
1346
        """
 
1347
        self.MapWindow.SaveRegion(display = False)
 
1348
        
 
1349
    def OnZoomMenu(self, event):
 
1350
        """Popup Zoom menu
 
1351
        """
 
1352
        zoommenu = wx.Menu()
 
1353
        
 
1354
        for label, handler in ((_('Zoom to default region'), self.OnZoomToDefault),
 
1355
                               (_('Zoom to saved region'), self.OnZoomToSaved),
 
1356
                               (None, None),
 
1357
                               (_('Set computational region extent from display'), self.OnSetDisplayToWind),
 
1358
                               (_('Set computational region extent interactively'), self.OnSetExtentToWind),
 
1359
                               (_('Set computational region from named region'),   self.OnSetWindToRegion),
 
1360
                               (None, None),
 
1361
                               (_('Save display geometry to named region'), self.OnSaveDisplayRegion),
 
1362
                               (_('Save computational region to named region'), self.OnSaveWindRegion)):
 
1363
            if label:
 
1364
                mid = wx.MenuItem(zoommenu, wx.ID_ANY, label)
 
1365
                zoommenu.AppendItem(mid)
 
1366
                self.Bind(wx.EVT_MENU, handler, mid)
 
1367
            else:
 
1368
                zoommenu.AppendSeparator()
 
1369
        
 
1370
        # Popup the menu. If an item is selected then its handler will
 
1371
        # be called before PopupMenu returns.
 
1372
        self.PopupMenu(zoommenu)
 
1373
        zoommenu.Destroy()
 
1374
 
 
1375
    def SetProperties(self, render = False, mode = 0, showCompExtent = False,
 
1376
                      constrainRes = False, projection = False, alignExtent = True):
 
1377
        """Set properies of map display window"""
 
1378
        self.mapWindowProperties.autoRender = render
 
1379
        self.statusbarManager.SetMode(mode)
 
1380
        self.StatusbarUpdate()
 
1381
        self.mapWindowProperties.showRegion = showCompExtent
 
1382
        self.mapWindowProperties.alignExtent = alignExtent
 
1383
        self.mapWindowProperties.resolution = constrainRes
 
1384
        self.SetProperty('projection', projection)
 
1385
        
 
1386
    def IsStandalone(self):
 
1387
        """Check if Map display is standalone
 
1388
 
 
1389
        .. deprecated:: 7.0
 
1390
        """
 
1391
        # TODO: once it is removed from 2 places in vdigit it can be deleted
 
1392
        # here and also in base class and other classes in the tree (hopefully)
 
1393
        # and one place here still uses IsStandalone
 
1394
        Debug.msg(1, "MapFrame.IsStandalone(): Method IsStandalone is"
 
1395
                  "depreciated, use some general approach instead such as"
 
1396
                  " Signals or giface")
 
1397
        if self._layerManager:
 
1398
            return False
 
1399
 
 
1400
        return True
 
1401
 
 
1402
    def GetLayerManager(self):
 
1403
        """Get reference to Layer Manager
 
1404
 
 
1405
        :return: window reference
 
1406
        :return: None (if standalone)
 
1407
 
 
1408
        .. deprecated:: 7.0
 
1409
        """
 
1410
        Debug.msg(1, "MapFrame.GetLayerManager(): Method GetLayerManager is"
 
1411
                  "depreciated, use some general approach instead such as"
 
1412
                  " Signals or giface")
 
1413
        return self._layerManager
 
1414
 
 
1415
    def GetMapToolbar(self):
 
1416
        """Returns toolbar with zooming tools"""
 
1417
        return self.toolbars['map']
 
1418
 
 
1419
    def OnVNet(self, event):
 
1420
        """Dialog for v.net* modules 
 
1421
        """
 
1422
        if self.dialogs['vnet']:
 
1423
            self.dialogs['vnet'].Raise()
 
1424
            return
 
1425
        
 
1426
        from vnet.dialogs import VNETDialog
 
1427
        self.dialogs['vnet'] = VNETDialog(parent=self, giface=self._giface)
 
1428
        self.dialogs['vnet'].CenterOnScreen()
 
1429
        self.dialogs['vnet'].Show()
 
1430
 
 
1431
    def ResetPointer(self):
 
1432
        """Sets pointer mode.
 
1433
 
 
1434
        Sets pointer and toggles it (e.g. after unregistration of mouse
 
1435
        handler).
 
1436
        """
 
1437
        self.GetMapToolbar().SelectDefault()
 
1438
 
 
1439
    def _switchMapWindow(self, map_win):
 
1440
        """Notifies activated and disactivated map_wins."""
 
1441
        self.MapWindow.DisactivateWin()
 
1442
        map_win.ActivateWin()
 
1443
 
 
1444
        self.MapWindow = map_win