GRASS Programmer's Manual  6.4.4(2014)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
render.py
Go to the documentation of this file.
1 """!
2 @package core.render
3 
4 @brief Rendering map layers and overlays into map composition image.
5 
6 Classes:
7  - render::Layer
8  - render::MapLayer
9  - render::Overlay
10  - render::Map
11 
12 (C) 2006-2011 by the GRASS Development Team
13 
14 This program is free software under the GNU General Public License
15 (>=v2). Read the file COPYING that comes with GRASS for details.
16 
17 @author Michael Barton
18 @author Jachym Cepicky
19 @author Martin Landa <landa.martin gmail.com>
20 """
21 
22 import os
23 import sys
24 import glob
25 import math
26 import copy
27 import tempfile
28 import types
29 
30 import wx
31 from wx.lib.newevent import NewEvent
32 
33 from grass.script import core as grass
34 
35 from core import utils
36 from core.gcmd import GException, GError, RunCommand
37 from core.debug import Debug
38 from core.settings import UserSettings
39 
40 wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent()
41 
42 #
43 # use g.pnmcomp for creating image composition or
44 # wxPython functionality
45 #
46 USE_GPNMCOMP = True
47 
48 class Layer(object):
49  """!Virtual class which stores information about layers (map layers and
50  overlays) of the map composition.
51 
52  For map layer use MapLayer class.
53  For overlays use Overlay class.
54  """
55  def __init__(self, type, cmd, name = None,
56  active = True, hidden = False, opacity = 1.0):
57  """!
58  @todo pass cmd as tuple instead of list
59 
60  @param type layer type ('raster', 'vector', 'overlay', 'command', etc.)
61  @param cmd GRASS command to render layer,
62  given as list, e.g. ['d.rast', 'map=elevation@PERMANENT']
63  @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree)
64  @param active layer is active, will be rendered only if True
65  @param hidden layer is hidden, won't be listed in Layer Manager if True
66  @param opacity layer opacity <0;1>
67  """
68  self.type = type
69  self.name = name
70 
71  if self.type == 'command':
72  self.cmd = list()
73  for c in cmd:
74  self.cmd.append(utils.CmdToTuple(c))
75  else:
76  self.cmd = utils.CmdToTuple(cmd)
77 
78  self.active = active
79  self.hidden = hidden
80  self.opacity = opacity
81 
82  self.force_render = True
83 
84  Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \
85  "active=%d, opacity=%d, hidden=%d" % \
86  (self.type, self.GetCmd(string = True), self.name, self.active,
87  self.opacity, self.hidden))
88 
89  # generated file for each layer
90  self.gtemp = tempfile.mkstemp()[1]
91  self.maskfile = self.gtemp + ".pgm"
92  if self.type == 'overlay':
93  self.mapfile = self.gtemp + ".png"
94  else:
95  self.mapfile = self.gtemp + ".ppm"
96 
97  def __del__(self):
98  Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
99  (self.name, self.GetCmd(string = True)))
100 
101  def Render(self):
102  """!Render layer to image
103 
104  @return rendered image filename
105  @return None on error
106  """
107  if not self.cmd:
108  return None
109 
110  # ignore in 2D
111  if self.type == '3d-raster':
112  return None
113 
114  Debug.msg (3, "Layer.Render(): type=%s, name=%s" % \
115  (self.type, self.name))
116 
117  # prepare command for each layer
118  layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
119  'vector','thememap','themechart',
120  'grid', 'geodesic', 'rhumb', 'labels',
121  'command', 'rastleg',
122  'overlay')
123 
124  if self.type not in layertypes:
125  raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
126  {'type' : self.type, 'name' : self.name})
127 
128  # start monitor
129  if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
130 # os.environ["GRASS_CAIROFILE"] = self.mapfile
131 # if 'cairo' not in gcmd.RunCommand('d.mon',
132 # flags='p',
133 # read = True):
134 # gcmd.RunCommand('d.mon',
135 # start = 'cairo')
136  if not self.mapfile:
137  self.gtemp = tempfile.mkstemp()[1]
138  self.maskfile = self.gtemp + ".pgm"
139  if self.type == 'overlay':
140  self.mapfile = self.gtemp + ".png"
141  else:
142  self.mapfile = self.gtemp + ".ppm"
143 
144  if self.mapfile:
145  os.environ["GRASS_CAIROFILE"] = self.mapfile
146  else:
147  if not self.mapfile:
148  self.gtemp = tempfile.mkstemp()[1]
149  self.maskfile = self.gtemp + ".pgm"
150  if self.type == 'overlay':
151  self.mapfile = self.gtemp + ".png"
152  else:
153  self.mapfile = self.gtemp + ".ppm"
154 
155  if self.mapfile:
156  os.environ["GRASS_PNGFILE"] = self.mapfile
157 
158  # execute command
159  try:
160  if self.type == 'command':
161  read = False
162  for c in self.cmd:
163  ret, msg = RunCommand(c[0],
164  getErrorMsg = True,
165  quiet = True,
166  **c[1])
167  if ret != 0:
168  break
169  if not read:
170  os.environ["GRASS_PNG_READ"] = "TRUE"
171 
172  os.environ["GRASS_PNG_READ"] = "FALSE"
173  else:
174  ret, msg = RunCommand(self.cmd[0],
175  getErrorMsg = True,
176  quiet = True,
177  **self.cmd[1])
178 
179  if ret != 0:
180  sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
181  if msg:
182  sys.stderr.write(_("Details: %s\n") % msg)
183  raise GException()
184 
185  except GException:
186  # clean up after problems
187  try:
188  os.remove(self.mapfile)
189  os.remove(self.maskfile)
190  os.remove(self.gtemp)
191  except (OSError, TypeError):
192  pass
193  self.mapfile = None
194  self.maskfile = None
195 
196  # stop monitor
197  if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
198 # gcmd.RunCommand('d.mon',
199 # stop = 'cairo')
200  del os.environ["GRASS_CAIROFILE"]
201  elif "GRASS_PNGFILE" in os.environ:
202  del os.environ["GRASS_PNGFILE"]
203 
204  self.force_render = False
205 
206  return self.mapfile
207 
208  def GetCmd(self, string = False):
209  """!Get GRASS command as list of string.
210 
211  @param string get command as string if True otherwise as list
212 
213  @return command list/string
214  """
215  if string:
216  if self.type == 'command':
217  scmd = []
218  for c in self.cmd:
219  scmd.append(utils.GetCmdString(c))
220 
221  return ';'.join(scmd)
222  else:
223  return utils.GetCmdString(self.cmd)
224  else:
225  return self.cmd
226 
227  def GetType(self):
228  """!Get map layer type"""
229  return self.type
230 
231  def GetElement(self):
232  """!Get map element type"""
233  if self.type == 'raster':
234  return 'cell'
235  return self.type
236 
237  def GetOpacity(self, float = False):
238  """
239  Get layer opacity level
240 
241  @param float get opacity level in <0,1> otherwise <0,100>
242 
243  @return opacity level
244  """
245  if float:
246  return self.opacity
247 
248  return int (self.opacity * 100)
249 
250  def GetName(self, fullyQualified = True):
251  """!Get map layer name
252 
253  @param fullyQualified True to return fully qualified name as a
254  string 'name@mapset' otherwise directory { 'name', 'mapset' }
255  is returned
256 
257  @return string / directory
258  """
259  if fullyQualified:
260  return self.name
261  else:
262  if '@' in self.name:
263  return { 'name' : self.name.split('@')[0],
264  'mapset' : self.name.split('@')[1] }
265  else:
266  return { 'name' : self.name,
267  'mapset' : '' }
268 
269  def IsActive(self):
270  """!Check if layer is activated for rendering"""
271  return self.active
272 
273  def SetType(self, type):
274  """!Set layer type"""
275  if type not in ('raster', '3d-raster', 'vector',
276  'overlay', 'command',
277  'shaded', 'rgb', 'his', 'rastarrow', 'rastnum',
278  'thememap', 'themechart', 'grid', 'labels',
279  'geodesic','rhumb'):
280  raise GException(_("Unsupported map layer type '%s'") % type)
281 
282  self.type = type
283 
284  def SetName(self, name):
285  """!Set layer name"""
286  self.name = name
287 
288  def SetActive(self, enable = True):
289  """!Active or deactive layer"""
290  self.active = bool(enable)
291 
292  def SetHidden(self, enable = False):
293  """!Hide or show map layer in Layer Manager"""
294  self.hidden = bool(enable)
295 
296  def SetOpacity(self, value):
297  """!Set opacity value"""
298  if value < 0:
299  value = 0.
300  elif value > 1:
301  value = 1.
302 
303  self.opacity = float(value)
304 
305  def SetCmd(self, cmd):
306  """!Set new command for layer"""
307  if self.type == 'command':
308  self.cmd = []
309  for c in cmd:
310  self.cmd.append(utils.CmdToTuple(c))
311  else:
312  self.cmd = utils.CmdToTuple(cmd)
313  Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string = True))
314 
315  # for re-rendering
316  self.force_render = True
317 
319  def __init__(self, type, cmd, name = None,
320  active = True, hidden = False, opacity = 1.0):
321  """!Represents map layer in the map canvas
322 
323  @param type layer type ('raster', 'vector', 'command', etc.)
324  @param cmd GRASS command to render layer,
325  given as list, e.g. ['d.rast', 'map=elevation@PERMANENT']
326  @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree) or None
327  @param active layer is active, will be rendered only if True
328  @param hidden layer is hidden, won't be listed in Layer Manager if True
329  @param opacity layer opacity <0;1>
330  """
331  Layer.__init__(self, type, cmd, name,
332  active, hidden, opacity)
333 
334  def GetMapset(self):
335  """!Get mapset of map layer
336 
337  @return mapset name
338  @return '' on error (no name given)
339  """
340  if not self.name:
341  return ''
342 
343  try:
344  return self.name.split('@')[1]
345  except IndexError:
346  return self.name
347 
348 class Overlay(Layer):
349  def __init__(self, id, type, cmd,
350  active = True, hidden = True, opacity = 1.0):
351  """!Represents overlay displayed in map canvas
352 
353  @param id overlay id (for PseudoDC)
354  @param type overlay type ('barscale', 'legend', etc.)
355  @param cmd GRASS command to render overlay,
356  given as list, e.g. ['d.legend', 'map=elevation@PERMANENT']
357  @param active layer is active, will be rendered only if True
358  @param hidden layer is hidden, won't be listed in Layer Manager if True
359  @param opacity layer opacity <0;1>
360  """
361  Layer.__init__(self, 'overlay', cmd, type,
362  active, hidden, opacity)
363 
364  self.id = id
365 
366 class Map(object):
367  """!Map composition (stack of map layers and overlays)
368  """
369  def __init__(self, gisrc = None):
370  # region/extent settigns
371  self.wind = dict() # WIND settings (wind file)
372  self.region = dict() # region settings (g.region)
373  self.width = 640 # map width
374  self.height = 480 # map height
375 
376  # list of layers
377  self.layers = list() # stack of available GRASS layer
378 
379  self.overlays = list() # stack of available overlays
380  self.ovlookup = dict() # lookup dictionary for overlay items and overlays
381 
382  # environment settings
383  # environment variables, like MAPSET, LOCATION_NAME, etc.
384  self.env = dict()
385  # path to external gisrc
386  self.gisrc = gisrc
387 
388  # generated file for g.pnmcomp output for rendering the map
389  self.mapfile = tempfile.mkstemp(suffix = '.ppm')[1]
390 
391  # setting some initial env. variables
392  self._initGisEnv() # g.gisenv
393  self.GetWindow()
394  # GRASS environment variable (for rendering)
395  os.environ["GRASS_TRANSPARENT"] = "TRUE"
396  os.environ["GRASS_BACKGROUNDCOLOR"] = "ffffff"
397 
398  # projection info
399  self.projinfo = self._projInfo()
400 
401  def _runCommand(self, cmd, **kwargs):
402  """!Run command in environment defined by self.gisrc if
403  defined"""
404  # use external gisrc if defined
405  gisrc_orig = os.getenv("GISRC")
406  if self.gisrc:
407  os.environ["GISRC"] = self.gisrc
408 
409  ret = cmd(**kwargs)
410 
411  # back to original gisrc
412  if self.gisrc:
413  os.environ["GISRC"] = gisrc_orig
414 
415  return ret
416 
417  def _initGisEnv(self):
418  """!Stores GRASS variables (g.gisenv) to self.env variable
419  """
420  if not os.getenv("GISBASE"):
421  sys.exit(_("GISBASE not set. You must be in GRASS GIS to run this program."))
422 
423  self.env = self._runCommand(grass.gisenv)
424 
425  def GetProjInfo(self):
426  """!Get projection info"""
427  return self.projinfo
428 
429  def _projInfo(self):
430  """!Return region projection and map units information
431  """
432  projinfo = dict()
433  if not grass.find_program('g.proj', ['--help']):
434  sys.exit(_("GRASS module '%s' not found. Unable to start map "
435  "display window.") % 'g.proj')
436 
437  ret = self._runCommand(RunCommand, prog = 'g.proj',
438  read = True, flags = 'p')
439 
440  if not ret:
441  return projinfo
442 
443  for line in ret.splitlines():
444  if ':' in line:
445  key, val = map(lambda x: x.strip(), line.split(':'))
446  if key in ['units']:
447  val = val.lower()
448  projinfo[key] = val
449  elif "XY location (unprojected)" in line:
450  projinfo['proj'] = 'xy'
451  projinfo['units'] = ''
452  break
453 
454  return projinfo
455 
456  def GetWindow(self):
457  """!Read WIND file and set up self.wind dictionary"""
458  # FIXME: duplicated region WIND == g.region (at least some values)
459  filename = os.path.join (self.env['GISDBASE'],
460  self.env['LOCATION_NAME'],
461  self.env['MAPSET'],
462  "WIND")
463  try:
464  windfile = open (filename, "r")
465  except IOError, e:
466  sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \
467  { 'file' : filename, 'ret' : e})
468 
469  for line in windfile.readlines():
470  line = line.strip()
471  key, value = line.split(":", 1)
472  self.wind[key.strip()] = value.strip()
473 
474  windfile.close()
475 
476  return self.wind
477 
478  def AdjustRegion(self):
479  """!Adjusts display resolution to match monitor size in
480  pixels. Maintains constant display resolution, not related to
481  computational region. Do NOT use the display resolution to set
482  computational resolution. Set computational resolution through
483  g.region.
484  """
485  mapwidth = abs(self.region["e"] - self.region["w"])
486  mapheight = abs(self.region['n'] - self.region['s'])
487 
488  self.region["nsres"] = mapheight / self.height
489  self.region["ewres"] = mapwidth / self.width
490  self.region['rows'] = round(mapheight / self.region["nsres"])
491  self.region['cols'] = round(mapwidth / self.region["ewres"])
492  self.region['cells'] = self.region['rows'] * self.region['cols']
493 
494  Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)
495 
496  return self.region
497 
498  def AlignResolution(self):
499  """!Sets display extents to even multiple of current
500  resolution defined in WIND file from SW corner. This must be
501  done manually as using the -a flag can produce incorrect
502  extents.
503  """
504  # new values to use for saving to region file
505  new = {}
506  n = s = e = w = 0.0
507  nwres = ewres = 0.0
508 
509  # Get current values for region and display
510  reg = self.GetRegion()
511  nsres = reg['nsres']
512  ewres = reg['ewres']
513 
514  n = float(self.region['n'])
515  s = float(self.region['s'])
516  e = float(self.region['e'])
517  w = float(self.region['w'])
518 
519  # Calculate rows, columns, and extents
520  new['rows'] = math.fabs(round((n-s)/nsres))
521  new['cols'] = math.fabs(round((e-w)/ewres))
522 
523  # Calculate new extents
524  new['s'] = nsres * round(s / nsres)
525  new['w'] = ewres * round(w / ewres)
526  new['n'] = new['s'] + (new['rows'] * nsres)
527  new['e'] = new['w'] + (new['cols'] * ewres)
528 
529  return new
530 
532  """!Align region extent based on display size from center
533  point"""
534  # calculate new bounding box based on center of display
535  if self.region["ewres"] > self.region["nsres"]:
536  res = self.region["ewres"]
537  else:
538  res = self.region["nsres"]
539 
540  Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
541  (self.width, self.height, res, self.region['center_easting'],
542  self.region['center_northing']))
543 
544  ew = (self.width / 2) * res
545  ns = (self.height / 2) * res
546 
547  self.region['n'] = self.region['center_northing'] + ns
548  self.region['s'] = self.region['center_northing'] - ns
549  self.region['e'] = self.region['center_easting'] + ew
550  self.region['w'] = self.region['center_easting'] - ew
551 
552  # LL locations
553  if self.projinfo['proj'] == 'll':
554  self.region['n'] = min(self.region['n'], 90.0)
555  self.region['s'] = max(self.region['s'], -90.0)
556 
557  def ChangeMapSize(self, (width, height)):
558  """!Change size of rendered map.
559 
560  @param width,height map size given as tuple
561  """
562  try:
563  self.width = int(width)
564  self.height = int(height)
565  if self.width < 1 or self.height < 1:
566  sys.stderr.write(_("Invalid map size %d,%d\n") % (self.width, self.height))
567  raise ValueError
568  except ValueError:
569  self.width = 640
570  self.height = 480
571 
572  Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
573  (self.width, self.height))
574 
575  def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
576  n = None, s = None, e = None, w = None, default = False,
577  update = False):
578  """!Get region settings (g.region -upgc)
579 
580  Optionally extent, raster or vector map layer can be given.
581 
582  @param rast list of raster maps
583  @param zoom zoom to raster map (ignore NULLs)
584  @param vect list of vector maps
585  @param regionName named region or None
586  @param n,s,e,w force extent
587  @param default force default region settings
588  @param update if True update current display region settings
589 
590  @return region settings as directory, e.g. {
591  'n':'4928010', 's':'4913700', 'w':'589980',...}
592 
593  @see GetCurrentRegion()
594  """
595  region = {}
596 
597  tmpreg = os.getenv("GRASS_REGION")
598  if tmpreg:
599  del os.environ["GRASS_REGION"]
600 
601  # use external gisrc if defined
602  gisrc_orig = os.getenv("GISRC")
603  if self.gisrc:
604  os.environ["GISRC"] = self.gisrc
605 
606  # do not update & shell style output
607  cmd = {}
608  cmd['flags'] = 'ugpc'
609 
610  if default:
611  cmd['flags'] += 'd'
612 
613  if regionName:
614  cmd['region'] = regionName
615 
616  if n:
617  cmd['n'] = n
618  if s:
619  cmd['s'] = s
620  if e:
621  cmd['e'] = e
622  if w:
623  cmd['w'] = w
624 
625  if rast:
626  if zoom:
627  cmd['zoom'] = rast[0]
628  else:
629  cmd['rast'] = ','.join(rast)
630 
631  if vect:
632  cmd['vect'] = ','.join(vect)
633 
634  ret, reg, msg = RunCommand('g.region',
635  read = True,
636  getErrorMsg = True,
637  **cmd)
638 
639  if ret != 0:
640  if rast:
641  message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
642  "\n\n" + _("Details:") + " %s" % msg
643  elif vect:
644  message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
645  "\n\n" + _("Details:") + " %s" % msg
646  else:
647  message = _("Unable to get current geographic extent. "
648  "Force quiting wxGUI. Please manually run g.region to "
649  "fix the problem.")
650  GError(message)
651  return self.region
652 
653  for r in reg.splitlines():
654  key, val = r.split("=", 1)
655  try:
656  region[key] = float(val)
657  except ValueError:
658  region[key] = val
659 
660  # back to original gisrc
661  if self.gisrc:
662  os.environ["GISRC"] = gisrc_orig
663 
664  # restore region
665  if tmpreg:
666  os.environ["GRASS_REGION"] = tmpreg
667 
668  Debug.msg (3, "Map.GetRegion(): %s" % region)
669 
670  if update:
671  self.region = region
672 
673  return region
674 
675  def GetCurrentRegion(self):
676  """!Get current display region settings
677 
678  @see GetRegion()
679  """
680  return self.region
681 
682  def SetRegion(self, windres = False):
683  """!Render string for GRASS_REGION env. variable, so that the
684  images will be rendered from desired zoom level.
685 
686  @param windres uses resolution from WIND file rather than
687  display (for modules that require set resolution like
688  d.rast.num)
689 
690  @return String usable for GRASS_REGION variable or None
691  """
692  grass_region = ""
693 
694  if windres:
695  compRegion = self.GetRegion()
696  region = copy.copy(self.region)
697  for key in ('nsres', 'ewres', 'cells'):
698  region[key] = compRegion[key]
699  else:
700  # adjust region settings to match monitor
701  region = self.AdjustRegion()
702 
703  # read values from wind file
704  try:
705  for key in self.wind.keys():
706  if key == 'north':
707  grass_region += "north: %s; " % \
708  (region['n'])
709  continue
710  elif key == "south":
711  grass_region += "south: %s; " % \
712  (region['s'])
713  continue
714  elif key == "east":
715  grass_region += "east: %s; " % \
716  (region['e'])
717  continue
718  elif key == "west":
719  grass_region += "west: %s; " % \
720  (region['w'])
721  continue
722  elif key == "e-w resol":
723  grass_region += "e-w resol: %f; " % \
724  (region['ewres'])
725  continue
726  elif key == "n-s resol":
727  grass_region += "n-s resol: %f; " % \
728  (region['nsres'])
729  continue
730  elif key == "cols":
731  if windres:
732  continue
733  grass_region += 'cols: %d; ' % \
734  region['cols']
735  continue
736  elif key == "rows":
737  if windres:
738  continue
739  grass_region += 'rows: %d; ' % \
740  region['rows']
741  continue
742  else:
743  grass_region += key + ": " + self.wind[key] + "; "
744 
745  Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
746 
747  return grass_region
748 
749  except:
750  return None
751 
752  def GetListOfLayers(self, l_type = None, l_mapset = None, l_name = None,
753  l_active = None, l_hidden = None):
754  """!Returns list of layers of selected properties or list of
755  all layers.
756 
757  @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
758  @param l_mapset all layers from given mapset (only for maplayers)
759  @param l_name all layers with given name
760  @param l_active only layers with 'active' attribute set to True or False
761  @param l_hidden only layers with 'hidden' attribute set to True or False
762 
763  @return list of selected layers
764  """
765  selected = []
766 
767  if type(l_type) == types.StringType:
768  one_type = True
769  else:
770  one_type = False
771 
772  if one_type and l_type == 'overlay':
773  llist = self.overlays
774  else:
775  llist = self.layers
776 
777  # ["raster", "vector", "wms", ... ]
778  for layer in llist:
779  # specified type only
780  if l_type != None:
781  if one_type and layer.type != l_type:
782  continue
783  elif not one_type and layer.type not in l_type:
784  continue
785 
786  # mapset
787  if (l_mapset != None and l_type != 'overlay') and \
788  layer.GetMapset() != l_mapset:
789  continue
790 
791  # name
792  if l_name != None and layer.name != l_name:
793  continue
794 
795  # hidden and active layers
796  if l_active != None and \
797  l_hidden != None:
798  if layer.active == l_active and \
799  layer.hidden == l_hidden:
800  selected.append(layer)
801 
802  # active layers
803  elif l_active != None:
804  if layer.active == l_active:
805  selected.append(layer)
806 
807  # hidden layers
808  elif l_hidden != None:
809  if layer.hidden == l_hidden:
810  selected.append(layer)
811 
812  # all layers
813  else:
814  selected.append(layer)
815 
816  Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
817 
818  return selected
819 
820  def _renderLayers(self, force, mapWindow, maps, masks, opacities):
821  # render map layers
822  ilayer = 1
823  for layer in self.layers + self.overlays:
824  # skip dead or disabled map layers
825  if layer == None or layer.active == False:
826  continue
827 
828  # render if there is no mapfile
829  if force or \
830  layer.force_render or \
831  layer.mapfile == None or \
832  (not os.path.isfile(layer.mapfile) or not os.path.getsize(layer.mapfile)):
833  if not layer.Render():
834  continue
835 
836  if mapWindow:
837  # update progress bar
838  ### wx.SafeYield(mapWindow)
839  event = wxUpdateProgressBar(value = ilayer)
840  wx.PostEvent(mapWindow, event)
841 
842  # add image to compositing list
843  if layer.type != "overlay":
844  maps.append(layer.mapfile)
845  masks.append(layer.maskfile)
846  opacities.append(str(layer.opacity))
847 
848  Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
849  ilayer += 1
850 
851  def Render(self, force = False, mapWindow = None, windres = False):
852  """!Creates final image composite
853 
854  This function can conditionaly use high-level tools, which
855  should be avaliable in wxPython library
856 
857  @param force force rendering
858  @param reference for MapFrame instance (for progress bar)
859  @param windres use region resolution (True) otherwise display resolution
860 
861  @return name of file with rendered image or None
862  """
863  maps = []
864  masks = []
865  opacities = []
866 
867  wx.BeginBusyCursor()
868  # use external gisrc if defined
869  gisrc_orig = os.getenv("GISRC")
870  if self.gisrc:
871  os.environ["GISRC"] = self.gisrc
872 
873  tmp_region = os.getenv("GRASS_REGION")
874  os.environ["GRASS_REGION"] = self.SetRegion(windres)
875  os.environ["GRASS_WIDTH"] = str(self.width)
876  os.environ["GRASS_HEIGHT"] = str(self.height)
877  if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
878  os.environ["GRASS_AUTO_WRITE"] = "TRUE"
879  if "GRASS_RENDER_IMMEDIATE" in os.environ:
880  del os.environ["GRASS_RENDER_IMMEDIATE"]
881  os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
882  else:
883  os.environ["GRASS_PNG_AUTO_WRITE"] = "TRUE"
884  os.environ["GRASS_PNG_READ"] = "FALSE"
885  os.environ["GRASS_PNG_COMPRESSION"] = "0"
886  os.environ["GRASS_TRUECOLOR"] = "TRUE"
887  os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
888 
889  self._renderLayers(force, mapWindow, maps, masks, opacities)
890 
891  # ugly hack for MSYS
892  if sys.platform != 'win32':
893  mapstr = ",".join(maps)
894  maskstr = ",".join(masks)
895  mapoutstr = self.mapfile
896  else:
897  mapstr = ""
898  for item in maps:
899  mapstr += item.replace('\\', '/')
900  mapstr = mapstr.rstrip(',')
901  maskstr = ""
902  for item in masks:
903  maskstr += item.replace('\\', '/')
904  maskstr = maskstr.rstrip(',')
905  mapoutstr = self.mapfile.replace('\\', '/')
906 
907  # compose command
908  bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
909  subkey = 'color')))
910 
911  # render overlays
912  if tmp_region:
913  os.environ["GRASS_REGION"] = tmp_region
914  else:
915  del os.environ["GRASS_REGION"]
916 
917  if maps:
918  # run g.pngcomp to get composite image
919  ret, msg = RunCommand('g.pnmcomp',
920  getErrorMsg = True,
921  input = '%s' % ",".join(maps),
922  mask = '%s' % ",".join(masks),
923  opacity = '%s' % ",".join(opacities),
924  background = bgcolor,
925  width = self.width,
926  height = self.height,
927  output = self.mapfile)
928 
929  if ret != 0:
930  print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
931  wx.EndBusyCursor()
932  return None
933 
934  Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))
935 
936  # back to original gisrc
937  if self.gisrc:
938  os.environ["GISRC"] = gisrc_orig
939 
940  wx.EndBusyCursor()
941  if not maps:
942  return None
943 
944  return self.mapfile
945 
946  def AddLayer(self, type, command, name = None,
947  l_active = True, l_hidden = False, l_opacity = 1.0, l_render = False,
948  pos = -1):
949  """!Adds generic map layer to list of layers
950 
951  @param type layer type ('raster', 'vector', etc.)
952  @param command GRASS command given as list
953  @param name layer name
954  @param l_active layer render only if True
955  @param l_hidden layer not displayed in layer tree if True
956  @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
957  @param l_render render an image if True
958  @param pos position in layer list (-1 for append)
959 
960  @return new layer on success
961  @return None on failure
962  """
963  wx.BeginBusyCursor()
964  # l_opacity must be <0;1>
965  if l_opacity < 0: l_opacity = 0
966  elif l_opacity > 1: l_opacity = 1
967  layer = MapLayer(type = type, name = name, cmd = command,
968  active = l_active, hidden = l_hidden, opacity = l_opacity)
969 
970  # add maplayer to the list of layers
971  if pos > -1:
972  self.layers.insert(pos, layer)
973  else:
974  self.layers.append(layer)
975 
976  Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
977  if l_render:
978  if not layer.Render():
979  raise GException(_("Unable to render map layer <%s>.") % name)
980 
981  wx.EndBusyCursor()
982 
983  return layer
984 
985  def DeleteLayer(self, layer, overlay = False):
986  """!Removes layer from list of layers
987 
988  @param layer layer instance in layer tree
989  @param overlay delete overlay (use self.DeleteOverlay() instead)
990 
991  @return removed layer on success or None
992  """
993  Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
994 
995  if overlay:
996  list = self.overlays
997  else:
998  list = self.layers
999 
1000  if layer in list:
1001  if layer.mapfile:
1002  base = os.path.split(layer.mapfile)[0]
1003  mapfile = os.path.split(layer.mapfile)[1]
1004  tempbase = mapfile.split('.')[0]
1005  if base == '' or tempbase == '':
1006  return None
1007  basefile = os.path.join(base, tempbase) + r'.*'
1008  for f in glob.glob(basefile):
1009  os.remove(f)
1010  list.remove(layer)
1011 
1012  return layer
1013 
1014  return None
1015 
1016  def ReorderLayers(self, layerList):
1017  """!Reorder list to match layer tree
1018 
1019  @param layerList list of layers
1020  """
1021  self.layers = layerList
1022 
1023  layerNameList = ""
1024  for layer in self.layers:
1025  if layer.name:
1026  layerNameList += layer.name + ','
1027  Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
1028  (layerNameList))
1029 
1030  def ChangeLayer(self, layer, render = False, **kargs):
1031  """!Change map layer properties
1032 
1033  @param layer map layer instance
1034  @param type layer type ('raster', 'vector', etc.)
1035  @param command GRASS command given as list
1036  @param name layer name
1037  @param active layer render only if True
1038  @param hidden layer not displayed in layer tree if True
1039  @param opacity opacity level range from 0(transparent) - 1(not transparent)
1040  @param render render an image if True
1041  """
1042  Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
1043 
1044  if 'type' in kargs:
1045  layer.SetType(kargs['type']) # check type
1046 
1047  if 'command' in kargs:
1048  layer.SetCmd(kargs['command'])
1049 
1050  if 'name' in kargs:
1051  layer.SetName(kargs['name'])
1052 
1053  if 'active' in kargs:
1054  layer.SetActive(kargs['active'])
1055 
1056  if 'hidden' in kargs:
1057  layer.SetHidden(kargs['hidden'])
1058 
1059  if 'opacity' in kargs:
1060  layer.SetOpacity(kargs['opacity'])
1061 
1062  if render and not layer.Render():
1063  raise GException(_("Unable to render map layer <%s>.") %
1064  name)
1065 
1066  return layer
1067 
1068  def ChangeOpacity(self, layer, l_opacity):
1069  """!Changes opacity value of map layer
1070 
1071  @param layer layer instance in layer tree
1072  @param l_opacity opacity level <0;1>
1073  """
1074  # l_opacity must be <0;1>
1075  if l_opacity < 0: l_opacity = 0
1076  elif l_opacity > 1: l_opacity = 1
1077 
1078  layer.opacity = l_opacity
1079  Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
1080  (layer.name, layer.opacity))
1081 
1082  def ChangeLayerActive(self, layer, active):
1083  """!Enable or disable map layer
1084 
1085  @param layer layer instance in layer tree
1086  @param active to be rendered (True)
1087  """
1088  layer.active = active
1089 
1090  Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
1091  (layer.name, layer.active))
1092 
1093  def ChangeLayerName (self, layer, name):
1094  """!Change name of the layer
1095 
1096  @param layer layer instance in layer tree
1097  @param name layer name to set up
1098  """
1099  Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
1100  (layer.name, name))
1101  layer.name = name
1102 
1103  def RemoveLayer(self, name = None, id = None):
1104  """!Removes layer from layer list
1105 
1106  Layer is defined by name@mapset or id.
1107 
1108  @param name layer name (must be unique)
1109  @param id layer index in layer list
1110 
1111  @return removed layer on success
1112  @return None on failure
1113  """
1114  # delete by name
1115  if name:
1116  retlayer = None
1117  for layer in self.layers:
1118  if layer.name == name:
1119  retlayer = layer
1120  os.remove(layer.mapfile)
1121  os.remove(layer.maskfile)
1122  self.layers.remove(layer)
1123  return layer
1124  # del by id
1125  elif id != None:
1126  return self.layers.pop(id)
1127 
1128  return None
1129 
1130  def GetLayerIndex(self, layer, overlay = False):
1131  """!Get index of layer in layer list.
1132 
1133  @param layer layer instace in layer tree
1134  @param overlay use list of overlays instead
1135 
1136  @return layer index
1137  @return -1 if layer not found
1138  """
1139  if overlay:
1140  list = self.overlay
1141  else:
1142  list = self.layers
1143 
1144  if layer in list:
1145  return list.index(layer)
1146 
1147  return -1
1148 
1149  def AddOverlay(self, id, type, command,
1150  l_active = True, l_hidden = True, l_opacity = 1.0, l_render = False):
1151  """!Adds overlay (grid, barscale, legend, etc.) to list of
1152  overlays
1153 
1154  @param id overlay id (PseudoDC)
1155  @param type overlay type (barscale, legend)
1156  @param command GRASS command to render overlay
1157  @param l_active overlay activated (True) or disabled (False)
1158  @param l_hidden overlay is not shown in layer tree (if True)
1159  @param l_render render an image (if True)
1160 
1161  @return new layer on success
1162  @retutn None on failure
1163  """
1164  Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
1165  overlay = Overlay(id = id, type = type, cmd = command,
1166  active = l_active, hidden = l_hidden, opacity = l_opacity)
1167 
1168  # add maplayer to the list of layers
1169  self.overlays.append(overlay)
1170 
1171  if l_render and command != '' and not overlay.Render():
1172  raise GException(_("Unable to render overlay <%s>.") %
1173  name)
1174 
1175  return self.overlays[-1]
1176 
1177  def ChangeOverlay(self, id, render = False, **kargs):
1178  """!Change overlay properities
1179 
1180  Add new overlay if overlay with 'id' doesn't exist.
1181 
1182  @param id overlay id (PseudoDC)
1183  @param type overlay type (barscale, legend)
1184  @param command GRASS command to render overlay
1185  @param l_active overlay activated (True) or disabled (False)
1186  @param l_hidden overlay is not shown in layer tree (if True)
1187  @param l_render render an image (if True)
1188 
1189  @return new layer on success
1190  """
1191  overlay = self.GetOverlay(id, list = False)
1192  if overlay is None:
1193  overlay = Overlay(id, type = None, cmd = None)
1194 
1195  if 'type' in kargs:
1196  overlay.SetName(kargs['type']) # type -> overlay
1197 
1198  if 'command' in kargs:
1199  overlay.SetCmd(kargs['command'])
1200 
1201  if 'active' in kargs:
1202  overlay.SetActive(kargs['active'])
1203 
1204  if 'hidden' in kargs:
1205  overlay.SetHidden(kargs['hidden'])
1206 
1207  if 'opacity' in kargs:
1208  overlay.SetOpacity(kargs['opacity'])
1209 
1210  if render and overlay.GetCmd() != [] and not overlay.Render():
1211  raise GException(_("Unable to render overlay <%s>.") %
1212  name)
1213 
1214  return overlay
1215 
1216  def GetOverlay(self, id, list = False):
1217  """!Return overlay(s) with 'id'
1218 
1219  @param id overlay id
1220  @param list return list of overlays of True
1221  otherwise suppose 'id' to be unique
1222 
1223  @return list of overlays (list=True)
1224  @return overlay (list=False)
1225  @retur None (list=False) if no overlay or more overlays found
1226  """
1227  ovl = []
1228  for overlay in self.overlays:
1229  if overlay.id == id:
1230  ovl.append(overlay)
1231 
1232  if not list:
1233  if len(ovl) != 1:
1234  return None
1235  else:
1236  return ovl[0]
1237 
1238  return ovl
1239 
1240  def DeleteOverlay(self, overlay):
1241  """!Delete overlay
1242 
1243  @param overlay overlay layer
1244 
1245  @return removed overlay on success or None
1246  """
1247  return self.DeleteLayer(overlay, overlay = True)
1248 
1249  def Clean(self):
1250  """!Clean layer stack - go trough all layers and remove them
1251  from layer list.
1252 
1253  Removes also l_mapfile and l_maskfile
1254 
1255  @return False on failure
1256  @return True on success
1257  """
1258  try:
1259  dir = os.path.dirname(self.mapfile)
1260  base = os.path.basename(self.mapfile).split('.')[0]
1261  removepath = os.path.join(dir,base)+r'*'
1262  for f in glob.glob(removepath):
1263  os.remove(f)
1264  for layer in self.layers:
1265  if layer.mapfile:
1266  dir = os.path.dirname(layer.mapfile)
1267  base = os.path.basename(layer.mapfile).split('.')[0]
1268  removepath = os.path.join(dir,base)+r'*'
1269  for f in glob.glob(removepath):
1270  os.remove(f)
1271  self.layers.remove(layer)
1272 
1273  for overlay in self.overlays:
1274  if overlay.mapfile:
1275  dir = os.path.dirname(overlay.mapfile)
1276  base = os.path.basename(overlay.mapfile).split('.')[0]
1277  removepath = os.path.join(dir,base)+r'*'
1278  for f in glob.glob(removepath):
1279  os.remove(f)
1280  self.overlays.remove(overlay)
1281  except:
1282  return False
1283 
1284  return True
1285 
1287  """!Reverse list of layers"""
1288  return self.layers.reverse()
1289 
1290  def RenderOverlays(self, force):
1291  """!Render overlays only (for nviz)"""
1292  for layer in self.overlays:
1293  if force or layer.force_render:
1294  layer.Render()
1295 
1296 if __name__ == "__main__":
1297  import gettext
1298  gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
1299 
1300  Map = Map()
1301  Map.GetRegion(update = True)
1302 
1303  Map.AddLayer(type = "raster",
1304  name = "elevation",
1305  command = ["d.rast", "map=elevation@PERMANENT"],
1306  l_opacity = .7)
1307 
1308  Map.AddLayer(type = "vector",
1309  name = "roadsmajor",
1310  command = ["d.vect", "map=roadsmajor@PERMANENT", "color=red", "width=3", "type=line"])
1311 
1312  image = Map.Render(force = True)
1313 
1314  if image:
1315  grass.call(["display", image])
def AlignResolution
Sets display extents to even multiple of current resolution defined in WIND file from SW corner...
Definition: render.py:498
def CmdToTuple
Convert command list to tuple for gcmd.RunCommand()
Definition: core/utils.py:527
def GetElement
Get map element type.
Definition: render.py:231
Virtual class which stores information about layers (map layers and overlays) of the map composition...
Definition: render.py:48
def GetCurrentRegion
Get current display region settings.
Definition: render.py:675
wxGUI command interface
def ChangeLayerActive
Enable or disable map layer.
Definition: render.py:1082
def GetCmdString
Definition: core/utils.py:499
def GetProjInfo
Get projection info.
Definition: render.py:425
def _initGisEnv
Stores GRASS variables (g.gisenv) to self.env variable.
Definition: render.py:417
def __init__
Definition: render.py:369
tuple cmd
Definition: forms.py:2019
#define min(x, y)
Definition: draw2.c:68
def SetHidden
Hide or show map layer in Layer Manager.
Definition: render.py:292
wxGUI debugging
def Render
Render layer to image.
Definition: render.py:101
def DeleteLayer
Removes layer from list of layers.
Definition: render.py:985
def RenderOverlays
Render overlays only (for nviz)
Definition: render.py:1290
def AdjustRegion
Adjusts display resolution to match monitor size in pixels.
Definition: render.py:478
def __del__
Definition: render.py:97
def ReverseListOfLayers
Reverse list of layers.
Definition: render.py:1286
def GetOverlay
Return overlay(s) with 'id'.
Definition: render.py:1216
def AddLayer
Adds generic map layer to list of layers.
Definition: render.py:948
def ChangeLayer
Change map layer properties.
Definition: render.py:1030
def ChangeLayerName
Change name of the layer.
Definition: render.py:1093
def GetName
Get map layer name.
Definition: render.py:250
def _projInfo
Return region projection and map units information.
Definition: render.py:429
def DeleteOverlay
Delete overlay.
Definition: render.py:1240
#define max(x, y)
Definition: draw2.c:69
def AddOverlay
Adds overlay (grid, barscale, legend, etc.) to list of overlays.
Definition: render.py:1150
def Clean
Clean layer stack - go trough all layers and remove them from layer list.
Definition: render.py:1249
def _runCommand
Run command in environment defined by self.gisrc if defined.
Definition: render.py:401
def GetWindow
Read WIND file and set up self.wind dictionary.
Definition: render.py:456
def __init__
Definition: render.py:56
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def Render
Creates final image composite.
Definition: render.py:851
def GetCmd
Get GRASS command as list of string.
Definition: render.py:208
def ReorderLayers
Reorder list to match layer tree.
Definition: render.py:1016
def IsActive
Check if layer is activated for rendering.
Definition: render.py:269
def ChangeMapSize
Change size of rendered map.
Definition: render.py:557
def AlignExtentFromDisplay
Align region extent based on display size from center point.
Definition: render.py:531
def GetType
Get map layer type.
Definition: render.py:227
def __init__
Represents overlay displayed in map canvas.
Definition: render.py:350
def _renderLayers
Definition: render.py:820
def GetMapset
Get mapset of map layer.
Definition: render.py:334
def SetCmd
Set new command for layer.
Definition: render.py:305
def SetName
Set layer name.
Definition: render.py:284
def GetLayerIndex
Get index of layer in layer list.
Definition: render.py:1130
Map composition (stack of map layers and overlays)
Definition: render.py:366
def ChangeOverlay
Change overlay properities.
Definition: render.py:1177
def ChangeOpacity
Changes opacity value of map layer.
Definition: render.py:1068
def GetOpacity
Definition: render.py:237
def RemoveLayer
Removes layer from layer list.
Definition: render.py:1103
def SetType
Set layer type.
Definition: render.py:273
#define round(x)
Definition: draw2.c:71
Default GUI settings.
def SetActive
Active or deactive layer.
Definition: render.py:288
def GetRegion
Get region settings (g.region -upgc)
Definition: render.py:577
def SetRegion
Render string for GRASS_REGION env.
Definition: render.py:682
def __init__
Represents map layer in the map canvas.
Definition: render.py:320
def GetListOfLayers
Returns list of layers of selected properties or list of all layers.
Definition: render.py:753
def RunCommand
Run GRASS command.
Definition: gcmd.py:633
def SetOpacity
Set opacity value.
Definition: render.py:296