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