123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353 |
- """
- @package render
- Rendering map layers and overlays into map composition image
- Classes:
- - Layer
- - MapLayer
- - Overlay
- - Map
- C) 2006-2008 by the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- @author Michael Barton, Jachym Cepicky,
- Martin Landa <landa.martin gmail.com>
- @date 2006-2008
- """
- import os
- import sys
- import glob
- import math
- import copy
- try:
- import subprocess
- except:
- compatPath = os.path.join(globalvar.ETCWXDIR, "compat")
- sys.path.append(compatPath)
- import subprocess
- import tempfile
- import wx
- from wx.lib.newevent import NewEvent
- from grass.script import core as grass
- import globalvar
- import utils
- import gcmd
- from debug import Debug as Debug
- from preferences import globalSettings as UserSettings
- wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent()
- #
- # use g.pnmcomp for creating image composition or
- # wxPython functionality
- #
- USE_GPNMCOMP = True
- class Layer(object):
- """!Virtual class which stores information about layers (map layers and
- overlays) of the map composition.
- For map layer use MapLayer class.
- For overlays use Overlay class.
- """
- def __init__(self, type, cmd, name=None,
- active=True, hidden=False, opacity=1.0):
- """
- @todo pass cmd as tuple instead of list
-
- @param type layer type ('raster', 'vector', 'overlay', 'command', etc.)
- @param cmd GRASS command to render layer,
- given as list, e.g. ['d.rast', 'map=elevation@PERMANENT']
- @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree)
- @param active layer is active, will be rendered only if True
- @param hidden layer is hidden, won't be listed in Layer Manager if True
- @param opacity layer opacity <0;1>
- """
- self.type = type
- self.name = name
- if self.type == 'command':
- self.cmd = []
- for c in cmd:
- self.cmd.append(utils.CmdToTuple(c))
- else:
- self.cmd = utils.CmdToTuple(cmd)
-
- self.active = active
- self.hidden = hidden
- self.opacity = opacity
-
- self.force_render = True
-
- Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \
- "active=%d, opacity=%d, hidden=%d" % \
- (self.type, self.GetCmd(string=True), self.name, self.active,
- self.opacity, self.hidden))
- # generated file for each layer
- self.gtemp = tempfile.mkstemp()[1]
- self.maskfile = self.gtemp + ".pgm"
- if self.type == 'overlay':
- self.mapfile = self.gtemp + ".png"
- else:
- self.mapfile = self.gtemp + ".ppm"
- def __del__(self):
- Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
- (self.name, self.GetCmd(string=True)))
- def Render(self):
- """!Render layer to image
- @return rendered image filename
- @return None on error
- """
- if not self.cmd:
- return None
-
- # ignore in 2D
- if self.type == '3d-raster':
- return None
-
- Debug.msg (3, "Layer.Render(): type=%s, name=%s" % \
- (self.type, self.name))
-
- #
- # prepare command for each layer
- #
- layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
- 'vector','thememap','themechart',
- 'grid', 'geodesic', 'rhumb', 'labels',
- 'command',
- 'overlay')
-
- if self.type not in layertypes:
- raise gcmd.GStdError(_("<%(name)s>: layer type <%(type)s> is not supported yet.") % \
- {'type' : self.type, 'name' : self.name})
-
- #
- # start monitor
- #
- if self.mapfile:
- os.environ["GRASS_PNGFILE"] = self.mapfile
-
- #
- # execute command
- #
- try:
- if self.type == 'command':
- read = False
- for c in self.cmd:
- ret = gcmd.RunCommand(c[0],
- quiet = True,
- **c[1])
- if ret != 0:
- break
- if not read:
- os.environ["GRASS_PNG_READ"] = "TRUE"
-
- os.environ["GRASS_PNG_READ"] = "FALSE"
- else:
- ret = gcmd.RunCommand(self.cmd[0],
- quiet = True,
- **self.cmd[1])
-
- if ret != 0:
- #clean up after probley
- try:
- os.remove(self.mapfile)
- os.remove(self.maskfile)
- os.remove(self.gtemp)
- except (OSError, TypeError):
- pass
- self.mapfile = None
- self.maskfile = None
-
- except gcmd.CmdError, e:
- print >> sys.stderr, e
- # clean up after problems
- try:
- os.remove(self.mapfile)
- os.remove(self.maskfile)
- os.remove(self.gtemp)
- except (OSError, TypeError):
- pass
- self.mapfile = None
- self.maskfile = None
-
- #
- # stop monitor
- #
- if os.environ.has_key("GRASS_PNGFILE"):
- del os.environ["GRASS_PNGFILE"]
-
- self.force_render = False
-
- return self.mapfile
-
- def GetCmd(self, string=False):
- """
- Get GRASS command as list of string.
- @param string get command as string if True otherwise as list
- @return command list/string
- """
- if string:
- if self.type == 'command':
- scmd = []
- for c in self.cmd:
- scmd.append(utils.GetCmdString(c))
-
- return ';'.join(scmd)
- else:
- return utils.GetCmdString(self.cmd)
- else:
- return self.cmd
- def GetType(self):
- """!Get map layer type"""
- return self.type
-
- def GetOpacity(self, float=False):
- """
- Get layer opacity level
- @param float get opacity level in <0,1> otherwise <0,100>
- @return opacity level
- """
- if float:
- return self.opacity
-
- return int (self.opacity * 100)
- def GetName(self, fullyQualified=True):
- """!Get map layer name
- @param fullyQualified if True return 'name@mapset' otherwise
- ('name', 'mapset')
- """
- if fullyQualified:
- return self.name
- else:
- if '@' in self.name:
- return { 'name' : self.name.split('@')[0],
- 'mapset' : self.name.split('@')[1] }
- else:
- return { 'name' : self.name,
- 'mapset' : '' }
-
- def IsActive(self):
- """!Check if layer is activated for rendering"""
- return self.active
- def SetType(self, type):
- """!Set layer type"""
- if type not in ('raster', '3d-raster', 'vector',
- 'overlay', 'command',
- 'shaded', 'rgb', 'his', 'rastarrow', 'rastnum',
- 'thememap', 'themechart', 'grid', 'labels',
- 'geodesic','rhumb'):
- raise gcmd.GStdError(_("Unsupported map layer type '%s'") % str(type))
-
- self.type = type
- def SetName(self, name):
- """!Set layer name"""
- self.name = name
-
- def SetActive(self, enable=True):
- """!Active or deactive layer"""
- self.active = bool(enable)
- def SetHidden(self, enable=False):
- """!Hide or show map layer in Layer Manager"""
- self.hidden = bool(enable)
- def SetOpacity(self, value):
- """!Set opacity value"""
- if value < 0:
- value = 0.
- elif value > 1:
- value = 1.
-
- self.opacity = float(value)
-
- def SetCmd(self, cmd):
- """!Set new command for layer"""
- if self.type == 'command':
- self.cmd = []
- for c in cmd:
- self.cmd.append(utils.CmdToTuple(c))
- else:
- self.cmd = utils.CmdToTuple(cmd)
- Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string=True))
-
- # for re-rendering
- self.force_render = True
-
- class MapLayer(Layer):
- """!Represents map layer in the map canvas"""
- def __init__(self, type, cmd, name=None,
- active=True, hidden=False, opacity=1.0):
- """
- @param type layer type ('raster', 'vector', 'command', etc.)
- @param cmd GRASS command to render layer,
- given as list, e.g. ['d.rast', 'map=elevation@PERMANENT']
- @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree) or None
- @param active layer is active, will be rendered only if True
- @param hidden layer is hidden, won't be listed in Layer Manager if True
- @param opacity layer opacity <0;1>
- """
- Layer.__init__(self, type, cmd, name,
- active, hidden, opacity)
- #self.mapfile = self.gtemp + ".ppm"
- def GetMapset(self):
- """
- Get mapset of map layer
- @return mapset name
- @return '' on error (no name given)
- """
- if not self.name:
- return ''
- try:
- return self.name.split('@')[1]
- except IndexError:
- return self.name
- class Overlay(Layer):
- """!Represents overlay displayed in map canvas"""
- def __init__(self, id, type, cmd,
- active=True, hidden=True, opacity=1.0):
- """
- @param id overlay id (for PseudoDC)
- @param type overlay type ('barscale', 'legend', etc.)
- @param cmd GRASS command to render overlay,
- given as list, e.g. ['d.legend', 'map=elevation@PERMANENT']
- @param active layer is active, will be rendered only if True
- @param hidden layer is hidden, won't be listed in Layer Manager if True
- @param opacity layer opacity <0;1>
- """
- Layer.__init__(self, 'overlay', cmd, type,
- active, hidden, opacity)
- self.id = id
- #self.mapfile = self.gtemp + ".png"
- class Map(object):
- """
- Map composition (stack of map layers and overlays)
- """
- def __init__(self, gisrc=None):
- #
- # region/extent settigns
- #
- self.wind = {} # WIND settings (wind file)
- self.region = {} # region settings (g.region)
- self.width = 640 # map width
- self.height = 480 # map height
- #
- # list of layers
- #
- self.layers = [] # stack of available GRASS layer
- self.overlays = [] # stack of available overlays
- self.ovlookup = {} # lookup dictionary for overlay items and overlays
- #
- # environment settings
- #
- # environment variables, like MAPSET, LOCATION_NAME, etc.
- self.env = {}
- # path to external gisrc
- self.gisrc = gisrc
-
- #
- # generated file for g.pnmcomp output for rendering the map
- #
- #self.mapfile = utils.GetTempfile()
- self.mapfile = tempfile.mkstemp(suffix='.ppm')[1]
- # setting some initial env. variables
- self.InitGisEnv() # g.gisenv
- self.InitRegion()
- #
- # GRASS environment variable (for rendering)
- #
- os.environ["GRASS_TRANSPARENT"] = "TRUE"
- os.environ["GRASS_BACKGROUNDCOLOR"] = "ffffff"
- self.projinfo = self.ProjInfo()
-
- def InitRegion(self):
- """
- Initialize current region settings.
- Set up 'self.region' using g.region command and
- self.wind according to the wind file.
- Adjust self.region based on map window size.
- """
- #
- # setting region ('g.region -upg')
- #
- ### not needed here (MapFrame.OnSize())
- # self.region = self.GetRegion()
- #
- # read WIND file
- #
- self.GetWindow()
- #
- # setting resolution
- #
- # not needed here (MapFrame.OnSize())
- # self.SetRegion()
- def InitGisEnv(self):
- """
- Stores GRASS variables (g.gisenv) to self.env variable
- """
- if not os.getenv("GISBASE"):
- print >> sys.stderr, _("GISBASE not set. You must be in GRASS GIS to run this program.")
- sys.exit(1)
-
- # use external gisrc if defined
- gisrc_orig = os.getenv("GISRC")
- if self.gisrc:
- os.environ["GISRC"] = self.gisrc
- self.env = grass.gisenv()
-
- # back to original gisrc
- if self.gisrc:
- os.environ["GISRC"] = gisrc_orig
- def GetWindow(self):
- """!Read WIND file and set up self.wind dictionary"""
- # FIXME: duplicated region WIND == g.region (at least some values)
- filename = os.path.join (self.env['GISDBASE'],
- self.env['LOCATION_NAME'],
- self.env['MAPSET'],
- "WIND")
- try:
- windfile = open (filename, "r")
- except IOError, e:
- sys.stderr.write("%s: %s <%s>: %s\n%s\n" % (_("Error"), _("Unable to open file"),
- filename, e,
- _("wxGUI closed.")))
- sys.exit(1)
- for line in windfile.readlines():
- line = line.strip()
- key, value = line.split(":",1)
- key = key.strip()
- value = value.strip()
- self.wind[key] = value
-
- windfile.close()
- return self.wind
-
- def AdjustRegion(self):
- """
- Adjusts display resolution to match monitor size in pixels.
- Maintains constant display resolution, not related to computational
- region. Do NOT use the display resolution to set computational
- resolution. Set computational resolution through g.region.
- """
- mapwidth = abs(self.region["e"] - self.region["w"])
- mapheight = abs(self.region['n'] - self.region['s'])
- self.region["nsres"] = mapheight / self.height
- self.region["ewres"] = mapwidth / self.width
- self.region['rows'] = round(mapheight / self.region["nsres"])
- self.region['cols'] = round(mapwidth / self.region["ewres"])
- self.region['cells'] = self.region['rows'] * self.region['cols']
- Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)
- return self.region
- def AlignResolution(self):
- """
- Sets display extents to even multiple of
- current resolution defined in WIND file from SW corner.
- This must be done manually as using the -a flag
- can produce incorrect extents.
- """
- # new values to use for saving to region file
- new = {}
- n = s = e = w = 0.0
- nwres = ewres = 0.0
- # Get current values for region and display
- nsres = self.GetRegion()['nsres']
- ewres = self.GetRegion()['ewres']
- n = float(self.region['n'])
- s = float(self.region['s'])
- e = float(self.region['e'])
- w = float(self.region['w'])
- # Calculate rows, columns, and extents
- new['rows'] = math.fabs(round((n-s)/nsres))
- new['cols'] = math.fabs(round((e-w)/ewres))
- # Calculate new extents
- new['s'] = nsres * round(s/nsres)
- new['w'] = ewres * round(w/ewres)
- new['n'] = new['s'] + (new['rows'] * nsres)
- new['e'] = new['w'] + (new['cols'] * ewres)
- return new
- def AlignExtentFromDisplay(self):
- """!Align region extent based on display size from center point"""
- # calculate new bounding box based on center of display
- if self.region["ewres"] > self.region["nsres"]:
- res = self.region["ewres"]
- else:
- res = self.region["nsres"]
- Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
- (self.width, self.height, res, self.region['center_easting'],
- self.region['center_northing']))
-
- ew = (self.width / 2) * res
- ns = (self.height / 2) * res
- self.region['n'] = self.region['center_northing'] + ns
- self.region['s'] = self.region['center_northing'] - ns
- self.region['e'] = self.region['center_easting'] + ew
- self.region['w'] = self.region['center_easting'] - ew
- # LL locations
- if self.projinfo['proj'] == 'll':
- if self.region['n'] > 90.0:
- self.region['n'] = 90.0
- if self.region['s'] < -90.0:
- self.region['s'] = -90.0
-
- def ChangeMapSize(self, (width, height)):
- """!Change size of rendered map.
-
- @param width,height map size
- @return True on success
- @return False on failure
- """
- try:
- self.width = int(width)
- self.height = int(height)
- Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
- (self.width, self.height))
- return True
- except:
- self.width = 640
- self.height = 480
- return False
- def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
- n = None, s = None, e = None, w = None, default = False,
- update = False):
- """!Get region settings (g.region -upgc)
- Optionally extent, raster or vector map layer can be given.
-
- @param rast list of raster maps
- @param zoom zoom to raster map (ignore NULLs)
- @param vect list of vector maps
- @param regionName named region or None
- @param n,s,e,w force extent
- @param default force default region settings
- @param update if True update current display region settings
-
- @return region settings as directory, e.g. {
- 'n':'4928010', 's':'4913700', 'w':'589980',...}
- """
- region = {}
- tmpreg = os.getenv("GRASS_REGION")
- if tmpreg:
- del os.environ["GRASS_REGION"]
- # use external gisrc if defined
- gisrc_orig = os.getenv("GISRC")
- if self.gisrc:
- os.environ["GISRC"] = self.gisrc
- # do not update & shell style output
- cmd = {}
- cmd['flags'] = 'ugpc'
- if default:
- cmd['flags'] += 'd'
-
- if regionName:
- cmd['region'] = regionName
-
- if n:
- cmd['n'] = n
- if s:
- cmd['s'] = s
- if e:
- cmd['e'] = e
- if w:
- cmd['w'] = w
- if rast:
- if zoom:
- cmd['zoom'] = rast[0]
- else:
- cmd['rast'] = ','.join(rast)
- if vect:
- cmd['vect'] = ','.join(vect)
-
- ret = gcmd.RunCommand('g.region',
- read = True,
- **cmd)
- if not ret:
- e = gcmd.CmdError(cmd = 'g.region', message = '')
- if rast:
- e.message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
- '%s%s' % (os.linesep, os.linesep) + e.message
- elif vect:
- e.message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
- '%s%s' % (os.linesep, os.linesep) + e.message
- else:
- e.message = _("Unable to get current geographic extent. "
- "Force quiting wxGUI. Please run manually g.region to "
- "fix the problem.")
- e.Show()
- return self.region
- for reg in ret.splitlines():
- key, val = reg.split("=", 1)
- try:
- region[key] = float(val)
- except ValueError:
- region[key] = val
- # back to original gisrc
- if self.gisrc:
- os.environ["GISRC"] = gisrc_orig
- # restore region
- if tmpreg:
- os.environ["GRASS_REGION"] = tmpreg
- Debug.msg (3, "Map.GetRegion(): %s" % region)
-
- if update:
- self.region = region
-
- return region
- def GetCurrentRegion(self):
- """!Get current display region settings"""
- return self.region
- def SetRegion(self, windres=False):
- """
- Render string for GRASS_REGION env. variable, so that the images will be rendered
- from desired zoom level.
- @param windres uses resolution from WIND file rather than display (for modules that require set
- resolution like d.rast.num)
- @return String usable for GRASS_REGION variable or None
- """
- grass_region = ""
- if windres:
- compRegion = self.GetRegion()
- region = copy.copy(self.region)
- for key in ('nsres', 'ewres',
- 'rows', 'cols', 'cells'):
- region[key] = compRegion[key]
- else:
- # adjust region settings to match monitor
- region = self.AdjustRegion()
-
- # read values from wind file
- try:
- for key in self.wind.keys():
- if key == 'north':
- grass_region += "north: %s; " % \
- (region['n'])
- continue
- elif key == "south":
- grass_region += "south: %s; " % \
- (region['s'])
- continue
- elif key == "east":
- grass_region += "east: %s; " % \
- (region['e'])
- continue
- elif key == "west":
- grass_region += "west: %s; " % \
- (region['w'])
- continue
- elif key == "e-w resol":
- grass_region += "e-w resol: %f; " % \
- (region['ewres'])
- continue
- elif key == "n-s resol":
- grass_region += "n-s resol: %f; " % \
- (region['nsres'])
- continue
- elif key == "cols":
- grass_region += 'cols: %d; ' % \
- region['cols']
- continue
- elif key == "rows":
- grass_region += 'rows: %d; ' % \
- region['rows']
- continue
- else:
- grass_region += key + ": " + self.wind[key] + "; "
- Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
- return grass_region
- except:
- return None
- def ProjInfo(self):
- """
- Return region projection and map units information
- """
- projinfo = {}
- ret = gcmd.RunCommand('g.proj',
- read = True,
- flags = 'p')
-
- if not ret:
- return projinfo
- for line in ret.splitlines():
- if ':' in line:
- key,val = line.split(':')
- key = key.strip()
- val = val.strip()
- projinfo[key] = val
- elif "XY location (unprojected)" in line:
- projinfo['proj'] = "xy"
- projinfo['units'] = ''
- break
-
- return projinfo
-
- def GetListOfLayers(self, l_type=None, l_mapset=None, l_name=None,
- l_active=None, l_hidden=None):
- """
- Returns list of layers of selected properties or list of all
- layers.
- @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
- @param l_mapset all layers from given mapset (only for maplayers)
- @param l_name all layers with given name
- @param l_active only layers with 'active' attribute set to True or False
- @param l_hidden only layers with 'hidden' attribute set to True or False
- @return list of selected layers
- """
- selected = []
- if type(l_type) == type(''):
- one_type = True
- else:
- one_type = False
- if one_type and l_type == 'overlay':
- list = self.overlays
- else:
- list = self.layers
- # ["raster", "vector", "wms", ... ]
- for layer in list:
- # specified type only
- if l_type != None:
- if one_type and layer.type != l_type:
- continue
- elif not one_type and layer.type not in l_type:
- continue
- # mapset
- if (l_mapset != None and type != 'overlay') and \
- layer.GetMapset() != l_mapset:
- continue
- # name
- if l_name != None and layer.name != l_name:
- continue
- # hidden and active layers
- if l_active != None and \
- l_hidden != None:
- if layer.active == l_active and \
- layer.hidden == l_hidden:
- selected.append(layer)
- # active layers
- elif l_active != None:
- if layer.active == l_active:
- selected.append(layer)
- # hidden layers
- elif l_hidden != None:
- if layer.hidden == l_hidden:
- selected.append(layer)
- # all layers
- else:
- selected.append(layer)
- Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
- return selected
- def _renderLayers(self, force, mapWindow, maps, masks, opacities):
- # render map layers
- ilayer = 1
- for layer in self.layers + self.overlays:
- # skip dead or disabled map layers
- if layer == None or layer.active == False:
- continue
-
- # render if there is no mapfile
- if force or \
- layer.force_render or \
- layer.mapfile == None or \
- (not os.path.isfile(layer.mapfile) or not os.path.getsize(layer.mapfile)):
- if not layer.Render():
- continue
-
- if mapWindow:
- # update progress bar
- ### wx.SafeYield(mapWindow)
- event = wxUpdateProgressBar(value=ilayer)
- wx.PostEvent(mapWindow, event)
-
- # add image to compositing list
- if layer.type != "overlay":
- maps.append(layer.mapfile)
- masks.append(layer.maskfile)
- opacities.append(str(layer.opacity))
-
- Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
- ilayer += 1
-
- def Render(self, force=False, mapWindow=None, windres=False):
- """
- Creates final image composite
- This function can conditionaly use high-level tools, which
- should be avaliable in wxPython library
-
- @param force force rendering
- @param reference for MapFrame instance (for progress bar)
- @param windres use region resolution (True) otherwise display resolution
-
- @return name of file with rendered image or None
- """
- maps = []
- masks =[]
- opacities = []
- # use external gisrc if defined
- gisrc_orig = os.getenv("GISRC")
- if self.gisrc:
- os.environ["GISRC"] = self.gisrc
- tmp_region = os.getenv("GRASS_REGION")
- os.environ["GRASS_REGION"] = self.SetRegion(windres)
- os.environ["GRASS_WIDTH"] = str(self.width)
- os.environ["GRASS_HEIGHT"] = str(self.height)
- os.environ["GRASS_PNG_AUTO_WRITE"] = "TRUE"
- os.environ["GRASS_COMPRESSION"] = "0"
- os.environ["GRASS_TRUECOLOR"] = "TRUE"
- driver = UserSettings.Get(group='display', key='driver', subkey='type')
- if driver == 'cairo':
- os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
- elif driver == 'png':
- os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
- else:
- os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
- os.environ["GRASS_PNG_READ"] = "FALSE"
-
- self._renderLayers(force, mapWindow, maps, masks, opacities)
-
- # ugly hack for MSYS
- if not subprocess.mswindows:
- mapstr = ",".join(maps)
- maskstr = ",".join(masks)
- mapoutstr = self.mapfile
- else:
- mapstr = ""
- for item in maps:
- mapstr += item.replace('\\', '/')
- mapstr = mapstr.rstrip(',')
- maskstr = ""
- for item in masks:
- maskstr += item.replace('\\', '/')
- maskstr = maskstr.rstrip(',')
- mapoutstr = self.mapfile.replace('\\', '/')
- # compose command
- bgcolor = ':'.join(map(str, UserSettings.Get(group='display', key='bgcolor',
- subkey='color')))
-
- complist = ["g.pnmcomp",
- "in=%s" % ",".join(maps),
- "mask=%s" % ",".join(masks),
- "opacity=%s" % ",".join(opacities),
- "background=%s" % bgcolor,
- "width=%s" % str(self.width),
- "height=%s" % str(self.height),
- "output=%s" % self.mapfile]
-
- # render overlays
- if tmp_region:
- os.environ["GRASS_REGION"] = tmp_region
- else:
- del os.environ["GRASS_REGION"]
-
- # run g.pngcomp to get composite image
- ret = gcmd.RunCommand('g.pnmcomp',
- input = '%s' % ",".join(maps),
- mask = '%s' % ",".join(masks),
- opacity = '%s' % ",".join(opacities),
- background = bgcolor,
- width = self.width,
- height = self.height,
- output = self.mapfile)
-
- if ret != 0:
- print >> sys.stderr, _("ERROR: Rendering failed")
- return None
- # back to original gisrc
- if self.gisrc:
- os.environ["GISRC"] = gisrc_orig
- Debug.msg (2, "Map.Render() force=%s file=%s" % (force, self.mapfile))
- return self.mapfile
- def AddLayer(self, type, command, name=None,
- l_active=True, l_hidden=False, l_opacity=1.0, l_render=False,
- pos=-1):
- """
- Adds generic map layer to list of layers
- @param type layer type ('raster', 'vector', etc.)
- @param command GRASS command given as list
- @param name layer name
- @param l_active layer render only if True
- @param l_hidden layer not displayed in layer tree if True
- @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
- @param l_render render an image if True
- @param pos position in layer list (-1 for append)
- @return new layer on success
- @return None on failure
- """
- # l_opacity must be <0;1>
- if l_opacity < 0: l_opacity = 0
- elif l_opacity > 1: l_opacity = 1
- layer = MapLayer(type=type, name=name, cmd=command,
- active=l_active, hidden=l_hidden, opacity=l_opacity)
- # add maplayer to the list of layers
- if pos > -1:
- self.layers.insert(pos, layer)
- else:
- self.layers.append(layer)
-
- Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
- if l_render:
- if not layer.Render():
- raise gcmd.GStdError(_("Unable to render map layer <%s>.") % (name))
- return layer
- def DeleteLayer(self, layer, overlay=False):
- """
- Removes layer from list of layers
- @param layer layer instance in layer tree
- @param overlay delete overlay (use self.DeleteOverlay() instead)
- @return removed layer on success or None
- """
- Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
- if overlay:
- list = self.overlays
- else:
- list = self.layers
- if layer in list:
- if layer.mapfile:
- base = os.path.split(layer.mapfile)[0]
- mapfile = os.path.split(layer.mapfile)[1]
- tempbase = mapfile.split('.')[0]
- if base == '' or tempbase == '':
- return None
- basefile = os.path.join(base, tempbase) + r'.*'
- for f in glob.glob(basefile):
- os.remove(f)
- list.remove(layer)
- return layer
- return None
- def ReorderLayers(self, layerList):
- """
- Reorder list to match layer tree
- @param layerList list of layers
- """
- self.layers = layerList
- layerNameList = ""
- for layer in self.layers:
- if layer.name:
- layerNameList += layer.name + ','
- Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
- (layerNameList))
- def ChangeLayer(self, layer, render=False, **kargs):
- """
- Change map layer properties
- @param layer map layer instance
- @param type layer type ('raster', 'vector', etc.)
- @param command GRASS command given as list
- @param name layer name
- @param active layer render only if True
- @param hidden layer not displayed in layer tree if True
- @param opacity opacity level range from 0(transparent) - 1(not transparent)
- @param render render an image if True
- """
- Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
-
- if kargs.has_key('type'):
- layer.SetType(kargs['type']) # check type
-
- if kargs.has_key('command'):
- layer.SetCmd(kargs['command'])
-
- if kargs.has_key('name'):
- layer.SetName(kargs['name'])
- if kargs.has_key('active'):
- layer.SetActive(kargs['active'])
- if kargs.has_key('hidden'):
- layer.SetHidden(kargs['hidden'])
- if kargs.has_key('opacity'):
- layer.SetOpacity(kargs['opacity'])
-
- if render and not layer.Render():
- raise gcmd.GException(_("Unable to render map layer <%s>.") %
- (name))
- return layer
- def ChangeOpacity(self, layer, l_opacity):
- """
- Changes opacity value of map layer
- @param layer layer instance in layer tree
- @param l_opacity opacity level <0;1>
- """
- # l_opacity must be <0;1>
- if l_opacity < 0: l_opacity = 0
- elif l_opacity > 1: l_opacity = 1
- layer.opacity = l_opacity
- Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
- (layer.name, layer.opacity))
- def ChangeLayerActive(self, layer, active):
- """
- Enable or disable map layer
- @param layer layer instance in layer tree
- @param active to be rendered (True)
- """
- layer.active = active
- Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
- (layer.name, layer.active))
- def ChangeLayerName (self, layer, name):
- """
- Change name of the layer
- @param layer layer instance in layer tree
- @param name layer name to set up
- """
- Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
- (layer.name, name))
- layer.name = name
- def RemoveLayer(self, name=None, id=None):
- """
- Removes layer from layer list
- Layer is defined by name@mapset or id.
- @param name layer name (must be unique)
- @param id layer index in layer list
- @return removed layer on success
- @return None on failure
- """
- # delete by name
- if name:
- retlayer = None
- for layer in self.layers:
- if layer.name == name:
- retlayer = layer
- os.remove(layer.mapfile)
- os.remove(layer.maskfile)
- self.layers.remove(layer)
- return layer
- # del by id
- elif id != None:
- return self.layers.pop(id)
- return None
- def GetLayerIndex(self, layer, overlay=False):
- """
- Get index of layer in layer list.
- @param layer layer instace in layer tree
- @param overlay use list of overlays instead
-
- @return layer index
- @return -1 if layer not found
- """
- if overlay:
- list = self.overlay
- else:
- list = self.layers
-
- if layer in list:
- return list.index(layer)
- return -1
- def AddOverlay(self, id, type, command,
- l_active=True, l_hidden=True, l_opacity=1.0, l_render=False):
- """
- Adds overlay (grid, barscale, legend, etc.) to list of overlays
-
- @param id overlay id (PseudoDC)
- @param type overlay type (barscale, legend)
- @param command GRASS command to render overlay
- @param l_active overlay activated (True) or disabled (False)
- @param l_hidden overlay is not shown in layer tree (if True)
- @param l_render render an image (if True)
- @return new layer on success
- @retutn None on failure
- """
- Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
- overlay = Overlay(id=id, type=type, cmd=command,
- active=l_active, hidden=l_hidden, opacity=l_opacity)
- # add maplayer to the list of layers
- self.overlays.append(overlay)
- if l_render and command != '' and not overlay.Render():
- raise gcmd.GException(_("Unable render overlay <%s>.") %
- (name))
- return self.overlays[-1]
- def ChangeOverlay(self, id, render=False, **kargs):
- """
- Change overlay properities
- Add new overlay if overlay with 'id' doesn't exist.
- @param id overlay id (PseudoDC)
- @param type overlay type (barscale, legend)
- @param command GRASS command to render overlay
- @param l_active overlay activated (True) or disabled (False)
- @param l_hidden overlay is not shown in layer tree (if True)
- @param l_render render an image (if True)
- @return new layer on success
- """
- overlay = self.GetOverlay(id, list=False)
- if overlay is None:
- overlay = Overlay(id, type = None, cmd = None)
-
- if kargs.has_key('type'):
- overlay.SetName(kargs['type']) # type -> overlay
-
- if kargs.has_key('command'):
- overlay.SetCmd(kargs['command'])
-
- if kargs.has_key('active'):
- overlay.SetActive(kargs['active'])
- if kargs.has_key('hidden'):
- overlay.SetHidden(kargs['hidden'])
- if kargs.has_key('opacity'):
- overlay.SetOpacity(kargs['opacity'])
-
- if render and command != [] and not overlay.Render():
- raise gcmd.GException(_("Unable render overlay <%s>") %
- (name))
-
- return overlay
- def GetOverlay(self, id, list=False):
- """!Return overlay(s) with 'id'
- @param id overlay id
- @param list return list of overlays of True
- otherwise suppose 'id' to be unique
-
- @return list of overlays (list=True)
- @return overlay (list=False)
- @retur None (list=False) if no overlay or more overlays found
- """
- ovl = []
- for overlay in self.overlays:
- if overlay.id == id:
- ovl.append(overlay)
-
- if not list:
- if len(ovl) != 1:
- return None
- else:
- return ovl[0]
- return ovl
- def DeleteOverlay(self, overlay):
- """!Delete overlay
- @param id overlay id
- @return removed overlay on success or None
- """
- return self.DeleteLayer(overlay, overlay=True)
- def Clean(self):
- """
- Clean layer stack - go trough all layers and remove them from layer list
- Removes also l_mapfile and l_maskfile
- @return 1 on faulure
- @return None on success
- """
- try:
- dir = os.path.dirname(self.mapfile)
- base = os.path.basename(self.mapfile).split('.')[0]
- removepath = os.path.join(dir,base)+r'*'
- for f in glob.glob(removepath):
- os.remove(f)
- for layer in self.layers:
- if layer.mapfile:
- dir = os.path.dirname(layer.mapfile)
- base = os.path.basename(layer.mapfile).split('.')[0]
- removepath = os.path.join(dir,base)+r'*'
- for f in glob.glob(removepath):
- os.remove(f)
- self.layers.remove(layer)
-
- for overlay in self.overlays:
- if overlay.mapfile:
- dir = os.path.dirname(overlay.mapfile)
- base = os.path.basename(overlay.mapfile).split('.')[0]
- removepath = os.path.join(dir,base)+r'*'
- for f in glob.glob(removepath):
- os.remove(f)
- self.overlays.remove(overlay)
- return None
- except:
- return 1
- self.layers = []
- def ReverseListOfLayers(self):
- """!Reverse list of layers"""
- return self.layers.reverse()
- if __name__ == "__main__":
- """
- Test of Display class.
- Usage: display=Render()
- """
- print "Initializing..."
- os.system("g.region -d")
- map = Map()
- map.width = 640
- map.height = 480
- map.AddLayer(item=None,
- type="raster",
- name="elevation.dem",
- command = ["d.rast", "elevation.dem@PERMANENT", "catlist=1000-1500", "-i"],
- l_opacity=.7)
- map.AddLayer(item=None,
- type="vector",
- name="streams",
- command = ["d.vect", "streams@PERMANENT", "color=red", "width=3", "type=line"])
- image = map.Render(force=True)
- if image:
- os.system("display %s" % image)
|