GRASS Programmer's Manual  6.4.3(2013)-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
561 
562  @return True on success
563  @return False on failure
564  """
565  try:
566  self.width = int(width)
567  self.height = int(height)
568  Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
569  (self.width, self.height))
570  return True
571  except:
572  self.width = 640
573  self.height = 480
574  return False
575 
576  def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
577  n = None, s = None, e = None, w = None, default = False,
578  update = False):
579  """!Get region settings (g.region -upgc)
580 
581  Optionally extent, raster or vector map layer can be given.
582 
583  @param rast list of raster maps
584  @param zoom zoom to raster map (ignore NULLs)
585  @param vect list of vector maps
586  @param regionName named region or None
587  @param n,s,e,w force extent
588  @param default force default region settings
589  @param update if True update current display region settings
590 
591  @return region settings as directory, e.g. {
592  'n':'4928010', 's':'4913700', 'w':'589980',...}
593 
594  @see GetCurrentRegion()
595  """
596  region = {}
597 
598  tmpreg = os.getenv("GRASS_REGION")
599  if tmpreg:
600  del os.environ["GRASS_REGION"]
601 
602  # use external gisrc if defined
603  gisrc_orig = os.getenv("GISRC")
604  if self.gisrc:
605  os.environ["GISRC"] = self.gisrc
606 
607  # do not update & shell style output
608  cmd = {}
609  cmd['flags'] = 'ugpc'
610 
611  if default:
612  cmd['flags'] += 'd'
613 
614  if regionName:
615  cmd['region'] = regionName
616 
617  if n:
618  cmd['n'] = n
619  if s:
620  cmd['s'] = s
621  if e:
622  cmd['e'] = e
623  if w:
624  cmd['w'] = w
625 
626  if rast:
627  if zoom:
628  cmd['zoom'] = rast[0]
629  else:
630  cmd['rast'] = ','.join(rast)
631 
632  if vect:
633  cmd['vect'] = ','.join(vect)
634 
635  ret, reg, msg = RunCommand('g.region',
636  read = True,
637  getErrorMsg = True,
638  **cmd)
639 
640  if ret != 0:
641  if rast:
642  message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
643  "\n\n" + _("Details:") + " %s" % msg
644  elif vect:
645  message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
646  "\n\n" + _("Details:") + " %s" % msg
647  else:
648  message = _("Unable to get current geographic extent. "
649  "Force quiting wxGUI. Please manually run g.region to "
650  "fix the problem.")
651  GError(message)
652  return self.region
653 
654  for r in reg.splitlines():
655  key, val = r.split("=", 1)
656  try:
657  region[key] = float(val)
658  except ValueError:
659  region[key] = val
660 
661  # back to original gisrc
662  if self.gisrc:
663  os.environ["GISRC"] = gisrc_orig
664 
665  # restore region
666  if tmpreg:
667  os.environ["GRASS_REGION"] = tmpreg
668 
669  Debug.msg (3, "Map.GetRegion(): %s" % region)
670 
671  if update:
672  self.region = region
673 
674  return region
675 
676  def GetCurrentRegion(self):
677  """!Get current display region settings
678 
679  @see GetRegion()
680  """
681  return self.region
682 
683  def SetRegion(self, windres = False):
684  """!Render string for GRASS_REGION env. variable, so that the
685  images will be rendered from desired zoom level.
686 
687  @param windres uses resolution from WIND file rather than
688  display (for modules that require set resolution like
689  d.rast.num)
690 
691  @return String usable for GRASS_REGION variable or None
692  """
693  grass_region = ""
694 
695  if windres:
696  compRegion = self.GetRegion()
697  region = copy.copy(self.region)
698  for key in ('nsres', 'ewres', 'cells'):
699  region[key] = compRegion[key]
700  else:
701  # adjust region settings to match monitor
702  region = self.AdjustRegion()
703 
704  # read values from wind file
705  try:
706  for key in self.wind.keys():
707  if key == 'north':
708  grass_region += "north: %s; " % \
709  (region['n'])
710  continue
711  elif key == "south":
712  grass_region += "south: %s; " % \
713  (region['s'])
714  continue
715  elif key == "east":
716  grass_region += "east: %s; " % \
717  (region['e'])
718  continue
719  elif key == "west":
720  grass_region += "west: %s; " % \
721  (region['w'])
722  continue
723  elif key == "e-w resol":
724  grass_region += "e-w resol: %f; " % \
725  (region['ewres'])
726  continue
727  elif key == "n-s resol":
728  grass_region += "n-s resol: %f; " % \
729  (region['nsres'])
730  continue
731  elif key == "cols":
732  if windres:
733  continue
734  grass_region += 'cols: %d; ' % \
735  region['cols']
736  continue
737  elif key == "rows":
738  if windres:
739  continue
740  grass_region += 'rows: %d; ' % \
741  region['rows']
742  continue
743  else:
744  grass_region += key + ": " + self.wind[key] + "; "
745 
746  Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
747 
748  return grass_region
749 
750  except:
751  return None
752 
753  def GetListOfLayers(self, l_type = None, l_mapset = None, l_name = None,
754  l_active = None, l_hidden = None):
755  """!Returns list of layers of selected properties or list of
756  all layers.
757 
758  @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
759  @param l_mapset all layers from given mapset (only for maplayers)
760  @param l_name all layers with given name
761  @param l_active only layers with 'active' attribute set to True or False
762  @param l_hidden only layers with 'hidden' attribute set to True or False
763 
764  @return list of selected layers
765  """
766  selected = []
767 
768  if type(l_type) == types.StringType:
769  one_type = True
770  else:
771  one_type = False
772 
773  if one_type and l_type == 'overlay':
774  llist = self.overlays
775  else:
776  llist = self.layers
777 
778  # ["raster", "vector", "wms", ... ]
779  for layer in llist:
780  # specified type only
781  if l_type != None:
782  if one_type and layer.type != l_type:
783  continue
784  elif not one_type and layer.type not in l_type:
785  continue
786 
787  # mapset
788  if (l_mapset != None and l_type != 'overlay') and \
789  layer.GetMapset() != l_mapset:
790  continue
791 
792  # name
793  if l_name != None and layer.name != l_name:
794  continue
795 
796  # hidden and active layers
797  if l_active != None and \
798  l_hidden != None:
799  if layer.active == l_active and \
800  layer.hidden == l_hidden:
801  selected.append(layer)
802 
803  # active layers
804  elif l_active != None:
805  if layer.active == l_active:
806  selected.append(layer)
807 
808  # hidden layers
809  elif l_hidden != None:
810  if layer.hidden == l_hidden:
811  selected.append(layer)
812 
813  # all layers
814  else:
815  selected.append(layer)
816 
817  Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
818 
819  return selected
820 
821  def _renderLayers(self, force, mapWindow, maps, masks, opacities):
822  # render map layers
823  ilayer = 1
824  for layer in self.layers + self.overlays:
825  # skip dead or disabled map layers
826  if layer == None or layer.active == False:
827  continue
828 
829  # render if there is no mapfile
830  if force or \
831  layer.force_render or \
832  layer.mapfile == None or \
833  (not os.path.isfile(layer.mapfile) or not os.path.getsize(layer.mapfile)):
834  if not layer.Render():
835  continue
836 
837  if mapWindow:
838  # update progress bar
839  ### wx.SafeYield(mapWindow)
840  event = wxUpdateProgressBar(value = ilayer)
841  wx.PostEvent(mapWindow, event)
842 
843  # add image to compositing list
844  if layer.type != "overlay":
845  maps.append(layer.mapfile)
846  masks.append(layer.maskfile)
847  opacities.append(str(layer.opacity))
848 
849  Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
850  ilayer += 1
851 
852  def Render(self, force = False, mapWindow = None, windres = False):
853  """!Creates final image composite
854 
855  This function can conditionaly use high-level tools, which
856  should be avaliable in wxPython library
857 
858  @param force force rendering
859  @param reference for MapFrame instance (for progress bar)
860  @param windres use region resolution (True) otherwise display resolution
861 
862  @return name of file with rendered image or None
863  """
864  maps = []
865  masks = []
866  opacities = []
867 
868  wx.BeginBusyCursor()
869  # use external gisrc if defined
870  gisrc_orig = os.getenv("GISRC")
871  if self.gisrc:
872  os.environ["GISRC"] = self.gisrc
873 
874  tmp_region = os.getenv("GRASS_REGION")
875  os.environ["GRASS_REGION"] = self.SetRegion(windres)
876  os.environ["GRASS_WIDTH"] = str(self.width)
877  os.environ["GRASS_HEIGHT"] = str(self.height)
878  if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
879  os.environ["GRASS_AUTO_WRITE"] = "TRUE"
880  if "GRASS_RENDER_IMMEDIATE" in os.environ:
881  del os.environ["GRASS_RENDER_IMMEDIATE"]
882  os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
883  else:
884  os.environ["GRASS_PNG_AUTO_WRITE"] = "TRUE"
885  os.environ["GRASS_PNG_READ"] = "FALSE"
886  os.environ["GRASS_PNG_COMPRESSION"] = "0"
887  os.environ["GRASS_TRUECOLOR"] = "TRUE"
888  os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
889 
890  self._renderLayers(force, mapWindow, maps, masks, opacities)
891 
892  # ugly hack for MSYS
893  if sys.platform != 'win32':
894  mapstr = ",".join(maps)
895  maskstr = ",".join(masks)
896  mapoutstr = self.mapfile
897  else:
898  mapstr = ""
899  for item in maps:
900  mapstr += item.replace('\\', '/')
901  mapstr = mapstr.rstrip(',')
902  maskstr = ""
903  for item in masks:
904  maskstr += item.replace('\\', '/')
905  maskstr = maskstr.rstrip(',')
906  mapoutstr = self.mapfile.replace('\\', '/')
907 
908  # compose command
909  bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
910  subkey = 'color')))
911 
912  # render overlays
913  if tmp_region:
914  os.environ["GRASS_REGION"] = tmp_region
915  else:
916  del os.environ["GRASS_REGION"]
917 
918  if maps:
919  # run g.pngcomp to get composite image
920  ret, msg = RunCommand('g.pnmcomp',
921  getErrorMsg = True,
922  input = '%s' % ",".join(maps),
923  mask = '%s' % ",".join(masks),
924  opacity = '%s' % ",".join(opacities),
925  background = bgcolor,
926  width = self.width,
927  height = self.height,
928  output = self.mapfile)
929 
930  if ret != 0:
931  print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
932  wx.EndBusyCursor()
933  return None
934 
935  Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))
936 
937  # back to original gisrc
938  if self.gisrc:
939  os.environ["GISRC"] = gisrc_orig
940 
941  wx.EndBusyCursor()
942  if not maps:
943  return None
944 
945  return self.mapfile
946 
947  def AddLayer(self, type, command, name = None,
948  l_active = True, l_hidden = False, l_opacity = 1.0, l_render = False,
949  pos = -1):
950  """!Adds generic map layer to list of layers
951 
952  @param type layer type ('raster', 'vector', etc.)
953  @param command GRASS command given as list
954  @param name layer name
955  @param l_active layer render only if True
956  @param l_hidden layer not displayed in layer tree if True
957  @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
958  @param l_render render an image if True
959  @param pos position in layer list (-1 for append)
960 
961  @return new layer on success
962  @return None on failure
963  """
964  wx.BeginBusyCursor()
965  # l_opacity must be <0;1>
966  if l_opacity < 0: l_opacity = 0
967  elif l_opacity > 1: l_opacity = 1
968  layer = MapLayer(type = type, name = name, cmd = command,
969  active = l_active, hidden = l_hidden, opacity = l_opacity)
970 
971  # add maplayer to the list of layers
972  if pos > -1:
973  self.layers.insert(pos, layer)
974  else:
975  self.layers.append(layer)
976 
977  Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
978  if l_render:
979  if not layer.Render():
980  raise GException(_("Unable to render map layer <%s>.") % name)
981 
982  wx.EndBusyCursor()
983 
984  return layer
985 
986  def DeleteLayer(self, layer, overlay = False):
987  """!Removes layer from list of layers
988 
989  @param layer layer instance in layer tree
990  @param overlay delete overlay (use self.DeleteOverlay() instead)
991 
992  @return removed layer on success or None
993  """
994  Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
995 
996  if overlay:
997  list = self.overlays
998  else:
999  list = self.layers
1000 
1001  if layer in list:
1002  if layer.mapfile:
1003  base = os.path.split(layer.mapfile)[0]
1004  mapfile = os.path.split(layer.mapfile)[1]
1005  tempbase = mapfile.split('.')[0]
1006  if base == '' or tempbase == '':
1007  return None
1008  basefile = os.path.join(base, tempbase) + r'.*'
1009  for f in glob.glob(basefile):
1010  os.remove(f)
1011  list.remove(layer)
1012 
1013  return layer
1014 
1015  return None
1016 
1017  def ReorderLayers(self, layerList):
1018  """!Reorder list to match layer tree
1019 
1020  @param layerList list of layers
1021  """
1022  self.layers = layerList
1023 
1024  layerNameList = ""
1025  for layer in self.layers:
1026  if layer.name:
1027  layerNameList += layer.name + ','
1028  Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
1029  (layerNameList))
1030 
1031  def ChangeLayer(self, layer, render = False, **kargs):
1032  """!Change map layer properties
1033 
1034  @param layer map layer instance
1035  @param type layer type ('raster', 'vector', etc.)
1036  @param command GRASS command given as list
1037  @param name layer name
1038  @param active layer render only if True
1039  @param hidden layer not displayed in layer tree if True
1040  @param opacity opacity level range from 0(transparent) - 1(not transparent)
1041  @param render render an image if True
1042  """
1043  Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
1044 
1045  if 'type' in kargs:
1046  layer.SetType(kargs['type']) # check type
1047 
1048  if 'command' in kargs:
1049  layer.SetCmd(kargs['command'])
1050 
1051  if 'name' in kargs:
1052  layer.SetName(kargs['name'])
1053 
1054  if 'active' in kargs:
1055  layer.SetActive(kargs['active'])
1056 
1057  if 'hidden' in kargs:
1058  layer.SetHidden(kargs['hidden'])
1059 
1060  if 'opacity' in kargs:
1061  layer.SetOpacity(kargs['opacity'])
1062 
1063  if render and not layer.Render():
1064  raise GException(_("Unable to render map layer <%s>.") %
1065  name)
1066 
1067  return layer
1068 
1069  def ChangeOpacity(self, layer, l_opacity):
1070  """!Changes opacity value of map layer
1071 
1072  @param layer layer instance in layer tree
1073  @param l_opacity opacity level <0;1>
1074  """
1075  # l_opacity must be <0;1>
1076  if l_opacity < 0: l_opacity = 0
1077  elif l_opacity > 1: l_opacity = 1
1078 
1079  layer.opacity = l_opacity
1080  Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
1081  (layer.name, layer.opacity))
1082 
1083  def ChangeLayerActive(self, layer, active):
1084  """!Enable or disable map layer
1085 
1086  @param layer layer instance in layer tree
1087  @param active to be rendered (True)
1088  """
1089  layer.active = active
1090 
1091  Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
1092  (layer.name, layer.active))
1093 
1094  def ChangeLayerName (self, layer, name):
1095  """!Change name of the layer
1096 
1097  @param layer layer instance in layer tree
1098  @param name layer name to set up
1099  """
1100  Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
1101  (layer.name, name))
1102  layer.name = name
1103 
1104  def RemoveLayer(self, name = None, id = None):
1105  """!Removes layer from layer list
1106 
1107  Layer is defined by name@mapset or id.
1108 
1109  @param name layer name (must be unique)
1110  @param id layer index in layer list
1111 
1112  @return removed layer on success
1113  @return None on failure
1114  """
1115  # delete by name
1116  if name:
1117  retlayer = None
1118  for layer in self.layers:
1119  if layer.name == name:
1120  retlayer = layer
1121  os.remove(layer.mapfile)
1122  os.remove(layer.maskfile)
1123  self.layers.remove(layer)
1124  return layer
1125  # del by id
1126  elif id != None:
1127  return self.layers.pop(id)
1128 
1129  return None
1130 
1131  def GetLayerIndex(self, layer, overlay = False):
1132  """!Get index of layer in layer list.
1133 
1134  @param layer layer instace in layer tree
1135  @param overlay use list of overlays instead
1136 
1137  @return layer index
1138  @return -1 if layer not found
1139  """
1140  if overlay:
1141  list = self.overlay
1142  else:
1143  list = self.layers
1144 
1145  if layer in list:
1146  return list.index(layer)
1147 
1148  return -1
1149 
1150  def AddOverlay(self, id, type, command,
1151  l_active = True, l_hidden = True, l_opacity = 1.0, l_render = False):
1152  """!Adds overlay (grid, barscale, legend, etc.) to list of
1153  overlays
1154 
1155  @param id overlay id (PseudoDC)
1156  @param type overlay type (barscale, legend)
1157  @param command GRASS command to render overlay
1158  @param l_active overlay activated (True) or disabled (False)
1159  @param l_hidden overlay is not shown in layer tree (if True)
1160  @param l_render render an image (if True)
1161 
1162  @return new layer on success
1163  @retutn None on failure
1164  """
1165  Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
1166  overlay = Overlay(id = id, type = type, cmd = command,
1167  active = l_active, hidden = l_hidden, opacity = l_opacity)
1168 
1169  # add maplayer to the list of layers
1170  self.overlays.append(overlay)
1171 
1172  if l_render and command != '' and not overlay.Render():
1173  raise GException(_("Unable to render overlay <%s>.") %
1174  name)
1175 
1176  return self.overlays[-1]
1177 
1178  def ChangeOverlay(self, id, render = False, **kargs):
1179  """!Change overlay properities
1180 
1181  Add new overlay if overlay with 'id' doesn't exist.
1182 
1183  @param id overlay id (PseudoDC)
1184  @param type overlay type (barscale, legend)
1185  @param command GRASS command to render overlay
1186  @param l_active overlay activated (True) or disabled (False)
1187  @param l_hidden overlay is not shown in layer tree (if True)
1188  @param l_render render an image (if True)
1189 
1190  @return new layer on success
1191  """
1192  overlay = self.GetOverlay(id, list = False)
1193  if overlay is None:
1194  overlay = Overlay(id, type = None, cmd = None)
1195 
1196  if 'type' in kargs:
1197  overlay.SetName(kargs['type']) # type -> overlay
1198 
1199  if 'command' in kargs:
1200  overlay.SetCmd(kargs['command'])
1201 
1202  if 'active' in kargs:
1203  overlay.SetActive(kargs['active'])
1204 
1205  if 'hidden' in kargs:
1206  overlay.SetHidden(kargs['hidden'])
1207 
1208  if 'opacity' in kargs:
1209  overlay.SetOpacity(kargs['opacity'])
1210 
1211  if render and overlay.GetCmd() != [] and not overlay.Render():
1212  raise GException(_("Unable to render overlay <%s>.") %
1213  name)
1214 
1215  return overlay
1216 
1217  def GetOverlay(self, id, list = False):
1218  """!Return overlay(s) with 'id'
1219 
1220  @param id overlay id
1221  @param list return list of overlays of True
1222  otherwise suppose 'id' to be unique
1223 
1224  @return list of overlays (list=True)
1225  @return overlay (list=False)
1226  @retur None (list=False) if no overlay or more overlays found
1227  """
1228  ovl = []
1229  for overlay in self.overlays:
1230  if overlay.id == id:
1231  ovl.append(overlay)
1232 
1233  if not list:
1234  if len(ovl) != 1:
1235  return None
1236  else:
1237  return ovl[0]
1238 
1239  return ovl
1240 
1241  def DeleteOverlay(self, overlay):
1242  """!Delete overlay
1243 
1244  @param overlay overlay layer
1245 
1246  @return removed overlay on success or None
1247  """
1248  return self.DeleteLayer(overlay, overlay = True)
1249 
1250  def Clean(self):
1251  """!Clean layer stack - go trough all layers and remove them
1252  from layer list.
1253 
1254  Removes also l_mapfile and l_maskfile
1255 
1256  @return False on failure
1257  @return True on success
1258  """
1259  try:
1260  dir = os.path.dirname(self.mapfile)
1261  base = os.path.basename(self.mapfile).split('.')[0]
1262  removepath = os.path.join(dir,base)+r'*'
1263  for f in glob.glob(removepath):
1264  os.remove(f)
1265  for layer in self.layers:
1266  if layer.mapfile:
1267  dir = os.path.dirname(layer.mapfile)
1268  base = os.path.basename(layer.mapfile).split('.')[0]
1269  removepath = os.path.join(dir,base)+r'*'
1270  for f in glob.glob(removepath):
1271  os.remove(f)
1272  self.layers.remove(layer)
1273 
1274  for overlay in self.overlays:
1275  if overlay.mapfile:
1276  dir = os.path.dirname(overlay.mapfile)
1277  base = os.path.basename(overlay.mapfile).split('.')[0]
1278  removepath = os.path.join(dir,base)+r'*'
1279  for f in glob.glob(removepath):
1280  os.remove(f)
1281  self.overlays.remove(overlay)
1282  except:
1283  return False
1284 
1285  return True
1286 
1288  """!Reverse list of layers"""
1289  return self.layers.reverse()
1290 
1291  def RenderOverlays(self, force):
1292  """!Render overlays only (for nviz)"""
1293  for layer in self.overlays:
1294  if force or layer.force_render:
1295  layer.Render()
1296 
1297 if __name__ == "__main__":
1298  import gettext
1299  gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
1300 
1301  Map = Map()
1302  Map.GetRegion(update = True)
1303 
1304  Map.AddLayer(type = "raster",
1305  name = "elevation",
1306  command = ["d.rast", "map=elevation@PERMANENT"],
1307  l_opacity = .7)
1308 
1309  Map.AddLayer(type = "vector",
1310  name = "roadsmajor",
1311  command = ["d.vect", "map=roadsmajor@PERMANENT", "color=red", "width=3", "type=line"])
1312 
1313  image = Map.Render(force = True)
1314 
1315  if image:
1316  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:676
wxGUI command interface
def ChangeLayerActive
Enable or disable map layer.
Definition: render.py:1083
def GetCmdString
Definition: core/utils.py:499
def GetProjInfo
Get projection info.
Definition: render.py:425
int
Definition: y.tab.c:1344
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:986
def RenderOverlays
Render overlays only (for nviz)
Definition: render.py:1291
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:1287
def GetOverlay
Return overlay(s) with &#39;id&#39;.
Definition: render.py:1217
def AddLayer
Adds generic map layer to list of layers.
Definition: render.py:949
def ChangeLayer
Change map layer properties.
Definition: render.py:1031
def ChangeLayerName
Change name of the layer.
Definition: render.py:1094
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:1241
#define max(x, y)
Definition: draw2.c:69
def AddOverlay
Adds overlay (grid, barscale, legend, etc.) to list of overlays.
Definition: render.py:1151
def Clean
Clean layer stack - go trough all layers and remove them from layer list.
Definition: render.py:1250
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:852
def GetCmd
Get GRASS command as list of string.
Definition: render.py:208
def ReorderLayers
Reorder list to match layer tree.
Definition: render.py:1017
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:821
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:1131
Map composition (stack of map layers and overlays)
Definition: render.py:366
def ChangeOverlay
Change overlay properities.
Definition: render.py:1178
def ChangeOpacity
Changes opacity value of map layer.
Definition: render.py:1069
def GetOpacity
Definition: render.py:237
def RemoveLayer
Removes layer from layer list.
Definition: render.py:1104
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:578
def SetRegion
Render string for GRASS_REGION env.
Definition: render.py:683
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:754
def RunCommand
Run GRASS command.
Definition: gcmd.py:633
def SetOpacity
Set opacity value.
Definition: render.py:296