|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package core.render 00003 00004 @brief Rendering map layers and overlays into map composition image. 00005 00006 Classes: 00007 - render::Layer 00008 - render::MapLayer 00009 - render::Overlay 00010 - render::Map 00011 00012 (C) 2006-2011 by the GRASS Development Team 00013 00014 This program is free software under the GNU General Public License 00015 (>=v2). Read the file COPYING that comes with GRASS for details. 00016 00017 @author Michael Barton 00018 @author Jachym Cepicky 00019 @author Martin Landa <landa.martin gmail.com> 00020 """ 00021 00022 import os 00023 import sys 00024 import glob 00025 import math 00026 import copy 00027 import tempfile 00028 import types 00029 00030 import wx 00031 from wx.lib.newevent import NewEvent 00032 00033 from grass.script import core as grass 00034 00035 from core import utils 00036 from core.gcmd import GException, GError, RunCommand 00037 from core.debug import Debug 00038 from core.settings import UserSettings 00039 00040 wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent() 00041 00042 # 00043 # use g.pnmcomp for creating image composition or 00044 # wxPython functionality 00045 # 00046 USE_GPNMCOMP = True 00047 00048 class Layer(object): 00049 """!Virtual class which stores information about layers (map layers and 00050 overlays) of the map composition. 00051 00052 For map layer use MapLayer class. 00053 For overlays use Overlay class. 00054 """ 00055 def __init__(self, type, cmd, name = None, 00056 active = True, hidden = False, opacity = 1.0): 00057 """! 00058 @todo pass cmd as tuple instead of list 00059 00060 @param type layer type ('raster', 'vector', 'overlay', 'command', etc.) 00061 @param cmd GRASS command to render layer, 00062 given as list, e.g. ['d.rast', 'map=elevation@PERMANENT'] 00063 @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree) 00064 @param active layer is active, will be rendered only if True 00065 @param hidden layer is hidden, won't be listed in Layer Manager if True 00066 @param opacity layer opacity <0;1> 00067 """ 00068 self.type = type 00069 self.name = name 00070 00071 if self.type == 'command': 00072 self.cmd = list() 00073 for c in cmd: 00074 self.cmd.append(utils.CmdToTuple(c)) 00075 else: 00076 self.cmd = utils.CmdToTuple(cmd) 00077 00078 self.active = active 00079 self.hidden = hidden 00080 self.opacity = opacity 00081 00082 self.force_render = True 00083 00084 Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \ 00085 "active=%d, opacity=%d, hidden=%d" % \ 00086 (self.type, self.GetCmd(string = True), self.name, self.active, 00087 self.opacity, self.hidden)) 00088 00089 # generated file for each layer 00090 self.gtemp = tempfile.mkstemp()[1] 00091 self.maskfile = self.gtemp + ".pgm" 00092 if self.type == 'overlay': 00093 self.mapfile = self.gtemp + ".png" 00094 else: 00095 self.mapfile = self.gtemp + ".ppm" 00096 00097 def __del__(self): 00098 Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" % 00099 (self.name, self.GetCmd(string = True))) 00100 00101 def Render(self): 00102 """!Render layer to image 00103 00104 @return rendered image filename 00105 @return None on error 00106 """ 00107 if not self.cmd: 00108 return None 00109 00110 # ignore in 2D 00111 if self.type == '3d-raster': 00112 return None 00113 00114 Debug.msg (3, "Layer.Render(): type=%s, name=%s" % \ 00115 (self.type, self.name)) 00116 00117 # prepare command for each layer 00118 layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum', 00119 'vector','thememap','themechart', 00120 'grid', 'geodesic', 'rhumb', 'labels', 00121 'command', 'rastleg', 00122 'overlay') 00123 00124 if self.type not in layertypes: 00125 raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \ 00126 {'type' : self.type, 'name' : self.name}) 00127 00128 # start monitor 00129 if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo': 00130 # os.environ["GRASS_CAIROFILE"] = self.mapfile 00131 # if 'cairo' not in gcmd.RunCommand('d.mon', 00132 # flags='p', 00133 # read = True): 00134 # gcmd.RunCommand('d.mon', 00135 # start = 'cairo') 00136 if not self.mapfile: 00137 self.gtemp = tempfile.mkstemp()[1] 00138 self.maskfile = self.gtemp + ".pgm" 00139 if self.type == 'overlay': 00140 self.mapfile = self.gtemp + ".png" 00141 else: 00142 self.mapfile = self.gtemp + ".ppm" 00143 00144 if self.mapfile: 00145 os.environ["GRASS_CAIROFILE"] = self.mapfile 00146 else: 00147 if not self.mapfile: 00148 self.gtemp = tempfile.mkstemp()[1] 00149 self.maskfile = self.gtemp + ".pgm" 00150 if self.type == 'overlay': 00151 self.mapfile = self.gtemp + ".png" 00152 else: 00153 self.mapfile = self.gtemp + ".ppm" 00154 00155 if self.mapfile: 00156 os.environ["GRASS_PNGFILE"] = self.mapfile 00157 00158 # execute command 00159 try: 00160 if self.type == 'command': 00161 read = False 00162 for c in self.cmd: 00163 ret, msg = RunCommand(c[0], 00164 getErrorMsg = True, 00165 quiet = True, 00166 **c[1]) 00167 if ret != 0: 00168 break 00169 if not read: 00170 os.environ["GRASS_PNG_READ"] = "TRUE" 00171 00172 os.environ["GRASS_PNG_READ"] = "FALSE" 00173 else: 00174 ret, msg = RunCommand(self.cmd[0], 00175 getErrorMsg = True, 00176 quiet = True, 00177 **self.cmd[1]) 00178 00179 if msg: 00180 sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True)) 00181 sys.stderr.write(_("Details: %s\n") % msg) 00182 if ret != 0: 00183 raise GException() 00184 00185 except GException: 00186 # clean up after problems 00187 try: 00188 os.remove(self.mapfile) 00189 os.remove(self.maskfile) 00190 os.remove(self.gtemp) 00191 except (OSError, TypeError): 00192 pass 00193 self.mapfile = None 00194 self.maskfile = None 00195 00196 # stop monitor 00197 if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo': 00198 # gcmd.RunCommand('d.mon', 00199 # stop = 'cairo') 00200 del os.environ["GRASS_CAIROFILE"] 00201 elif "GRASS_PNGFILE" in os.environ: 00202 del os.environ["GRASS_PNGFILE"] 00203 00204 self.force_render = False 00205 00206 return self.mapfile 00207 00208 def GetCmd(self, string = False): 00209 """!Get GRASS command as list of string. 00210 00211 @param string get command as string if True otherwise as list 00212 00213 @return command list/string 00214 """ 00215 if string: 00216 if self.type == 'command': 00217 scmd = [] 00218 for c in self.cmd: 00219 scmd.append(utils.GetCmdString(c)) 00220 00221 return ';'.join(scmd) 00222 else: 00223 return utils.GetCmdString(self.cmd) 00224 else: 00225 return self.cmd 00226 00227 def GetType(self): 00228 """!Get map layer type""" 00229 return self.type 00230 00231 def GetElement(self): 00232 """!Get map element type""" 00233 if self.type == 'raster': 00234 return 'cell' 00235 return self.type 00236 00237 def GetOpacity(self, float = False): 00238 """ 00239 Get layer opacity level 00240 00241 @param float get opacity level in <0,1> otherwise <0,100> 00242 00243 @return opacity level 00244 """ 00245 if float: 00246 return self.opacity 00247 00248 return int (self.opacity * 100) 00249 00250 def GetName(self, fullyQualified = True): 00251 """!Get map layer name 00252 00253 @param fullyQualified True to return fully qualified name as a 00254 string 'name@mapset' otherwise directory { 'name', 'mapset' } 00255 is returned 00256 00257 @return string / directory 00258 """ 00259 if fullyQualified: 00260 return self.name 00261 else: 00262 if '@' in self.name: 00263 return { 'name' : self.name.split('@')[0], 00264 'mapset' : self.name.split('@')[1] } 00265 else: 00266 return { 'name' : self.name, 00267 'mapset' : '' } 00268 00269 def IsActive(self): 00270 """!Check if layer is activated for rendering""" 00271 return self.active 00272 00273 def SetType(self, type): 00274 """!Set layer type""" 00275 if type not in ('raster', '3d-raster', 'vector', 00276 'overlay', 'command', 00277 'shaded', 'rgb', 'his', 'rastarrow', 'rastnum', 00278 'thememap', 'themechart', 'grid', 'labels', 00279 'geodesic','rhumb'): 00280 raise GException(_("Unsupported map layer type '%s'") % type) 00281 00282 self.type = type 00283 00284 def SetName(self, name): 00285 """!Set layer name""" 00286 self.name = name 00287 00288 def SetActive(self, enable = True): 00289 """!Active or deactive layer""" 00290 self.active = bool(enable) 00291 00292 def SetHidden(self, enable = False): 00293 """!Hide or show map layer in Layer Manager""" 00294 self.hidden = bool(enable) 00295 00296 def SetOpacity(self, value): 00297 """!Set opacity value""" 00298 if value < 0: 00299 value = 0. 00300 elif value > 1: 00301 value = 1. 00302 00303 self.opacity = float(value) 00304 00305 def SetCmd(self, cmd): 00306 """!Set new command for layer""" 00307 if self.type == 'command': 00308 self.cmd = [] 00309 for c in cmd: 00310 self.cmd.append(utils.CmdToTuple(c)) 00311 else: 00312 self.cmd = utils.CmdToTuple(cmd) 00313 Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string = True)) 00314 00315 # for re-rendering 00316 self.force_render = True 00317 00318 class MapLayer(Layer): 00319 def __init__(self, type, cmd, name = None, 00320 active = True, hidden = False, opacity = 1.0): 00321 """!Represents map layer in the map canvas 00322 00323 @param type layer type ('raster', 'vector', 'command', etc.) 00324 @param cmd GRASS command to render layer, 00325 given as list, e.g. ['d.rast', 'map=elevation@PERMANENT'] 00326 @param name layer name, e.g. 'elevation@PERMANENT' (for layer tree) or None 00327 @param active layer is active, will be rendered only if True 00328 @param hidden layer is hidden, won't be listed in Layer Manager if True 00329 @param opacity layer opacity <0;1> 00330 """ 00331 Layer.__init__(self, type, cmd, name, 00332 active, hidden, opacity) 00333 00334 def GetMapset(self): 00335 """!Get mapset of map layer 00336 00337 @return mapset name 00338 @return '' on error (no name given) 00339 """ 00340 if not self.name: 00341 return '' 00342 00343 try: 00344 return self.name.split('@')[1] 00345 except IndexError: 00346 return self.name 00347 00348 class Overlay(Layer): 00349 def __init__(self, id, type, cmd, 00350 active = True, hidden = True, opacity = 1.0): 00351 """!Represents overlay displayed in map canvas 00352 00353 @param id overlay id (for PseudoDC) 00354 @param type overlay type ('barscale', 'legend', etc.) 00355 @param cmd GRASS command to render overlay, 00356 given as list, e.g. ['d.legend', 'map=elevation@PERMANENT'] 00357 @param active layer is active, will be rendered only if True 00358 @param hidden layer is hidden, won't be listed in Layer Manager if True 00359 @param opacity layer opacity <0;1> 00360 """ 00361 Layer.__init__(self, 'overlay', cmd, type, 00362 active, hidden, opacity) 00363 00364 self.id = id 00365 00366 class Map(object): 00367 """!Map composition (stack of map layers and overlays) 00368 """ 00369 def __init__(self, gisrc = None): 00370 # region/extent settigns 00371 self.wind = dict() # WIND settings (wind file) 00372 self.region = dict() # region settings (g.region) 00373 self.width = 640 # map width 00374 self.height = 480 # map height 00375 00376 # list of layers 00377 self.layers = list() # stack of available GRASS layer 00378 00379 self.overlays = list() # stack of available overlays 00380 self.ovlookup = dict() # lookup dictionary for overlay items and overlays 00381 00382 # environment settings 00383 # environment variables, like MAPSET, LOCATION_NAME, etc. 00384 self.env = dict() 00385 # path to external gisrc 00386 self.gisrc = gisrc 00387 00388 # generated file for g.pnmcomp output for rendering the map 00389 self.mapfile = tempfile.mkstemp(suffix = '.ppm')[1] 00390 00391 # setting some initial env. variables 00392 self._initGisEnv() # g.gisenv 00393 self.GetWindow() 00394 # GRASS environment variable (for rendering) 00395 os.environ["GRASS_TRANSPARENT"] = "TRUE" 00396 os.environ["GRASS_BACKGROUNDCOLOR"] = "ffffff" 00397 00398 # projection info 00399 self.projinfo = self._projInfo() 00400 00401 def _runCommand(self, cmd, **kwargs): 00402 """!Run command in environment defined by self.gisrc if 00403 defined""" 00404 # use external gisrc if defined 00405 gisrc_orig = os.getenv("GISRC") 00406 if self.gisrc: 00407 os.environ["GISRC"] = self.gisrc 00408 00409 ret = cmd(**kwargs) 00410 00411 # back to original gisrc 00412 if self.gisrc: 00413 os.environ["GISRC"] = gisrc_orig 00414 00415 return ret 00416 00417 def _initGisEnv(self): 00418 """!Stores GRASS variables (g.gisenv) to self.env variable 00419 """ 00420 if not os.getenv("GISBASE"): 00421 sys.exit(_("GISBASE not set. You must be in GRASS GIS to run this program.")) 00422 00423 self.env = self._runCommand(grass.gisenv) 00424 00425 def GetProjInfo(self): 00426 """!Get projection info""" 00427 return self.projinfo 00428 00429 def _projInfo(self): 00430 """!Return region projection and map units information 00431 """ 00432 projinfo = dict() 00433 if not grass.find_program('g.proj', ['--help']): 00434 sys.exit(_("GRASS module '%s' not found. Unable to start map " 00435 "display window.") % 'g.proj') 00436 00437 ret = self._runCommand(RunCommand, prog = 'g.proj', 00438 read = True, flags = 'p') 00439 00440 if not ret: 00441 return projinfo 00442 00443 for line in ret.splitlines(): 00444 if ':' in line: 00445 key, val = map(lambda x: x.strip(), line.split(':')) 00446 if key in ['units']: 00447 val = val.lower() 00448 projinfo[key] = val 00449 elif "XY location (unprojected)" in line: 00450 projinfo['proj'] = 'xy' 00451 projinfo['units'] = '' 00452 break 00453 00454 return projinfo 00455 00456 def GetWindow(self): 00457 """!Read WIND file and set up self.wind dictionary""" 00458 # FIXME: duplicated region WIND == g.region (at least some values) 00459 filename = os.path.join (self.env['GISDBASE'], 00460 self.env['LOCATION_NAME'], 00461 self.env['MAPSET'], 00462 "WIND") 00463 try: 00464 windfile = open (filename, "r") 00465 except IOError, e: 00466 sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \ 00467 { 'file' : filename, 'ret' : e}) 00468 00469 for line in windfile.readlines(): 00470 line = line.strip() 00471 key, value = line.split(":", 1) 00472 self.wind[key.strip()] = value.strip() 00473 00474 windfile.close() 00475 00476 return self.wind 00477 00478 def AdjustRegion(self): 00479 """!Adjusts display resolution to match monitor size in 00480 pixels. Maintains constant display resolution, not related to 00481 computational region. Do NOT use the display resolution to set 00482 computational resolution. Set computational resolution through 00483 g.region. 00484 """ 00485 mapwidth = abs(self.region["e"] - self.region["w"]) 00486 mapheight = abs(self.region['n'] - self.region['s']) 00487 00488 self.region["nsres"] = mapheight / self.height 00489 self.region["ewres"] = mapwidth / self.width 00490 self.region['rows'] = round(mapheight / self.region["nsres"]) 00491 self.region['cols'] = round(mapwidth / self.region["ewres"]) 00492 self.region['cells'] = self.region['rows'] * self.region['cols'] 00493 00494 Debug.msg (3, "Map.AdjustRegion(): %s" % self.region) 00495 00496 return self.region 00497 00498 def AlignResolution(self): 00499 """!Sets display extents to even multiple of current 00500 resolution defined in WIND file from SW corner. This must be 00501 done manually as using the -a flag can produce incorrect 00502 extents. 00503 """ 00504 # new values to use for saving to region file 00505 new = {} 00506 n = s = e = w = 0.0 00507 nwres = ewres = 0.0 00508 00509 # Get current values for region and display 00510 reg = self.GetRegion() 00511 nsres = reg['nsres'] 00512 ewres = reg['ewres'] 00513 00514 n = float(self.region['n']) 00515 s = float(self.region['s']) 00516 e = float(self.region['e']) 00517 w = float(self.region['w']) 00518 00519 # Calculate rows, columns, and extents 00520 new['rows'] = math.fabs(round((n-s)/nsres)) 00521 new['cols'] = math.fabs(round((e-w)/ewres)) 00522 00523 # Calculate new extents 00524 new['s'] = nsres * round(s / nsres) 00525 new['w'] = ewres * round(w / ewres) 00526 new['n'] = new['s'] + (new['rows'] * nsres) 00527 new['e'] = new['w'] + (new['cols'] * ewres) 00528 00529 return new 00530 00531 def AlignExtentFromDisplay(self): 00532 """!Align region extent based on display size from center 00533 point""" 00534 # calculate new bounding box based on center of display 00535 if self.region["ewres"] > self.region["nsres"]: 00536 res = self.region["ewres"] 00537 else: 00538 res = self.region["nsres"] 00539 00540 Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \ 00541 (self.width, self.height, res, self.region['center_easting'], 00542 self.region['center_northing'])) 00543 00544 ew = (self.width / 2) * res 00545 ns = (self.height / 2) * res 00546 00547 self.region['n'] = self.region['center_northing'] + ns 00548 self.region['s'] = self.region['center_northing'] - ns 00549 self.region['e'] = self.region['center_easting'] + ew 00550 self.region['w'] = self.region['center_easting'] - ew 00551 00552 # LL locations 00553 if self.projinfo['proj'] == 'll': 00554 self.region['n'] = min(self.region['n'], 90.0) 00555 self.region['s'] = max(self.region['s'], -90.0) 00556 00557 def ChangeMapSize(self, (width, height)): 00558 """!Change size of rendered map. 00559 00560 @param width,height map size 00561 00562 @return True on success 00563 @return False on failure 00564 """ 00565 try: 00566 self.width = int(width) 00567 self.height = int(height) 00568 Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \ 00569 (self.width, self.height)) 00570 return True 00571 except: 00572 self.width = 640 00573 self.height = 480 00574 return False 00575 00576 def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None, 00577 n = None, s = None, e = None, w = None, default = False, 00578 update = False): 00579 """!Get region settings (g.region -upgc) 00580 00581 Optionally extent, raster or vector map layer can be given. 00582 00583 @param rast list of raster maps 00584 @param zoom zoom to raster map (ignore NULLs) 00585 @param vect list of vector maps 00586 @param regionName named region or None 00587 @param n,s,e,w force extent 00588 @param default force default region settings 00589 @param update if True update current display region settings 00590 00591 @return region settings as directory, e.g. { 00592 'n':'4928010', 's':'4913700', 'w':'589980',...} 00593 00594 @see GetCurrentRegion() 00595 """ 00596 region = {} 00597 00598 tmpreg = os.getenv("GRASS_REGION") 00599 if tmpreg: 00600 del os.environ["GRASS_REGION"] 00601 00602 # use external gisrc if defined 00603 gisrc_orig = os.getenv("GISRC") 00604 if self.gisrc: 00605 os.environ["GISRC"] = self.gisrc 00606 00607 # do not update & shell style output 00608 cmd = {} 00609 cmd['flags'] = 'ugpc' 00610 00611 if default: 00612 cmd['flags'] += 'd' 00613 00614 if regionName: 00615 cmd['region'] = regionName 00616 00617 if n: 00618 cmd['n'] = n 00619 if s: 00620 cmd['s'] = s 00621 if e: 00622 cmd['e'] = e 00623 if w: 00624 cmd['w'] = w 00625 00626 if rast: 00627 if zoom: 00628 cmd['zoom'] = rast[0] 00629 else: 00630 cmd['rast'] = ','.join(rast) 00631 00632 if vect: 00633 cmd['vect'] = ','.join(vect) 00634 00635 ret, reg, msg = RunCommand('g.region', 00636 read = True, 00637 getErrorMsg = True, 00638 **cmd) 00639 00640 if ret != 0: 00641 if rast: 00642 message = _("Unable to zoom to raster map <%s>.") % rast[0] + \ 00643 "\n\n" + _("Details:") + " %s" % msg 00644 elif vect: 00645 message = _("Unable to zoom to vector map <%s>.") % vect[0] + \ 00646 "\n\n" + _("Details:") + " %s" % msg 00647 else: 00648 message = _("Unable to get current geographic extent. " 00649 "Force quiting wxGUI. Please manually run g.region to " 00650 "fix the problem.") 00651 GError(message) 00652 return self.region 00653 00654 for r in reg.splitlines(): 00655 key, val = r.split("=", 1) 00656 try: 00657 region[key] = float(val) 00658 except ValueError: 00659 region[key] = val 00660 00661 # back to original gisrc 00662 if self.gisrc: 00663 os.environ["GISRC"] = gisrc_orig 00664 00665 # restore region 00666 if tmpreg: 00667 os.environ["GRASS_REGION"] = tmpreg 00668 00669 Debug.msg (3, "Map.GetRegion(): %s" % region) 00670 00671 if update: 00672 self.region = region 00673 00674 return region 00675 00676 def GetCurrentRegion(self): 00677 """!Get current display region settings 00678 00679 @see GetRegion() 00680 """ 00681 return self.region 00682 00683 def SetRegion(self, windres = False): 00684 """!Render string for GRASS_REGION env. variable, so that the 00685 images will be rendered from desired zoom level. 00686 00687 @param windres uses resolution from WIND file rather than 00688 display (for modules that require set resolution like 00689 d.rast.num) 00690 00691 @return String usable for GRASS_REGION variable or None 00692 """ 00693 grass_region = "" 00694 00695 if windres: 00696 compRegion = self.GetRegion() 00697 region = copy.copy(self.region) 00698 for key in ('nsres', 'ewres', 'cells'): 00699 region[key] = compRegion[key] 00700 else: 00701 # adjust region settings to match monitor 00702 region = self.AdjustRegion() 00703 00704 # read values from wind file 00705 try: 00706 for key in self.wind.keys(): 00707 if key == 'north': 00708 grass_region += "north: %s; " % \ 00709 (region['n']) 00710 continue 00711 elif key == "south": 00712 grass_region += "south: %s; " % \ 00713 (region['s']) 00714 continue 00715 elif key == "east": 00716 grass_region += "east: %s; " % \ 00717 (region['e']) 00718 continue 00719 elif key == "west": 00720 grass_region += "west: %s; " % \ 00721 (region['w']) 00722 continue 00723 elif key == "e-w resol": 00724 grass_region += "e-w resol: %f; " % \ 00725 (region['ewres']) 00726 continue 00727 elif key == "n-s resol": 00728 grass_region += "n-s resol: %f; " % \ 00729 (region['nsres']) 00730 continue 00731 elif key == "cols": 00732 if windres: 00733 continue 00734 grass_region += 'cols: %d; ' % \ 00735 region['cols'] 00736 continue 00737 elif key == "rows": 00738 if windres: 00739 continue 00740 grass_region += 'rows: %d; ' % \ 00741 region['rows'] 00742 continue 00743 else: 00744 grass_region += key + ": " + self.wind[key] + "; " 00745 00746 Debug.msg (3, "Map.SetRegion(): %s" % grass_region) 00747 00748 return grass_region 00749 00750 except: 00751 return None 00752 00753 def GetListOfLayers(self, l_type = None, l_mapset = None, l_name = None, 00754 l_active = None, l_hidden = None): 00755 """!Returns list of layers of selected properties or list of 00756 all layers. 00757 00758 @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values) 00759 @param l_mapset all layers from given mapset (only for maplayers) 00760 @param l_name all layers with given name 00761 @param l_active only layers with 'active' attribute set to True or False 00762 @param l_hidden only layers with 'hidden' attribute set to True or False 00763 00764 @return list of selected layers 00765 """ 00766 selected = [] 00767 00768 if type(l_type) == types.StringType: 00769 one_type = True 00770 else: 00771 one_type = False 00772 00773 if one_type and l_type == 'overlay': 00774 llist = self.overlays 00775 else: 00776 llist = self.layers 00777 00778 # ["raster", "vector", "wms", ... ] 00779 for layer in llist: 00780 # specified type only 00781 if l_type != None: 00782 if one_type and layer.type != l_type: 00783 continue 00784 elif not one_type and layer.type not in l_type: 00785 continue 00786 00787 # mapset 00788 if (l_mapset != None and l_type != 'overlay') and \ 00789 layer.GetMapset() != l_mapset: 00790 continue 00791 00792 # name 00793 if l_name != None and layer.name != l_name: 00794 continue 00795 00796 # hidden and active layers 00797 if l_active != None and \ 00798 l_hidden != None: 00799 if layer.active == l_active and \ 00800 layer.hidden == l_hidden: 00801 selected.append(layer) 00802 00803 # active layers 00804 elif l_active != None: 00805 if layer.active == l_active: 00806 selected.append(layer) 00807 00808 # hidden layers 00809 elif l_hidden != None: 00810 if layer.hidden == l_hidden: 00811 selected.append(layer) 00812 00813 # all layers 00814 else: 00815 selected.append(layer) 00816 00817 Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected)) 00818 00819 return selected 00820 00821 def _renderLayers(self, force, mapWindow, maps, masks, opacities): 00822 # render map layers 00823 ilayer = 1 00824 for layer in self.layers + self.overlays: 00825 # skip dead or disabled map layers 00826 if layer == None or layer.active == False: 00827 continue 00828 00829 # render if there is no mapfile 00830 if force or \ 00831 layer.force_render or \ 00832 layer.mapfile == None or \ 00833 (not os.path.isfile(layer.mapfile) or not os.path.getsize(layer.mapfile)): 00834 if not layer.Render(): 00835 continue 00836 00837 if mapWindow: 00838 # update progress bar 00839 ### wx.SafeYield(mapWindow) 00840 event = wxUpdateProgressBar(value = ilayer) 00841 wx.PostEvent(mapWindow, event) 00842 00843 # add image to compositing list 00844 if layer.type != "overlay": 00845 maps.append(layer.mapfile) 00846 masks.append(layer.maskfile) 00847 opacities.append(str(layer.opacity)) 00848 00849 Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name)) 00850 ilayer += 1 00851 00852 def Render(self, force = False, mapWindow = None, windres = False): 00853 """!Creates final image composite 00854 00855 This function can conditionaly use high-level tools, which 00856 should be avaliable in wxPython library 00857 00858 @param force force rendering 00859 @param reference for MapFrame instance (for progress bar) 00860 @param windres use region resolution (True) otherwise display resolution 00861 00862 @return name of file with rendered image or None 00863 """ 00864 maps = [] 00865 masks = [] 00866 opacities = [] 00867 00868 wx.BeginBusyCursor() 00869 # use external gisrc if defined 00870 gisrc_orig = os.getenv("GISRC") 00871 if self.gisrc: 00872 os.environ["GISRC"] = self.gisrc 00873 00874 tmp_region = os.getenv("GRASS_REGION") 00875 os.environ["GRASS_REGION"] = self.SetRegion(windres) 00876 os.environ["GRASS_WIDTH"] = str(self.width) 00877 os.environ["GRASS_HEIGHT"] = str(self.height) 00878 if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo': 00879 os.environ["GRASS_AUTO_WRITE"] = "TRUE" 00880 if "GRASS_RENDER_IMMEDIATE" in os.environ: 00881 del os.environ["GRASS_RENDER_IMMEDIATE"] 00882 os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE" 00883 else: 00884 os.environ["GRASS_PNG_AUTO_WRITE"] = "TRUE" 00885 os.environ["GRASS_PNG_READ"] = "FALSE" 00886 os.environ["GRASS_COMPRESSION"] = "0" 00887 os.environ["GRASS_TRUECOLOR"] = "TRUE" 00888 os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE" 00889 00890 self._renderLayers(force, mapWindow, maps, masks, opacities) 00891 00892 # ugly hack for MSYS 00893 if sys.platform != 'win32': 00894 mapstr = ",".join(maps) 00895 maskstr = ",".join(masks) 00896 mapoutstr = self.mapfile 00897 else: 00898 mapstr = "" 00899 for item in maps: 00900 mapstr += item.replace('\\', '/') 00901 mapstr = mapstr.rstrip(',') 00902 maskstr = "" 00903 for item in masks: 00904 maskstr += item.replace('\\', '/') 00905 maskstr = maskstr.rstrip(',') 00906 mapoutstr = self.mapfile.replace('\\', '/') 00907 00908 # compose command 00909 bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor', 00910 subkey = 'color'))) 00911 00912 # render overlays 00913 if tmp_region: 00914 os.environ["GRASS_REGION"] = tmp_region 00915 else: 00916 del os.environ["GRASS_REGION"] 00917 00918 if maps: 00919 # run g.pngcomp to get composite image 00920 ret, msg = RunCommand('g.pnmcomp', 00921 getErrorMsg = True, 00922 input = '%s' % ",".join(maps), 00923 mask = '%s' % ",".join(masks), 00924 opacity = '%s' % ",".join(opacities), 00925 background = bgcolor, 00926 width = self.width, 00927 height = self.height, 00928 output = self.mapfile) 00929 00930 if ret != 0: 00931 print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg 00932 wx.EndBusyCursor() 00933 return None 00934 00935 Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile)) 00936 00937 # back to original gisrc 00938 if self.gisrc: 00939 os.environ["GISRC"] = gisrc_orig 00940 00941 wx.EndBusyCursor() 00942 if not maps: 00943 return None 00944 00945 return self.mapfile 00946 00947 def AddLayer(self, type, command, name = None, 00948 l_active = True, l_hidden = False, l_opacity = 1.0, l_render = False, 00949 pos = -1): 00950 """!Adds generic map layer to list of layers 00951 00952 @param type layer type ('raster', 'vector', etc.) 00953 @param command GRASS command given as list 00954 @param name layer name 00955 @param l_active layer render only if True 00956 @param l_hidden layer not displayed in layer tree if True 00957 @param l_opacity opacity level range from 0(transparent) - 1(not transparent) 00958 @param l_render render an image if True 00959 @param pos position in layer list (-1 for append) 00960 00961 @return new layer on success 00962 @return None on failure 00963 """ 00964 wx.BeginBusyCursor() 00965 # l_opacity must be <0;1> 00966 if l_opacity < 0: l_opacity = 0 00967 elif l_opacity > 1: l_opacity = 1 00968 layer = MapLayer(type = type, name = name, cmd = command, 00969 active = l_active, hidden = l_hidden, opacity = l_opacity) 00970 00971 # add maplayer to the list of layers 00972 if pos > -1: 00973 self.layers.insert(pos, layer) 00974 else: 00975 self.layers.append(layer) 00976 00977 Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name) 00978 if l_render: 00979 if not layer.Render(): 00980 raise GException(_("Unable to render map layer <%s>.") % name) 00981 00982 wx.EndBusyCursor() 00983 00984 return layer 00985 00986 def DeleteLayer(self, layer, overlay = False): 00987 """!Removes layer from list of layers 00988 00989 @param layer layer instance in layer tree 00990 @param overlay delete overlay (use self.DeleteOverlay() instead) 00991 00992 @return removed layer on success or None 00993 """ 00994 Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name) 00995 00996 if overlay: 00997 list = self.overlays 00998 else: 00999 list = self.layers 01000 01001 if layer in list: 01002 if layer.mapfile: 01003 base = os.path.split(layer.mapfile)[0] 01004 mapfile = os.path.split(layer.mapfile)[1] 01005 tempbase = mapfile.split('.')[0] 01006 if base == '' or tempbase == '': 01007 return None 01008 basefile = os.path.join(base, tempbase) + r'.*' 01009 for f in glob.glob(basefile): 01010 os.remove(f) 01011 list.remove(layer) 01012 01013 return layer 01014 01015 return None 01016 01017 def ReorderLayers(self, layerList): 01018 """!Reorder list to match layer tree 01019 01020 @param layerList list of layers 01021 """ 01022 self.layers = layerList 01023 01024 layerNameList = "" 01025 for layer in self.layers: 01026 if layer.name: 01027 layerNameList += layer.name + ',' 01028 Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \ 01029 (layerNameList)) 01030 01031 def ChangeLayer(self, layer, render = False, **kargs): 01032 """!Change map layer properties 01033 01034 @param layer map layer instance 01035 @param type layer type ('raster', 'vector', etc.) 01036 @param command GRASS command given as list 01037 @param name layer name 01038 @param active layer render only if True 01039 @param hidden layer not displayed in layer tree if True 01040 @param opacity opacity level range from 0(transparent) - 1(not transparent) 01041 @param render render an image if True 01042 """ 01043 Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name) 01044 01045 if 'type' in kargs: 01046 layer.SetType(kargs['type']) # check type 01047 01048 if 'command' in kargs: 01049 layer.SetCmd(kargs['command']) 01050 01051 if 'name' in kargs: 01052 layer.SetName(kargs['name']) 01053 01054 if 'active' in kargs: 01055 layer.SetActive(kargs['active']) 01056 01057 if 'hidden' in kargs: 01058 layer.SetHidden(kargs['hidden']) 01059 01060 if 'opacity' in kargs: 01061 layer.SetOpacity(kargs['opacity']) 01062 01063 if render and not layer.Render(): 01064 raise GException(_("Unable to render map layer <%s>.") % 01065 name) 01066 01067 return layer 01068 01069 def ChangeOpacity(self, layer, l_opacity): 01070 """!Changes opacity value of map layer 01071 01072 @param layer layer instance in layer tree 01073 @param l_opacity opacity level <0;1> 01074 """ 01075 # l_opacity must be <0;1> 01076 if l_opacity < 0: l_opacity = 0 01077 elif l_opacity > 1: l_opacity = 1 01078 01079 layer.opacity = l_opacity 01080 Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \ 01081 (layer.name, layer.opacity)) 01082 01083 def ChangeLayerActive(self, layer, active): 01084 """!Enable or disable map layer 01085 01086 @param layer layer instance in layer tree 01087 @param active to be rendered (True) 01088 """ 01089 layer.active = active 01090 01091 Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \ 01092 (layer.name, layer.active)) 01093 01094 def ChangeLayerName (self, layer, name): 01095 """!Change name of the layer 01096 01097 @param layer layer instance in layer tree 01098 @param name layer name to set up 01099 """ 01100 Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \ 01101 (layer.name, name)) 01102 layer.name = name 01103 01104 def RemoveLayer(self, name = None, id = None): 01105 """!Removes layer from layer list 01106 01107 Layer is defined by name@mapset or id. 01108 01109 @param name layer name (must be unique) 01110 @param id layer index in layer list 01111 01112 @return removed layer on success 01113 @return None on failure 01114 """ 01115 # delete by name 01116 if name: 01117 retlayer = None 01118 for layer in self.layers: 01119 if layer.name == name: 01120 retlayer = layer 01121 os.remove(layer.mapfile) 01122 os.remove(layer.maskfile) 01123 self.layers.remove(layer) 01124 return layer 01125 # del by id 01126 elif id != None: 01127 return self.layers.pop(id) 01128 01129 return None 01130 01131 def GetLayerIndex(self, layer, overlay = False): 01132 """!Get index of layer in layer list. 01133 01134 @param layer layer instace in layer tree 01135 @param overlay use list of overlays instead 01136 01137 @return layer index 01138 @return -1 if layer not found 01139 """ 01140 if overlay: 01141 list = self.overlay 01142 else: 01143 list = self.layers 01144 01145 if layer in list: 01146 return list.index(layer) 01147 01148 return -1 01149 01150 def AddOverlay(self, id, type, command, 01151 l_active = True, l_hidden = True, l_opacity = 1.0, l_render = False): 01152 """!Adds overlay (grid, barscale, legend, etc.) to list of 01153 overlays 01154 01155 @param id overlay id (PseudoDC) 01156 @param type overlay type (barscale, legend) 01157 @param command GRASS command to render overlay 01158 @param l_active overlay activated (True) or disabled (False) 01159 @param l_hidden overlay is not shown in layer tree (if True) 01160 @param l_render render an image (if True) 01161 01162 @return new layer on success 01163 @retutn None on failure 01164 """ 01165 Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render)) 01166 overlay = Overlay(id = id, type = type, cmd = command, 01167 active = l_active, hidden = l_hidden, opacity = l_opacity) 01168 01169 # add maplayer to the list of layers 01170 self.overlays.append(overlay) 01171 01172 if l_render and command != '' and not overlay.Render(): 01173 raise GException(_("Unable to render overlay <%s>.") % 01174 name) 01175 01176 return self.overlays[-1] 01177 01178 def ChangeOverlay(self, id, render = False, **kargs): 01179 """!Change overlay properities 01180 01181 Add new overlay if overlay with 'id' doesn't exist. 01182 01183 @param id overlay id (PseudoDC) 01184 @param type overlay type (barscale, legend) 01185 @param command GRASS command to render overlay 01186 @param l_active overlay activated (True) or disabled (False) 01187 @param l_hidden overlay is not shown in layer tree (if True) 01188 @param l_render render an image (if True) 01189 01190 @return new layer on success 01191 """ 01192 overlay = self.GetOverlay(id, list = False) 01193 if overlay is None: 01194 overlay = Overlay(id, type = None, cmd = None) 01195 01196 if 'type' in kargs: 01197 overlay.SetName(kargs['type']) # type -> overlay 01198 01199 if 'command' in kargs: 01200 overlay.SetCmd(kargs['command']) 01201 01202 if 'active' in kargs: 01203 overlay.SetActive(kargs['active']) 01204 01205 if 'hidden' in kargs: 01206 overlay.SetHidden(kargs['hidden']) 01207 01208 if 'opacity' in kargs: 01209 overlay.SetOpacity(kargs['opacity']) 01210 01211 if render and overlay.GetCmd() != [] and not overlay.Render(): 01212 raise GException(_("Unable to render overlay <%s>.") % 01213 name) 01214 01215 return overlay 01216 01217 def GetOverlay(self, id, list = False): 01218 """!Return overlay(s) with 'id' 01219 01220 @param id overlay id 01221 @param list return list of overlays of True 01222 otherwise suppose 'id' to be unique 01223 01224 @return list of overlays (list=True) 01225 @return overlay (list=False) 01226 @retur None (list=False) if no overlay or more overlays found 01227 """ 01228 ovl = [] 01229 for overlay in self.overlays: 01230 if overlay.id == id: 01231 ovl.append(overlay) 01232 01233 if not list: 01234 if len(ovl) != 1: 01235 return None 01236 else: 01237 return ovl[0] 01238 01239 return ovl 01240 01241 def DeleteOverlay(self, overlay): 01242 """!Delete overlay 01243 01244 @param overlay overlay layer 01245 01246 @return removed overlay on success or None 01247 """ 01248 return self.DeleteLayer(overlay, overlay = True) 01249 01250 def Clean(self): 01251 """!Clean layer stack - go trough all layers and remove them 01252 from layer list. 01253 01254 Removes also l_mapfile and l_maskfile 01255 01256 @return False on failure 01257 @return True on success 01258 """ 01259 try: 01260 dir = os.path.dirname(self.mapfile) 01261 base = os.path.basename(self.mapfile).split('.')[0] 01262 removepath = os.path.join(dir,base)+r'*' 01263 for f in glob.glob(removepath): 01264 os.remove(f) 01265 for layer in self.layers: 01266 if layer.mapfile: 01267 dir = os.path.dirname(layer.mapfile) 01268 base = os.path.basename(layer.mapfile).split('.')[0] 01269 removepath = os.path.join(dir,base)+r'*' 01270 for f in glob.glob(removepath): 01271 os.remove(f) 01272 self.layers.remove(layer) 01273 01274 for overlay in self.overlays: 01275 if overlay.mapfile: 01276 dir = os.path.dirname(overlay.mapfile) 01277 base = os.path.basename(overlay.mapfile).split('.')[0] 01278 removepath = os.path.join(dir,base)+r'*' 01279 for f in glob.glob(removepath): 01280 os.remove(f) 01281 self.overlays.remove(overlay) 01282 except: 01283 return False 01284 01285 return True 01286 01287 def ReverseListOfLayers(self): 01288 """!Reverse list of layers""" 01289 return self.layers.reverse() 01290 01291 def RenderOverlays(self, force): 01292 """!Render overlays only (for nviz)""" 01293 for layer in self.overlays: 01294 if force or layer.force_render: 01295 layer.Render() 01296 01297 if __name__ == "__main__": 01298 import gettext 01299 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True) 01300 01301 Map = Map() 01302 Map.GetRegion(update = True) 01303 01304 Map.AddLayer(type = "raster", 01305 name = "elevation", 01306 command = ["d.rast", "map=elevation@PERMANENT"], 01307 l_opacity = .7) 01308 01309 Map.AddLayer(type = "vector", 01310 name = "roadsmajor", 01311 command = ["d.vect", "map=roadsmajor@PERMANENT", "color=red", "width=3", "type=line"]) 01312 01313 image = Map.Render(force = True) 01314 01315 if image: 01316 grass.call(["display", image])