GRASS Programmer's Manual  6.4.2(2012)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
layertree.py
Go to the documentation of this file.
1 """!
2 @package layertree.py
3 
4 @brief Utility classes for map layer management.
5 
6 Classes:
7  - AbstractLayer
8  - Layer
9  - LayerTree
10 
11 (C) 2007-2011 by the GRASS Development Team
12 This program is free software under the GNU General Public
13 License (>=v2). Read the file COPYING that comes with GRASS
14 for details.
15 
16 @author Michael Barton (Arizona State University)
17 @author Jachym Cepicky (Mendel University of Agriculture)
18 @author Martin Landa <landa.martin gmail.com>
19 """
20 
21 import os
22 import sys
23 import string
24 
25 import wx
26 try:
27  import wx.lib.agw.customtreectrl as CT
28 except ImportError:
29  import wx.lib.customtreectrl as CT
30 import wx.combo
31 import wx.lib.newevent
32 import wx.lib.buttons as buttons
33 try:
34  import treemixin
35 except ImportError:
36  from wx.lib.mixins import treemixin
37 
38 import globalvar
39 
40 from grass.script import core as grass
41 
42 import gdialogs
43 import menuform
44 import toolbars
45 import mapdisp
46 import render
47 import histogram
48 import utils
49 import profile
50 from debug import Debug as Debug
51 from icon import Icons as Icons
52 from preferences import globalSettings as UserSettings
53 from vdigit import haveVDigit
54 from gcmd import GWarning
55 
56 TREE_ITEM_HEIGHT = 25
57 
58 class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
59  """!Creates layer tree structure
60  """
61  def __init__(self, parent,
62  id = wx.ID_ANY, style = wx.SUNKEN_BORDER,
63  ctstyle = CT.TR_HAS_BUTTONS | CT.TR_HAS_VARIABLE_ROW_HEIGHT |
64  CT.TR_HIDE_ROOT | CT.TR_ROW_LINES | CT.TR_FULL_ROW_HIGHLIGHT |
65  CT.TR_MULTIPLE, **kwargs):
66 
67  if 'style' in kwargs:
68  ctstyle |= kwargs['style']
69  del kwargs['style']
70  self.disp_idx = kwargs['idx']
71  del kwargs['idx']
72  self.lmgr = kwargs['lmgr']
73  del kwargs['lmgr']
74  self.notebook = kwargs['notebook'] # GIS Manager notebook for layer tree
75  del kwargs['notebook']
76  self.auimgr = kwargs['auimgr'] # aui manager
77  del kwargs['auimgr']
78  showMapDisplay = kwargs['showMapDisplay']
79  del kwargs['showMapDisplay']
80  self.treepg = parent # notebook page holding layer tree
81  self.Map = render.Map() # instance of render.Map to be associated with display
82  self.root = None # ID of layer tree root node
83  self.groupnode = 0 # index value for layers
84  self.optpage = {} # dictionary of notebook option pages for each map layer
85  self.layer_selected = None # ID of currently selected layer
86  self.saveitem = {} # dictionary to preserve layer attributes for drag and drop
87  self.first = True # indicates if a layer is just added or not
88  self.flag = '' # flag for drag and drop hittest
89  self.rerender = False # layer change requires a rerendering if auto render
90  self.reorder = False # layer change requires a reordering
91 
92  try:
93  ctstyle |= CT.TR_ALIGN_WINDOWS
94  except AttributeError:
95  pass
96 
97  if globalvar.hasAgw:
98  super(LayerTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
99  else:
100  super(LayerTree, self).__init__(parent, id, style = ctstyle, **kwargs)
101  self.SetName("LayerTree")
102 
103  ### SetAutoLayout() causes that no vertical scrollbar is displayed
104  ### when some layers are not visible in layer tree
105  # self.SetAutoLayout(True)
106  self.SetGradientStyle(1)
107  self.EnableSelectionGradient(True)
108  self._setGradient()
109 
110  # init associated map display
111  pos = wx.Point((self.disp_idx + 1) * 25, (self.disp_idx + 1) * 25)
113  id = wx.ID_ANY, pos = pos,
114  size = globalvar.MAP_WINDOW_SIZE,
115  style = wx.DEFAULT_FRAME_STYLE,
116  tree = self, notebook = self.notebook,
117  lmgr = self.lmgr, page = self.treepg,
118  Map = self.Map, auimgr = self.auimgr)
119 
120  # title
121  self.mapdisplay.SetTitle(_("GRASS GIS Map Display: %(id)d - Location: %(loc)s") % \
122  { 'id' : self.disp_idx + 1,
123  'loc' : grass.gisenv()["LOCATION_NAME"] })
124 
125  # show new display
126  if showMapDisplay is True:
127  self.mapdisplay.Show()
128  self.mapdisplay.Refresh()
129  self.mapdisplay.Update()
130 
131  self.root = self.AddRoot(_("Map Layers"))
132  self.SetPyData(self.root, (None, None))
133 
134  # create image list to use with layer tree
135  il = wx.ImageList(16, 16, mask = False)
136 
137  trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_OTHER, (16, 16))
138  self.folder_open = il.Add(trart)
139  trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16, 16))
140  self.folder = il.Add(trart)
141 
142  bmpsize = (16, 16)
143  icons = Icons['layerManager']
144  trgif = icons["addRast"].GetBitmap(bmpsize)
145  self.rast_icon = il.Add(trgif)
146 
147  trgif = icons["addRast3d"].GetBitmap(bmpsize)
148  self.rast3d_icon = il.Add(trgif)
149 
150  trgif = icons["addRgb"].GetBitmap(bmpsize)
151  self.rgb_icon = il.Add(trgif)
152 
153  trgif = icons["addHis"].GetBitmap(bmpsize)
154  self.his_icon = il.Add(trgif)
155 
156  trgif = icons["addShaded"].GetBitmap(bmpsize)
157  self.shaded_icon = il.Add(trgif)
158 
159  trgif = icons["addRArrow"].GetBitmap(bmpsize)
160  self.rarrow_icon = il.Add(trgif)
161 
162  trgif = icons["addRNum"].GetBitmap(bmpsize)
163  self.rnum_icon = il.Add(trgif)
164 
165  trgif = icons["addVect"].GetBitmap(bmpsize)
166  self.vect_icon = il.Add(trgif)
167 
168  trgif = icons["addThematic"].GetBitmap(bmpsize)
169  self.theme_icon = il.Add(trgif)
170 
171  trgif = icons["addChart"].GetBitmap(bmpsize)
172  self.chart_icon = il.Add(trgif)
173 
174  trgif = icons["addGrid"].GetBitmap(bmpsize)
175  self.grid_icon = il.Add(trgif)
176 
177  trgif = icons["addGeodesic"].GetBitmap(bmpsize)
178  self.geodesic_icon = il.Add(trgif)
179 
180  trgif = icons["addRhumb"].GetBitmap(bmpsize)
181  self.rhumb_icon = il.Add(trgif)
182 
183  trgif = icons["addLabels"].GetBitmap(bmpsize)
184  self.labels_icon = il.Add(trgif)
185 
186  trgif = icons["addCmd"].GetBitmap(bmpsize)
187  self.cmd_icon = il.Add(trgif)
188 
189  self.AssignImageList(il)
190 
191  self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnExpandNode)
192  self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnCollapseNode)
193  self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivateLayer)
194  self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnChangeSel)
195  self.Bind(CT.EVT_TREE_ITEM_CHECKED, self.OnLayerChecked)
196  self.Bind(wx.EVT_TREE_DELETE_ITEM, self.OnDeleteLayer)
197  self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnLayerContextMenu)
198  self.Bind(wx.EVT_TREE_END_DRAG, self.OnEndDrag)
199  self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnRenamed)
200  self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
201  self.Bind(wx.EVT_IDLE, self.OnIdle)
202 
203  def _setGradient(self, iType = None):
204  """!Set gradient for items
205 
206  @param iType bgmap, vdigit or None
207  """
208  if iType == 'bgmap':
209  self.SetFirstGradientColour(wx.Colour(0, 100, 0))
210  self.SetSecondGradientColour(wx.Colour(0, 150, 0))
211  elif iType == 'vdigit':
212  self.SetFirstGradientColour(wx.Colour(100, 0, 0))
213  self.SetSecondGradientColour(wx.Colour(150, 0, 0))
214  else:
215  self.SetFirstGradientColour(wx.Colour(100, 100, 100))
216  self.SetSecondGradientColour(wx.Colour(150, 150, 150))
217 
218  def GetMap(self):
219  """!Get map instace"""
220  return self.Map
221 
222  def GetMapDisplay(self):
223  """!Get associated MapFrame"""
224  return self.mapdisplay
225 
226  def OnIdle(self, event):
227  """!Only re-order and re-render a composite map image from GRASS during
228  idle time instead of multiple times during layer changing.
229  """
230  if self.rerender:
231  if self.mapdisplay.toolbars['vdigit']:
232  vector = True
233  else:
234  vector = False
235  if self.mapdisplay.statusbarWin['render'].GetValue():
236  self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = vector)
237 
238  self.rerender = False
239 
240  event.Skip()
241 
242  def OnKeyUp(self, event):
243  """!Key pressed"""
244  key = event.GetKeyCode()
245 
246  if key == wx.WXK_DELETE and self.lmgr and \
247  not self.GetEditControl():
248  self.lmgr.OnDeleteLayer(None)
249 
250  event.Skip()
251 
252  def OnLayerContextMenu (self, event):
253  """!Contextual menu for item/layer"""
254  if not self.layer_selected:
255  event.Skip()
256  return
257 
258  ltype = self.GetPyData(self.layer_selected)[0]['type']
259  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
260 
261  Debug.msg (4, "LayerTree.OnContextMenu: layertype=%s" % \
262  ltype)
263 
264  if not hasattr (self, "popupID"):
265  self.popupID = dict()
266  for key in ('remove', 'rename', 'opacity', 'nviz', 'zoom',
267  'region', 'export', 'attr', 'edit0', 'edit1',
268  'bgmap', 'topo', 'meta', 'null', 'zoom1', 'region1',
269  'color', 'hist', 'univar', 'prof', 'properties'):
270  self.popupID[key] = wx.NewId()
271 
272  self.popupMenu = wx.Menu()
273 
274  numSelected = len(self.GetSelections())
275 
276  self.popupMenu.Append(self.popupID['remove'], text = _("Remove"))
277  self.Bind(wx.EVT_MENU, self.lmgr.OnDeleteLayer, id = self.popupID['remove'])
278 
279  if ltype != "command":
280  self.popupMenu.Append(self.popupID['rename'], text = _("Rename"))
281  self.Bind(wx.EVT_MENU, self.OnRenameLayer, id = self.popupID['rename'])
282  if numSelected > 1:
283  self.popupMenu.Enable(self.popupID['rename'], False)
284 
285  # map layer items
286  if ltype not in ("group", "command"):
287  self.popupMenu.AppendSeparator()
288  self.popupMenu.Append(self.popupID['opacity'], text = _("Change opacity level"))
289  self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id = self.popupID['opacity'])
290  self.popupMenu.Append(self.popupID['properties'], text = _("Properties"))
291  self.Bind(wx.EVT_MENU, self.OnPopupProperties, id = self.popupID['properties'])
292 
293  if numSelected > 1:
294  self.popupMenu.Enable(self.popupID['opacity'], False)
295  self.popupMenu.Enable(self.popupID['properties'], False)
296 
297  if ltype in ('raster', 'vector', '3d-raster') and self.mapdisplay.toolbars['nviz']:
298  self.popupMenu.Append(self.popupID['nviz'], _("3D view properties"))
299  self.Bind (wx.EVT_MENU, self.OnNvizProperties, id = self.popupID['nviz'])
300 
301  if ltype in ('raster', 'vector', 'rgb'):
302  self.popupMenu.Append(self.popupID['zoom'], text = _("Zoom to selected map(s)"))
303  self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToMap, id = self.popupID['zoom'])
304  self.popupMenu.Append(self.popupID['region'], text = _("Set computational region from selected map(s)"))
305  self.Bind(wx.EVT_MENU, self.OnSetCompRegFromMap, id = self.popupID['region'])
306 
307  # specific items
308  try:
309  mltype = self.GetPyData(self.layer_selected)[0]['type']
310  except:
311  mltype = None
312 
313  # vector layers (specific items)
314  if mltype and mltype == "vector":
315  self.popupMenu.AppendSeparator()
316  self.popupMenu.Append(self.popupID['export'], text = _("Export"))
317  self.Bind(wx.EVT_MENU, lambda x: self.lmgr.OnMenuCmd(cmd = ['v.out.ogr',
318  'input=%s' % mapLayer.GetName()]),
319  id = self.popupID['export'])
320 
321  self.popupMenu.AppendSeparator()
322  self.popupMenu.Append(self.popupID['attr'], text = _("Show attribute data"))
323  self.Bind(wx.EVT_MENU, self.lmgr.OnShowAttributeTable, id = self.popupID['attr'])
324 
325  self.popupMenu.Append(self.popupID['edit0'], text = _("Start editing"))
326  self.popupMenu.Append(self.popupID['edit1'], text = _("Stop editing"))
327  self.popupMenu.Enable(self.popupID['edit1'], False)
328  self.Bind (wx.EVT_MENU, self.OnStartEditing, id = self.popupID['edit0'])
329  self.Bind (wx.EVT_MENU, self.OnStopEditing, id = self.popupID['edit1'])
330 
331  layer = self.GetPyData(self.layer_selected)[0]['maplayer']
332  # enable editing only for vector map layers available in the current mapset
333  digitToolbar = self.mapdisplay.toolbars['vdigit']
334  if digitToolbar:
335  # background vector map
336  self.popupMenu.Append(self.popupID['bgmap'],
337  text = _("Use as background vector map for digitizer"),
338  kind = wx.ITEM_CHECK)
339  self.Bind(wx.EVT_MENU, self.OnSetBgMap, id = self.popupID['bgmap'])
340  if UserSettings.Get(group = 'vdigit', key = 'bgmap', subkey = 'value',
341  internal = True) == layer.GetName():
342  self.popupMenu.Check(self.popupID['bgmap'], True)
343 
344  self.popupMenu.Append(self.popupID['topo'], text = _("Rebuild topology"))
345  self.Bind(wx.EVT_MENU, self.OnTopology, id = self.popupID['topo'])
346 
347  if layer.GetMapset() != grass.gisenv()['MAPSET']:
348  # only vector map in current mapset can be edited
349  self.popupMenu.Enable (self.popupID['edit0'], False)
350  self.popupMenu.Enable (self.popupID['edit1'], False)
351  self.popupMenu.Enable (self.popupID['topo'], False)
352  elif digitToolbar and digitToolbar.GetLayer():
353  # vector map already edited
354  vdigitLayer = digitToolbar.GetLayer()
355  if vdigitLayer is layer:
356  self.popupMenu.Enable(self.popupID['edit0'], False)
357  self.popupMenu.Enable(self.popupID['edit1'], True)
358  self.popupMenu.Enable(self.popupID['remove'], False)
359  self.popupMenu.Enable(self.popupID['bgmap'], False)
360  self.popupMenu.Enable(self.popupID['topo'], False)
361  else:
362  self.popupMenu.Enable(self.popupID['edit0'], False)
363  self.popupMenu.Enable(self.popupID['edit1'], False)
364  self.popupMenu.Enable(self.popupID['bgmap'], True)
365 
366  self.popupMenu.Append(self.popupID['meta'], _("Metadata"))
367  self.Bind (wx.EVT_MENU, self.OnMetadata, id = self.popupID['meta'])
368  if numSelected > 1:
369  self.popupMenu.Enable(self.popupID['attr'], False)
370  self.popupMenu.Enable(self.popupID['edit0'], False)
371  self.popupMenu.Enable(self.popupID['edit1'], False)
372  self.popupMenu.Enable(self.popupID['meta'], False)
373  self.popupMenu.Enable(self.popupID['bgmap'], False)
374  self.popupMenu.Enable(self.popupID['topo'], False)
375  self.popupMenu.Enable(self.popupID['export'], False)
376 
377  # raster layers (specific items)
378  elif mltype and mltype == "raster":
379  self.popupMenu.Append(self.popupID['zoom1'], text = _("Zoom to selected map(s) (ignore NULLs)"))
380  self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToRaster, id = self.popupID['zoom1'])
381  self.popupMenu.Append(self.popupID['region1'], text = _("Set computational region from selected map(s) (ignore NULLs)"))
382  self.Bind(wx.EVT_MENU, self.OnSetCompRegFromRaster, id = self.popupID['region1'])
383 
384  self.popupMenu.AppendSeparator()
385  self.popupMenu.Append(self.popupID['export'], text = _("Export"))
386  self.Bind(wx.EVT_MENU, lambda x: self.lmgr.OnMenuCmd(cmd = ['r.out.gdal',
387  'input=%s' % mapLayer.GetName()]),
388  id = self.popupID['export'])
389 
390  self.popupMenu.AppendSeparator()
391  self.popupMenu.Append(self.popupID['color'], _("Set color table"))
392  self.Bind (wx.EVT_MENU, self.OnColorTable, id = self.popupID['color'])
393  self.popupMenu.Append(self.popupID['hist'], _("Histogram"))
394  self.Bind (wx.EVT_MENU, self.OnHistogram, id = self.popupID['hist'])
395  self.popupMenu.Append(self.popupID['univar'], _("Univariate raster statistics"))
396  self.Bind (wx.EVT_MENU, self.OnUnivariateStats, id = self.popupID['univar'])
397  self.popupMenu.Append(self.popupID['prof'], _("Profile"))
398  self.Bind (wx.EVT_MENU, self.OnProfile, id = self.popupID['prof'])
399  self.popupMenu.Append(self.popupID['meta'], _("Metadata"))
400  self.Bind (wx.EVT_MENU, self.OnMetadata, id = self.popupID['meta'])
401 
402  if numSelected > 1:
403  self.popupMenu.Enable(self.popupID['zoom1'], False)
404  self.popupMenu.Enable(self.popupID['region1'], False)
405  self.popupMenu.Enable(self.popupID['color'], False)
406  self.popupMenu.Enable(self.popupID['hist'], False)
407  self.popupMenu.Enable(self.popupID['univar'], False)
408  self.popupMenu.Enable(self.popupID['prof'], False)
409  self.popupMenu.Enable(self.popupID['meta'], False)
410  self.popupMenu.Enable(self.popupID['nviz'], False)
411  self.popupMenu.Enable(self.popupID['export'], False)
412 
413  self.PopupMenu(self.popupMenu)
414  self.popupMenu.Destroy()
415 
416  def OnTopology(self, event):
417  """!Rebuild topology of selected vector map"""
418  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
419  cmd = ['v.build',
420  'map=%s' % mapLayer.GetName()]
421  self.lmgr.goutput.RunCmd(cmd, switchPage = True)
422 
423  def OnMetadata(self, event):
424  """!Print metadata of raster/vector map layer
425  TODO: Dialog to modify metadata
426  """
427  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
428  mltype = self.GetPyData(self.layer_selected)[0]['type']
429 
430  if mltype == 'raster':
431  cmd = ['r.info']
432  elif mltype == 'vector':
433  cmd = ['v.info']
434  cmd.append('map=%s' % mapLayer.GetName())
435 
436  # print output to command log area
437  self.lmgr.goutput.RunCmd(cmd, switchPage = True)
438 
439  def OnSetCompRegFromRaster(self, event):
440  """!Set computational region from selected raster map (ignore NULLs)"""
441  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
442 
443  cmd = ['g.region',
444  '-p',
445  'zoom=%s' % mapLayer.GetName()]
446 
447  # print output to command log area
448  self.lmgr.goutput.RunCmd(cmd)
449 
450  def OnSetCompRegFromMap(self, event):
451  """!Set computational region from selected raster/vector map
452  """
453  rast = []
454  vect = []
455  rast3d = []
456  for layer in self.GetSelections():
457  mapLayer = self.GetPyData(layer)[0]['maplayer']
458  mltype = self.GetPyData(layer)[0]['type']
459 
460  if mltype == 'raster':
461  rast.append(mapLayer.GetName())
462  elif mltype == 'vector':
463  vect.append(mapLayer.GetName())
464  elif mltype == '3d-raster':
465  rast3d.append(mapLayer.GetName())
466  elif mltype == 'rgb':
467  for rname in mapLayer.GetName().splitlines():
468  rast.append(rname)
469 
470  cmd = ['g.region']
471  if rast:
472  cmd.append('rast=%s' % ','.join(rast))
473  if vect:
474  cmd.append('vect=%s' % ','.join(vect))
475  if rast3d:
476  cmd.append('rast3d=%s' % ','.join(rast3d))
477 
478  # print output to command log area
479  if len(cmd) > 1:
480  cmd.append('-p')
481  self.lmgr.goutput.RunCmd(cmd)
482 
483  def OnProfile(self, event):
484  """!Plot profile of given raster map layer"""
485  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
486  if not mapLayer.GetName():
487  wx.MessageBox(parent = self,
488  message = _("Unable to create profile of "
489  "raster map."),
490  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
491  return False
492 
493  if not hasattr (self, "profileFrame"):
494  self.profileFrame = None
495 
496  if hasattr (self.mapdisplay, "profile") and self.mapdisplay.profile:
497  self.profileFrame = self.mapdisplay.profile
498 
499  if not self.profileFrame:
501  id = wx.ID_ANY, pos = wx.DefaultPosition, size = (700,300),
502  style = wx.DEFAULT_FRAME_STYLE, rasterList = [mapLayer.GetName()])
503  # show new display
504  self.profileFrame.Show()
505 
506  def OnColorTable(self, event):
507  """!Set color table for raster map"""
508  name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
509  menuform.GUI(parent = self).ParseCommand(['r.colors',
510  'map=%s' % name])
511 
512  def OnHistogram(self, event):
513  """
514  Plot histogram for given raster map layer
515  """
516  mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
517  if not mapLayer.GetName():
518  wx.MessageBox(parent = self,
519  message = _("Unable to display histogram of "
520  "raster map."),
521  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
522  return False
523 
524  if not hasattr (self, "histogramFrame"):
525  self.histogramFrame = None
526 
527  if hasattr (self.mapdisplay, "histogram") and self.mapdisplay.histogram:
528  self.histogramFrame = self.mapdisplay.histogram
529 
530  if not self.histogramFrame:
532  id = wx.ID_ANY,
533  pos = wx.DefaultPosition, size = globalvar.HIST_WINDOW_SIZE,
534  style = wx.DEFAULT_FRAME_STYLE)
535  # show new display
536  self.histogramFrame.Show()
537 
538  self.histogramFrame.SetHistLayer(mapLayer.GetName())
539  self.histogramFrame.HistWindow.UpdateHist()
540  self.histogramFrame.Refresh()
541  self.histogramFrame.Update()
542 
543  return True
544 
545  def OnUnivariateStats(self, event):
546  """!Univariate raster statistics"""
547  name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
548  menuform.GUI(parent = self).ParseCommand(['r.univar',
549  'map=%s' % name])
550 
551  def OnStartEditing(self, event):
552  """!Start editing vector map layer requested by the user
553  """
554  try:
555  maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
556  except:
557  event.Skip()
558  return
559 
560  if not self.mapdisplay.toolbars['vdigit']: # enable tool
561  self.mapdisplay.AddToolbar('vdigit')
562 
563  if not self.mapdisplay.toolbars['vdigit']:
564  return
565 
566  self.mapdisplay.toolbars['vdigit'].StartEditing(maplayer)
567 
568  self._setGradient('vdigit')
569  self.RefreshLine(self.layer_selected)
570 
571  def OnStopEditing(self, event):
572  """!Stop editing the current vector map layer
573  """
574  maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
575 
576  self.mapdisplay.toolbars['vdigit'].OnExit()
577  if self.lmgr:
578  self.lmgr.toolbars['tools'].Enable('vdigit', enable = True)
579 
580  self._setGradient()
581  self.RefreshLine(self.layer_selected)
582 
583  def OnSetBgMap(self, event):
584  """!Set background vector map for editing sesstion"""
585  digit = self.mapdisplay.GetWindow().digit
586  if event.IsChecked():
587  mapName = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
588  UserSettings.Set(group = 'vdigit', key = 'bgmap', subkey = 'value',
589  value = str(mapName), internal = True)
590  digit.OpenBackgroundMap(mapName)
591  self._setGradient('bgmap')
592  else:
593  UserSettings.Set(group = 'vdigit', key = 'bgmap', subkey = 'value',
594  value = '', internal = True)
595  digit.CloseBackgroundMap()
596  self._setGradient()
597 
598  self.RefreshLine(self.layer_selected)
599 
600  def OnPopupProperties (self, event):
601  """!Popup properties dialog"""
603 
604  def OnPopupOpacityLevel(self, event):
605  """!Popup opacity level indicator"""
606  if not self.GetPyData(self.layer_selected)[0]['ctrl']:
607  return
608 
609  maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
610  current_opacity = maplayer.GetOpacity()
611 
612  dlg = gdialogs.SetOpacityDialog(self, opacity = current_opacity,
613  title = _("Set opacity <%s>") % maplayer.GetName())
614  dlg.CentreOnParent()
615 
616  if dlg.ShowModal() == wx.ID_OK:
617  new_opacity = dlg.GetOpacity() # string
618  self.Map.ChangeOpacity(maplayer, new_opacity)
619  maplayer.SetOpacity(new_opacity)
620  self.SetItemText(self.layer_selected,
621  self._getLayerName(self.layer_selected))
622 
623  # vector layer currently edited
624  if self.mapdisplay.toolbars['vdigit'] and \
625  self.mapdisplay.toolbars['vdigit'].GetLayer() == maplayer:
626  alpha = int(new_opacity * 255)
627  self.mapdisplay.GetWindow().digit.GetDisplay().UpdateSettings(alpha = alpha)
628 
629  # redraw map if auto-rendering is enabled
630  self.rerender = True
631  self.reorder = True
632 
633  def OnNvizProperties(self, event):
634  """!Nviz-related properties (raster/vector/volume)
635 
636  @todo vector/volume
637  """
638  self.lmgr.notebook.SetSelectionByName('nviz')
639  ltype = self.GetPyData(self.layer_selected)[0]['type']
640  if ltype == 'raster':
641  self.lmgr.nviz.SetPage('surface')
642  elif ltype == 'vector':
643  self.lmgr.nviz.SetPage('vector')
644  elif ltype == '3d-raster':
645  self.lmgr.nviz.SetPage('volume')
646 
647  def OnRenameLayer (self, event):
648  """!Rename layer"""
649  self.EditLabel(self.layer_selected)
650  self.GetEditControl().SetSelection(-1, -1)
651 
652  def OnRenamed(self, event):
653  """!Layer renamed"""
654  item = self.layer_selected
655  self.GetPyData(item)[0]['label'] = event.GetLabel()
656  self.SetItemText(item, self._getLayerName(item)) # not working, why?
657 
658  event.Skip()
659 
660  def AddLayer(self, ltype, lname = None, lchecked = None,
661  lopacity = 1.0, lcmd = None, lgroup = None, lvdigit = None, lnviz = None, multiple = True):
662  """!Add new item to the layer tree, create corresponding MapLayer instance.
663  Launch property dialog if needed (raster, vector, etc.)
664 
665  @param ltype layer type (raster, vector, 3d-raster, ...)
666  @param lname layer name
667  @param lchecked if True layer is checked
668  @param lopacity layer opacity level
669  @param lcmd command (given as a list)
670  @param lgroup index of group item (-1 for root) or None
671  @param lvdigit vector digitizer settings (eg. geometry attributes)
672  @param lnviz layer Nviz properties
673  @param multiple True to allow multiple map layers in layer tree
674  """
675  if lname and not multiple:
676  # check for duplicates
677  item = self.GetFirstVisibleItem()
678  while item and item.IsOk():
679  if self.GetPyData(item)[0]['type'] == 'vector':
680  name = self.GetPyData(item)[0]['maplayer'].GetName()
681  if name == lname:
682  return
683  item = self.GetNextVisible(item)
684 
685  self.first = True
686  params = {} # no initial options parameters
687 
688  # deselect active item
689  if self.layer_selected:
690  self.SelectItem(self.layer_selected, select = False)
691 
692  Debug.msg (3, "LayerTree().AddLayer(): ltype=%s" % (ltype))
693 
694  if ltype == 'command':
695  # generic command item
696  ctrl = wx.TextCtrl(self, id = wx.ID_ANY, value = '',
697  pos = wx.DefaultPosition, size = (self.GetSize()[0]-100,25),
698  # style = wx.TE_MULTILINE|wx.TE_WORDWRAP)
699  style = wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
700  ctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged)
701  # ctrl.Bind(wx.EVT_TEXT, self.OnCmdChanged)
702  elif ltype == 'group':
703  # group item
704  ctrl = None
705  grouptext = _('Layer group:') + str(self.groupnode)
706  self.groupnode += 1
707  else:
708  btnbmp = Icons['layerManager']["layerOptions"].GetBitmap((16,16))
709  ctrl = buttons.GenBitmapButton(self, id = wx.ID_ANY, bitmap = btnbmp, size = (24,24))
710  ctrl.SetToolTipString(_("Click to edit layer settings"))
711  self.Bind(wx.EVT_BUTTON, self.OnLayerContextMenu, ctrl)
712  # add layer to the layer tree
713  if self.layer_selected and self.layer_selected != self.GetRootItem():
714  if self.GetPyData(self.layer_selected)[0]['type'] == 'group' \
715  and self.IsExpanded(self.layer_selected):
716  # add to group (first child of self.layer_selected) if group expanded
717  layer = self.PrependItem(parent = self.layer_selected,
718  text = '', ct_type = 1, wnd = ctrl)
719  else:
720  # prepend to individual layer or non-expanded group
721  if lgroup == -1:
722  # -> last child of root (loading from workspace)
723  layer = self.AppendItem(parentId = self.root,
724  text = '', ct_type = 1, wnd = ctrl)
725  elif lgroup > -1:
726  # -> last child of group (loading from workspace)
727  parent = self.FindItemByIndex(index = lgroup)
728  if not parent:
729  parent = self.root
730  layer = self.AppendItem(parentId = parent,
731  text = '', ct_type = 1, wnd = ctrl)
732  elif lgroup is None:
733  # -> previous sibling of selected layer
734  parent = self.GetItemParent(self.layer_selected)
735  layer = self.InsertItem(parentId = parent,
736  input = self.GetPrevSibling(self.layer_selected),
737  text = '', ct_type = 1, wnd = ctrl)
738  else: # add first layer to the layer tree (first child of root)
739  layer = self.PrependItem(parent = self.root, text = '', ct_type = 1, wnd = ctrl)
740 
741  # layer is initially unchecked as inactive (beside 'command')
742  # use predefined value if given
743  if lchecked is not None:
744  checked = lchecked
745  else:
746  checked = True
747 
748  self.CheckItem(layer, checked = checked)
749 
750  # add text and icons for each layer ltype
751  label = _('(double click to set properties)') + ' ' * 15
752  if ltype == 'raster':
753  self.SetItemImage(layer, self.rast_icon)
754  self.SetItemText(layer, '%s %s' % (_('raster'), label))
755  elif ltype == '3d-raster':
756  self.SetItemImage(layer, self.rast3d_icon)
757  self.SetItemText(layer, '%s %s' % (_('3d raster'), label))
758  elif ltype == 'rgb':
759  self.SetItemImage(layer, self.rgb_icon)
760  self.SetItemText(layer, '%s %s' % (_('RGB'), label))
761  elif ltype == 'his':
762  self.SetItemImage(layer, self.his_icon)
763  self.SetItemText(layer, '%s %s' % (_('HIS'), label))
764  elif ltype == 'shaded':
765  self.SetItemImage(layer, self.shaded_icon)
766  self.SetItemText(layer, '%s %s' % (_('Shaded relief'), label))
767  elif ltype == 'rastnum':
768  self.SetItemImage(layer, self.rnum_icon)
769  self.SetItemText(layer, '%s %s' % (_('raster cell numbers'), label))
770  elif ltype == 'rastarrow':
771  self.SetItemImage(layer, self.rarrow_icon)
772  self.SetItemText(layer, '%s %s' % (_('raster flow arrows'), label))
773  elif ltype == 'vector':
774  self.SetItemImage(layer, self.vect_icon)
775  self.SetItemText(layer, '%s %s' % (_('vector'), label))
776  elif ltype == 'thememap':
777  self.SetItemImage(layer, self.theme_icon)
778  self.SetItemText(layer, '%s %s' % (_('thematic map'), label))
779  elif ltype == 'themechart':
780  self.SetItemImage(layer, self.chart_icon)
781  self.SetItemText(layer, '%s %s' % (_('thematic charts'), label))
782  elif ltype == 'grid':
783  self.SetItemImage(layer, self.grid_icon)
784  self.SetItemText(layer, '%s %s' % (_('grid'), label))
785  elif ltype == 'geodesic':
786  self.SetItemImage(layer, self.geodesic_icon)
787  self.SetItemText(layer, '%s %s' % (_('geodesic line'), label))
788  elif ltype == 'rhumb':
789  self.SetItemImage(layer, self.rhumb_icon)
790  self.SetItemText(layer, '%s %s' % (_('rhumbline'), label))
791  elif ltype == 'labels':
792  self.SetItemImage(layer, self.labels_icon)
793  self.SetItemText(layer, '%s %s' % (_('vector labels'), label))
794  elif ltype == 'command':
795  self.SetItemImage(layer, self.cmd_icon)
796  elif ltype == 'group':
797  self.SetItemImage(layer, self.folder)
798  self.SetItemText(layer, grouptext)
799 
800  self.first = False
801 
802  if ltype != 'group':
803  if lcmd and len(lcmd) > 1:
804  cmd = lcmd
805  render = False
806  name, found = utils.GetLayerNameFromCmd(lcmd)
807  else:
808  cmd = []
809  if ltype == 'command' and lname:
810  for c in lname.split(';'):
811  cmd.append(c.split(' '))
812 
813  render = False
814  name = None
815 
816  if ctrl:
817  ctrlId = ctrl.GetId()
818  else:
819  ctrlId = None
820 
821  # add a data object to hold the layer's command (does not apply to generic command layers)
822  self.SetPyData(layer, ({'cmd' : cmd,
823  'type' : ltype,
824  'ctrl' : ctrlId,
825  'label' : None,
826  'maplayer' : None,
827  'vdigit' : lvdigit,
828  'nviz' : lnviz,
829  'propwin' : None},
830  None))
831 
832  # find previous map layer instance
833  prevItem = self.GetFirstChild(self.root)[0]
834  prevMapLayer = None
835  pos = -1
836  while prevItem and prevItem.IsOk() and prevItem != layer:
837  if self.GetPyData(prevItem)[0]['maplayer']:
838  prevMapLayer = self.GetPyData(prevItem)[0]['maplayer']
839 
840  prevItem = self.GetNextSibling(prevItem)
841 
842  if prevMapLayer:
843  pos = self.Map.GetLayerIndex(prevMapLayer)
844  else:
845  pos = -1
846 
847  maplayer = self.Map.AddLayer(pos = pos,
848  type = ltype, command = self.GetPyData(layer)[0]['cmd'], name = name,
849  l_active = checked, l_hidden = False,
850  l_opacity = lopacity, l_render = render)
851  self.GetPyData(layer)[0]['maplayer'] = maplayer
852 
853  # run properties dialog if no properties given
854  if len(cmd) == 0:
855  self.PropertiesDialog(layer, show = True)
856 
857  else: # group
858  self.SetPyData(layer, ({'cmd' : None,
859  'type' : ltype,
860  'ctrl' : None,
861  'label' : None,
862  'maplayer' : None,
863  'propwin' : None},
864  None))
865 
866  # select new item
867  self.SelectItem(layer, select = True)
868  self.layer_selected = layer
869 
870  # use predefined layer name if given
871  if lname:
872  if ltype == 'group':
873  self.SetItemText(layer, lname)
874  elif ltype == 'command':
875  ctrl.SetValue(lname)
876  else:
877  self.SetItemText(layer, self._getLayerName(layer, lname))
878 
879  # updated progress bar range (mapwindow statusbar)
880  if checked is True:
881  self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
882 
883  return layer
884 
885  def PropertiesDialog (self, layer, show = True):
886  """!Launch the properties dialog"""
887  if 'propwin' in self.GetPyData(layer)[0] and \
888  self.GetPyData(layer)[0]['propwin'] is not None:
889  # recycle GUI dialogs
890  win = self.GetPyData(layer)[0]['propwin']
891  # update properties (columns, layers)
892  win.notebookpanel.OnUpdateSelection(None)
893  if win.IsShown():
894  win.SetFocus()
895  else:
896  win.Show()
897 
898  return
899 
900  completed = ''
901  params = self.GetPyData(layer)[1]
902  ltype = self.GetPyData(layer)[0]['type']
903 
904  Debug.msg (3, "LayerTree.PropertiesDialog(): ltype=%s" % \
905  ltype)
906 
907  if self.GetPyData(layer)[0]['cmd']:
908  module = menuform.GUI(parent = self, show = show, centreOnParent = False)
909  module.ParseCommand(self.GetPyData(layer)[0]['cmd'],
910  completed = (self.GetOptData,layer,params))
911 
912  self.GetPyData(layer)[0]['cmd'] = module.GetCmd()
913  elif ltype == 'raster':
914  cmd = ['d.rast']
915 
916  if UserSettings.Get(group='cmd', key='rasterOverlay', subkey='enabled'):
917  cmd.append('-o')
918 
919  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
920  completed = (self.GetOptData,layer,params))
921 
922  elif ltype == '3d-raster':
923  cmd = ['d.rast3d']
924  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
925  completed = (self.GetOptData,layer,params))
926 
927  elif ltype == 'rgb':
928  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rgb'],
929  completed = (self.GetOptData,layer,params))
930 
931  elif ltype == 'his':
932  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.his'],
933  completed = (self.GetOptData,layer,params))
934 
935  elif ltype == 'shaded':
936  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.shadedmap'],
937  completed = (self.GetOptData,layer,params))
938 
939  elif ltype == 'rastarrow':
940  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.arrow'],
941  completed = (self.GetOptData,layer,params))
942 
943  elif ltype == 'rastnum':
944  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.num'],
945  completed = (self.GetOptData,layer,params))
946 
947  elif ltype == 'vector':
948  types = list()
949  for ftype in ['point', 'line', 'boundary', 'centroid', 'area', 'face']:
950  if UserSettings.Get(group = 'cmd', key = 'showType', subkey = [ftype, 'enabled']):
951  types.append(ftype)
952 
953  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect', 'type=%s' % ','.join(types)],
954  completed = (self.GetOptData,layer,params))
955 
956  elif ltype == 'thememap':
957  # -s flag requested, otherwise only first thematic category is displayed
958  # should be fixed by C-based d.thematic.* modules
959  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect.thematic', '-s'],
960  completed = (self.GetOptData,layer,params))
961 
962  elif ltype == 'themechart':
963  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect.chart'],
964  completed = (self.GetOptData,layer,params))
965 
966  elif ltype == 'grid':
967  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.grid'],
968  completed = (self.GetOptData,layer,params))
969 
970  elif ltype == 'geodesic':
971  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.geodesic'],
972  completed = (self.GetOptData,layer,params))
973 
974  elif ltype == 'rhumb':
975  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rhumbline'],
976  completed = (self.GetOptData,layer,params))
977 
978  elif ltype == 'labels':
979  menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.labels'],
980  completed = (self.GetOptData,layer,params))
981 
982  elif ltype == 'cmdlayer':
983  pass
984  elif ltype == 'group':
985  pass
986 
987  def OnActivateLayer(self, event):
988  """!Double click on the layer item.
989  Launch property dialog, or expand/collapse group of items, etc.
990  """
991  self.lmgr.WorkspaceChanged()
992  layer = event.GetItem()
993  self.layer_selected = layer
994 
995  self.PropertiesDialog (layer)
996 
997  if self.GetPyData(layer)[0]['type'] == 'group':
998  if self.IsExpanded(layer):
999  self.Collapse(layer)
1000  else:
1001  self.Expand(layer)
1002 
1003  def OnDeleteLayer(self, event):
1004  """!Remove selected layer item from the layer tree"""
1005  self.lmgr.WorkspaceChanged()
1006  item = event.GetItem()
1007 
1008  try:
1009  item.properties.Close(True)
1010  except:
1011  pass
1012 
1013  if item != self.root:
1014  Debug.msg (3, "LayerTree.OnDeleteLayer(): name=%s" % \
1015  (self.GetItemText(item)))
1016  else:
1017  self.root = None
1018 
1019  # unselect item
1020  self.Unselect()
1021  self.layer_selected = None
1022 
1023  try:
1024  if self.GetPyData(item)[0]['type'] != 'group':
1025  self.Map.DeleteLayer( self.GetPyData(item)[0]['maplayer'])
1026  except:
1027  pass
1028 
1029  # redraw map if auto-rendering is enabled
1030  self.rerender = True
1031  self.reorder = True
1032 
1033  if self.mapdisplay.toolbars['vdigit']:
1034  self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers (updateTool = True)
1035 
1036  # update progress bar range (mapwindow statusbar)
1037  self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
1038 
1039  event.Skip()
1040 
1041  def OnLayerChecked(self, event):
1042  """!Enable/disable data layer"""
1043  self.lmgr.WorkspaceChanged()
1044 
1045  item = event.GetItem()
1046  checked = item.IsChecked()
1047 
1048  digitToolbar = self.mapdisplay.toolbars['vdigit']
1049  if self.first == False:
1050  # change active parameter for item in layers list in render.Map
1051  if self.GetPyData(item)[0]['type'] == 'group':
1052  child, cookie = self.GetFirstChild(item)
1053  while child:
1054  self.CheckItem(child, checked)
1055  mapLayer = self.GetPyData(child)[0]['maplayer']
1056  if not digitToolbar or \
1057  (digitToolbar and digitToolbar.GetLayer() != mapLayer):
1058  # ignore when map layer is edited
1059  self.Map.ChangeLayerActive(mapLayer, checked)
1060  child = self.GetNextSibling(child)
1061  else:
1062  mapLayer = self.GetPyData(item)[0]['maplayer']
1063  if not digitToolbar or \
1064  (digitToolbar and digitToolbar.GetLayer() != mapLayer):
1065  # ignore when map layer is edited
1066  self.Map.ChangeLayerActive(mapLayer, checked)
1067 
1068  #
1069  # update progress bar range (mapwindow statusbar)
1070  #
1071  self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
1072 
1073  #
1074  # nviz
1075  #
1076  if self.mapdisplay.toolbars['nviz'] and \
1077  self.GetPyData(item) is not None:
1078  # nviz - load/unload data layer
1079  mapLayer = self.GetPyData(item)[0]['maplayer']
1080 
1081  self.mapdisplay.SetStatusText(_("Please wait, updating data..."), 0)
1082 
1083  if checked: # enable
1084  if mapLayer.type == 'raster':
1085  self.mapdisplay.MapWindow.LoadRaster(item)
1086  elif mapLayer.type == '3d-raster':
1087  self.mapdisplay.MapWindow.LoadRaster3d(item)
1088  elif mapLayer.type == 'vector':
1089  npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(mapLayer)
1090  if npoints > 0:
1091  self.mapdisplay.MapWindow.LoadVector(item, points = True)
1092  if nlines > 0:
1093  self.mapdisplay.MapWindow.LoadVector(item, points = False)
1094 
1095  else: # disable
1096  if mapLayer.type == 'raster':
1097  self.mapdisplay.MapWindow.UnloadRaster(item)
1098  elif mapLayer.type == '3d-raster':
1099  self.mapdisplay.MapWindow.UnloadRaster3d(item)
1100  elif mapLayer.type == 'vector':
1101  self.mapdisplay.MapWindow.UnloadVector(item)
1102 
1103  self.mapdisplay.SetStatusText("", 0)
1104 
1105  # redraw map if auto-rendering is enabled
1106  self.rerender = True
1107  self.reorder = True
1108 
1109  def OnCmdChanged(self, event):
1110  """!Change command string"""
1111  ctrl = event.GetEventObject().GetId()
1112  cmd = event.GetString()
1113 
1114  layer = self.GetFirstVisibleItem()
1115 
1116  while layer and layer.IsOk():
1117  if self.GetPyData(layer)[0]['ctrl'] == ctrl:
1118  break
1119 
1120  layer = self.GetNextVisible(layer)
1121 
1122  # change parameters for item in layers list in render.Map
1123  self.ChangeLayer(layer)
1124 
1125  event.Skip()
1126 
1127  def OnChangeSel(self, event):
1128  """!Selection changed"""
1129  oldlayer = event.GetOldItem()
1130  layer = event.GetItem()
1131  if layer == oldlayer:
1132  event.Veto()
1133  return
1134 
1135  digitToolbar = self.mapdisplay.toolbars['vdigit']
1136  if digitToolbar:
1137  mapLayer = self.GetPyData(layer)[0]['maplayer']
1138  bgmap = UserSettings.Get(group = 'vdigit', key = 'bgmap', subkey = 'value',
1139  internal = True)
1140 
1141  if digitToolbar.GetLayer() == mapLayer:
1142  self._setGradient('vdigit')
1143  elif bgmap == mapLayer.GetName():
1144  self._setGradient('bgmap')
1145  else:
1146  self._setGradient()
1147  else:
1148  self._setGradient()
1149 
1150  self.layer_selected = layer
1151 
1152  try:
1153  if self.IsSelected(oldlayer):
1154  self.SetItemWindowEnabled(oldlayer, True)
1155  else:
1156  self.SetItemWindowEnabled(oldlayer, False)
1157 
1158  if self.IsSelected(layer):
1159  self.SetItemWindowEnabled(layer, True)
1160  else:
1161  self.SetItemWindowEnabled(layer, False)
1162  except:
1163  pass
1164 
1165  try:
1166  self.RefreshLine(oldlayer)
1167  self.RefreshLine(layer)
1168  except:
1169  pass
1170 
1171  # update statusbar -> show command string
1172  if self.GetPyData(layer) and self.GetPyData(layer)[0]['maplayer']:
1173  cmd = self.GetPyData(layer)[0]['maplayer'].GetCmd(string = True)
1174  if len(cmd) > 0:
1175  self.lmgr.SetStatusText(cmd)
1176 
1177  # set region if auto-zooming is enabled
1178  if self.GetPyData(layer) and self.GetPyData(layer)[0]['cmd'] and \
1179  UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled'):
1180  mapLayer = self.GetPyData(layer)[0]['maplayer']
1181  if mapLayer.GetType() in ('raster', 'vector'):
1182  render = self.mapdisplay.statusbarWin['render'].IsChecked()
1183  self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
1184  render = render)
1185 
1186  # update nviz tools
1187  if self.mapdisplay.toolbars['nviz'] and \
1188  self.GetPyData(self.layer_selected) is not None:
1189  if self.layer_selected.IsChecked():
1190  # update Nviz tool window
1191  type = self.GetPyData(self.layer_selected)[0]['maplayer'].type
1192 
1193  if type == 'raster':
1194  self.lmgr.nviz.UpdatePage('surface')
1195  self.lmgr.nviz.SetPage('surface')
1196  elif type == 'vector':
1197  self.lmgr.nviz.UpdatePage('vector')
1198  self.lmgr.nviz.SetPage('vector')
1199  elif type == '3d-raster':
1200  self.lmgr.nviz.UpdatePage('volume')
1201  self.lmgr.nviz.SetPage('volume')
1202 
1203  def OnCollapseNode(self, event):
1204  """!Collapse node
1205  """
1206  if self.GetPyData(self.layer_selected)[0]['type'] == 'group':
1207  self.SetItemImage(self.layer_selected, self.folder)
1208 
1209  def OnExpandNode(self, event):
1210  """!Expand node
1211  """
1212  self.layer_selected = event.GetItem()
1213  if self.GetPyData(self.layer_selected)[0]['type'] == 'group':
1214  self.SetItemImage(self.layer_selected, self.folder_open)
1215 
1216  def OnEndDrag(self, event):
1217  self.StopDragging()
1218  dropTarget = event.GetItem()
1219  self.flag = self.HitTest(event.GetPoint())[1]
1220  if self.IsValidDropTarget(dropTarget):
1221  self.UnselectAll()
1222  if dropTarget != None:
1223  self.SelectItem(dropTarget)
1224  self.OnDrop(dropTarget, self._dragItem)
1225  elif dropTarget == None:
1226  self.OnDrop(dropTarget, self._dragItem)
1227 
1228  def OnDrop(self, dropTarget, dragItem):
1229  # save everthing associated with item to drag
1230  try:
1231  old = dragItem # make sure this member exists
1232  except:
1233  return
1234 
1235  Debug.msg (4, "LayerTree.OnDrop(): layer=%s" % \
1236  (self.GetItemText(dragItem)))
1237 
1238  # recreate data layer, insert copy of layer in new position, and delete original at old position
1239  newItem = self.RecreateItem (dragItem, dropTarget)
1240 
1241  # if recreated layer is a group, also recreate its children
1242  if self.GetPyData(newItem)[0]['type'] == 'group':
1243  (child, cookie) = self.GetFirstChild(dragItem)
1244  if child:
1245  while child:
1246  self.RecreateItem(child, dropTarget, parent = newItem)
1247  self.Delete(child)
1248  child = self.GetNextChild(old, cookie)[0]
1249 
1250  # delete layer at original position
1251  try:
1252  self.Delete(old) # entry in render.Map layers list automatically deleted by OnDeleteLayer handler
1253  except AttributeError:
1254  pass
1255 
1256  # redraw map if auto-rendering is enabled
1257  self.rerender = True
1258  self.reorder = True
1259 
1260  # select new item
1261  self.SelectItem(newItem)
1262 
1263  def RecreateItem (self, dragItem, dropTarget, parent = None):
1264  """!Recreate item (needed for OnEndDrag())
1265  """
1266  Debug.msg (4, "LayerTree.RecreateItem(): layer=%s" % \
1267  self.GetItemText(dragItem))
1268 
1269  # fetch data (dragItem)
1270  checked = self.IsItemChecked(dragItem)
1271  image = self.GetItemImage(dragItem, 0)
1272  text = self.GetItemText(dragItem)
1273  if self.GetPyData(dragItem)[0]['ctrl']:
1274  # recreate data layer
1275  btnbmp = Icons['layerManager']["layerOptions"].GetBitmap((16,16))
1276  newctrl = buttons.GenBitmapButton(self, id = wx.ID_ANY, bitmap = btnbmp, size = (24, 24))
1277  newctrl.SetToolTipString(_("Click to edit layer settings"))
1278  self.Bind(wx.EVT_BUTTON, self.OnLayerContextMenu, newctrl)
1279  data = self.GetPyData(dragItem)
1280 
1281  elif self.GetPyData(dragItem)[0]['type'] == 'command':
1282  # recreate command layer
1283  oldctrl = None
1284  newctrl = wx.TextCtrl(self, id = wx.ID_ANY, value = '',
1285  pos = wx.DefaultPosition, size = (250,25),
1286  style = wx.TE_MULTILINE|wx.TE_WORDWRAP)
1287  try:
1288  newctrl.SetValue(self.GetPyData(dragItem)[0]['maplayer'].GetCmd(string = True))
1289  except:
1290  pass
1291  newctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged)
1292  newctrl.Bind(wx.EVT_TEXT, self.OnCmdChanged)
1293  data = self.GetPyData(dragItem)
1294 
1295  elif self.GetPyData(dragItem)[0]['type'] == 'group':
1296  # recreate group
1297  newctrl = None
1298  data = None
1299 
1300  # decide where to put recreated item
1301  if dropTarget != None and dropTarget != self.GetRootItem():
1302  if parent:
1303  # new item is a group
1304  afteritem = parent
1305  else:
1306  # new item is a single layer
1307  afteritem = dropTarget
1308 
1309  # dragItem dropped on group
1310  if self.GetPyData(afteritem)[0]['type'] == 'group':
1311  newItem = self.PrependItem(afteritem, text = text, \
1312  ct_type = 1, wnd = newctrl, image = image, \
1313  data = data)
1314  self.Expand(afteritem)
1315  else:
1316  #dragItem dropped on single layer
1317  newparent = self.GetItemParent(afteritem)
1318  newItem = self.InsertItem(newparent, self.GetPrevSibling(afteritem), \
1319  text = text, ct_type = 1, wnd = newctrl, \
1320  image = image, data = data)
1321  else:
1322  # if dragItem not dropped on a layer or group, append or prepend it to the layer tree
1323  if self.flag & wx.TREE_HITTEST_ABOVE:
1324  newItem = self.PrependItem(self.root, text = text, \
1325  ct_type = 1, wnd = newctrl, image = image, \
1326  data = data)
1327  elif (self.flag & wx.TREE_HITTEST_BELOW) or (self.flag & wx.TREE_HITTEST_NOWHERE) \
1328  or (self.flag & wx.TREE_HITTEST_TOLEFT) or (self.flag & wx.TREE_HITTEST_TORIGHT):
1329  newItem = self.AppendItem(self.root, text = text, \
1330  ct_type = 1, wnd = newctrl, image = image, \
1331  data = data)
1332 
1333  #update new layer
1334  self.SetPyData(newItem, self.GetPyData(dragItem))
1335  if newctrl:
1336  self.GetPyData(newItem)[0]['ctrl'] = newctrl.GetId()
1337  else:
1338  self.GetPyData(newItem)[0]['ctrl'] = None
1339 
1340  self.CheckItem(newItem, checked = checked) # causes a new render
1341 
1342  return newItem
1343 
1344  def _getLayerName(self, item, lname = ''):
1345  """!Get layer name string
1346 
1347  @param lname optional layer name
1348  """
1349  mapLayer = self.GetPyData(item)[0]['maplayer']
1350  if not lname:
1351  lname = self.GetPyData(item)[0]['label']
1352  opacity = int(mapLayer.GetOpacity(float = True) * 100)
1353  if not lname:
1354  dcmd = self.GetPyData(item)[0]['cmd']
1355  lname, found = utils.GetLayerNameFromCmd(dcmd, layerType = mapLayer.GetType(),
1356  fullyQualified = True)
1357  if not found:
1358  return None
1359 
1360  if opacity < 100:
1361  return lname + ' (%s %d' % (_('opacity:'), opacity) + '%)'
1362 
1363  return lname
1364 
1365  def GetOptData(self, dcmd, layer, params, propwin):
1366  """!Process layer data (when changes in propertiesdialog are applied)"""
1367  # set layer text to map name
1368  if dcmd:
1369  self.GetPyData(layer)[0]['cmd'] = dcmd
1370  mapText = self._getLayerName(layer)
1371  mapName, found = utils.GetLayerNameFromCmd(dcmd)
1372  mapLayer = self.GetPyData(layer)[0]['maplayer']
1373  self.SetItemText(layer, mapName)
1374 
1375  if not mapText or not found:
1376  propwin.Hide()
1377  GWarning(parent = self,
1378  message = _("Map <%s> not found.") % mapName)
1379  return
1380 
1381  # update layer data
1382  if params:
1383  self.SetPyData(layer, (self.GetPyData(layer)[0], params))
1384  self.GetPyData(layer)[0]['propwin'] = propwin
1385 
1386  # change parameters for item in layers list in render.Map
1387  self.ChangeLayer(layer)
1388 
1389  # set region if auto-zooming is enabled
1390  if dcmd and UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled'):
1391  mapLayer = self.GetPyData(layer)[0]['maplayer']
1392  if mapLayer.GetType() in ('raster', 'vector'):
1393  render = UserSettings.Get(group = 'display', key = 'autoRendering', subkey = 'enabled')
1394  self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
1395  render = render)
1396 
1397  # update nviz session
1398  if self.mapdisplay.toolbars['nviz'] and dcmd:
1399  mapLayer = self.GetPyData(layer)[0]['maplayer']
1400  mapWin = self.mapdisplay.MapWindow
1401  if len(mapLayer.GetCmd()) > 0:
1402  id = -1
1403  if mapLayer.type == 'raster':
1404  if mapWin.IsLoaded(layer):
1405  mapWin.UnloadRaster(layer)
1406 
1407  mapWin.LoadRaster(layer)
1408 
1409  elif mapLayer.type == '3d-raster':
1410  if mapWin.IsLoaded(layer):
1411  mapWin.UnloadRaster3d(layer)
1412 
1413  mapWin.LoadRaster3d(layer)
1414 
1415  elif mapLayer.type == 'vector':
1416  if mapWin.IsLoaded(layer):
1417  mapWin.UnloadVector(layer)
1418 
1419  mapWin.LoadVector(layer)
1420 
1421  # reset view when first layer loaded
1422  nlayers = len(mapWin.Map.GetListOfLayers(l_type = ('raster', 'vector'),
1423  l_active = True))
1424  if nlayers < 2:
1425  mapWin.ResetView()
1426 
1427  def ReorderLayers(self):
1428  """!Add commands from data associated with any valid layers
1429  (checked or not) to layer list in order to match layers in
1430  layer tree."""
1431 
1432  # make a list of visible layers
1433  treelayers = []
1434 
1435  vislayer = self.GetFirstVisibleItem()
1436 
1437  if not vislayer or self.GetPyData(vislayer) is None:
1438  return
1439 
1440  itemList = ""
1441 
1442  for item in range(self.GetCount()):
1443  itemList += self.GetItemText(vislayer) + ','
1444  if self.GetPyData(vislayer)[0]['type'] != 'group':
1445  treelayers.append(self.GetPyData(vislayer)[0]['maplayer'])
1446 
1447  if not self.GetNextVisible(vislayer):
1448  break
1449  else:
1450  vislayer = self.GetNextVisible(vislayer)
1451 
1452  Debug.msg (4, "LayerTree.ReorderLayers(): items=%s" % \
1453  (itemList))
1454 
1455  # reorder map layers
1456  treelayers.reverse()
1457  self.Map.ReorderLayers(treelayers)
1458  self.reorder = False
1459 
1460  def ChangeLayer(self, item):
1461  """!Change layer"""
1462  type = self.GetPyData(item)[0]['type']
1463  layerName = None
1464 
1465  if type == 'command':
1466  win = self.FindWindowById(self.GetPyData(item)[0]['ctrl'])
1467  if win.GetValue() != None:
1468  cmd = win.GetValue().split(';')
1469  cmdlist = []
1470  for c in cmd:
1471  cmdlist.append(c.split(' '))
1472  opac = 1.0
1473  chk = self.IsItemChecked(item)
1474  hidden = not self.IsVisible(item)
1475  elif type != 'group':
1476  if self.GetPyData(item)[0] is not None:
1477  cmdlist = self.GetPyData(item)[0]['cmd']
1478  opac = self.GetPyData(item)[0]['maplayer'].GetOpacity(float = True)
1479  chk = self.IsItemChecked(item)
1480  hidden = not self.IsVisible(item)
1481  # determine layer name
1482  layerName, found = utils.GetLayerNameFromCmd(cmdlist, fullyQualified = True)
1483  if not found:
1484  layerName = self.GetItemText(item)
1485 
1486  maplayer = self.Map.ChangeLayer(layer = self.GetPyData(item)[0]['maplayer'], type = type,
1487  command = cmdlist, name = layerName,
1488  l_active = chk, l_hidden = hidden, l_opacity = opac, l_render = False)
1489 
1490  self.GetPyData(item)[0]['maplayer'] = maplayer
1491 
1492  # if digitization tool enabled -> update list of available vector map layers
1493  if self.mapdisplay.toolbars['vdigit']:
1494  self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers(updateTool = True)
1495 
1496  # redraw map if auto-rendering is enabled
1497  self.rerender = True
1498  self.reorder = True
1499 
1500  def OnCloseWindow(self, event):
1501  pass
1502  # self.Map.Clean()
1503 
1504  def FindItemByData(self, key, value):
1505  """!Find item based on key and value (see PyData[0])
1506 
1507  @return item instance
1508  @return None not found
1509  """
1510  item = self.GetFirstChild(self.root)[0]
1511  return self.__FindSubItemByData(item, key, value)
1512 
1513  def FindItemByIndex(self, index):
1514  """!Find item by index (starting at 0)
1515 
1516  @return item instance
1517  @return None not found
1518  """
1519  item = self.GetFirstChild(self.root)[0]
1520  i = 0
1521  while item and item.IsOk():
1522  if i == index:
1523  return item
1524 
1525  item = self.GetNextVisible(item)
1526  i += 1
1527 
1528  return None
1529 
1530  def EnableItemType(self, type, enable = True):
1531  """!Enable/disable items in layer tree"""
1532  item = self.GetFirstChild(self.root)[0]
1533  while item and item.IsOk():
1534  mapLayer = self.GetPyData(item)[0]['maplayer']
1535  if mapLayer and type == mapLayer.type:
1536  self.EnableItem(item, enable)
1537 
1538  item = self.GetNextSibling(item)
1539 
1540  def __FindSubItemByData(self, item, key, value):
1541  """!Support method for FindItemByValue"""
1542  while item and item.IsOk():
1543  try:
1544  itemValue = self.GetPyData(item)[0][key]
1545  except KeyError:
1546  return None
1547 
1548  if value == itemValue:
1549  return item
1550  if self.GetPyData(item)[0]['type'] == 'group':
1551  subItem = self.GetFirstChild(item)[0]
1552  found = self.__FindSubItemByData(subItem, key, value)
1553  if found:
1554  return found
1555  item = self.GetNextSibling(item)
1556 
1557  return None
1558