GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
core.py
Go to the documentation of this file.
1 """!@package grass.script.core
2 
3 @brief GRASS Python scripting module (core functions)
4 
5 Core functions to be used in Python scripts.
6 
7 Usage:
8 
9 @code
10 from grass.script import core as grass
11 
12 grass.parser()
13 ...
14 @endcode
15 
16 (C) 2008-2011 by the GRASS Development Team
17 This program is free software under the GNU General Public
18 License (>=v2). Read the file COPYING that comes with GRASS
19 for details.
20 
21 @author Glynn Clements
22 @author Martin Landa <landa.martin gmail.com>
23 @author Michael Barton <michael.barton asu.edu>
24 """
25 
26 import os
27 import sys
28 import types
29 import re
30 import atexit
31 import subprocess
32 import shutil
33 import locale
34 import codecs
35 
36 # i18N
37 import gettext
38 gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
39 
40 # subprocess wrapper that uses shell on Windows
41 
42 class Popen(subprocess.Popen):
43  def __init__(self, args, bufsize = 0, executable = None,
44  stdin = None, stdout = None, stderr = None,
45  preexec_fn = None, close_fds = False, shell = None,
46  cwd = None, env = None, universal_newlines = False,
47  startupinfo = None, creationflags = 0):
48 
49  if shell == None:
50  shell = (sys.platform == "win32")
51 
52  subprocess.Popen.__init__(self, args, bufsize, executable,
53  stdin, stdout, stderr,
54  preexec_fn, close_fds, shell,
55  cwd, env, universal_newlines,
56  startupinfo, creationflags)
57 
58 PIPE = subprocess.PIPE
59 STDOUT = subprocess.STDOUT
60 
61 class ScriptError(Exception):
62  def __init__(self, msg):
63  self.value = msg
64 
65  def __str__(self):
66  return self.value
67 
68 raise_on_error = False # raise exception instead of calling fatal()
69 debug_level = 0 # DEBUG level
70 
71 def call(*args, **kwargs):
72  return Popen(*args, **kwargs).wait()
73 
74 # GRASS-oriented interface to subprocess module
75 
76 _popen_args = ["bufsize", "executable", "stdin", "stdout", "stderr",
77  "preexec_fn", "close_fds", "cwd", "env",
78  "universal_newlines", "startupinfo", "creationflags"]
79 
80 def decode(string):
81  enc = locale.getdefaultlocale()[1]
82  if enc:
83  return string.decode(enc)
84 
85  return string
86 
87 def _make_val(val):
88  if isinstance(val, types.StringType) or \
89  isinstance(val, types.UnicodeType):
90  return val
91  if isinstance(val, types.ListType):
92  return ",".join(map(_make_val, val))
93  if isinstance(val, types.TupleType):
94  return _make_val(list(val))
95  return str(val)
96 
97 def make_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **options):
98  """!Return a list of strings suitable for use as the args parameter to
99  Popen() or call(). Example:
100 
101  @code
102  >>> grass.make_command("g.message", flags = 'w', message = 'this is a warning')
103  ['g.message', '-w', 'message=this is a warning']
104  @endcode
105 
106  @param prog GRASS module
107  @param flags flags to be used (given as a string)
108  @param overwrite True to enable overwriting the output (<tt>--o</tt>)
109  @param quiet True to run quietly (<tt>--q</tt>)
110  @param verbose True to run verbosely (<tt>--v</tt>)
111  @param options module's parameters
112 
113  @return list of arguments
114  """
115  args = [prog]
116  if overwrite:
117  args.append("--o")
118  if quiet:
119  args.append("--q")
120  if verbose:
121  args.append("--v")
122  if flags:
123  if '-' in flags:
124  raise ScriptError("'-' is not a valid flag")
125  args.append("-%s" % flags)
126  for opt, val in options.iteritems():
127  if val != None:
128  if opt[0] == '_':
129  opt = opt[1:]
130  args.append("%s=%s" % (opt, _make_val(val)))
131  return args
132 
133 def start_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **kwargs):
134  """!Returns a Popen object with the command created by make_command.
135  Accepts any of the arguments which Popen() accepts apart from "args"
136  and "shell".
137 
138  \code
139  >>> p = grass.start_command("g.gisenv", stdout = subprocess.PIPE)
140  >>> print p
141  <subprocess.Popen object at 0xb7c12f6c>
142  >>> print p.communicate()[0]
143  GISDBASE='/opt/grass-data';
144  LOCATION_NAME='spearfish60';
145  MAPSET='glynn';
146  GRASS_DB_ENCODING='ascii';
147  GRASS_GUI='text';
148  MONITOR='x0';
149  \endcode
150 
151  @param prog GRASS module
152  @param flags flags to be used (given as a string)
153  @param overwrite True to enable overwriting the output (<tt>--o</tt>)
154  @param quiet True to run quietly (<tt>--q</tt>)
155  @param verbose True to run verbosely (<tt>--v</tt>)
156  @param kwargs module's parameters
157 
158  @return Popen object
159  """
160  options = {}
161  popts = {}
162  for opt, val in kwargs.iteritems():
163  if opt in _popen_args:
164  popts[opt] = val
165  else:
166  options[opt] = val
167  args = make_command(prog, flags, overwrite, quiet, verbose, **options)
168  if sys.platform == 'win32' and os.path.splitext(prog)[1] == '.py':
169  os.chdir(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'scripts'))
170  args.insert(0, sys.executable)
171 
172  global debug_level
173  if debug_level > 0:
174  sys.stderr.write("D1/%d: %s.start_command(): %s\n" % (debug_level, __name__, ' '.join(args)))
175  sys.stderr.flush()
176 
177  return Popen(args, **popts)
178 
179 def run_command(*args, **kwargs):
180  """!Passes all arguments to start_command(), then waits for the process to
181  complete, returning its exit code. Similar to subprocess.call(), but
182  with the make_command() interface.
183 
184  @param args list of unnamed arguments (see start_command() for details)
185  @param kwargs list of named arguments (see start_command() for details)
186 
187  @return exit code (0 for success)
188  """
189  ps = start_command(*args, **kwargs)
190  return ps.wait()
191 
192 def pipe_command(*args, **kwargs):
193  """!Passes all arguments to start_command(), but also adds
194  "stdout = PIPE". Returns the Popen object.
195 
196  \code
197  >>> p = grass.pipe_command("g.gisenv")
198  >>> print p
199  <subprocess.Popen object at 0xb7c12f6c>
200  >>> print p.communicate()[0]
201  GISDBASE='/opt/grass-data';
202  LOCATION_NAME='spearfish60';
203  MAPSET='glynn';
204  GRASS_DB_ENCODING='ascii';
205  GRASS_GUI='text';
206  MONITOR='x0';
207  \endcode
208 
209  @param args list of unnamed arguments (see start_command() for details)
210  @param kwargs list of named arguments (see start_command() for details)
211 
212  @return Popen object
213  """
214  kwargs['stdout'] = PIPE
215  return start_command(*args, **kwargs)
216 
217 def feed_command(*args, **kwargs):
218  """!Passes all arguments to start_command(), but also adds
219  "stdin = PIPE". Returns the Popen object.
220 
221  @param args list of unnamed arguments (see start_command() for details)
222  @param kwargs list of named arguments (see start_command() for details)
223 
224  @return Popen object
225  """
226  kwargs['stdin'] = PIPE
227  return start_command(*args, **kwargs)
228 
229 def read_command(*args, **kwargs):
230  """!Passes all arguments to pipe_command, then waits for the process to
231  complete, returning its stdout (i.e. similar to shell `backticks`).
232 
233  @param args list of unnamed arguments (see start_command() for details)
234  @param kwargs list of named arguments (see start_command() for details)
235 
236  @return stdout
237  """
238  ps = pipe_command(*args, **kwargs)
239  return ps.communicate()[0]
240 
241 def parse_command(*args, **kwargs):
242  """!Passes all arguments to read_command, then parses the output
243  by parse_key_val().
244 
245  Parsing function can be optionally given by <em>parse</em> parameter
246  including its arguments, e.g.
247 
248  @code
249  parse_command(..., parse = (grass.parse_key_val, { 'sep' : ':' }))
250  @endcode
251 
252  or you can simply define <em>delimiter</em>
253 
254  @code
255  parse_command(..., delimiter = ':')
256  @endcode
257 
258  @param args list of unnamed arguments (see start_command() for details)
259  @param kwargs list of named arguments (see start_command() for details)
260 
261  @return parsed module output
262  """
263  parse = None
264  parse_args = {}
265  if 'parse' in kwargs:
266  if type(kwargs['parse']) is types.TupleType:
267  parse = kwargs['parse'][0]
268  parse_args = kwargs['parse'][1]
269  del kwargs['parse']
270 
271  if 'delimiter' in kwargs:
272  parse_args = { 'sep' : kwargs['delimiter'] }
273  del kwargs['delimiter']
274 
275  if not parse:
276  parse = parse_key_val # use default fn
277 
278  res = read_command(*args, **kwargs)
279 
280  return parse(res, **parse_args)
281 
282 def write_command(*args, **kwargs):
283  """!Passes all arguments to feed_command, with the string specified
284  by the 'stdin' argument fed to the process' stdin.
285 
286  @param args list of unnamed arguments (see start_command() for details)
287  @param kwargs list of named arguments (see start_command() for details)
288 
289  @return return code
290  """
291  stdin = kwargs['stdin']
292  p = feed_command(*args, **kwargs)
293  p.stdin.write(stdin)
294  p.stdin.close()
295  return p.wait()
296 
297 def exec_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, env = None, **kwargs):
298  """!Interface to os.execvpe(), but with the make_command() interface.
299 
300  @param prog GRASS module
301  @param flags flags to be used (given as a string)
302  @param overwrite True to enable overwriting the output (<tt>--o</tt>)
303  @param quiet True to run quietly (<tt>--q</tt>)
304  @param verbose True to run verbosely (<tt>--v</tt>)
305  @param env directory with environmental variables
306  @param kwargs module's parameters
307 
308  """
309  args = make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
310  if env == None:
311  env = os.environ
312  os.execvpe(prog, args, env)
313 
314 # interface to g.message
315 
316 def message(msg, flag = None):
317  """!Display a message using `g.message`
318 
319  @param msg message to be displayed
320  @param flag flags (given as string)
321  """
322  run_command("g.message", flags = flag, message = msg)
323 
324 def debug(msg, debug = 1):
325  """!Display a debugging message using `g.message -d`
326 
327  @param msg debugging message to be displayed
328  @param debug debug level (0-5)
329  """
330  run_command("g.message", flags = 'd', message = msg, debug = debug)
331 
332 def verbose(msg):
333  """!Display a verbose message using `g.message -v`
334 
335  @param msg verbose message to be displayed
336  """
337  message(msg, flag = 'v')
338 
339 def info(msg):
340  """!Display an informational message using `g.message -i`
341 
342  @param msg informational message to be displayed
343  """
344  message(msg, flag = 'i')
345 
346 def percent(i, n, s):
347  """!Display a progress info message using `g.message -p`
348 
349  @code
350  message(_("Percent complete..."))
351  n = 100
352  for i in range(n):
353  percent(i, n, 1)
354  percent(1, 1, 1)
355  @endcode
356 
357  @param i current item
358  @param n total number of items
359  @param s increment size
360  """
361  message("%d %d %d" % (i, n, s), flag = 'p')
362 
363 def warning(msg):
364  """!Display a warning message using `g.message -w`
365 
366  @param msg warning message to be displayed
367  """
368  message(msg, flag = 'w')
369 
370 def error(msg):
371  """!Display an error message using `g.message -e`
372 
373  Raise exception when on_error is 'raise'.
374 
375  @param msg error message to be displayed
376  """
377  global raise_on_error
378  if raise_on_error:
379  raise ScriptError(msg)
380  else:
381  message(msg, flag = 'e')
382 
383 def fatal(msg):
384  """!Display an error message using `g.message -e`, then abort
385 
386  @param msg error message to be displayed
387  """
388  error(msg)
389  sys.exit(1)
390 
391 def set_raise_on_error(raise_exp = True):
392  """!Define behaviour on error (error() called)
393 
394  @param raise_exp True to raise ScriptError instead of calling
395  error()
396 
397  @return current status
398  """
399  global raise_on_error
400  tmp_raise = raise_on_error
401  raise_on_error = raise_exp
402 
403 # interface to g.parser
404 
405 def _parse_opts(lines):
406  options = {}
407  flags = {}
408  for line in lines:
409  line = line.rstrip('\r\n')
410  if not line:
411  break
412  try:
413  [var, val] = line.split('=', 1)
414  except:
415  raise SyntaxError("invalid output from g.parser: %s" % line)
416 
417  if var.startswith('flag_'):
418  flags[var[5:]] = bool(int(val))
419  elif var.startswith('opt_'):
420  options[var[4:]] = val
421  elif var in ['GRASS_OVERWRITE', 'GRASS_VERBOSE']:
422  os.environ[var] = val
423  else:
424  raise SyntaxError("invalid output from g.parser: %s" % line)
425 
426  return (options, flags)
427 
428 def parser():
429  """!Interface to g.parser, intended to be run from the top-level, e.g.:
430 
431  @code
432  if __name__ == "__main__":
433  options, flags = grass.parser()
434  main()
435  @endcode
436 
437  Thereafter, the global variables "options" and "flags" will be
438  dictionaries containing option/flag values, keyed by lower-case
439  option/flag names. The values in "options" are strings, those in
440  "flags" are Python booleans.
441  """
442  if not os.getenv("GISBASE"):
443  print >> sys.stderr, "You must be in GRASS GIS to run this program."
444  sys.exit(1)
445 
446  cmdline = [basename(sys.argv[0])]
447  cmdline += ['"' + arg + '"' for arg in sys.argv[1:]]
448  os.environ['CMDLINE'] = ' '.join(cmdline)
449 
450  argv = sys.argv[:]
451  name = argv[0]
452  if not os.path.isabs(name):
453  if os.sep in name or (os.altsep and os.altsep in name):
454  argv[0] = os.path.abspath(name)
455  else:
456  argv[0] = os.path.join(sys.path[0], name)
457 
458  p = Popen(['g.parser', '-s'] + argv, stdout = PIPE)
459  s = p.communicate()[0]
460  lines = s.splitlines()
461 
462  if not lines or lines[0].rstrip('\r\n') != "@ARGS_PARSED@":
463  sys.stdout.write(s)
464  sys.exit(p.returncode)
465 
466  return _parse_opts(lines[1:])
467 
468 # interface to g.tempfile
469 
470 def tempfile():
471  """!Returns the name of a temporary file, created with g.tempfile."""
472  return read_command("g.tempfile", pid = os.getpid()).strip()
473 
474 def tempdir():
475  """!Returns the name of a temporary dir, created with g.tempfile."""
476  tmp = read_command("g.tempfile", pid = os.getpid()).strip()
477  try_remove(tmp)
478  os.mkdir(tmp)
479 
480  return tmp
481 
482 class KeyValue(dict):
483  """A general-purpose key-value store.
484 
485  KeyValue is a subclass of dict, but also allows entries to be read and
486  written using attribute syntax. Example:
487 
488  \code
489  >>> region = grass.region()
490  >>> region['rows']
491  477
492  >>> region.rows
493  477
494  \endcode
495  """
496 
497  def __getattr__(self, key):
498  return self[key]
499 
500  def __setattr__(self, key, value):
501  self[key] = value
502 
503 # key-value parsers
504 
505 def parse_key_val(s, sep = '=', dflt = None, val_type = None, vsep = None):
506  """!Parse a string into a dictionary, where entries are separated
507  by newlines and the key and value are separated by `sep' (default: `=')
508 
509  @param s string to be parsed
510  @param sep key/value separator
511  @param dflt default value to be used
512  @param val_type value type (None for no cast)
513  @param vsep vertical separator (default os.linesep)
514 
515  @return parsed input (dictionary of keys/values)
516  """
517  result = KeyValue()
518 
519  if not s:
520  return result
521 
522  if vsep:
523  lines = s.split(vsep)
524  try:
525  lines.remove('\n')
526  except ValueError:
527  pass
528  else:
529  lines = s.splitlines()
530 
531  for line in lines:
532  kv = line.split(sep, 1)
533  k = kv[0].strip()
534  if len(kv) > 1:
535  v = kv[1]
536  else:
537  v = dflt
538  if val_type:
539  result[k] = val_type(v)
540  else:
541  result[k] = v
542  return result
543 
544 # interface to g.gisenv
545 
546 def gisenv():
547  """!Returns the output from running g.gisenv (with no arguments), as a
548  dictionary. Example:
549 
550  \code
551  >>> env = grass.gisenv()
552  >>> print env['GISDBASE']
553  /opt/grass-data
554  \endcode
555 
556  @return list of GRASS variables
557  """
558  s = read_command("g.gisenv", flags='n')
559  return parse_key_val(s)
560 
562  """!Tests if location is lat/long. Value is obtained
563  by checking the "g.region -pu" projection code.
564 
565  @return True for a lat/long region, False otherwise
566  """
567  s = read_command("g.region", flags='pu')
568  kv = parse_key_val(s, ':')
569  if kv['projection'].split(' ')[1] == '3':
570  return True
571  else:
572  return False
573 
574 # interface to g.region
575 
576 def region(region3d = False):
577  """!Returns the output from running "g.region -gu", as a
578  dictionary. Example:
579 
580  \param region3d True to get 3D region
581 
582  \code
583  >>> region = grass.region()
584  >>> [region[key] for key in "nsew"]
585  [228500.0, 215000.0, 645000.0, 630000.0]
586  >>> (region['nsres'], region['ewres'])
587  (10.0, 10.0)
588  \endcode
589 
590  @return dictionary of region values
591  """
592  flgs = 'gu'
593  if region3d:
594  flgs += '3'
595 
596  s = read_command("g.region", flags = flgs)
597  reg = parse_key_val(s, val_type = float)
598  for k in ['rows', 'cols', 'cells',
599  'rows3', 'cols3', 'cells3', 'depths']:
600  if k not in reg:
601  continue
602  reg[k] = int(reg[k])
603 
604  return reg
605 
606 def region_env(region3d = False,
607  **kwargs):
608  """!Returns region settings as a string which can used as
609  GRASS_REGION environmental variable.
610 
611  If no 'kwargs' are given then the current region is used. Note
612  that this function doesn't modify the current region!
613 
614  See also use_temp_region() for alternative method how to define
615  temporary region used for raster-based computation.
616 
617  \param region3d True to get 3D region
618  \param kwargs g.region's parameters like 'rast', 'vect' or 'region'
619  \code
620  os.environ['GRASS_REGION'] = grass.region_env(region = 'detail')
621  grass.mapcalc('map = 1', overwrite = True)
622  os.environ.pop('GRASS_REGION')
623  \endcode
624 
625  @return string with region values
626  @return empty string on error
627  """
628  # read proj/zone from WIND file
629  env = gisenv()
630  windfile = os.path.join (env['GISDBASE'], env['LOCATION_NAME'],
631  env['MAPSET'], "WIND")
632  fd = open(windfile, "r")
633  grass_region = ''
634  for line in fd.readlines():
635  key, value = map(lambda x: x.strip(), line.split(":", 1))
636  if kwargs and key not in ('proj', 'zone'):
637  continue
638  if not kwargs and not region3d and \
639  key in ('top', 'bottom', 'cols3', 'rows3',
640  'depths', 'e-w resol3', 'n-s resol3', 't-b resol'):
641  continue
642 
643  grass_region += '%s: %s;' % (key, value)
644 
645  if not kwargs: # return current region
646  return grass_region
647 
648  # read other values from `g.region -gu`
649  flgs = 'ug'
650  if region3d:
651  flgs += '3'
652 
653  s = read_command('g.region', flags = flgs, **kwargs)
654  if not s:
655  return ''
656  reg = parse_key_val(s)
657 
658  kwdata = [('north', 'n'),
659  ('south', 's'),
660  ('east', 'e'),
661  ('west', 'w'),
662  ('cols', 'cols'),
663  ('rows', 'rows'),
664  ('e-w resol', 'ewres'),
665  ('n-s resol', 'nsres')]
666  if region3d:
667  kwdata += [('top', 't'),
668  ('bottom', 'b'),
669  ('cols3', 'cols3'),
670  ('rows3', 'rows3'),
671  ('depths', 'depths'),
672  ('e-w resol3', 'ewres3'),
673  ('n-s resol3', 'nsres3'),
674  ('t-b resol', 'tbres')]
675 
676  for wkey, rkey in kwdata:
677  grass_region += '%s: %s;' % (wkey, reg[rkey])
678 
679  return grass_region
680 
682  """!Copies the current region to a temporary region with "g.region save=",
683  then sets WIND_OVERRIDE to refer to that region. Installs an atexit
684  handler to delete the temporary region upon termination.
685  """
686  name = "tmp.%s.%d" % (os.path.basename(sys.argv[0]), os.getpid())
687  run_command("g.region", save = name, overwrite = True)
688  os.environ['WIND_OVERRIDE'] = name
689  atexit.register(del_temp_region)
690 
692  """!Unsets WIND_OVERRIDE and removes any region named by it."""
693  try:
694  name = os.environ.pop('WIND_OVERRIDE')
695  run_command("g.remove", quiet = True, region = name)
696  except:
697  pass
698 
699 # interface to g.findfile
700 
701 def find_file(name, element = 'cell', mapset = None):
702  """!Returns the output from running g.findfile as a
703  dictionary. Example:
704 
705  \code
706  >>> result = grass.find_file('fields', element = 'vector')
707  >>> print result['fullname']
708  fields@PERMANENT
709  >>> print result['file']
710  /opt/grass-data/spearfish60/PERMANENT/vector/fields
711  \endcode
712 
713  @param name file name
714  @param element element type (default 'cell')
715  @param mapset mapset name (default all mapsets in search path)
716 
717  @return parsed output of g.findfile
718  """
719  s = read_command("g.findfile", flags='n', element = element, file = name, mapset = mapset)
720  return parse_key_val(s)
721 
722 # interface to g.list
723 
724 def list_grouped(type, check_search_path = True):
725  """!List elements grouped by mapsets.
726 
727  Returns the output from running g.list, as a dictionary where the
728  keys are mapset names and the values are lists of maps in that
729  mapset. Example:
730 
731  @code
732  >>> grass.list_grouped('rast')['PERMANENT']
733  ['aspect', 'erosion1', 'quads', 'soils', 'strm.dist', ...
734  @endcode
735 
736  @param type element type (rast, vect, rast3d, region, ...)
737  @param check_search_path True to add mapsets for the search path with no found elements
738 
739  @return directory of mapsets/elements
740  """
741  dashes_re = re.compile("^----+$")
742  mapset_re = re.compile("<(.*)>")
743  result = {}
744  if check_search_path:
745  for mapset in mapsets(search_path = True):
746  result[mapset] = []
747 
748  mapset = None
749  for line in read_command("g.list", type = type).splitlines():
750  if line == "":
751  continue
752  if dashes_re.match(line):
753  continue
754  m = mapset_re.search(line)
755  if m:
756  mapset = m.group(1)
757  if mapset not in result.keys():
758  result[mapset] = []
759  continue
760  if mapset:
761  result[mapset].extend(line.split())
762 
763  return result
764 
765 def _concat(xs):
766  result = []
767  for x in xs:
768  result.extend(x)
769  return result
770 
771 def list_pairs(type):
772  """!List of elements as tuples.
773 
774  Returns the output from running g.list, as a list of (map, mapset)
775  pairs. Example:
776 
777  @code
778  >>> grass.list_pairs('rast')
779  [('aspect', 'PERMANENT'), ('erosion1', 'PERMANENT'), ('quads', 'PERMANENT'), ...
780  @endcode
781 
782  @param type element type (rast, vect, rast3d, region, ...)
783 
784  @return list of tuples (map, mapset)
785  """
786  return _concat([[(map, mapset) for map in maps]
787  for mapset, maps in list_grouped(type).iteritems()])
788 
789 def list_strings(type):
790  """!List of elements as strings.
791 
792  Returns the output from running g.list, as a list of qualified
793  names. Example:
794 
795  @code
796  >>> grass.list_strings('rast')
797  ['aspect@PERMANENT', 'erosion1@PERMANENT', 'quads@PERMANENT', 'soils@PERMANENT', ...
798  @endcode
799 
800  @param type element type
801 
802  @return list of strings ('map@@mapset')
803  """
804  return ["%s@%s" % pair for pair in list_pairs(type)]
805 
806 # interface to g.mlist
807 
808 def mlist_strings(type, pattern = None, mapset = None, flag = ''):
809  """!List of elements as strings.
810 
811  Returns the output from running g.mlist, as a list of qualified
812  names.
813 
814  @param type element type (rast, vect, rast3d, region, ...)
815  @param pattern pattern string
816  @param mapset mapset name (if not given use search path)
817  @param flag pattern type: 'r' (basic regexp), 'e' (extended regexp), or '' (glob pattern)
818 
819  @return list of elements
820  """
821  result = list()
822  for line in read_command("g.mlist",
823  quiet = True,
824  flags = 'm' + flag,
825  type = type,
826  pattern = pattern,
827  mapset = mapset).splitlines():
828  result.append(line.strip())
829 
830  return result
831 
832 def mlist_pairs(type, pattern = None, mapset = None, flag = ''):
833  """!List of elements as pairs
834 
835  Returns the output from running g.mlist, as a list of
836  (name, mapset) pairs
837 
838  @param type element type (rast, vect, rast3d, region, ...)
839  @param pattern pattern string
840  @param mapset mapset name (if not given use search path)
841  @param flag pattern type: 'r' (basic regexp), 'e' (extended regexp), or '' (glob pattern)
842 
843  @return list of elements
844  """
845  return [tuple(map.split('@', 1)) for map in mlist_strings(type, pattern, mapset, flag)]
846 
847 def mlist_grouped(type, pattern = None, check_search_path = True, flag = ''):
848  """!List of elements grouped by mapsets.
849 
850  Returns the output from running g.mlist, as a dictionary where the
851  keys are mapset names and the values are lists of maps in that
852  mapset. Example:
853 
854  @code
855  >>> grass.mlist_grouped('rast', pattern='r*')['PERMANENT']
856  ['railroads', 'roads', 'rstrct.areas', 'rushmore']
857  @endcode
858 
859  @param type element type (rast, vect, rast3d, region, ...)
860  @param pattern pattern string
861  @param check_search_path True to add mapsets for the search path with no found elements
862  @param flag pattern type: 'r' (basic regexp), 'e' (extended regexp), or '' (glob pattern)
863 
864  @return directory of mapsets/elements
865  """
866  result = {}
867  if check_search_path:
868  for mapset in mapsets(search_path = True):
869  result[mapset] = []
870 
871  mapset = None
872  for line in read_command("g.mlist", quiet = True, flags = "m" + flag,
873  type = type, pattern = pattern).splitlines():
874  try:
875  name, mapset = line.split('@')
876  except ValueError:
877  warning(_("Invalid element '%s'") % line)
878  continue
879 
880  if mapset in result:
881  result[mapset].append(name)
882  else:
883  result[mapset] = [name, ]
884 
885  return result
886 
887 # color parsing
888 
889 named_colors = {
890  "white": (1.00, 1.00, 1.00),
891  "black": (0.00, 0.00, 0.00),
892  "red": (1.00, 0.00, 0.00),
893  "green": (0.00, 1.00, 0.00),
894  "blue": (0.00, 0.00, 1.00),
895  "yellow": (1.00, 1.00, 0.00),
896  "magenta": (1.00, 0.00, 1.00),
897  "cyan": (0.00, 1.00, 1.00),
898  "aqua": (0.00, 0.75, 0.75),
899  "grey": (0.75, 0.75, 0.75),
900  "gray": (0.75, 0.75, 0.75),
901  "orange": (1.00, 0.50, 0.00),
902  "brown": (0.75, 0.50, 0.25),
903  "purple": (0.50, 0.00, 1.00),
904  "violet": (0.50, 0.00, 1.00),
905  "indigo": (0.00, 0.50, 1.00)}
906 
907 def parse_color(val, dflt = None):
908  """!Parses the string "val" as a GRASS colour, which can be either one of
909  the named colours or an R:G:B tuple e.g. 255:255:255. Returns an
910  (r,g,b) triple whose components are floating point values between 0
911  and 1. Example:
912 
913  \code
914  >>> grass.parse_color("red")
915  (1.0, 0.0, 0.0)
916  >>> grass.parse_color("255:0:0")
917  (1.0, 0.0, 0.0)
918  \endcode
919 
920  @param val color value
921  @param dflt default color value
922 
923  @return tuple RGB
924  """
925  if val in named_colors:
926  return named_colors[val]
927 
928  vals = val.split(':')
929  if len(vals) == 3:
930  return tuple(float(v) / 255 for v in vals)
931 
932  return dflt
933 
934 # check GRASS_OVERWRITE
935 
936 def overwrite():
937  """!Return True if existing files may be overwritten"""
938  owstr = 'GRASS_OVERWRITE'
939  return owstr in os.environ and os.environ[owstr] != '0'
940 
941 # check GRASS_VERBOSE
942 
943 def verbosity():
944  """!Return the verbosity level selected by GRASS_VERBOSE"""
945  vbstr = os.getenv('GRASS_VERBOSE')
946  if vbstr:
947  return int(vbstr)
948  else:
949  return 2
950 
951 ## various utilities, not specific to GRASS
952 
953 # basename inc. extension stripping
954 
955 def basename(path, ext = None):
956  """!Remove leading directory components and an optional extension
957  from the specified path
958 
959  @param path path
960  @param ext extension
961  """
962  name = os.path.basename(path)
963  if not ext:
964  return name
965  fs = name.rsplit('.', 1)
966  if len(fs) > 1 and fs[1].lower() == ext:
967  name = fs[0]
968  return name
969 
970 # find a program (replacement for "which")
971 
972 def find_program(pgm, args = []):
973  """!Attempt to run a program, with optional arguments.
974  You must call the program in a way that will return a successful
975  exit code. For GRASS modules this means you need to pass it some
976  valid CLI option, like "--help". For other programs a common
977  valid do-little option is "--version".
978 
979  Example:
980 
981  @code
982  >>> grass.find_program('r.sun', ['help'])
983  True
984  >>> grass.find_program('gdalwarp', ['--version'])
985  True
986  @endcode
987 
988  @param pgm program name
989  @param args list of arguments
990 
991  @return False if the attempt failed due to a missing executable
992  or non-zero return code
993  @return True otherwise
994  """
995  nuldev = file(os.devnull, 'w+')
996  try:
997  ret = call([pgm] + args, stdin = nuldev, stdout = nuldev, stderr = nuldev)
998  if ret == 0:
999  found = True
1000  else:
1001  found = False
1002  except:
1003  found = False
1004  nuldev.close()
1005 
1006  return found
1007 
1008 # try to remove a file, without complaints
1009 
1010 def try_remove(path):
1011  """!Attempt to remove a file; no exception is generated if the
1012  attempt fails.
1013 
1014  @param path path to file to remove
1015  """
1016  try:
1017  os.remove(path)
1018  except:
1019  pass
1020 
1021 # try to remove a directory, without complaints
1022 
1023 def try_rmdir(path):
1024  """!Attempt to remove a directory; no exception is generated if the
1025  attempt fails.
1026 
1027  @param path path to directory to remove
1028  """
1029  try:
1030  os.rmdir(path)
1031  except:
1032  shutil.rmtree(path, ignore_errors = True)
1033 
1034 def float_or_dms(s):
1035  """!Convert DMS to float.
1036 
1037  @param s DMS value
1038 
1039  @return float value
1040  """
1041  return sum(float(x) / 60 ** n for (n, x) in enumerate(s.split(':')))
1042 
1043 # interface to g.mapsets
1044 
1045 def mapsets(search_path = False):
1046  """!List available mapsets
1047 
1048  @param searchPatch True to list mapsets only in search path
1049 
1050  @return list of mapsets
1051  """
1052  if search_path:
1053  flags = 'p'
1054  else:
1055  flags = 'l'
1056  mapsets = read_command('g.mapsets',
1057  flags = flags,
1058  fs = 'newline',
1059  quiet = True)
1060  if not mapsets:
1061  fatal(_("Unable to list mapsets"))
1062 
1063  return mapsets.splitlines()
1064 
1065 # interface to `g.proj -c`
1066 
1067 def create_location(dbase, location,
1068  epsg = None, proj4 = None, filename = None, wkt = None,
1069  datum = None, datumtrans = None, desc = None):
1070  """!Create new location
1071 
1072  Raise ScriptError on error.
1073 
1074  @param dbase path to GRASS database
1075  @param location location name to create
1076  @param epsg if given create new location based on EPSG code
1077  @param proj4 if given create new location based on Proj4 definition
1078  @param filename if given create new location based on georeferenced file
1079  @param wkt if given create new location based on WKT definition (path to PRJ file)
1080  @param datum GRASS format datum code
1081  @param datumtrans datum transformation parameters (used for epsg and proj4)
1082  @param desc description of the location (creates MYNAME file)
1083  """
1084  gisdbase = None
1085  if epsg or proj4 or filename or wkt:
1086  gisdbase = gisenv()['GISDBASE']
1087  # FIXME: changing GISDBASE mid-session is not background-job safe
1088  run_command('g.gisenv',
1089  set = 'GISDBASE=%s' % dbase)
1090  if not os.path.exists(dbase):
1091  os.mkdir(dbase)
1092 
1093  kwargs = dict()
1094  if datum:
1095  kwargs['datum'] = datum
1096  if datumtrans:
1097  kwargs['datumtrans'] = datumtrans
1098 
1099  if epsg:
1100  ps = pipe_command('g.proj',
1101  quiet = True,
1102  flags = 't',
1103  epsg = epsg,
1104  location = location,
1105  stderr = PIPE,
1106  **kwargs)
1107  elif proj4:
1108  ps = pipe_command('g.proj',
1109  quiet = True,
1110  flags = 't',
1111  proj4 = proj4,
1112  location = location,
1113  stderr = PIPE,
1114  **kwargs)
1115  elif filename:
1116  ps = pipe_command('g.proj',
1117  quiet = True,
1118  georef = filename,
1119  location = location,
1120  stderr = PIPE)
1121  elif wkt:
1122  ps = pipe_command('g.proj',
1123  quiet = True,
1124  wkt = wkt,
1125  location = location,
1126  stderr = PIPE)
1127  else:
1128  _create_location_xy(dbase, location)
1129 
1130  if epsg or proj4 or filename or wkt:
1131  error = ps.communicate()[1]
1132  run_command('g.gisenv',
1133  set = 'GISDBASE=%s' % gisdbase)
1134 
1135  if ps.returncode != 0 and error:
1136  raise ScriptError(repr(error))
1137 
1138  try:
1139  fd = codecs.open(os.path.join(dbase, location,
1140  'PERMANENT', 'MYNAME'),
1141  encoding = 'utf-8', mode = 'w')
1142  if desc:
1143  fd.write(desc + os.linesep)
1144  else:
1145  fd.write(os.linesep)
1146  fd.close()
1147  except OSError, e:
1148  raise ScriptError(repr(e))
1149 
1150 def _create_location_xy(database, location):
1151  """!Create unprojected location
1152 
1153  Raise ScriptError on error.
1154 
1155  @param database GRASS database where to create new location
1156  @param location location name
1157  """
1158  cur_dir = os.getcwd()
1159  try:
1160  os.chdir(database)
1161  os.mkdir(location)
1162  os.mkdir(os.path.join(location, 'PERMANENT'))
1163 
1164  # create DEFAULT_WIND and WIND files
1165  regioninfo = ['proj: 0',
1166  'zone: 0',
1167  'north: 1',
1168  'south: 0',
1169  'east: 1',
1170  'west: 0',
1171  'cols: 1',
1172  'rows: 1',
1173  'e-w resol: 1',
1174  'n-s resol: 1',
1175  'top: 1',
1176  'bottom: 0',
1177  'cols3: 1',
1178  'rows3: 1',
1179  'depths: 1',
1180  'e-w resol3: 1',
1181  'n-s resol3: 1',
1182  't-b resol: 1']
1183 
1184  defwind = open(os.path.join(location,
1185  "PERMANENT", "DEFAULT_WIND"), 'w')
1186  for param in regioninfo:
1187  defwind.write(param + '%s' % os.linesep)
1188  defwind.close()
1189 
1190  shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"),
1191  os.path.join(location, "PERMANENT", "WIND"))
1192 
1193  os.chdir(cur_dir)
1194  except OSError, e:
1195  raise ScriptError(repr(e))
1196 
1197 # interface to g.version
1198 
1199 def version():
1200  """!Get GRASS version as dictionary
1201 
1202  @code
1203  print version()
1204 
1205  {'date': '2011', 'libgis_date': '2011-02-26 21:31:24 +0100 (Sat, 26 Feb 2011)',
1206  'version': '6.5.svn', 'libgis_revision': '45467', 'revision': '47305'}
1207  @endcode
1208  """
1209  data = parse_command('g.version',
1210  flags = 'rg')
1211  for k, v in data.iteritems():
1212  data[k.strip()] = v.replace('"', '').strip()
1213 
1214  return data
1215 
1216 # get debug_level
1217 if find_program('g.gisenv', ['--help']):
1218  debug_level = int(gisenv().get('DEBUG', 0))
1219 
1220 def legal_name(s):
1221  """!Checks if the string contains only allowed characters.
1222 
1223  This is the Python implementation of G_legal_filename() function.
1224 
1225  @note It is not clear when to use this function.
1226  """
1227  if not s or s[0] == '.':
1228  warning(_("Illegal filename <%s>. Cannot be empty or start with '.'.") % s)
1229  return False
1230 
1231  illegal = [c
1232  for c in s
1233  if c in '/"\'@,=*~' or c <= ' ' or c >= '\177']
1234  if illegal:
1235  illegal = ''.join(sorted(set(illegal)))
1236  warning(_("Illegal filename <%s>. <%s> not allowed.\n") % (s, illegal))
1237  return False
1238 
1239  return True
def list_strings
List of elements as strings.
Definition: core.py:789
def make_command
Return a list of strings suitable for use as the args parameter to Popen() or call().
Definition: core.py:97
def info
Display an informational message using g.message -i
Definition: core.py:339
def decode
Definition: core.py:80
def list_grouped
List elements grouped by mapsets.
Definition: core.py:724
def locn_is_latlong
Tests if location is lat/long.
Definition: core.py:561
def parse_command
Passes all arguments to read_command, then parses the output by parse_key_val().
Definition: core.py:241
def use_temp_region
Copies the current region to a temporary region with &quot;g.region save=&quot;, then sets WIND_OVERRIDE to ref...
Definition: core.py:681
def mlist_strings
List of elements as strings.
Definition: core.py:808
def exec_command
Interface to os.execvpe(), but with the make_command() interface.
Definition: core.py:297
def feed_command
Passes all arguments to start_command(), but also adds &quot;stdin = PIPE&quot;.
Definition: core.py:217
def start_command
Returns a Popen object with the command created by make_command.
Definition: core.py:133
def error
Display an error message using g.message -e
Definition: core.py:370
def del_temp_region
Unsets WIND_OVERRIDE and removes any region named by it.
Definition: core.py:691
def message
Display a message using g.message
Definition: core.py:316
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def fatal
Display an error message using g.message -e, then abort.
Definition: core.py:383
def basename
various utilities, not specific to GRASS
Definition: core.py:955
def percent
Display a progress info message using g.message -p
Definition: core.py:346
def call
Definition: core.py:71
def tempdir
Returns the name of a temporary dir, created with g.tempfile.
Definition: core.py:474
def parse_key_val
Parse a string into a dictionary, where entries are separated by newlines and the key and value are s...
Definition: core.py:505
def region
Returns the output from running &quot;g.region -gu&quot;, as a dictionary.
Definition: core.py:576
def mapsets
List available mapsets.
Definition: core.py:1045
def pipe_command
Passes all arguments to start_command(), but also adds &quot;stdout = PIPE&quot;.
Definition: core.py:192
def try_remove
Attempt to remove a file; no exception is generated if the attempt fails.
Definition: core.py:1010
def set_raise_on_error
Define behaviour on error (error() called)
Definition: core.py:391
def debug
Display a debugging message using g.message -d
Definition: core.py:324
def write_command
Passes all arguments to feed_command, with the string specified by the &#39;stdin&#39; argument fed to the pr...
Definition: core.py:282
def warning
Display a warning message using g.message -w
Definition: core.py:363
def __init__
Definition: core.py:47
def run_command
Passes all arguments to start_command(), then waits for the process to complete, returning its exit c...
Definition: core.py:179
def read_command
Passes all arguments to pipe_command, then waits for the process to complete, returning its stdout (i...
Definition: core.py:229
def region_env
Returns region settings as a string which can used as GRASS_REGION environmental variable.
Definition: core.py:607
def tempfile
Returns the name of a temporary file, created with g.tempfile.
Definition: core.py:470
def parser
Interface to g.parser, intended to be run from the top-level, e.g.
Definition: core.py:428
def gisenv
Returns the output from running g.gisenv (with no arguments), as a dictionary.
Definition: core.py:546
def find_file
Returns the output from running g.findfile as a dictionary.
Definition: core.py:701
def verbose
Display a verbose message using g.message -v
Definition: core.py:332
def list_pairs
List of elements as tuples.
Definition: core.py:771