core.py 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. """!@package grass.script.core
  2. @brief GRASS Python scripting module (core functions)
  3. Core functions to be used in Python scripts.
  4. Usage:
  5. @code
  6. from grass.script import core as grass
  7. grass.parser()
  8. ...
  9. @endcode
  10. (C) 2008-2011 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Glynn Clements
  15. @author Martin Landa <landa.martin gmail.com>
  16. @author Michael Barton <michael.barton asu.edu>
  17. """
  18. import os
  19. import sys
  20. import types
  21. import re
  22. import atexit
  23. import subprocess
  24. import shutil
  25. import locale
  26. import codecs
  27. # i18N
  28. import gettext
  29. gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
  30. # subprocess wrapper that uses shell on Windows
  31. class Popen(subprocess.Popen):
  32. def __init__(self, args, bufsize = 0, executable = None,
  33. stdin = None, stdout = None, stderr = None,
  34. preexec_fn = None, close_fds = False, shell = None,
  35. cwd = None, env = None, universal_newlines = False,
  36. startupinfo = None, creationflags = 0):
  37. if shell == None:
  38. shell = (sys.platform == "win32")
  39. subprocess.Popen.__init__(self, args, bufsize, executable,
  40. stdin, stdout, stderr,
  41. preexec_fn, close_fds, shell,
  42. cwd, env, universal_newlines,
  43. startupinfo, creationflags)
  44. PIPE = subprocess.PIPE
  45. STDOUT = subprocess.STDOUT
  46. class ScriptError(Exception):
  47. def __init__(self, msg):
  48. self.value = msg
  49. def __str__(self):
  50. return repr(self.value)
  51. raise_on_error = False # raise exception instead of calling fatal()
  52. debug_level = 0 # DEBUG level
  53. def call(*args, **kwargs):
  54. return Popen(*args, **kwargs).wait()
  55. # GRASS-oriented interface to subprocess module
  56. _popen_args = ["bufsize", "executable", "stdin", "stdout", "stderr",
  57. "preexec_fn", "close_fds", "cwd", "env",
  58. "universal_newlines", "startupinfo", "creationflags"]
  59. def _decode(string):
  60. enc = locale.getdefaultlocale()[1]
  61. if enc:
  62. return string.decode(enc)
  63. return string
  64. def _make_val(val):
  65. if isinstance(val, types.StringType) or \
  66. isinstance(val, types.UnicodeType):
  67. return val
  68. if isinstance(val, types.ListType):
  69. return ",".join(map(_make_val, val))
  70. if isinstance(val, types.TupleType):
  71. return _make_val(list(val))
  72. return str(val)
  73. def _get_error(string):
  74. try:
  75. return string.split('\n')[-2]
  76. except:
  77. return ''
  78. def make_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **options):
  79. """!Return a list of strings suitable for use as the args parameter to
  80. Popen() or call(). Example:
  81. @code
  82. >>> grass.make_command("g.message", flags = 'w', message = 'this is a warning')
  83. ['g.message', '-w', 'message=this is a warning']
  84. @endcode
  85. @param prog GRASS module
  86. @param flags flags to be used (given as a string)
  87. @param overwrite True to enable overwriting the output (<tt>--o</tt>)
  88. @param quiet True to run quietly (<tt>--q</tt>)
  89. @param verbose True to run verbosely (<tt>--v</tt>)
  90. @param options module's parameters
  91. @return list of arguments
  92. """
  93. args = [prog]
  94. if overwrite:
  95. args.append("--o")
  96. if quiet:
  97. args.append("--q")
  98. if verbose:
  99. args.append("--v")
  100. if flags:
  101. args.append("-%s" % flags)
  102. for opt, val in options.iteritems():
  103. if val != None:
  104. if opt[0] == '_':
  105. opt = opt[1:]
  106. args.append("%s=%s" % (opt, _make_val(val)))
  107. return args
  108. def start_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **kwargs):
  109. """!Returns a Popen object with the command created by make_command.
  110. Accepts any of the arguments which Popen() accepts apart from "args"
  111. and "shell".
  112. \code
  113. >>> p = grass.start_command("g.gisenv", stdout = subprocess.PIPE)
  114. >>> print p
  115. <subprocess.Popen object at 0xb7c12f6c>
  116. >>> print p.communicate()[0]
  117. GISDBASE='/opt/grass-data';
  118. LOCATION_NAME='spearfish60';
  119. MAPSET='glynn';
  120. GRASS_DB_ENCODING='ascii';
  121. GRASS_GUI='text';
  122. MONITOR='x0';
  123. \endcode
  124. @param prog GRASS module
  125. @param flags flags to be used (given as a string)
  126. @param overwrite True to enable overwriting the output (<tt>--o</tt>)
  127. @param quiet True to run quietly (<tt>--q</tt>)
  128. @param verbose True to run verbosely (<tt>--v</tt>)
  129. @param kwargs module's parameters
  130. @return Popen object
  131. """
  132. options = {}
  133. popts = {}
  134. for opt, val in kwargs.iteritems():
  135. if opt in _popen_args:
  136. popts[opt] = val
  137. else:
  138. options[opt] = val
  139. args = make_command(prog, flags, overwrite, quiet, verbose, **options)
  140. global debug_level
  141. if debug_level > 0:
  142. sys.stderr.write("D1/%d: %s.start_command(): %s\n" % (debug_level, __name__, ' '.join(args)))
  143. sys.stderr.flush()
  144. return Popen(args, **popts)
  145. def run_command(*args, **kwargs):
  146. """!Passes all arguments to start_command(), then waits for the process to
  147. complete, returning its exit code. Similar to subprocess.call(), but
  148. with the make_command() interface.
  149. @param args list of unnamed arguments (see start_command() for details)
  150. @param kwargs list of named arguments (see start_command() for details)
  151. @return exit code (0 for success)
  152. """
  153. if get_raise_on_error():
  154. kwargs['stderr'] = PIPE
  155. env = os.getenv('GRASS_MESSAGE_FORMAT')
  156. os.environ['GRASS_MESSAGE_FORMAT'] = 'plain'
  157. ps = start_command(*args, **kwargs)
  158. if get_raise_on_error():
  159. err = ps.communicate()[1]
  160. if env:
  161. os.environ['GRASS_MESSAGE_FORMAT'] = env
  162. if ps.returncode != 0:
  163. raise ScriptError(_("Error in %s(%s): %s") % ('run_command', args[0], _get_error(err)))
  164. return ps.returncode
  165. else:
  166. return ps.wait()
  167. def pipe_command(*args, **kwargs):
  168. """!Passes all arguments to start_command(), but also adds
  169. "stdout = PIPE". Returns the Popen object.
  170. \code
  171. >>> p = grass.pipe_command("g.gisenv")
  172. >>> print p
  173. <subprocess.Popen object at 0xb7c12f6c>
  174. >>> print p.communicate()[0]
  175. GISDBASE='/opt/grass-data';
  176. LOCATION_NAME='spearfish60';
  177. MAPSET='glynn';
  178. GRASS_DB_ENCODING='ascii';
  179. GRASS_GUI='text';
  180. MONITOR='x0';
  181. \endcode
  182. @param args list of unnamed arguments (see start_command() for details)
  183. @param kwargs list of named arguments (see start_command() for details)
  184. @return Popen object
  185. """
  186. kwargs['stdout'] = PIPE
  187. return start_command(*args, **kwargs)
  188. def feed_command(*args, **kwargs):
  189. """!Passes all arguments to start_command(), but also adds
  190. "stdin = PIPE". Returns the Popen object.
  191. @param args list of unnamed arguments (see start_command() for details)
  192. @param kwargs list of named arguments (see start_command() for details)
  193. @return Popen object
  194. """
  195. kwargs['stdin'] = PIPE
  196. return start_command(*args, **kwargs)
  197. def read_command(*args, **kwargs):
  198. """!Passes all arguments to pipe_command, then waits for the process to
  199. complete, returning its stdout (i.e. similar to shell `backticks`).
  200. @param args list of unnamed arguments (see start_command() for details)
  201. @param kwargs list of named arguments (see start_command() for details)
  202. @return stdout
  203. """
  204. if get_raise_on_error():
  205. kwargs['stderr'] = PIPE
  206. env = os.getenv('GRASS_MESSAGE_FORMAT')
  207. os.environ['GRASS_MESSAGE_FORMAT'] = 'plain'
  208. ps = pipe_command(*args, **kwargs)
  209. if get_raise_on_error():
  210. out, err = ps.communicate()
  211. if env:
  212. os.environ['GRASS_MESSAGE_FORMAT'] = env
  213. if ps.returncode != 0:
  214. raise ScriptError(_("Error in %s(%s): %s") % ('read_command', args[0], _get_error(err)))
  215. return _decode(out)
  216. else:
  217. return _decode(ps.communicate()[0])
  218. def parse_command(*args, **kwargs):
  219. """!Passes all arguments to read_command, then parses the output by
  220. parse_key_val().
  221. Parsing function can be optionally given by <b>parse</b> parameter
  222. including its arguments, e.g.
  223. @code
  224. parse_command(..., parse = (grass.parse_key_val, { 'sep' : ':' }))
  225. @endcode
  226. @param args list of unnamed arguments (see start_command() for details)
  227. @param kwargs list of named arguments (see start_command() for details)
  228. @return parsed module output
  229. """
  230. parse = None
  231. if kwargs.has_key('parse'):
  232. if type(kwargs['parse']) is types.TupleType:
  233. parse = kwargs['parse'][0]
  234. parse_args = kwargs['parse'][1]
  235. del kwargs['parse']
  236. if not parse:
  237. parse = parse_key_val # use default fn
  238. parse_args = {}
  239. res = read_command(*args, **kwargs)
  240. return parse(res, **parse_args)
  241. def write_command(*args, **kwargs):
  242. """!Passes all arguments to feed_command, with the string specified
  243. by the 'stdin' argument fed to the process' stdin.
  244. @param args list of unnamed arguments (see start_command() for details)
  245. @param kwargs list of named arguments (see start_command() for details)
  246. @return return code
  247. """
  248. stdin = kwargs['stdin']
  249. if get_raise_on_error():
  250. kwargs['stderr'] = PIPE
  251. env = os.getenv('GRASS_MESSAGE_FORMAT')
  252. os.environ['GRASS_MESSAGE_FORMAT'] = 'plain'
  253. p = feed_command(*args, **kwargs)
  254. p.stdin.write(stdin)
  255. if get_raise_on_error():
  256. err = p.communicate()[1]
  257. if env:
  258. os.environ['GRASS_MESSAGE_FORMAT'] = env
  259. p.stdin.close()
  260. if p.returncode != 0:
  261. raise ScriptError(_("Error in %s(%s): %s") % ('write_command', args[0], _get_error(err)))
  262. return p.returncode
  263. else:
  264. p.stdin.close()
  265. return p.wait()
  266. def exec_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, env = None, **kwargs):
  267. """!Interface to os.execvpe(), but with the make_command() interface.
  268. @param prog GRASS module
  269. @param flags flags to be used (given as a string)
  270. @param overwrite True to enable overwriting the output (<tt>--o</tt>)
  271. @param quiet True to run quietly (<tt>--q</tt>)
  272. @param verbose True to run verbosely (<tt>--v</tt>)
  273. @param env directory with enviromental variables
  274. @param kwargs module's parameters
  275. """
  276. args = make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
  277. if env == None:
  278. env = os.environ
  279. os.execvpe(prog, args, env)
  280. # interface to g.message
  281. def message(msg, flag = None):
  282. """!Display a message using `g.message`
  283. @param msg message to be displayed
  284. @param flag flags (given as string)
  285. """
  286. run_command("g.message", flags = flag, message = msg)
  287. def debug(msg, debug = 1):
  288. """!Display a debugging message using `g.message -d`
  289. @param msg debugging message to be displayed
  290. @param debug debug level (0-5)
  291. """
  292. run_command("g.message", flags = 'd', message = msg, debug = debug)
  293. def verbose(msg):
  294. """!Display a verbose message using `g.message -v`
  295. @param msg verbose message to be displayed
  296. """
  297. message(msg, flag = 'v')
  298. def info(msg):
  299. """!Display an informational message using `g.message -i`
  300. @param msg informational message to be displayed
  301. """
  302. message(msg, flag = 'i')
  303. def percent(i, n, s):
  304. """!Display a progress info message using `g.message -p`
  305. @code
  306. message(_("Percent complete..."))
  307. n = 100
  308. for i in range(n):
  309. percent(i, n, 1)
  310. percent(1, 1, 1)
  311. @endcode
  312. @param i current item
  313. @param n total number of items
  314. @param s increment size
  315. """
  316. message("%d %d %d" % (i, n, s), flag = 'p')
  317. def warning(msg):
  318. """!Display a warning message using `g.message -w`
  319. @param msg warning message to be displayed
  320. """
  321. message(msg, flag = 'w')
  322. def error(msg):
  323. """!Display an error message using `g.message -e`
  324. Raise exception when on_error is 'raise'.
  325. @param msg error message to be displayed
  326. """
  327. global raise_on_error
  328. if raise_on_error:
  329. raise ScriptError(msg)
  330. else:
  331. message(msg, flag = 'e')
  332. def fatal(msg):
  333. """!Display an error message using `g.message -e`, then abort
  334. @param msg error message to be displayed
  335. """
  336. error(msg)
  337. sys.exit(1)
  338. def set_raise_on_error(raise_exp = True):
  339. """!Define behaviour on error (error() called)
  340. @param raise_exp True to raise ScriptError instead of calling
  341. error()
  342. @return current status
  343. """
  344. global raise_on_error
  345. tmp_raise = raise_on_error
  346. raise_on_error = raise_exp
  347. return tmp_raise
  348. def get_raise_on_error():
  349. """!Get raise_on_error status
  350. @return True to raise exception
  351. """
  352. global raise_on_error
  353. return raise_on_error
  354. # interface to g.parser
  355. def _parse_opts(lines):
  356. options = {}
  357. flags = {}
  358. for line in lines:
  359. line = line.rstrip('\r\n')
  360. if not line:
  361. break
  362. try:
  363. [var, val] = line.split('=', 1)
  364. except:
  365. raise SyntaxError("invalid output from g.parser: %s" % line)
  366. if var.startswith('flag_'):
  367. flags[var[5:]] = bool(int(val))
  368. elif var.startswith('opt_'):
  369. options[var[4:]] = val
  370. elif var in ['GRASS_OVERWRITE', 'GRASS_VERBOSE']:
  371. os.environ[var] = val
  372. else:
  373. raise SyntaxError("invalid output from g.parser: %s" % line)
  374. return (options, flags)
  375. def parser():
  376. """!Interface to g.parser, intended to be run from the top-level, e.g.:
  377. @code
  378. if __name__ == "__main__":
  379. options, flags = grass.parser()
  380. main()
  381. @endcode
  382. Thereafter, the global variables "options" and "flags" will be
  383. dictionaries containing option/flag values, keyed by lower-case
  384. option/flag names. The values in "options" are strings, those in
  385. "flags" are Python booleans.
  386. """
  387. if not os.getenv("GISBASE"):
  388. print >> sys.stderr, "You must be in GRASS GIS to run this program."
  389. sys.exit(1)
  390. cmdline = [basename(sys.argv[0])]
  391. cmdline += ['"' + arg + '"' for arg in sys.argv[1:]]
  392. os.environ['CMDLINE'] = ' '.join(cmdline)
  393. argv = sys.argv[:]
  394. name = argv[0]
  395. if not os.path.isabs(name):
  396. if os.sep in name or (os.altsep and os.altsep in name):
  397. argv[0] = os.path.abspath(name)
  398. else:
  399. argv[0] = os.path.join(sys.path[0], name)
  400. p = Popen(['g.parser', '-s'] + argv, stdout = PIPE)
  401. s = p.communicate()[0]
  402. lines = s.splitlines()
  403. if not lines or lines[0].rstrip('\r\n') != "@ARGS_PARSED@":
  404. sys.stdout.write(s)
  405. sys.exit(1)
  406. return _parse_opts(lines[1:])
  407. # interface to g.tempfile
  408. def tempfile():
  409. """!Returns the name of a temporary file, created with g.tempfile."""
  410. return read_command("g.tempfile", pid = os.getpid()).strip()
  411. def tempdir():
  412. """!Returns the name of a temporary dir, created with g.tempfile."""
  413. tmp = read_command("g.tempfile", pid = os.getpid()).strip()
  414. try_remove(tmp)
  415. os.mkdir(tmp)
  416. return tmp
  417. # key-value parsers
  418. def parse_key_val(s, sep = '=', dflt = None, val_type = None, vsep = None):
  419. """!Parse a string into a dictionary, where entries are separated
  420. by newlines and the key and value are separated by `sep' (default: `=')
  421. @param s string to be parsed
  422. @param sep key/value separator
  423. @param dflt default value to be used
  424. @param val_type value type (None for no cast)
  425. @param vsep vertical separator (default os.linesep)
  426. @return parsed input (dictionary of keys/values)
  427. """
  428. result = {}
  429. if not s:
  430. return result
  431. if vsep:
  432. lines = s.split(vsep)
  433. try:
  434. lines.remove('\n')
  435. except ValueError:
  436. pass
  437. else:
  438. lines = s.splitlines()
  439. for line in lines:
  440. kv = line.split(sep, 1)
  441. k = kv[0].strip()
  442. if len(kv) > 1:
  443. v = kv[1]
  444. else:
  445. v = dflt
  446. if val_type:
  447. result[k] = val_type(v)
  448. else:
  449. result[k] = v
  450. return result
  451. # interface to g.gisenv
  452. def gisenv():
  453. """!Returns the output from running g.gisenv (with no arguments), as a
  454. dictionary. Example:
  455. \code
  456. >>> env = grass.gisenv()
  457. >>> print env['GISDBASE']
  458. /opt/grass-data
  459. \endcode
  460. @return list of GRASS variables
  461. """
  462. s = read_command("g.gisenv", flags='n')
  463. return parse_key_val(s)
  464. # interface to g.region
  465. def region():
  466. """!Returns the output from running "g.region -g", as a
  467. dictionary. Example:
  468. \code
  469. >>> region = grass.region()
  470. >>> [region[key] for key in "nsew"]
  471. [228500.0, 215000.0, 645000.0, 630000.0]
  472. >>> (region['nsres'], region['ewres'])
  473. (10.0, 10.0)
  474. \endcode
  475. @return dictionary of region values
  476. """
  477. s = read_command("g.region", flags='g')
  478. reg = parse_key_val(s, val_type = float)
  479. for k in ['rows', 'cols']:
  480. reg[k] = int(reg[k])
  481. return reg
  482. def use_temp_region():
  483. """!Copies the current region to a temporary region with "g.region save=",
  484. then sets WIND_OVERRIDE to refer to that region. Installs an atexit
  485. handler to delete the temporary region upon termination.
  486. """
  487. name = "tmp.%s.%d" % (os.path.basename(sys.argv[0]), os.getpid())
  488. run_command("g.region", save = name)
  489. os.environ['WIND_OVERRIDE'] = name
  490. atexit.register(del_temp_region)
  491. def del_temp_region():
  492. """!Unsets WIND_OVERRIDE and removes any region named by it."""
  493. try:
  494. name = os.environ.pop('WIND_OVERRIDE')
  495. run_command("g.remove", quiet = True, region = name)
  496. except:
  497. pass
  498. # interface to g.findfile
  499. def find_file(name, element = 'cell', mapset = None):
  500. """!Returns the output from running g.findfile as a
  501. dictionary. Example:
  502. \code
  503. >>> result = grass.find_file('fields', element = 'vector')
  504. >>> print result['fullname']
  505. fields@PERMANENT
  506. >>> print result['file']
  507. /opt/grass-data/spearfish60/PERMANENT/vector/fields
  508. \endcode
  509. @param name file name
  510. @param element element type (default 'cell')
  511. @param mapset mapset name (default all mapsets in search path)
  512. @return parsed output of g.findfile
  513. """
  514. s = read_command("g.findfile", flags='n', element = element, file = name, mapset = mapset)
  515. return parse_key_val(s)
  516. # interface to g.list
  517. def list_grouped(type):
  518. """!List elements grouped by mapsets.
  519. Returns the output from running g.list, as a dictionary where the
  520. keys are mapset names and the values are lists of maps in that
  521. mapset. Example:
  522. @code
  523. >>> grass.list_grouped('rast')['PERMANENT']
  524. ['aspect', 'erosion1', 'quads', 'soils', 'strm.dist', ...
  525. @endcode
  526. @param type element type (rast, vect, rast3d, region, ...)
  527. @return directory of mapsets/elements
  528. """
  529. dashes_re = re.compile("^----+$")
  530. mapset_re = re.compile("<(.*)>")
  531. result = {}
  532. mapset = None
  533. for line in read_command("g.list", type = type).splitlines():
  534. if line == "":
  535. continue
  536. if dashes_re.match(line):
  537. continue
  538. m = mapset_re.search(line)
  539. if m:
  540. mapset = m.group(1)
  541. result[mapset] = []
  542. continue
  543. if mapset:
  544. result[mapset].extend(line.split())
  545. return result
  546. def _concat(xs):
  547. result = []
  548. for x in xs:
  549. result.extend(x)
  550. return result
  551. def list_pairs(type):
  552. """!List of elements as tuples.
  553. Returns the output from running g.list, as a list of (map, mapset)
  554. pairs. Example:
  555. @code
  556. >>> grass.list_pairs('rast')
  557. [('aspect', 'PERMANENT'), ('erosion1', 'PERMANENT'), ('quads', 'PERMANENT'), ...
  558. @endcode
  559. @param type element type (rast, vect, rast3d, region, ...)
  560. @return list of tuples (map, mapset)
  561. """
  562. return _concat([[(map, mapset) for map in maps]
  563. for mapset, maps in list_grouped(type).iteritems()])
  564. def list_strings(type):
  565. """!List of elements as strings.
  566. Returns the output from running g.list, as a list of qualified
  567. names. Example:
  568. @code
  569. >>> grass.list_strings('rast')
  570. ['aspect@PERMANENT', 'erosion1@PERMANENT', 'quads@PERMANENT', 'soils@PERMANENT', ...
  571. @endcode
  572. @param type element type
  573. @return list of strings ('map@@mapset')
  574. """
  575. return ["%s@%s" % pair for pair in list_pairs(type)]
  576. # interface to g.mlist
  577. def mlist(type, pattern = None, mapset = None):
  578. """!List of elements
  579. @param type element type (rast, vect, rast3d, region, ...)
  580. @param pattern pattern string
  581. @param mapset mapset name (if not given use search path)
  582. @return list of elements
  583. """
  584. result = list()
  585. for line in read_command("g.mlist",
  586. type = type,
  587. pattern = pattern,
  588. mapset = mapset).splitlines():
  589. result.append(line.strip())
  590. return result
  591. def mlist_grouped(type, pattern = None):
  592. """!List of elements grouped by mapsets.
  593. Returns the output from running g.mlist, as a dictionary where the
  594. keys are mapset names and the values are lists of maps in that
  595. mapset. Example:
  596. @code
  597. >>> grass.mlist_grouped('rast', pattern='r*')['PERMANENT']
  598. ['railroads', 'roads', 'rstrct.areas', 'rushmore']
  599. @endcode
  600. @param type element type (rast, vect, rast3d, region, ...)
  601. @param pattern pattern string
  602. @return directory of mapsets/elements
  603. """
  604. result = dict()
  605. mapset_element = None
  606. for line in read_command("g.mlist", flags = "m",
  607. type = type, pattern = pattern).splitlines():
  608. try:
  609. map, mapset_element = line.split('@')
  610. except ValueError:
  611. warning(_("Invalid element '%s'") % line)
  612. continue
  613. if result.has_key(mapset_element):
  614. result[mapset_element].append(map)
  615. else:
  616. result[mapset_element] = [map, ]
  617. return result
  618. # color parsing
  619. named_colors = {
  620. "white": (1.00, 1.00, 1.00),
  621. "black": (0.00, 0.00, 0.00),
  622. "red": (1.00, 0.00, 0.00),
  623. "green": (0.00, 1.00, 0.00),
  624. "blue": (0.00, 0.00, 1.00),
  625. "yellow": (1.00, 1.00, 0.00),
  626. "magenta": (1.00, 0.00, 1.00),
  627. "cyan": (0.00, 1.00, 1.00),
  628. "aqua": (0.00, 0.75, 0.75),
  629. "grey": (0.75, 0.75, 0.75),
  630. "gray": (0.75, 0.75, 0.75),
  631. "orange": (1.00, 0.50, 0.00),
  632. "brown": (0.75, 0.50, 0.25),
  633. "purple": (0.50, 0.00, 1.00),
  634. "violet": (0.50, 0.00, 1.00),
  635. "indigo": (0.00, 0.50, 1.00)}
  636. def parse_color(val, dflt = None):
  637. """!Parses the string "val" as a GRASS colour, which can be either one of
  638. the named colours or an R:G:B tuple e.g. 255:255:255. Returns an
  639. (r,g,b) triple whose components are floating point values between 0
  640. and 1. Example:
  641. \code
  642. >>> grass.parse_color("red")
  643. (1.0, 0.0, 0.0)
  644. >>> grass.parse_color("255:0:0")
  645. (1.0, 0.0, 0.0)
  646. \endcode
  647. @param val color value
  648. @param dflt default color value
  649. @return tuple RGB
  650. """
  651. if val in named_colors:
  652. return named_colors[val]
  653. vals = val.split(':')
  654. if len(vals) == 3:
  655. return tuple(float(v) / 255 for v in vals)
  656. return dflt
  657. # check GRASS_OVERWRITE
  658. def overwrite():
  659. """!Return True if existing files may be overwritten"""
  660. owstr = 'GRASS_OVERWRITE'
  661. return owstr in os.environ and os.environ[owstr] != '0'
  662. # check GRASS_VERBOSE
  663. def verbosity():
  664. """!Return the verbosity level selected by GRASS_VERBOSE"""
  665. vbstr = os.getenv('GRASS_VERBOSE')
  666. if vbstr:
  667. return int(vbstr)
  668. else:
  669. return 2
  670. ## various utilities, not specific to GRASS
  671. # basename inc. extension stripping
  672. def basename(path, ext = None):
  673. """!Remove leading directory components and an optional extension
  674. from the specified path
  675. @param path path
  676. @param ext extension
  677. """
  678. name = os.path.basename(path)
  679. if not ext:
  680. return name
  681. fs = name.rsplit('.', 1)
  682. if len(fs) > 1 and fs[1].lower() == ext:
  683. name = fs[0]
  684. return name
  685. # find a program (replacement for "which")
  686. def find_program(pgm, args = []):
  687. """!Attempt to run a program, with optional arguments.
  688. @param pgm program name
  689. @param args list of arguments
  690. @return False if the attempt failed due to a missing executable
  691. @return True otherwise
  692. """
  693. nuldev = file(os.devnull, 'w+')
  694. try:
  695. ret = call([pgm] + args, stdin = nuldev, stdout = nuldev, stderr = nuldev)
  696. if ret == 0:
  697. found = True
  698. else:
  699. found = False
  700. except:
  701. found = False
  702. nuldev.close()
  703. return found
  704. # try to remove a file, without complaints
  705. def try_remove(path):
  706. """!Attempt to remove a file; no exception is generated if the
  707. attempt fails.
  708. @param path path to file to remove
  709. """
  710. try:
  711. os.remove(path)
  712. except:
  713. pass
  714. # try to remove a directory, without complaints
  715. def try_rmdir(path):
  716. """!Attempt to remove a directory; no exception is generated if the
  717. attempt fails.
  718. @param path path to directory to remove
  719. """
  720. try:
  721. os.rmdir(path)
  722. except:
  723. shutil.rmtree(path, ignore_errors = True)
  724. def float_or_dms(s):
  725. """!Convert DMS to float.
  726. @param s DMS value
  727. @return float value
  728. """
  729. return sum(float(x) / 60 ** n for (n, x) in enumerate(s.split(':')))
  730. def command_info(cmd):
  731. """!Returns 'help' information for any command as dictionary with entries
  732. for description, keywords, usage, flags, and parameters"""
  733. cmdinfo = {}
  734. s = start_command(cmd, 'help', stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  735. out, err = s.communicate()
  736. sections = err.split('\n\n')
  737. #Description
  738. first, desc = sections[0].split(':\n', 1)
  739. desclines = desc.splitlines()
  740. for line in desclines:
  741. line = line.strip()+' '
  742. # Keywords
  743. first, keywords = sections[1].split(':\n', 1)
  744. keylines = keywords.splitlines()
  745. list = []
  746. list = keywords.strip().split(',')
  747. cmdinfo['keywords'] = list
  748. cmdinfo['description'] = ''.join(desclines).strip()
  749. # Usage
  750. first, usage = sections[2].split(':\n', 1)
  751. usagelines = usage.splitlines()
  752. list = []
  753. for line in usagelines:
  754. line = line.strip()
  755. if line == '': continue
  756. line = line+' '
  757. list.append(line)
  758. cmdinfo['usage'] = ''.join(list).strip()
  759. # Flags
  760. first, flags = sections[3].split(':\n', 1)
  761. flaglines = flags.splitlines()
  762. dict = {}
  763. for line in flaglines:
  764. line = line.strip()
  765. if line == '': continue
  766. item = line.split(' ',1)[0].strip()
  767. val = line.split(' ',1)[1].strip()
  768. dict[item] = val
  769. cmdinfo['flags'] = dict
  770. # Parameters
  771. first, params = err.rsplit(':\n', 1)
  772. paramlines = params.splitlines()
  773. dict = {}
  774. for line in paramlines:
  775. line = line.strip()
  776. if line == '': continue
  777. item = line.split(' ',1)[0].strip()
  778. val = line.split(' ',1)[1].strip()
  779. dict[item] = val
  780. cmdinfo['parameters'] = dict
  781. return cmdinfo
  782. # interface to g.mapsets
  783. def mapsets(accessible = True):
  784. """!List accessible mapsets (mapsets in search path)
  785. @param accessible False to list all mapsets in the location
  786. @return list of mapsets
  787. """
  788. if accessible:
  789. flags = 'p'
  790. else:
  791. flags = 'l'
  792. mapsets = read_command('g.mapsets',
  793. flags = flags,
  794. fs = 'newline',
  795. quiet = True)
  796. if not mapsets:
  797. fatal(_("Unable to list mapsets"))
  798. return mapsets.splitlines()
  799. # interface to `g.proj -c`
  800. def create_location(dbase, location,
  801. epsg = None, proj4 = None, filename = None, wkt = None,
  802. datum = None, desc = None):
  803. """!Create new location
  804. Raise ScriptError on error.
  805. @param dbase path to GRASS database
  806. @param location location name to create
  807. @param epgs if given create new location based on EPSG code
  808. @param proj4 if given create new location based on Proj4 definition
  809. @param filename if given create new location based on georeferenced file
  810. @param wkt if given create new location based on WKT definition (path to PRJ file)
  811. @param datum datum transformation parameters (used for epsg and proj4)
  812. @param desc description of the location (creates MYNAME file)
  813. """
  814. gisdbase = None
  815. if epsg or proj4 or filename or wkt:
  816. gisdbase = gisenv()['GISDBASE']
  817. run_command('g.gisenv',
  818. set = 'GISDBASE=%s' % dbase)
  819. if not os.path.exists(dbase):
  820. os.mkdir(dbase)
  821. kwargs = dict()
  822. if datum:
  823. kwargs['datum'] = datum
  824. if epsg:
  825. ps = pipe_command('g.proj',
  826. quiet = True,
  827. flags = 'c',
  828. epsg = epsg,
  829. location = location,
  830. stderr = PIPE,
  831. **kwargs)
  832. elif proj4:
  833. ps = pipe_command('g.proj',
  834. quiet = True,
  835. flags = 'c',
  836. proj4 = proj4,
  837. location = location,
  838. stderr = PIPE,
  839. **kwargs)
  840. elif filename:
  841. ps = pipe_command('g.proj',
  842. quiet = True,
  843. flags = 'c',
  844. georef = filename,
  845. location = location,
  846. stderr = PIPE)
  847. elif wkt:
  848. ps = pipe_command('g.proj',
  849. quiet = True,
  850. flags = 'c',
  851. wkt = wktfile,
  852. location = location,
  853. stderr = PIPE)
  854. else:
  855. _create_location_xy(dbase, location)
  856. if epsg or proj4 or filename or wkt:
  857. error = ps.communicate()[1]
  858. run_command('g.gisenv',
  859. set = 'GISDBASE=%s' % gisdbase)
  860. if ps.returncode != 0 and error:
  861. raise ScriptError(repr(error))
  862. try:
  863. fd = codecs.open(os.path.join(dbase, location,
  864. 'PERMANENT', 'MYNAME'),
  865. encoding = 'utf-8', mode = 'w')
  866. if desc:
  867. fd.write(desc + os.linesep)
  868. else:
  869. fd.write(os.linesep)
  870. fd.close()
  871. except OSError, e:
  872. raise ScriptError(repr(e))
  873. def _create_location_xy(database, location):
  874. """!Create unprojected location
  875. Raise ScriptError on error.
  876. @param database GRASS database where to create new location
  877. @param location location name
  878. """
  879. cur_dir = os.getcwd()
  880. try:
  881. os.chdir(database)
  882. os.mkdir(location)
  883. os.mkdir(os.path.join(location, 'PERMANENT'))
  884. # create DEFAULT_WIND and WIND files
  885. regioninfo = ['proj: 0',
  886. 'zone: 0',
  887. 'north: 1',
  888. 'south: 0',
  889. 'east: 1',
  890. 'west: 0',
  891. 'cols: 1',
  892. 'rows: 1',
  893. 'e-w resol: 1',
  894. 'n-s resol: 1',
  895. 'top: 1',
  896. 'bottom: 0',
  897. 'cols3: 1',
  898. 'rows3: 1',
  899. 'depths: 1',
  900. 'e-w resol3: 1',
  901. 'n-s resol3: 1',
  902. 't-b resol: 1']
  903. defwind = open(os.path.join(location,
  904. "PERMANENT", "DEFAULT_WIND"), 'w')
  905. for param in regioninfo:
  906. defwind.write(param + '%s' % os.linesep)
  907. defwind.close()
  908. shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"),
  909. os.path.join(location, "PERMANENT", "WIND"))
  910. os.chdir(cur_dir)
  911. except OSError, e:
  912. raise ScriptError(repr(e))
  913. # interface to g.version
  914. def version():
  915. """!Get GRASS version as dictionary
  916. @code
  917. version()
  918. {'date': '2011', 'libgis_revision': '45093 ', 'version': '7.0.svn',
  919. 'libgis_date': '2011-01-20 13:10:50 +0100 (Thu, 20 Jan 2011) ', 'revision': '45136M'}
  920. @endcode
  921. """
  922. return parse_command('g.version',
  923. flags = 'rg')
  924. # get debug_level
  925. if find_program('g.gisenv', ['--help']):
  926. debug_level = int(gisenv().get('DEBUG', 0))