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