core.py 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559
  1. """
  2. Core functions to be used in Python scripts.
  3. Usage:
  4. ::
  5. from grass.script import core as grass
  6. grass.parser()
  7. (C) 2008-2014 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. .. sectionauthor:: Glynn Clements
  12. .. sectionauthor:: Martin Landa <landa.martin gmail.com>
  13. .. sectionauthor:: Michael Barton <michael.barton asu.edu>
  14. """
  15. import os
  16. import sys
  17. import types
  18. import re
  19. import atexit
  20. import subprocess
  21. import shutil
  22. import codecs
  23. import types as python_types
  24. from utils import KeyValue, parse_key_val, basename, encode
  25. from grass.exceptions import ScriptError, CalledModuleError
  26. # i18N
  27. import gettext
  28. gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'))
  29. import __builtin__
  30. __builtin__.__dict__['_'] = __builtin__.__dict__['_'].im_self.lgettext
  31. # subprocess wrapper that uses shell on Windows
  32. class Popen(subprocess.Popen):
  33. _builtin_exts = set(['.com', '.exe', '.bat', '.cmd'])
  34. @staticmethod
  35. def _escape_for_shell(arg):
  36. # TODO: what are cmd.exe's parsing rules?
  37. return arg
  38. def __init__(self, args, **kwargs):
  39. if (sys.platform == 'win32'
  40. and isinstance(args, list)
  41. and not kwargs.get('shell', False)
  42. and kwargs.get('executable') is None):
  43. cmd = shutil_which(args[0])
  44. if cmd is None:
  45. raise OSError(_("Cannot find the executable {}")
  46. .format(args[0]))
  47. args = [cmd] + args[1:]
  48. name, ext = os.path.splitext(cmd)
  49. if ext.lower() not in self._builtin_exts:
  50. kwargs['shell'] = True
  51. args = [self._escape_for_shell(arg) for arg in args]
  52. subprocess.Popen.__init__(self, args, **kwargs)
  53. PIPE = subprocess.PIPE
  54. STDOUT = subprocess.STDOUT
  55. raise_on_error = False # raise exception instead of calling fatal()
  56. def call(*args, **kwargs):
  57. return Popen(*args, **kwargs).wait()
  58. # GRASS-oriented interface to subprocess module
  59. _popen_args = ["bufsize", "executable", "stdin", "stdout", "stderr",
  60. "preexec_fn", "close_fds", "cwd", "env",
  61. "universal_newlines", "startupinfo", "creationflags"]
  62. def _make_val(val):
  63. if isinstance(val, types.StringType) or \
  64. isinstance(val, types.UnicodeType):
  65. return val
  66. if isinstance(val, types.ListType):
  67. return ",".join(map(_make_val, val))
  68. if isinstance(val, types.TupleType):
  69. return _make_val(list(val))
  70. return str(val)
  71. def get_commands():
  72. """Create list of available GRASS commands to use when parsing
  73. string from the command line
  74. :return: list of commands (set) and directory of scripts (collected
  75. by extension - MS Windows only)
  76. >>> cmds = list(get_commands()[0])
  77. >>> cmds.sort()
  78. >>> cmds[:5]
  79. ['d.barscale', 'd.colorlist', 'd.colortable', 'd.correlate', 'd.erase']
  80. """
  81. gisbase = os.environ['GISBASE']
  82. cmd = list()
  83. scripts = {'.py': list()} if sys.platform == 'win32' else {}
  84. def scan(gisbase, directory):
  85. dir_path = os.path.join(gisbase, directory)
  86. if os.path.exists(dir_path):
  87. for fname in os.listdir(os.path.join(gisbase, directory)):
  88. if scripts: # win32
  89. name, ext = os.path.splitext(fname)
  90. if ext != '.manifest':
  91. cmd.append(name)
  92. if ext in scripts.keys():
  93. scripts[ext].append(name)
  94. else:
  95. cmd.append(fname)
  96. for directory in ('bin', 'scripts'):
  97. scan(gisbase, directory)
  98. # scan gui/scripts/
  99. gui_path = os.path.join(gisbase, 'etc', 'gui', 'scripts')
  100. if os.path.exists(gui_path):
  101. os.environ["PATH"] = os.getenv("PATH") + os.pathsep + gui_path
  102. cmd = cmd + os.listdir(gui_path)
  103. return set(cmd), scripts
  104. # replacement for which function from shutil (not available in all versions)
  105. # from http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068
  106. # added because of Python scripts running Python scripts on MS Windows
  107. # see also ticket #2008 which is unrelated but same function was proposed
  108. def shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None):
  109. """Given a command, mode, and a PATH string, return the path which
  110. conforms to the given mode on the PATH, or None if there is no such
  111. file.
  112. `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
  113. of os.environ.get("PATH"), or can be overridden with a custom search
  114. path.
  115. :param cmd: the command
  116. :param mode:
  117. :param path:
  118. """
  119. # Check that a given file can be accessed with the correct mode.
  120. # Additionally check that `file` is not a directory, as on Windows
  121. # directories pass the os.access check.
  122. def _access_check(fn, mode):
  123. return (os.path.exists(fn) and os.access(fn, mode)
  124. and not os.path.isdir(fn))
  125. # If we're given a path with a directory part, look it up directly rather
  126. # than referring to PATH directories. This includes checking relative to the
  127. # current directory, e.g. ./script
  128. if os.path.dirname(cmd):
  129. if _access_check(cmd, mode):
  130. return cmd
  131. return None
  132. if path is None:
  133. path = os.environ.get("PATH", os.defpath)
  134. if not path:
  135. return None
  136. path = path.split(os.pathsep)
  137. if sys.platform == "win32":
  138. # The current directory takes precedence on Windows.
  139. if not os.curdir in path:
  140. path.insert(0, os.curdir)
  141. # PATHEXT is necessary to check on Windows.
  142. pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
  143. map(lambda x: x.lower(), pathext) # force lowercase
  144. if '.py' not in pathext: # we assume that PATHEXT contains always '.py'
  145. pathext.insert(0, '.py')
  146. # See if the given file matches any of the expected path extensions.
  147. # This will allow us to short circuit when given "python.exe".
  148. # If it does match, only test that one, otherwise we have to try
  149. # others.
  150. if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
  151. files = [cmd]
  152. else:
  153. files = [cmd + ext for ext in pathext]
  154. else:
  155. # On other platforms you don't have things like PATHEXT to tell you
  156. # what file suffixes are executable, so just pass on cmd as-is.
  157. files = [cmd]
  158. seen = set()
  159. for dir in path:
  160. normdir = os.path.normcase(dir)
  161. if not normdir in seen:
  162. seen.add(normdir)
  163. for thefile in files:
  164. name = os.path.join(dir, thefile)
  165. if _access_check(name, mode):
  166. return name
  167. return None
  168. # Added because of scripts calling scripts on MS Windows.
  169. # Module name (here cmd) differs from the file name (does not have extension).
  170. # Additionally, we don't run scripts using system executable mechanism,
  171. # so we need the full path name.
  172. # However, scripts are on the PATH and '.PY' in in PATHEXT, so we can use
  173. # shutil.which to get the full file path. Addons are on PATH too.
  174. # An alternative to which function call would be to check the script path and
  175. # addons path. This is proposed improvement for the future.
  176. # Another alternative is to check some global list of scripts but this list
  177. # needs to be created first. The question is what is less expensive.
  178. # Note that getting the full path is only part of the solution,
  179. # the other part is to use the right Python as an executable and pass the full
  180. # script path as a parameter.
  181. # Nevertheless, it is unclear on which places which extensions are added.
  182. # This function also could skip the check for platform but depends
  183. # how will be used, this is most general but not most effective.
  184. def get_real_command(cmd):
  185. """Returns the real file commad for a module (cmd)
  186. For Python scripts on MS Windows it returns full path to the script
  187. and adds a '.py' extension.
  188. For other cases it just returns a module (name).
  189. So, you can just use this function for all without further check.
  190. >>> get_real_command('g.region')
  191. 'g.region'
  192. :param cmd: the command
  193. """
  194. if sys.platform == 'win32':
  195. # we in fact expect pure module name (without extension)
  196. # so, lets remove extension
  197. if os.path.splitext(cmd)[1] == '.py':
  198. cmd = cmd[:-3]
  199. full_path = shutil_which(cmd + '.py')
  200. if full_path:
  201. return full_path
  202. return cmd
  203. def make_command(prog, flags="", overwrite=False, quiet=False, verbose=False,
  204. errors=None, **options):
  205. """Return a list of strings suitable for use as the args parameter to
  206. Popen() or call(). Example:
  207. >>> make_command("g.message", flags = 'w', message = 'this is a warning')
  208. ['g.message', '-w', 'message=this is a warning']
  209. :param str prog: GRASS module
  210. :param str flags: flags to be used (given as a string)
  211. :param bool overwrite: True to enable overwriting the output (<tt>--o</tt>)
  212. :param bool quiet: True to run quietly (<tt>--q</tt>)
  213. :param bool verbose: True to run verbosely (<tt>--v</tt>)
  214. :param options: module's parameters
  215. :return: list of arguments
  216. """
  217. args = [prog]
  218. if overwrite:
  219. args.append("--o")
  220. if quiet:
  221. args.append("--q")
  222. if verbose:
  223. args.append("--v")
  224. if flags:
  225. if '-' in flags:
  226. raise ScriptError("'-' is not a valid flag")
  227. args.append("-%s" % flags)
  228. for opt, val in options.iteritems():
  229. if opt in _popen_args:
  230. continue
  231. if val != None:
  232. if opt.startswith('_'):
  233. opt = opt[1:]
  234. warning(_("To run the module <%s> add underscore at the end"
  235. " of the option <%s> to avoid conflict with Python"
  236. " keywords. Underscore at the beginning is"
  237. " depreciated in GRASS GIS 7.0 and will be removed"
  238. " in version 7.1.") % (prog, opt))
  239. elif opt.endswith('_'):
  240. opt = opt[:-1]
  241. args.append("%s=%s" % (opt, _make_val(val)))
  242. return args
  243. def handle_errors(returncode, result, args, kwargs):
  244. if returncode == 0:
  245. return result
  246. handler = kwargs.get('errors', 'raise')
  247. if handler.lower() == 'ignore':
  248. return result
  249. elif handler.lower() == 'status':
  250. return returncode
  251. elif handler.lower() == 'exit':
  252. sys.exit(1)
  253. else:
  254. # TODO: construction of the whole command is far from perfect
  255. args = make_command(*args, **kwargs)
  256. raise CalledModuleError(module=None, code=repr(args),
  257. returncode=returncode)
  258. def start_command(prog, flags="", overwrite=False, quiet=False,
  259. verbose=False, **kwargs):
  260. """Returns a Popen object with the command created by make_command.
  261. Accepts any of the arguments which Popen() accepts apart from "args"
  262. and "shell".
  263. >>> p = start_command("g.gisenv", stdout=subprocess.PIPE)
  264. >>> print p # doctest: +ELLIPSIS
  265. <...Popen object at 0x...>
  266. >>> print p.communicate()[0] # doctest: +SKIP
  267. GISDBASE='/opt/grass-data';
  268. LOCATION_NAME='spearfish60';
  269. MAPSET='glynn';
  270. GUI='text';
  271. MONITOR='x0';
  272. If the module parameter is the same as Python keyword, add
  273. underscore at the end of the parameter. For example, use
  274. ``lambda_=1.6`` instead of ``lambda=1.6``.
  275. :param str prog: GRASS module
  276. :param str flags: flags to be used (given as a string)
  277. :param bool overwrite: True to enable overwriting the output (<tt>--o</tt>)
  278. :param bool quiet: True to run quietly (<tt>--q</tt>)
  279. :param bool verbose: True to run verbosely (<tt>--v</tt>)
  280. :param kwargs: module's parameters
  281. :return: Popen object
  282. """
  283. options = {}
  284. popts = {}
  285. for opt, val in kwargs.iteritems():
  286. if opt in _popen_args:
  287. popts[opt] = val
  288. else:
  289. if isinstance(val, unicode):
  290. val = encode(val)
  291. options[opt] = val
  292. args = make_command(prog, flags, overwrite, quiet, verbose, **options)
  293. if debug_level() > 0:
  294. sys.stderr.write("D1/%d: %s.start_command(): %s\n" % (debug_level(),
  295. __name__,
  296. ' '.join(args)))
  297. sys.stderr.flush()
  298. return Popen(args, **popts)
  299. def run_command(*args, **kwargs):
  300. """Execute a module synchronously
  301. This function passes all arguments to ``start_command()``,
  302. then waits for the process to complete. It is similar to
  303. ``subprocess.check_call()``, but with the ``make_command()``
  304. interface.
  305. For backward compatibility, the function returns exit code
  306. by default but only if it is equal to zero. An exception is raised
  307. in case of an non-zero return code.
  308. >>> run_command('g.region', raster='elevation')
  309. 0
  310. See :func:`start_command()` for details about parameters and usage.
  311. ..note::
  312. You should ignore the return value of this function unless, you
  313. change the default behavior using *errors* parameter.
  314. :param *args: unnamed arguments passed to ``start_command()``
  315. :param **kwargs: named arguments passed to ``start_command()``
  316. :returns: 0 with default parameters for backward compatibility only
  317. :raises: ``CalledModuleError`` when module returns non-zero return code
  318. """
  319. ps = start_command(*args, **kwargs)
  320. returncode = ps.wait()
  321. return handle_errors(returncode, returncode, args, kwargs)
  322. def pipe_command(*args, **kwargs):
  323. """Passes all arguments to start_command(), but also adds
  324. "stdout = PIPE". Returns the Popen object.
  325. >>> p = pipe_command("g.gisenv")
  326. >>> print p # doctest: +ELLIPSIS
  327. <....Popen object at 0x...>
  328. >>> print p.communicate()[0] # doctest: +SKIP
  329. GISDBASE='/opt/grass-data';
  330. LOCATION_NAME='spearfish60';
  331. MAPSET='glynn';
  332. GUI='text';
  333. MONITOR='x0';
  334. :param list args: list of unnamed arguments (see start_command() for details)
  335. :param list kwargs: list of named arguments (see start_command() for details)
  336. :return: Popen object
  337. """
  338. kwargs['stdout'] = PIPE
  339. return start_command(*args, **kwargs)
  340. def feed_command(*args, **kwargs):
  341. """Passes all arguments to start_command(), but also adds
  342. "stdin = PIPE". Returns the Popen object.
  343. :param list args: list of unnamed arguments (see start_command() for details)
  344. :param list kwargs: list of named arguments (see start_command() for details)
  345. :return: Popen object
  346. """
  347. kwargs['stdin'] = PIPE
  348. return start_command(*args, **kwargs)
  349. def read_command(*args, **kwargs):
  350. """Passes all arguments to pipe_command, then waits for the process to
  351. complete, returning its stdout (i.e. similar to shell `backticks`).
  352. :param list args: list of unnamed arguments (see start_command() for details)
  353. :param list kwargs: list of named arguments (see start_command() for details)
  354. :return: stdout
  355. """
  356. process = pipe_command(*args, **kwargs)
  357. stdout, unused = process.communicate()
  358. returncode = process.poll()
  359. return handle_errors(returncode, stdout, args, kwargs)
  360. def parse_command(*args, **kwargs):
  361. """Passes all arguments to read_command, then parses the output
  362. by parse_key_val().
  363. Parsing function can be optionally given by <em>parse</em> parameter
  364. including its arguments, e.g.
  365. ::
  366. parse_command(..., parse = (grass.parse_key_val, { 'sep' : ':' }))
  367. or you can simply define <em>delimiter</em>
  368. ::
  369. parse_command(..., delimiter = ':')
  370. :param args: list of unnamed arguments (see start_command() for details)
  371. :param kwargs: list of named arguments (see start_command() for details)
  372. :return: parsed module output
  373. """
  374. parse = None
  375. parse_args = {}
  376. if 'parse' in kwargs:
  377. if type(kwargs['parse']) is types.TupleType:
  378. parse = kwargs['parse'][0]
  379. parse_args = kwargs['parse'][1]
  380. del kwargs['parse']
  381. if 'delimiter' in kwargs:
  382. parse_args = {'sep': kwargs['delimiter']}
  383. del kwargs['delimiter']
  384. if not parse:
  385. parse = parse_key_val # use default fn
  386. res = read_command(*args, **kwargs)
  387. return parse(res, **parse_args)
  388. def write_command(*args, **kwargs):
  389. """Execute a module with standard input given by *stdin* parameter.
  390. Passes all arguments to ``feed_command()``, with the string specified
  391. by the *stdin* argument fed to the process' standard input.
  392. >>> gscript.write_command(
  393. ... 'v.in.ascii', input='-',
  394. ... stdin='%s|%s' % (635818.8, 221342.4),
  395. ... output='view_point')
  396. 0
  397. See ``start_command()`` for details about parameters and usage.
  398. :param *args: unnamed arguments passed to ``start_command()``
  399. :param **kwargs: named arguments passed to ``start_command()``
  400. :returns: 0 with default parameters for backward compatibility only
  401. :raises: ``CalledModuleError`` when module returns non-zero return code
  402. """
  403. # TODO: should we delete it from kwargs?
  404. stdin = kwargs['stdin']
  405. process = feed_command(*args, **kwargs)
  406. process.communicate(stdin)
  407. returncode = process.poll()
  408. return handle_errors(returncode, returncode, args, kwargs)
  409. def exec_command(prog, flags="", overwrite=False, quiet=False, verbose=False,
  410. env=None, **kwargs):
  411. """Interface to os.execvpe(), but with the make_command() interface.
  412. :param str prog: GRASS module
  413. :param str flags: flags to be used (given as a string)
  414. :param bool overwrite: True to enable overwriting the output (<tt>--o</tt>)
  415. :param bool quiet: True to run quietly (<tt>--q</tt>)
  416. :param bool verbose: True to run verbosely (<tt>--v</tt>)
  417. :param env: directory with environmental variables
  418. :param list kwargs: module's parameters
  419. """
  420. args = make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
  421. if env is None:
  422. env = os.environ
  423. os.execvpe(prog, args, env)
  424. # interface to g.message
  425. def message(msg, flag=None):
  426. """Display a message using `g.message`
  427. :param str msg: message to be displayed
  428. :param str flag: flags (given as string)
  429. """
  430. run_command("g.message", flags=flag, message=msg, errors='ignore')
  431. def debug(msg, debug=1):
  432. """Display a debugging message using `g.message -d`
  433. :param str msg: debugging message to be displayed
  434. :param str debug: debug level (0-5)
  435. """
  436. if debug_level() >= debug:
  437. # TODO: quite a random hack here, do we need it somewhere else too?
  438. if sys.platform == "win32":
  439. msg = msg.replace('&', '^&')
  440. run_command("g.message", flags='d', message=msg, debug=debug)
  441. def verbose(msg):
  442. """Display a verbose message using `g.message -v`
  443. :param str msg: verbose message to be displayed
  444. """
  445. message(msg, flag='v')
  446. def info(msg):
  447. """Display an informational message using `g.message -i`
  448. :param str msg: informational message to be displayed
  449. """
  450. message(msg, flag='i')
  451. def percent(i, n, s):
  452. """Display a progress info message using `g.message -p`
  453. ::
  454. message(_("Percent complete..."))
  455. n = 100
  456. for i in range(n):
  457. percent(i, n, 1)
  458. percent(1, 1, 1)
  459. :param int i: current item
  460. :param int n: total number of items
  461. :param int s: increment size
  462. """
  463. message("%d %d %d" % (i, n, s), flag='p')
  464. def warning(msg):
  465. """Display a warning message using `g.message -w`
  466. :param str msg: warning message to be displayed
  467. """
  468. message(msg, flag='w')
  469. def error(msg):
  470. """Display an error message using `g.message -e`
  471. This function does not end the execution of the program.
  472. The right action after the error is up to the caller.
  473. For error handling using the standard mechanism use :func:`fatal()`.
  474. :param str msg: error message to be displayed
  475. """
  476. message(msg, flag='e')
  477. def fatal(msg):
  478. """Display an error message using `g.message -e`, then abort or raise
  479. Raises exception when module global raise_on_error is 'True', abort
  480. (calls exit) otherwise.
  481. Use :func:`set_raise_on_error()` to set the behavior.
  482. :param str msg: error message to be displayed
  483. """
  484. global raise_on_error
  485. if raise_on_error:
  486. raise ScriptError(msg)
  487. error(msg)
  488. sys.exit(1)
  489. def set_raise_on_error(raise_exp=True):
  490. """Define behaviour on fatal error (fatal() called)
  491. :param bool raise_exp: True to raise ScriptError instead of calling
  492. sys.exit(1) in fatal()
  493. :return: current status
  494. """
  495. global raise_on_error
  496. tmp_raise = raise_on_error
  497. raise_on_error = raise_exp
  498. return tmp_raise
  499. def get_raise_on_error():
  500. """Return True if a ScriptError exception is raised instead of calling
  501. sys.exit(1) in case a fatal error was invoked with fatal()
  502. """
  503. global raise_on_error
  504. return raise_on_error
  505. # interface to g.parser
  506. def _parse_opts(lines):
  507. options = {}
  508. flags = {}
  509. for line in lines:
  510. if not line:
  511. break
  512. try:
  513. [var, val] = line.split('=', 1)
  514. except:
  515. raise SyntaxError("invalid output from g.parser: %s" % line)
  516. if var.startswith('flag_'):
  517. flags[var[5:]] = bool(int(val))
  518. elif var.startswith('opt_'):
  519. options[var[4:]] = val
  520. elif var in ['GRASS_OVERWRITE', 'GRASS_VERBOSE']:
  521. os.environ[var] = val
  522. else:
  523. raise SyntaxError("invalid output from g.parser: %s" % line)
  524. return (options, flags)
  525. def parser():
  526. """Interface to g.parser, intended to be run from the top-level, e.g.:
  527. ::
  528. if __name__ == "__main__":
  529. options, flags = grass.parser()
  530. main()
  531. Thereafter, the global variables "options" and "flags" will be
  532. dictionaries containing option/flag values, keyed by lower-case
  533. option/flag names. The values in "options" are strings, those in
  534. "flags" are Python booleans.
  535. """
  536. if not os.getenv("GISBASE"):
  537. print >> sys.stderr, "You must be in GRASS GIS to run this program."
  538. sys.exit(1)
  539. cmdline = [basename(sys.argv[0])]
  540. cmdline += ['"' + arg + '"' for arg in sys.argv[1:]]
  541. os.environ['CMDLINE'] = ' '.join(cmdline)
  542. argv = sys.argv[:]
  543. name = argv[0]
  544. if not os.path.isabs(name):
  545. if os.sep in name or (os.altsep and os.altsep in name):
  546. argv[0] = os.path.abspath(name)
  547. else:
  548. argv[0] = os.path.join(sys.path[0], name)
  549. prog = "g.parser.exe" if sys.platform == "win32" else "g.parser"
  550. p = subprocess.Popen([prog, '-n'] + argv, stdout=subprocess.PIPE)
  551. s = p.communicate()[0]
  552. lines = s.split('\0')
  553. if not lines or lines[0] != "@ARGS_PARSED@":
  554. sys.stdout.write(s)
  555. sys.exit(p.returncode)
  556. return _parse_opts(lines[1:])
  557. # interface to g.tempfile
  558. def tempfile(create=True):
  559. """Returns the name of a temporary file, created with g.tempfile.
  560. :param bool create: True to create a file
  561. :return: path to a tmp file
  562. """
  563. flags = ''
  564. if not create:
  565. flags += 'd'
  566. return read_command("g.tempfile", flags=flags, pid=os.getpid()).strip()
  567. def tempdir():
  568. """Returns the name of a temporary dir, created with g.tempfile."""
  569. tmp = tempfile(create=False)
  570. os.mkdir(tmp)
  571. return tmp
  572. def _compare_projection(dic):
  573. """Check if projection has some possibility of duplicate names like
  574. Universal Transverse Mercator and Universe Transverse Mercator and
  575. unify them
  576. :param dic: The dictionary containing information about projection
  577. :return: The dictionary with the new values if needed
  578. """
  579. # the lookup variable is a list of list, each list contains all the
  580. # possible name for a projection system
  581. lookup = [['Universal Transverse Mercator', 'Universe Transverse Mercator']]
  582. for lo in lookup:
  583. for n in range(len(dic['name'])):
  584. if dic['name'][n] in lo:
  585. dic['name'][n] = lo[0]
  586. return dic
  587. def _compare_units(dic):
  588. """Check if units has some possibility of duplicate names like
  589. meter and metre and unify them
  590. :param dic: The dictionary containing information about units
  591. :return: The dictionary with the new values if needed
  592. """
  593. # the lookup variable is a list of list, each list contains all the
  594. # possible name for a units
  595. lookup = [['meter', 'metre'], ['meters', 'metres'], ['kilometer',
  596. 'kilometre'], ['kilometers', 'kilometres']]
  597. for l in lookup:
  598. for n in range(len(dic['unit'])):
  599. if dic['unit'][n].lower() in l:
  600. dic['unit'][n] = l[0]
  601. for n in range(len(dic['units'])):
  602. if dic['units'][n].lower() in l:
  603. dic['units'][n] = l[0]
  604. return dic
  605. def _text_to_key_value_dict(filename, sep=":", val_sep=",", checkproj=False,
  606. checkunits=False):
  607. """Convert a key-value text file, where entries are separated by newlines
  608. and the key and value are separated by `sep', into a key-value dictionary
  609. and discover/use the correct data types (float, int or string) for values.
  610. :param str filename: The name or name and path of the text file to convert
  611. :param str sep: The character that separates the keys and values, default
  612. is ":"
  613. :param str val_sep: The character that separates the values of a single
  614. key, default is ","
  615. :param bool checkproj: True if it has to check some information about
  616. projection system
  617. :param bool checkproj: True if it has to check some information about units
  618. :return: The dictionary
  619. A text file with this content:
  620. ::
  621. a: Hello
  622. b: 1.0
  623. c: 1,2,3,4,5
  624. d : hello,8,0.1
  625. Will be represented as this dictionary:
  626. ::
  627. {'a': ['Hello'], 'c': [1, 2, 3, 4, 5], 'b': [1.0], 'd': ['hello', 8, 0.1]}
  628. """
  629. text = open(filename, "r").readlines()
  630. kvdict = KeyValue()
  631. for line in text:
  632. if line.find(sep) >= 0:
  633. key, value = line.split(sep)
  634. key = key.strip()
  635. value = value.strip()
  636. else:
  637. # Jump over empty values
  638. continue
  639. values = value.split(val_sep)
  640. value_list = []
  641. for value in values:
  642. not_float = False
  643. not_int = False
  644. # Convert values into correct types
  645. # We first try integer then float
  646. try:
  647. value_converted = int(value)
  648. except:
  649. not_int = True
  650. if not_int:
  651. try:
  652. value_converted = float(value)
  653. except:
  654. not_float = True
  655. if not_int and not_float:
  656. value_converted = value.strip()
  657. value_list.append(value_converted)
  658. kvdict[key] = value_list
  659. if checkproj:
  660. kvdict = _compare_projection(kvdict)
  661. if checkunits:
  662. kvdict = _compare_units(kvdict)
  663. return kvdict
  664. def compare_key_value_text_files(filename_a, filename_b, sep=":",
  665. val_sep=",", precision=0.000001,
  666. proj=False, units=False):
  667. """Compare two key-value text files
  668. This method will print a warning in case keys that are present in the first
  669. file are not present in the second one.
  670. The comparison method tries to convert the values into their native format
  671. (float, int or string) to allow correct comparison.
  672. An example key-value text file may have this content:
  673. ::
  674. a: Hello
  675. b: 1.0
  676. c: 1,2,3,4,5
  677. d : hello,8,0.1
  678. :param str filename_a: name of the first key-value text file
  679. :param str filenmae_b: name of the second key-value text file
  680. :param str sep: character that separates the keys and values, default is ":"
  681. :param str val_sep: character that separates the values of a single key, default is ","
  682. :param double precision: precision with which the floating point values are compared
  683. :param bool proj: True if it has to check some information about projection system
  684. :param bool units: True if it has to check some information about units
  685. :return: True if full or almost identical, False if different
  686. """
  687. dict_a = _text_to_key_value_dict(filename_a, sep, checkproj=proj,
  688. checkunits=units)
  689. dict_b = _text_to_key_value_dict(filename_b, sep, checkproj=proj,
  690. checkunits=units)
  691. if sorted(dict_a.keys()) != sorted(dict_b.keys()):
  692. return False
  693. # We compare matching keys
  694. for key in dict_a.keys():
  695. # Floating point values must be handled separately
  696. if isinstance(dict_a[key], float) and isinstance(dict_b[key], float):
  697. if abs(dict_a[key] - dict_b[key]) > precision:
  698. return False
  699. elif isinstance(dict_a[key], float) or isinstance(dict_b[key], float):
  700. warning(_("Mixing value types. Will try to compare after "
  701. "integer conversion"))
  702. return int(dict_a[key]) == int(dict_b[key])
  703. elif key == "+towgs84":
  704. # We compare the sum of the entries
  705. if abs(sum(dict_a[key]) - sum(dict_b[key])) > precision:
  706. return False
  707. else:
  708. if dict_a[key] != dict_b[key]:
  709. return False
  710. return True
  711. # interface to g.gisenv
  712. def gisenv():
  713. """Returns the output from running g.gisenv (with no arguments), as a
  714. dictionary. Example:
  715. >>> env = gisenv()
  716. >>> print env['GISDBASE'] # doctest: +SKIP
  717. /opt/grass-data
  718. :return: list of GRASS variables
  719. """
  720. s = read_command("g.gisenv", flags='n')
  721. return parse_key_val(s)
  722. # interface to g.region
  723. def locn_is_latlong():
  724. """Tests if location is lat/long. Value is obtained
  725. by checking the "g.region -pu" projection code.
  726. :return: True for a lat/long region, False otherwise
  727. """
  728. s = read_command("g.region", flags='pu')
  729. kv = parse_key_val(s, ':')
  730. if kv['projection'].split(' ')[0] == '3':
  731. return True
  732. else:
  733. return False
  734. def region(region3d=False, complete=False):
  735. """Returns the output from running "g.region -gu", as a
  736. dictionary. Example:
  737. :param bool region3d: True to get 3D region
  738. :param bool complete:
  739. >>> curent_region = region()
  740. >>> # obtain n, s, e and w values
  741. >>> [curent_region[key] for key in "nsew"] # doctest: +ELLIPSIS
  742. [..., ..., ..., ...]
  743. >>> # obtain ns and ew resulutions
  744. >>> (curent_region['nsres'], curent_region['ewres']) # doctest: +ELLIPSIS
  745. (..., ...)
  746. :return: dictionary of region values
  747. """
  748. flgs = 'gu'
  749. if region3d:
  750. flgs += '3'
  751. if complete:
  752. flgs += 'cep'
  753. s = read_command("g.region", flags=flgs)
  754. reg = parse_key_val(s, val_type=float)
  755. for k in ['rows', 'cols', 'cells',
  756. 'rows3', 'cols3', 'cells3', 'depths']:
  757. if k not in reg:
  758. continue
  759. reg[k] = int(reg[k])
  760. return reg
  761. def region_env(region3d=False, **kwargs):
  762. """Returns region settings as a string which can used as
  763. GRASS_REGION environmental variable.
  764. If no 'kwargs' are given then the current region is used. Note
  765. that this function doesn't modify the current region!
  766. See also :func:`use_temp_region()` for alternative method how to define
  767. temporary region used for raster-based computation.
  768. :param bool region3d: True to get 3D region
  769. :param kwargs: g.region's parameters like 'raster', 'vector' or 'region'
  770. ::
  771. os.environ['GRASS_REGION'] = grass.region_env(region='detail')
  772. grass.mapcalc('map=1', overwrite=True)
  773. os.environ.pop('GRASS_REGION')
  774. :return: string with region values
  775. :return: empty string on error
  776. """
  777. # read proj/zone from WIND file
  778. env = gisenv()
  779. windfile = os.path.join(env['GISDBASE'], env['LOCATION_NAME'],
  780. env['MAPSET'], "WIND")
  781. fd = open(windfile, "r")
  782. grass_region = ''
  783. for line in fd.readlines():
  784. key, value = map(lambda x: x.strip(), line.split(":", 1))
  785. if kwargs and key not in ('proj', 'zone'):
  786. continue
  787. if not kwargs and not region3d and \
  788. key in ('top', 'bottom', 'cols3', 'rows3',
  789. 'depths', 'e-w resol3', 'n-s resol3', 't-b resol'):
  790. continue
  791. grass_region += '%s: %s;' % (key, value)
  792. if not kwargs: # return current region
  793. return grass_region
  794. # read other values from `g.region -gu`
  795. flgs = 'ug'
  796. if region3d:
  797. flgs += '3'
  798. s = read_command('g.region', flags=flgs, **kwargs)
  799. if not s:
  800. return ''
  801. reg = parse_key_val(s)
  802. kwdata = [('north', 'n'),
  803. ('south', 's'),
  804. ('east', 'e'),
  805. ('west', 'w'),
  806. ('cols', 'cols'),
  807. ('rows', 'rows'),
  808. ('e-w resol', 'ewres'),
  809. ('n-s resol', 'nsres')]
  810. if region3d:
  811. kwdata += [('top', 't'),
  812. ('bottom', 'b'),
  813. ('cols3', 'cols3'),
  814. ('rows3', 'rows3'),
  815. ('depths', 'depths'),
  816. ('e-w resol3', 'ewres3'),
  817. ('n-s resol3', 'nsres3'),
  818. ('t-b resol', 'tbres')]
  819. for wkey, rkey in kwdata:
  820. grass_region += '%s: %s;' % (wkey, reg[rkey])
  821. return grass_region
  822. def use_temp_region():
  823. """Copies the current region to a temporary region with "g.region save=",
  824. then sets WIND_OVERRIDE to refer to that region. Installs an atexit
  825. handler to delete the temporary region upon termination.
  826. """
  827. name = "tmp.%s.%d" % (os.path.basename(sys.argv[0]), os.getpid())
  828. run_command("g.region", save=name, overwrite=True)
  829. os.environ['WIND_OVERRIDE'] = name
  830. atexit.register(del_temp_region)
  831. def del_temp_region():
  832. """Unsets WIND_OVERRIDE and removes any region named by it."""
  833. try:
  834. name = os.environ.pop('WIND_OVERRIDE')
  835. run_command("g.remove", flags='f', quiet=True, type='region', name=name)
  836. except:
  837. pass
  838. # interface to g.findfile
  839. def find_file(name, element='cell', mapset=None):
  840. """Returns the output from running g.findfile as a
  841. dictionary. Example:
  842. >>> result = find_file('elevation', element='cell')
  843. >>> print result['fullname']
  844. elevation@PERMANENT
  845. >>> print result['file'] # doctest: +ELLIPSIS
  846. /.../PERMANENT/cell/elevation
  847. :param str name: file name
  848. :param str element: element type (default 'cell')
  849. :param str mapset: mapset name (default all mapsets in search path)
  850. :return: parsed output of g.findfile
  851. """
  852. if element == 'raster' or element == 'rast':
  853. verbose(_('Element type should be "cell" and not "%s"') % element)
  854. element = 'cell'
  855. # g.findfile returns non-zero when file was not found
  856. # se we ignore return code and just focus on stdout
  857. process = start_command('g.findfile', flags='n',
  858. element=element, file=name, mapset=mapset,
  859. stdout=PIPE)
  860. stdout = process.communicate()[0]
  861. return parse_key_val(stdout)
  862. # interface to g.list
  863. def list_strings(type, pattern=None, mapset=None, exclude=None, flag=''):
  864. """List of elements as strings.
  865. Returns the output from running g.list, as a list of qualified
  866. names.
  867. :param str type: element type (raster, vector, raster_3d, region, ...)
  868. :param str pattern: pattern string
  869. :param str mapset: mapset name (if not given use search path)
  870. :param str exclude: pattern string to exclude maps from the research
  871. :param str flag: pattern type: 'r' (basic regexp), 'e' (extended regexp),
  872. or '' (glob pattern)
  873. :return: list of elements
  874. """
  875. if type == 'cell':
  876. verbose(_('Element type should be "raster" and not "%s"') % type)
  877. result = list()
  878. for line in read_command("g.list",
  879. quiet=True,
  880. flags='m' + flag,
  881. type=type,
  882. pattern=pattern,
  883. exclude=exclude,
  884. mapset=mapset).splitlines():
  885. result.append(line.strip())
  886. return result
  887. def list_pairs(type, pattern=None, mapset=None, exclude=None, flag=''):
  888. """List of elements as pairs
  889. Returns the output from running g.list, as a list of
  890. (name, mapset) pairs
  891. :param str type: element type (raster, vector, raster_3d, region, ...)
  892. :param str pattern: pattern string
  893. :param str mapset: mapset name (if not given use search path)
  894. :param str exclude: pattern string to exclude maps from the research
  895. :param str flag: pattern type: 'r' (basic regexp), 'e' (extended regexp),
  896. or '' (glob pattern)
  897. :return: list of elements
  898. """
  899. return [tuple(map.split('@', 1)) for map in list_strings(type, pattern,
  900. mapset, exclude,
  901. flag)]
  902. def list_grouped(type, pattern=None, check_search_path=True, exclude=None,
  903. flag=''):
  904. """List of elements grouped by mapsets.
  905. Returns the output from running g.list, as a dictionary where the
  906. keys are mapset names and the values are lists of maps in that
  907. mapset. Example:
  908. >>> list_grouped('vect', pattern='*roads*')['PERMANENT']
  909. ['railroads', 'roadsmajor']
  910. :param str type: element type (raster, vector, raster_3d, region, ...) or list of elements
  911. :param str pattern: pattern string
  912. :param str check_search_path: True to add mapsets for the search path
  913. with no found elements
  914. :param str exclude: pattern string to exclude maps from the research
  915. :param str flag: pattern type: 'r' (basic regexp), 'e' (extended regexp),
  916. or '' (glob pattern)
  917. :return: directory of mapsets/elements
  918. """
  919. if isinstance(type, python_types.StringTypes) or len(type) == 1:
  920. types = [type]
  921. store_types = False
  922. else:
  923. types = type
  924. store_types = True
  925. flag += 't'
  926. for i in range(len(types)):
  927. if types[i] == 'cell':
  928. verbose(_('Element type should be "raster" and not "%s"') % types[i])
  929. types[i] = 'raster'
  930. result = {}
  931. if check_search_path:
  932. for mapset in mapsets(search_path=True):
  933. if store_types:
  934. result[mapset] = {}
  935. else:
  936. result[mapset] = []
  937. mapset = None
  938. for line in read_command("g.list", quiet=True, flags="m" + flag,
  939. type=types, pattern=pattern, exclude=exclude).splitlines():
  940. try:
  941. name, mapset = line.split('@')
  942. except ValueError:
  943. warning(_("Invalid element '%s'") % line)
  944. continue
  945. if store_types:
  946. type_, name = name.split('/')
  947. if mapset in result:
  948. if type_ in result[mapset]:
  949. result[mapset][type_].append(name)
  950. else:
  951. result[mapset][type_] = [name, ]
  952. else:
  953. result[mapset] = {type_: [name, ]}
  954. else:
  955. if mapset in result:
  956. result[mapset].append(name)
  957. else:
  958. result[mapset] = [name, ]
  959. return result
  960. # color parsing
  961. named_colors = {
  962. "white": (1.00, 1.00, 1.00),
  963. "black": (0.00, 0.00, 0.00),
  964. "red": (1.00, 0.00, 0.00),
  965. "green": (0.00, 1.00, 0.00),
  966. "blue": (0.00, 0.00, 1.00),
  967. "yellow": (1.00, 1.00, 0.00),
  968. "magenta": (1.00, 0.00, 1.00),
  969. "cyan": (0.00, 1.00, 1.00),
  970. "aqua": (0.00, 0.75, 0.75),
  971. "grey": (0.75, 0.75, 0.75),
  972. "gray": (0.75, 0.75, 0.75),
  973. "orange": (1.00, 0.50, 0.00),
  974. "brown": (0.75, 0.50, 0.25),
  975. "purple": (0.50, 0.00, 1.00),
  976. "violet": (0.50, 0.00, 1.00),
  977. "indigo": (0.00, 0.50, 1.00)}
  978. def parse_color(val, dflt=None):
  979. """Parses the string "val" as a GRASS colour, which can be either one of
  980. the named colours or an R:G:B tuple e.g. 255:255:255. Returns an
  981. (r,g,b) triple whose components are floating point values between 0
  982. and 1. Example:
  983. >>> parse_color("red")
  984. (1.0, 0.0, 0.0)
  985. >>> parse_color("255:0:0")
  986. (1.0, 0.0, 0.0)
  987. :param val: color value
  988. :param dflt: default color value
  989. :return: tuple RGB
  990. """
  991. if val in named_colors:
  992. return named_colors[val]
  993. vals = val.split(':')
  994. if len(vals) == 3:
  995. return tuple(float(v) / 255 for v in vals)
  996. return dflt
  997. # check GRASS_OVERWRITE
  998. def overwrite():
  999. """Return True if existing files may be overwritten"""
  1000. owstr = 'GRASS_OVERWRITE'
  1001. return owstr in os.environ and os.environ[owstr] != '0'
  1002. # check GRASS_VERBOSE
  1003. def verbosity():
  1004. """Return the verbosity level selected by GRASS_VERBOSE"""
  1005. vbstr = os.getenv('GRASS_VERBOSE')
  1006. if vbstr:
  1007. return int(vbstr)
  1008. else:
  1009. return 2
  1010. ## various utilities, not specific to GRASS
  1011. def find_program(pgm, *args):
  1012. """Attempt to run a program, with optional arguments.
  1013. You must call the program in a way that will return a successful
  1014. exit code. For GRASS modules this means you need to pass it some
  1015. valid CLI option, like "--help". For other programs a common
  1016. valid do-little option is usually "--version".
  1017. Example:
  1018. >>> find_program('r.sun', '--help')
  1019. True
  1020. >>> find_program('ls', '--version')
  1021. True
  1022. :param str pgm: program name
  1023. :param args: list of arguments
  1024. :return: False if the attempt failed due to a missing executable
  1025. or non-zero return code
  1026. :return: True otherwise
  1027. """
  1028. nuldev = file(os.devnull, 'w+')
  1029. try:
  1030. # TODO: the doc or impl is not correct, any return code is accepted
  1031. call([pgm] + list(args), stdin = nuldev, stdout = nuldev, stderr = nuldev)
  1032. found = True
  1033. except:
  1034. found = False
  1035. nuldev.close()
  1036. return found
  1037. # interface to g.mapsets
  1038. def mapsets(search_path=False):
  1039. """List available mapsets
  1040. :param bool search_path: True to list mapsets only in search path
  1041. :return: list of mapsets
  1042. """
  1043. if search_path:
  1044. flags = 'p'
  1045. else:
  1046. flags = 'l'
  1047. mapsets = read_command('g.mapsets',
  1048. flags=flags,
  1049. sep='newline',
  1050. quiet=True)
  1051. if not mapsets:
  1052. fatal(_("Unable to list mapsets"))
  1053. return mapsets.splitlines()
  1054. # interface to `g.proj -c`
  1055. def create_location(dbase, location, epsg=None, proj4=None, filename=None,
  1056. wkt=None, datum=None, datum_trans=None, desc=None,
  1057. overwrite=False):
  1058. """Create new location
  1059. Raise ScriptError on error.
  1060. :param str dbase: path to GRASS database
  1061. :param str location: location name to create
  1062. :param epsg: if given create new location based on EPSG code
  1063. :param proj4: if given create new location based on Proj4 definition
  1064. :param str filename: if given create new location based on georeferenced file
  1065. :param str wkt: if given create new location based on WKT definition
  1066. (path to PRJ file)
  1067. :param datum: GRASS format datum code
  1068. :param datum_trans: datum transformation parameters (used for epsg and proj4)
  1069. :param desc: description of the location (creates MYNAME file)
  1070. :param bool overwrite: True to overwrite location if exists(WARNING:
  1071. ALL DATA from existing location ARE DELETED!)
  1072. """
  1073. gisdbase = None
  1074. if epsg or proj4 or filename or wkt:
  1075. # FIXME: changing GISDBASE mid-session is not background-job safe
  1076. gisdbase = gisenv()['GISDBASE']
  1077. run_command('g.gisenv', set='GISDBASE=%s' % dbase)
  1078. # create dbase if not exists
  1079. if not os.path.exists(dbase):
  1080. os.mkdir(dbase)
  1081. # check if location already exists
  1082. if os.path.exists(os.path.join(dbase, location)):
  1083. if not overwrite:
  1084. warning(_("Location <%s> already exists. Operation canceled.") % location)
  1085. return
  1086. else:
  1087. warning(_("Location <%s> already exists and will be overwritten") % location)
  1088. shutil.rmtree(os.path.join(dbase, location))
  1089. kwargs = dict()
  1090. if datum:
  1091. kwargs['datum'] = datum
  1092. if datum_trans:
  1093. kwargs['datum_trans'] = datum_trans
  1094. if epsg:
  1095. ps = pipe_command('g.proj', quiet=True, flags='t', epsg=epsg,
  1096. location=location, stderr=PIPE, **kwargs)
  1097. elif proj4:
  1098. ps = pipe_command('g.proj', quiet=True, flags='t', proj4=proj4,
  1099. location=location, stderr=PIPE, **kwargs)
  1100. elif filename:
  1101. ps = pipe_command('g.proj', quiet=True, georef=filename,
  1102. location=location, stderr=PIPE)
  1103. elif wkt:
  1104. ps = pipe_command('g.proj', quiet=True, wkt=wkt, location=location,
  1105. stderr=PIPE)
  1106. else:
  1107. _create_location_xy(dbase, location)
  1108. if epsg or proj4 or filename or wkt:
  1109. error = ps.communicate()[1]
  1110. run_command('g.gisenv', set='GISDBASE=%s' % gisdbase)
  1111. if ps.returncode != 0 and error:
  1112. raise ScriptError(repr(error))
  1113. try:
  1114. fd = codecs.open(os.path.join(dbase, location, 'PERMANENT', 'MYNAME'),
  1115. encoding='utf-8', mode='w')
  1116. if desc:
  1117. fd.write(desc + os.linesep)
  1118. else:
  1119. fd.write(os.linesep)
  1120. fd.close()
  1121. except OSError as e:
  1122. raise ScriptError(repr(e))
  1123. def _create_location_xy(database, location):
  1124. """Create unprojected location
  1125. Raise ScriptError on error.
  1126. :param database: GRASS database where to create new location
  1127. :param location: location name
  1128. """
  1129. cur_dir = os.getcwd()
  1130. try:
  1131. os.chdir(database)
  1132. os.mkdir(location)
  1133. os.mkdir(os.path.join(location, 'PERMANENT'))
  1134. # create DEFAULT_WIND and WIND files
  1135. regioninfo = ['proj: 0',
  1136. 'zone: 0',
  1137. 'north: 1',
  1138. 'south: 0',
  1139. 'east: 1',
  1140. 'west: 0',
  1141. 'cols: 1',
  1142. 'rows: 1',
  1143. 'e-w resol: 1',
  1144. 'n-s resol: 1',
  1145. 'top: 1',
  1146. 'bottom: 0',
  1147. 'cols3: 1',
  1148. 'rows3: 1',
  1149. 'depths: 1',
  1150. 'e-w resol3: 1',
  1151. 'n-s resol3: 1',
  1152. 't-b resol: 1']
  1153. defwind = open(os.path.join(location,
  1154. "PERMANENT", "DEFAULT_WIND"), 'w')
  1155. for param in regioninfo:
  1156. defwind.write(param + '%s' % os.linesep)
  1157. defwind.close()
  1158. shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"),
  1159. os.path.join(location, "PERMANENT", "WIND"))
  1160. os.chdir(cur_dir)
  1161. except OSError as e:
  1162. raise ScriptError(repr(e))
  1163. # interface to g.version
  1164. def version():
  1165. """Get GRASS version as dictionary
  1166. ::
  1167. print version()
  1168. {'proj4': '4.8.0', 'geos': '3.3.5', 'libgis_revision': '52468',
  1169. 'libgis_date': '2012-07-27 22:53:30 +0200 (Fri, 27 Jul 2012)',
  1170. 'version': '7.0.svn', 'date': '2012', 'gdal': '2.0dev',
  1171. 'revision': '53670'}
  1172. """
  1173. data = parse_command('g.version', flags='rge', errors='ignore')
  1174. for k, v in data.iteritems():
  1175. data[k.strip()] = v.replace('"', '').strip()
  1176. return data
  1177. # get debug_level
  1178. _debug_level = None
  1179. def debug_level(force=False):
  1180. global _debug_level
  1181. if not force and _debug_level is not None:
  1182. return _debug_level
  1183. _debug_level = 0
  1184. if find_program('g.gisenv', '--help'):
  1185. try:
  1186. _debug_level = int(gisenv().get('DEBUG', 0))
  1187. if _debug_level < 0 or _debug_level > 5:
  1188. raise ValueError(_("Debug level {}").format(_debug_level))
  1189. except ValueError as e:
  1190. _debug_level = 0
  1191. sys.stderr.write(_("WARNING: Ignoring unsupported debug level (must be >=0 and <=5). {}\n").format(e))
  1192. return _debug_level
  1193. def legal_name(s):
  1194. """Checks if the string contains only allowed characters.
  1195. This is the Python implementation of :func:`G_legal_filename()` function.
  1196. ..note::
  1197. It is not clear when exactly use this function, but it might be
  1198. useful anyway for checking map names and column names.
  1199. """
  1200. if not s or s[0] == '.':
  1201. warning(_("Illegal filename <%s>. Cannot be 'NULL' or start with " \
  1202. "'.'.") % s)
  1203. return False
  1204. illegal = [c
  1205. for c in s
  1206. if c in '/"\'@,=*~' or c <= ' ' or c >= '\177']
  1207. if illegal:
  1208. illegal = ''.join(sorted(set(illegal)))
  1209. warning(_("Illegal filename <%(s)s>. <%(il)s> not allowed.\n") % {
  1210. 's': s, 'il': illegal})
  1211. return False
  1212. return True
  1213. if __name__ == '__main__':
  1214. import doctest
  1215. doctest.testmod()