GRASS Programmer's Manual  6.4.2(2012)
core.py
Go to the documentation of this file.
00001 """!@package grass.script.core
00002 
00003 @brief GRASS Python scripting module (core functions)
00004 
00005 Core functions to be used in Python scripts.
00006 
00007 Usage:
00008 
00009 @code
00010 from grass.script import core as grass
00011 
00012 grass.parser()
00013 ...
00014 @endcode
00015 
00016 (C) 2008-2011 by the GRASS Development Team
00017 This program is free software under the GNU General Public
00018 License (>=v2). Read the file COPYING that comes with GRASS
00019 for details.
00020 
00021 @author Glynn Clements
00022 @author Martin Landa <landa.martin gmail.com>
00023 @author Michael Barton <michael.barton asu.edu>
00024 """
00025 
00026 import os
00027 import sys
00028 import types
00029 import re
00030 import atexit
00031 import subprocess
00032 import shutil
00033 import locale
00034 import codecs
00035 
00036 # i18N
00037 import gettext
00038 gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
00039 
00040 # subprocess wrapper that uses shell on Windows
00041 
00042 class Popen(subprocess.Popen):
00043     def __init__(self, args, bufsize = 0, executable = None,
00044                  stdin = None, stdout = None, stderr = None,
00045                  preexec_fn = None, close_fds = False, shell = None,
00046                  cwd = None, env = None, universal_newlines = False,
00047                  startupinfo = None, creationflags = 0):
00048 
00049         if shell == None:
00050             shell = (sys.platform == "win32")
00051 
00052         subprocess.Popen.__init__(self, args, bufsize, executable,
00053                                   stdin, stdout, stderr,
00054                                   preexec_fn, close_fds, shell,
00055                                   cwd, env, universal_newlines,
00056                                   startupinfo, creationflags)
00057         
00058 PIPE = subprocess.PIPE
00059 STDOUT = subprocess.STDOUT
00060 
00061 class ScriptError(Exception):
00062     def __init__(self, msg):
00063         self.value = msg
00064     
00065     def __str__(self):
00066         return repr(self.value)
00067         
00068 raise_on_error = False # raise exception instead of calling fatal()
00069 debug_level = 0        # DEBUG level
00070 
00071 def call(*args, **kwargs):
00072     return Popen(*args, **kwargs).wait()
00073 
00074 # GRASS-oriented interface to subprocess module
00075 
00076 _popen_args = ["bufsize", "executable", "stdin", "stdout", "stderr",
00077                "preexec_fn", "close_fds", "cwd", "env",
00078                "universal_newlines", "startupinfo", "creationflags"]
00079 
00080 def decode(string):
00081     enc = locale.getdefaultlocale()[1]
00082     if enc:
00083         return string.decode(enc)
00084     
00085     return string
00086 
00087 def _make_val(val):
00088     if isinstance(val, types.StringType) or \
00089             isinstance(val, types.UnicodeType):
00090         return val
00091     if isinstance(val, types.ListType):
00092         return ",".join(map(_make_val, val))
00093     if isinstance(val, types.TupleType):
00094         return _make_val(list(val))
00095     return str(val)
00096 
00097 def make_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **options):
00098     """!Return a list of strings suitable for use as the args parameter to
00099     Popen() or call(). Example:
00100 
00101     @code
00102     >>> grass.make_command("g.message", flags = 'w', message = 'this is a warning')
00103     ['g.message', '-w', 'message=this is a warning']
00104     @endcode
00105 
00106     @param prog GRASS module
00107     @param flags flags to be used (given as a string)
00108     @param overwrite True to enable overwriting the output (<tt>--o</tt>)
00109     @param quiet True to run quietly (<tt>--q</tt>)
00110     @param verbose True to run verbosely (<tt>--v</tt>)
00111     @param options module's parameters
00112 
00113     @return list of arguments
00114     """
00115     args = [prog]
00116     if overwrite:
00117         args.append("--o")
00118     if quiet:
00119         args.append("--q")
00120     if verbose:
00121         args.append("--v")
00122     if flags:
00123         if '-' in flags:
00124             raise ScriptError("'-' is not a valid flag")
00125         args.append("-%s" % flags)
00126     for opt, val in options.iteritems():
00127         if val != None:
00128             if opt[0] == '_':
00129                 opt = opt[1:]
00130             args.append("%s=%s" % (opt, _make_val(val)))
00131     return args
00132 
00133 def start_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **kwargs):
00134     """!Returns a Popen object with the command created by make_command.
00135     Accepts any of the arguments which Popen() accepts apart from "args"
00136     and "shell".
00137 
00138     \code
00139     >>> p = grass.start_command("g.gisenv", stdout = subprocess.PIPE)
00140     >>> print p
00141     <subprocess.Popen object at 0xb7c12f6c>
00142     >>> print p.communicate()[0]
00143     GISDBASE='/opt/grass-data';
00144     LOCATION_NAME='spearfish60';
00145     MAPSET='glynn';
00146     GRASS_DB_ENCODING='ascii';
00147     GRASS_GUI='text';
00148     MONITOR='x0';
00149     \endcode
00150     
00151     @param prog GRASS module
00152     @param flags flags to be used (given as a string)
00153     @param overwrite True to enable overwriting the output (<tt>--o</tt>)
00154     @param quiet True to run quietly (<tt>--q</tt>)
00155     @param verbose True to run verbosely (<tt>--v</tt>)
00156     @param kwargs module's parameters
00157     
00158     @return Popen object
00159     """
00160     options = {}
00161     popts = {}
00162     for opt, val in kwargs.iteritems():
00163         if opt in _popen_args:
00164             popts[opt] = val
00165         else:
00166             options[opt] = val
00167     args = make_command(prog, flags, overwrite, quiet, verbose, **options)
00168     if sys.platform == 'win32' and os.path.splitext(prog)[1] == '.py':
00169         os.chdir(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'scripts'))
00170         args.insert(0, sys.executable)
00171     
00172     global debug_level
00173     if debug_level > 0:
00174         sys.stderr.write("D1/%d: %s.start_command(): %s\n" % (debug_level, __name__, ' '.join(args)))
00175         sys.stderr.flush()
00176     
00177     return Popen(args, **popts)
00178 
00179 def run_command(*args, **kwargs):
00180     """!Passes all arguments to start_command(), then waits for the process to
00181     complete, returning its exit code. Similar to subprocess.call(), but
00182     with the make_command() interface.
00183 
00184     @param args list of unnamed arguments (see start_command() for details)
00185     @param kwargs list of named arguments (see start_command() for details)
00186 
00187     @return exit code (0 for success)
00188     """
00189     ps = start_command(*args, **kwargs)
00190     return ps.wait()
00191 
00192 def pipe_command(*args, **kwargs):
00193     """!Passes all arguments to start_command(), but also adds
00194     "stdout = PIPE". Returns the Popen object.
00195 
00196     \code
00197     >>> p = grass.pipe_command("g.gisenv")
00198     >>> print p
00199     <subprocess.Popen object at 0xb7c12f6c>
00200     >>> print p.communicate()[0]
00201     GISDBASE='/opt/grass-data';
00202     LOCATION_NAME='spearfish60';
00203     MAPSET='glynn';
00204     GRASS_DB_ENCODING='ascii';
00205     GRASS_GUI='text';
00206     MONITOR='x0';
00207     \endcode
00208     
00209     @param args list of unnamed arguments (see start_command() for details)
00210     @param kwargs list of named arguments (see start_command() for details)
00211 
00212     @return Popen object
00213     """
00214     kwargs['stdout'] = PIPE
00215     return start_command(*args, **kwargs)
00216 
00217 def feed_command(*args, **kwargs):
00218     """!Passes all arguments to start_command(), but also adds
00219     "stdin = PIPE". Returns the Popen object.
00220 
00221     @param args list of unnamed arguments (see start_command() for details)
00222     @param kwargs list of named arguments (see start_command() for details)
00223     
00224     @return Popen object
00225     """
00226     kwargs['stdin'] = PIPE
00227     return start_command(*args, **kwargs)
00228 
00229 def read_command(*args, **kwargs):
00230     """!Passes all arguments to pipe_command, then waits for the process to
00231     complete, returning its stdout (i.e. similar to shell `backticks`).
00232 
00233     @param args list of unnamed arguments (see start_command() for details)
00234     @param kwargs list of named arguments (see start_command() for details)
00235     
00236     @return stdout
00237     """
00238     ps = pipe_command(*args, **kwargs)
00239     return ps.communicate()[0]
00240 
00241 def parse_command(*args, **kwargs):
00242     """!Passes all arguments to read_command, then parses the output
00243     by parse_key_val().
00244 
00245     Parsing function can be optionally given by <em>parse</em> parameter
00246     including its arguments, e.g.
00247 
00248     @code
00249     parse_command(..., parse = (grass.parse_key_val, { 'sep' : ':' }))
00250     @endcode
00251 
00252     or you can simply define <em>delimiter</em>
00253 
00254     @code
00255     parse_command(..., delimiter = ':')
00256     @endcode
00257 
00258     @param args list of unnamed arguments (see start_command() for details)
00259     @param kwargs list of named arguments (see start_command() for details)
00260     
00261     @return parsed module output
00262     """
00263     parse = None
00264     parse_args = {}
00265     if 'parse' in kwargs:
00266         if type(kwargs['parse']) is types.TupleType:
00267             parse = kwargs['parse'][0]
00268             parse_args = kwargs['parse'][1]
00269         del kwargs['parse']
00270     
00271     if 'delimiter' in kwargs:
00272         parse_args = { 'sep' : kwargs['delimiter'] }
00273         del kwargs['delimiter']
00274     
00275     if not parse:
00276         parse = parse_key_val # use default fn
00277         
00278     res = read_command(*args, **kwargs)
00279 
00280     return parse(res, **parse_args)
00281 
00282 def write_command(*args, **kwargs):
00283     """!Passes all arguments to feed_command, with the string specified
00284     by the 'stdin' argument fed to the process' stdin.
00285 
00286     @param args list of unnamed arguments (see start_command() for details)
00287     @param kwargs list of named arguments (see start_command() for details)
00288 
00289     @return return code
00290     """
00291     stdin = kwargs['stdin']
00292     p = feed_command(*args, **kwargs)
00293     p.stdin.write(stdin)
00294     p.stdin.close()
00295     return p.wait()
00296 
00297 def exec_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, env = None, **kwargs):
00298     """!Interface to os.execvpe(), but with the make_command() interface.
00299 
00300     @param prog GRASS module
00301     @param flags flags to be used (given as a string)
00302     @param overwrite True to enable overwriting the output (<tt>--o</tt>)
00303     @param quiet True to run quietly (<tt>--q</tt>)
00304     @param verbose True to run verbosely (<tt>--v</tt>)
00305     @param env directory with enviromental variables
00306     @param kwargs module's parameters
00307 
00308     """
00309     args = make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
00310     if env == None:
00311         env = os.environ
00312     os.execvpe(prog, args, env)
00313 
00314 # interface to g.message
00315 
00316 def message(msg, flag = None):
00317     """!Display a message using `g.message`
00318 
00319     @param msg message to be displayed
00320     @param flag flags (given as string)
00321     """
00322     run_command("g.message", flags = flag, message = msg)
00323 
00324 def debug(msg, debug = 1):
00325     """!Display a debugging message using `g.message -d`
00326 
00327     @param msg debugging message to be displayed
00328     @param debug debug level (0-5)
00329     """
00330     run_command("g.message", flags = 'd', message = msg, debug = debug)
00331     
00332 def verbose(msg):
00333     """!Display a verbose message using `g.message -v`
00334     
00335     @param msg verbose message to be displayed
00336     """
00337     message(msg, flag = 'v')
00338 
00339 def info(msg):
00340     """!Display an informational message using `g.message -i`
00341 
00342     @param msg informational message to be displayed
00343     """
00344     message(msg, flag = 'i')
00345 
00346 def percent(i, n, s):
00347     """!Display a progress info message using `g.message -p`
00348     
00349     @code
00350     message(_("Percent complete..."))
00351     n = 100
00352     for i in range(n):
00353         percent(i, n, 1)
00354     percent(1, 1, 1)
00355     @endcode
00356     
00357     @param i current item
00358     @param n total number of items
00359     @param s increment size
00360     """
00361     message("%d %d %d" % (i, n, s), flag = 'p')
00362 
00363 def warning(msg):
00364     """!Display a warning message using `g.message -w`
00365 
00366     @param msg warning message to be displayed
00367     """
00368     message(msg, flag = 'w')
00369 
00370 def error(msg):
00371     """!Display an error message using `g.message -e`
00372 
00373     Raise exception when on_error is 'raise'.
00374     
00375     @param msg error message to be displayed
00376     """
00377     global raise_on_error
00378     if raise_on_error:
00379         raise ScriptError(msg)
00380     else:
00381         message(msg, flag = 'e')
00382 
00383 def fatal(msg):
00384     """!Display an error message using `g.message -e`, then abort
00385     
00386     @param msg error message to be displayed
00387     """
00388     error(msg)
00389     sys.exit(1)
00390     
00391 def set_raise_on_error(raise_exp = True):
00392     """!Define behaviour on error (error() called)
00393 
00394     @param raise_exp True to raise ScriptError instead of calling
00395     error()
00396     
00397     @return current status
00398     """
00399     global raise_on_error
00400     tmp_raise = raise_on_error
00401     raise_on_error = raise_exp
00402 
00403 # interface to g.parser
00404 
00405 def _parse_opts(lines):
00406     options = {}
00407     flags = {}
00408     for line in lines:
00409         line = line.rstrip('\r\n')
00410         if not line:
00411             break
00412         try:
00413             [var, val] = line.split('=', 1)
00414         except:
00415             raise SyntaxError("invalid output from g.parser: %s" % line)
00416 
00417         if var.startswith('flag_'):
00418             flags[var[5:]] = bool(int(val))
00419         elif var.startswith('opt_'):
00420             options[var[4:]] = val
00421         elif var in ['GRASS_OVERWRITE', 'GRASS_VERBOSE']:
00422             os.environ[var] = val
00423         else:
00424             raise SyntaxError("invalid output from g.parser: %s" % line)
00425 
00426     return (options, flags)
00427 
00428 def parser():
00429     """!Interface to g.parser, intended to be run from the top-level, e.g.:
00430 
00431     @code
00432         if __name__ == "__main__":
00433             options, flags = grass.parser()
00434             main()
00435     @endcode
00436 
00437     Thereafter, the global variables "options" and "flags" will be
00438     dictionaries containing option/flag values, keyed by lower-case
00439     option/flag names. The values in "options" are strings, those in
00440     "flags" are Python booleans.
00441     """
00442     if not os.getenv("GISBASE"):
00443         print >> sys.stderr, "You must be in GRASS GIS to run this program."
00444         sys.exit(1)
00445     
00446     cmdline = [basename(sys.argv[0])]
00447     cmdline += ['"' + arg + '"' for arg in sys.argv[1:]]
00448     os.environ['CMDLINE'] = ' '.join(cmdline)
00449     
00450     argv = sys.argv[:]
00451     name = argv[0]
00452     if not os.path.isabs(name):
00453         if os.sep in name or (os.altsep and os.altsep in name):
00454             argv[0] = os.path.abspath(name)
00455         else:
00456             argv[0] = os.path.join(sys.path[0], name)
00457     
00458     p = Popen(['g.parser', '-s'] + argv, stdout = PIPE)
00459     s = p.communicate()[0]
00460     lines = s.splitlines()
00461     
00462     if not lines or lines[0].rstrip('\r\n') != "@ARGS_PARSED@":
00463         sys.stdout.write(s)
00464         sys.exit(p.returncode)
00465 
00466     return _parse_opts(lines[1:])
00467 
00468 # interface to g.tempfile
00469 
00470 def tempfile():
00471     """!Returns the name of a temporary file, created with g.tempfile."""
00472     return read_command("g.tempfile", pid = os.getpid()).strip()
00473 
00474 def tempdir():
00475     """!Returns the name of a temporary dir, created with g.tempfile."""
00476     tmp = read_command("g.tempfile", pid = os.getpid()).strip()
00477     try_remove(tmp)
00478     os.mkdir(tmp)
00479     
00480     return tmp
00481 
00482 # key-value parsers
00483 
00484 def parse_key_val(s, sep = '=', dflt = None, val_type = None, vsep = None):
00485     """!Parse a string into a dictionary, where entries are separated
00486     by newlines and the key and value are separated by `sep' (default: `=')
00487 
00488     @param s string to be parsed
00489     @param sep key/value separator
00490     @param dflt default value to be used
00491     @param val_type value type (None for no cast)
00492     @param vsep vertical separator (default os.linesep)
00493 
00494     @return parsed input (dictionary of keys/values)
00495     """
00496     result = {}
00497 
00498     if not s:
00499         return result
00500     
00501     if vsep:
00502         lines = s.split(vsep)
00503         try:
00504             lines.remove('\n')
00505         except ValueError:
00506             pass
00507     else:
00508         lines = s.splitlines()
00509     
00510     for line in lines:
00511         kv = line.split(sep, 1)
00512         k = kv[0].strip()
00513         if len(kv) > 1:
00514             v = kv[1]
00515         else:
00516             v = dflt
00517         if val_type:
00518             result[k] = val_type(v)
00519         else:
00520             result[k] = v
00521     return result
00522 
00523 # interface to g.gisenv
00524 
00525 def gisenv():
00526     """!Returns the output from running g.gisenv (with no arguments), as a
00527     dictionary. Example:
00528 
00529     \code
00530     >>> env = grass.gisenv()
00531     >>> print env['GISDBASE']
00532     /opt/grass-data
00533     \endcode
00534 
00535     @return list of GRASS variables
00536     """
00537     s = read_command("g.gisenv", flags='n')
00538     return parse_key_val(s)
00539 
00540 # interface to g.region
00541 
00542 def region():
00543     """!Returns the output from running "g.region -g", as a
00544     dictionary. Example:
00545 
00546     \code
00547     >>> region = grass.region()
00548     >>> [region[key] for key in "nsew"]
00549     [228500.0, 215000.0, 645000.0, 630000.0]
00550     >>> (region['nsres'], region['ewres'])
00551     (10.0, 10.0)
00552     \endcode
00553 
00554     @return dictionary of region values
00555     """
00556     s = read_command("g.region", flags='g')
00557     reg = parse_key_val(s, val_type = float)
00558     for k in ['rows', 'cols']:
00559         reg[k] = int(reg[k])
00560     return reg
00561 
00562 def use_temp_region():
00563     """!Copies the current region to a temporary region with "g.region save=",
00564     then sets WIND_OVERRIDE to refer to that region. Installs an atexit
00565     handler to delete the temporary region upon termination.
00566     """
00567     name = "tmp.%s.%d" % (os.path.basename(sys.argv[0]), os.getpid())
00568     run_command("g.region", save = name, overwrite = True)
00569     os.environ['WIND_OVERRIDE'] = name
00570     atexit.register(del_temp_region)
00571 
00572 def del_temp_region():
00573     """!Unsets WIND_OVERRIDE and removes any region named by it."""
00574     try:
00575         name = os.environ.pop('WIND_OVERRIDE')
00576         run_command("g.remove", quiet = True, region = name)
00577     except:
00578         pass
00579 
00580 # interface to g.findfile
00581 
00582 def find_file(name, element = 'cell', mapset = None):
00583     """!Returns the output from running g.findfile as a
00584     dictionary. Example:
00585 
00586     \code
00587     >>> result = grass.find_file('fields', element = 'vector')
00588     >>> print result['fullname']
00589     fields@PERMANENT
00590     >>> print result['file']
00591     /opt/grass-data/spearfish60/PERMANENT/vector/fields
00592     \endcode
00593     
00594     @param name file name
00595     @param element element type (default 'cell')
00596     @param mapset mapset name (default all mapsets in search path)
00597 
00598     @return parsed output of g.findfile
00599     """
00600     s = read_command("g.findfile", flags='n', element = element, file = name, mapset = mapset)
00601     return parse_key_val(s)
00602 
00603 # interface to g.list
00604 
00605 def list_grouped(type, check_search_path = True):
00606     """!List elements grouped by mapsets.
00607 
00608     Returns the output from running g.list, as a dictionary where the
00609     keys are mapset names and the values are lists of maps in that
00610     mapset. Example:
00611 
00612     @code
00613     >>> grass.list_grouped('rast')['PERMANENT']
00614     ['aspect', 'erosion1', 'quads', 'soils', 'strm.dist', ...
00615     @endcode
00616     
00617     @param type element type (rast, vect, rast3d, region, ...)
00618     @param check_search_path True to add mapsets for the search path with no found elements
00619 
00620     @return directory of mapsets/elements
00621     """
00622     dashes_re = re.compile("^----+$")
00623     mapset_re = re.compile("<(.*)>")
00624     result = {}
00625     if check_search_path:
00626         for mapset in mapsets(search_path = True):
00627             result[mapset] = []
00628     
00629     mapset = None
00630     for line in read_command("g.list", type = type).splitlines():
00631         if line == "":
00632             continue
00633         if dashes_re.match(line):
00634             continue
00635         m = mapset_re.search(line)
00636         if m:
00637             mapset = m.group(1)
00638             if mapset not in result.keys():
00639                 result[mapset] = []
00640             continue
00641         if mapset:
00642             result[mapset].extend(line.split())
00643     
00644     return result
00645 
00646 def _concat(xs):
00647     result = []
00648     for x in xs:
00649         result.extend(x)
00650     return result
00651 
00652 def list_pairs(type):
00653     """!List of elements as tuples.
00654 
00655     Returns the output from running g.list, as a list of (map, mapset)
00656     pairs. Example:
00657 
00658     @code
00659     >>> grass.list_pairs('rast')
00660     [('aspect', 'PERMANENT'), ('erosion1', 'PERMANENT'), ('quads', 'PERMANENT'), ...
00661     @endcode
00662     
00663     @param type element type (rast, vect, rast3d, region, ...)
00664     
00665     @return list of tuples (map, mapset)
00666     """
00667     return _concat([[(map, mapset) for map in maps]
00668                     for mapset, maps in list_grouped(type).iteritems()])
00669 
00670 def list_strings(type):
00671     """!List of elements as strings.
00672 
00673     Returns the output from running g.list, as a list of qualified
00674     names. Example:
00675 
00676     @code
00677     >>> grass.list_strings('rast')
00678     ['aspect@PERMANENT', 'erosion1@PERMANENT', 'quads@PERMANENT', 'soils@PERMANENT', ...
00679     @endcode
00680 
00681     @param type element type
00682     
00683     @return list of strings ('map@@mapset')
00684     """
00685     return ["%s@%s" % pair for pair in list_pairs(type)]
00686 
00687 # interface to g.mlist
00688 
00689 def mlist_grouped(type, pattern = None, check_search_path = True):
00690     """!List of elements grouped by mapsets.
00691 
00692     Returns the output from running g.mlist, as a dictionary where the
00693     keys are mapset names and the values are lists of maps in that
00694     mapset. Example:
00695 
00696     @code
00697     >>> grass.mlist_grouped('rast', pattern='r*')['PERMANENT']
00698     ['railroads', 'roads', 'rstrct.areas', 'rushmore']
00699     @endcode
00700     
00701     @param type element type (rast, vect, rast3d, region, ...)
00702     @param pattern pattern string
00703     @param check_search_path True to add mapsets for the search path with no found elements
00704 
00705     @return directory of mapsets/elements
00706     """
00707     result = {}
00708     if check_search_path:
00709         for mapset in mapsets(search_path = True):
00710             result[mapset] = []
00711     
00712     mapset = None
00713     for line in read_command("g.mlist", flags = "m",
00714                              type = type, pattern = pattern).splitlines():
00715         try:
00716             name, mapset = line.split('@')
00717         except ValueError:
00718             warning(_("Invalid element '%s'") % line)
00719             continue
00720         
00721         if mapset in result:
00722             result[mapset].append(name) 
00723         else:            
00724             result[mapset] = [name, ] 
00725     
00726     return result
00727 
00728 # color parsing
00729 
00730 named_colors = {
00731     "white":   (1.00, 1.00, 1.00),
00732     "black":   (0.00, 0.00, 0.00),
00733     "red":     (1.00, 0.00, 0.00),
00734     "green":   (0.00, 1.00, 0.00),
00735     "blue":    (0.00, 0.00, 1.00),
00736     "yellow":  (1.00, 1.00, 0.00),
00737     "magenta": (1.00, 0.00, 1.00),
00738     "cyan":    (0.00, 1.00, 1.00),
00739     "aqua":    (0.00, 0.75, 0.75),
00740     "grey":    (0.75, 0.75, 0.75),
00741     "gray":    (0.75, 0.75, 0.75),
00742     "orange":  (1.00, 0.50, 0.00),
00743     "brown":   (0.75, 0.50, 0.25),
00744     "purple":  (0.50, 0.00, 1.00),
00745     "violet":  (0.50, 0.00, 1.00),
00746     "indigo":  (0.00, 0.50, 1.00)}
00747 
00748 def parse_color(val, dflt = None):
00749     """!Parses the string "val" as a GRASS colour, which can be either one of
00750     the named colours or an R:G:B tuple e.g. 255:255:255. Returns an
00751     (r,g,b) triple whose components are floating point values between 0
00752     and 1. Example:
00753 
00754     \code
00755     >>> grass.parse_color("red")
00756     (1.0, 0.0, 0.0)
00757     >>> grass.parse_color("255:0:0")
00758     (1.0, 0.0, 0.0)
00759     \endcode
00760 
00761     @param val color value
00762     @param dflt default color value
00763 
00764     @return tuple RGB
00765     """
00766     if val in named_colors:
00767         return named_colors[val]
00768 
00769     vals = val.split(':')
00770     if len(vals) == 3:
00771         return tuple(float(v) / 255 for v in vals)
00772 
00773     return dflt
00774 
00775 # check GRASS_OVERWRITE
00776 
00777 def overwrite():
00778     """!Return True if existing files may be overwritten"""
00779     owstr = 'GRASS_OVERWRITE'
00780     return owstr in os.environ and os.environ[owstr] != '0'
00781 
00782 # check GRASS_VERBOSE
00783 
00784 def verbosity():
00785     """!Return the verbosity level selected by GRASS_VERBOSE"""
00786     vbstr = os.getenv('GRASS_VERBOSE')
00787     if vbstr:
00788         return int(vbstr)
00789     else:
00790         return 2
00791 
00792 ## various utilities, not specific to GRASS
00793 
00794 # basename inc. extension stripping
00795 
00796 def basename(path, ext = None):
00797     """!Remove leading directory components and an optional extension
00798     from the specified path
00799 
00800     @param path path
00801     @param ext extension
00802     """
00803     name = os.path.basename(path)
00804     if not ext:
00805         return name
00806     fs = name.rsplit('.', 1)
00807     if len(fs) > 1 and fs[1].lower() == ext:
00808         name = fs[0]
00809     return name
00810 
00811 # find a program (replacement for "which")
00812 
00813 def find_program(pgm, args = []):
00814     """!Attempt to run a program, with optional arguments. 
00815 
00816     @param pgm program name
00817     @param args list of arguments
00818 
00819     @return False if the attempt failed due to a missing executable
00820     @return True otherwise
00821     """
00822     nuldev = file(os.devnull, 'w+')
00823     try:
00824         ret = call([pgm] + args, stdin = nuldev, stdout = nuldev, stderr = nuldev)
00825         if ret == 0:
00826             found = True
00827         else:
00828             found = False
00829     except:
00830         found = False
00831     nuldev.close()
00832     
00833     return found
00834 
00835 # try to remove a file, without complaints
00836 
00837 def try_remove(path):
00838     """!Attempt to remove a file; no exception is generated if the
00839     attempt fails.
00840 
00841     @param path path to file to remove
00842     """
00843     try:
00844         os.remove(path)
00845     except:
00846         pass
00847 
00848 # try to remove a directory, without complaints
00849 
00850 def try_rmdir(path):
00851     """!Attempt to remove a directory; no exception is generated if the
00852     attempt fails.
00853 
00854     @param path path to directory to remove
00855     """
00856     try:
00857         os.rmdir(path)
00858     except:
00859         shutil.rmtree(path, ignore_errors = True)
00860 
00861 def float_or_dms(s):
00862     """!Convert DMS to float.
00863 
00864     @param s DMS value
00865 
00866     @return float value
00867     """
00868     return sum(float(x) / 60 ** n for (n, x) in enumerate(s.split(':')))
00869 
00870 # interface to g.mapsets
00871 
00872 def mapsets(search_path = False):
00873     """!List available mapsets
00874 
00875     @param searchPatch True to list mapsets only in search path
00876     
00877     @return list of mapsets
00878     """
00879     if search_path:
00880         flags = 'p'
00881     else:
00882         flags = 'l'
00883     mapsets = read_command('g.mapsets',
00884                            flags = flags,
00885                            fs = 'newline',
00886                            quiet = True)
00887     if not mapsets:
00888         fatal(_("Unable to list mapsets"))
00889     
00890     return mapsets.splitlines()
00891 
00892 # interface to `g.proj -c`
00893 
00894 def create_location(dbase, location,
00895                     epsg = None, proj4 = None, filename = None, wkt = None,
00896                     datum = None, desc = None):
00897     """!Create new location
00898 
00899     Raise ScriptError on error.
00900     
00901     @param dbase path to GRASS database
00902     @param location location name to create
00903     @param epgs if given create new location based on EPSG code
00904     @param proj4 if given create new location based on Proj4 definition
00905     @param filename if given create new location based on georeferenced file
00906     @param wkt if given create new location based on WKT definition (path to PRJ file)
00907     @param datum datum transformation parameters (used for epsg and proj4)
00908     @param desc description of the location (creates MYNAME file)
00909     """
00910     gisdbase = None
00911     if epsg or proj4 or filename or wkt:
00912         gisdbase = gisenv()['GISDBASE']
00913         run_command('g.gisenv',
00914                     set = 'GISDBASE=%s' % dbase)
00915     if not os.path.exists(dbase):
00916             os.mkdir(dbase)
00917     
00918     kwargs = dict()
00919     if datum:
00920         kwargs['datum'] = datum
00921     
00922     if epsg:
00923         ps = pipe_command('g.proj',
00924                           quiet = True,
00925                           flags = 'c',
00926                           epsg = epsg,
00927                           location = location,
00928                           stderr = PIPE,
00929                           **kwargs)
00930     elif proj4:
00931         ps = pipe_command('g.proj',
00932                           quiet = True,
00933                           flags = 'c',
00934                           proj4 = proj4,
00935                           location = location,
00936                           stderr = PIPE,
00937                           **kwargs)
00938     elif filename:
00939         ps = pipe_command('g.proj',
00940                           quiet = True,
00941                           flags = 'c',
00942                           georef = filename,
00943                           location = location,
00944                           stderr = PIPE)
00945     elif wkt:
00946         ps = pipe_command('g.proj',
00947                           quiet = True,
00948                           flags = 'c',
00949                           wkt = wkt,
00950                           location = location,
00951                           stderr = PIPE)
00952     else:
00953         _create_location_xy(dbase, location)
00954     
00955     if epsg or proj4 or filename or wkt:
00956         error = ps.communicate()[1]
00957         run_command('g.gisenv',
00958                     set = 'GISDBASE=%s' % gisdbase)
00959         
00960         if ps.returncode != 0 and error:
00961             raise ScriptError(repr(error))
00962 
00963     try:
00964         fd = codecs.open(os.path.join(dbase, location,
00965                                       'PERMANENT', 'MYNAME'),
00966                          encoding = 'utf-8', mode = 'w')
00967         if desc:
00968             fd.write(desc + os.linesep)
00969         else:
00970             fd.write(os.linesep)
00971         fd.close()
00972     except OSError, e:
00973         raise ScriptError(repr(e))
00974         
00975 def _create_location_xy(database, location):
00976     """!Create unprojected location
00977 
00978     Raise ScriptError on error.
00979     
00980     @param database GRASS database where to create new location
00981     @param location location name
00982     """
00983     cur_dir = os.getcwd()
00984     try:
00985         os.chdir(database)
00986         os.mkdir(location)
00987         os.mkdir(os.path.join(location, 'PERMANENT'))
00988         
00989         # create DEFAULT_WIND and WIND files
00990         regioninfo = ['proj:       0',
00991                       'zone:       0',
00992                       'north:      1',
00993                       'south:      0',
00994                       'east:       1',
00995                       'west:       0',
00996                       'cols:       1',
00997                       'rows:       1',
00998                       'e-w resol:  1',
00999                       'n-s resol:  1',
01000                       'top:        1',
01001                       'bottom:     0',
01002                       'cols3:      1',
01003                       'rows3:      1',
01004                       'depths:     1',
01005                       'e-w resol3: 1',
01006                       'n-s resol3: 1',
01007                       't-b resol:  1']
01008         
01009         defwind = open(os.path.join(location,
01010                                     "PERMANENT", "DEFAULT_WIND"), 'w')
01011         for param in regioninfo:
01012             defwind.write(param + '%s' % os.linesep)
01013         defwind.close()
01014             
01015         shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"),
01016                     os.path.join(location, "PERMANENT", "WIND"))
01017         
01018         os.chdir(cur_dir)
01019     except OSError, e:
01020         raise ScriptError(repr(e))
01021 
01022 # interface to g.version
01023 
01024 def version():
01025     """!Get GRASS version as dictionary
01026 
01027     @code
01028     print version()
01029 
01030     {'date': '2011', 'libgis_date': '2011-04-13 13:19:03 +0200 (Wed, 13 Apr 2011)',
01031     'version': '6.4.2svn', 'libgis_revision': '45934', 'revision': '47445'}
01032     @endcode
01033     """
01034     data = parse_command('g.version',
01035                          flags = 'rg')
01036     for k, v in data.iteritems():
01037         data[k.strip()] = v.replace('"', '').strip()
01038         
01039     return data
01040 
01041 # get debug_level
01042 if find_program('g.gisenv', ['--help']):
01043     debug_level = int(gisenv().get('DEBUG', 0))
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines