setup.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. """Setup, initialization, and clean-up functions
  2. Functions can be used in Python scripts to setup a GRASS environment
  3. and session without using grassXY.
  4. Usage::
  5. import os
  6. import sys
  7. import subprocess
  8. # define GRASS Database
  9. # add your path to grassdata (GRASS GIS database) directory
  10. gisdb = os.path.join(os.path.expanduser("~"), "grassdata")
  11. # the following path is the default path on MS Windows
  12. # gisdb = os.path.join(os.path.expanduser("~"), "Documents/grassdata")
  13. # specify (existing) Location and Mapset
  14. location = "nc_spm_08"
  15. mapset = "user1"
  16. # path to the GRASS GIS launch script
  17. # we assume that the GRASS GIS start script is available and on PATH
  18. # query GRASS itself for its GISBASE
  19. # (with fixes for specific platforms)
  20. # needs to be edited by the user
  21. grass7bin = 'grass79'
  22. if sys.platform.startswith('win'):
  23. # MS Windows
  24. grass7bin = r'C:\OSGeo4W\bin\grass79.bat'
  25. # uncomment when using standalone WinGRASS installer
  26. # grass7bin = r'C:\Program Files (x86)\GRASS GIS 7.9.0\grass79.bat'
  27. # this can be avoided if GRASS executable is added to PATH
  28. elif sys.platform == 'darwin':
  29. # Mac OS X
  30. # TODO: this have to be checked, maybe unix way is good enough
  31. grass7bin = '/Applications/GRASS/GRASS-7.9.app/'
  32. # query GRASS GIS itself for its GISBASE
  33. startcmd = [grass7bin, '--config', 'path']
  34. try:
  35. p = subprocess.Popen(startcmd, shell=False,
  36. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  37. out, err = p.communicate()
  38. except OSError as error:
  39. sys.exit("ERROR: Cannot find GRASS GIS start script"
  40. " {cmd}: {error}".format(cmd=startcmd[0], error=error))
  41. if p.returncode != 0:
  42. sys.exit("ERROR: Issues running GRASS GIS start script"
  43. " {cmd}: {error}"
  44. .format(cmd=' '.join(startcmd), error=err))
  45. gisbase = out.strip(os.linesep)
  46. # set GISBASE environment variable
  47. os.environ['GISBASE'] = gisbase
  48. # define GRASS-Python environment
  49. grass_pydir = os.path.join(gisbase, "etc", "python")
  50. sys.path.append(grass_pydir)
  51. # import (some) GRASS Python bindings
  52. import grass.script as gscript
  53. import grass.script.setup as gsetup
  54. # launch session
  55. rcfile = gsetup.init(gisbase, gisdb, location, mapset)
  56. # example calls
  57. gscript.message('Current GRASS GIS 7 environment:')
  58. print gscript.gisenv()
  59. gscript.message('Available raster maps:')
  60. for rast in gscript.list_strings(type='raster'):
  61. print rast
  62. gscript.message('Available vector maps:')
  63. for vect in gscript.list_strings(type='vector'):
  64. print vect
  65. # clean up at the end
  66. gsetup.cleanup()
  67. (C) 2010-2019 by the GRASS Development Team
  68. This program is free software under the GNU General Public
  69. License (>=v2). Read the file COPYING that comes with GRASS
  70. for details.
  71. @author Martin Landa <landa.martin gmail.com>
  72. @author Vaclav Petras <wenzeslaus gmail.com>
  73. @author Markus Metz
  74. """
  75. # TODO: this should share code from lib/init/grass.py
  76. # perhaps grass.py can import without much trouble once GISBASE
  77. # is known, this would allow moving things from there, here
  78. # then this could even do locking
  79. import os
  80. import sys
  81. import tempfile as tmpfile
  82. windows = sys.platform == 'win32'
  83. def write_gisrc(dbase, location, mapset):
  84. """Write the ``gisrc`` file and return its path."""
  85. gisrc = tmpfile.mktemp()
  86. with open(gisrc, 'w') as rc:
  87. rc.write("GISDBASE: %s\n" % dbase)
  88. rc.write("LOCATION_NAME: %s\n" % location)
  89. rc.write("MAPSET: %s\n" % mapset)
  90. return gisrc
  91. def set_gui_path():
  92. """Insert wxPython GRASS path to sys.path."""
  93. gui_path = os.path.join(os.environ['GISBASE'], 'gui', 'wxpython')
  94. if gui_path and gui_path not in sys.path:
  95. sys.path.insert(0, gui_path)
  96. def init(gisbase, dbase='', location='demolocation', mapset='PERMANENT'):
  97. """Initialize system variables to run GRASS modules
  98. This function is for running GRASS GIS without starting it with the
  99. standard script grassXY. No GRASS modules shall be called before
  100. call of this function but any module or user script can be called
  101. afterwards because a GRASS session has been set up. GRASS Python
  102. libraries are usable as well in general but the ones using C
  103. libraries through ``ctypes`` are not (which is caused by library
  104. path not being updated for the current process which is a common
  105. operating system limitation).
  106. To create a GRASS session a ``gisrc`` file is created.
  107. Caller is responsible for deleting the ``gisrc`` file.
  108. Basic usage::
  109. # ... setup GISBASE and PYTHON path before import
  110. import grass.script as gscript
  111. gisrc = gscript.setup.init("/usr/bin/grass7",
  112. "/home/john/grassdata",
  113. "nc_spm_08", "user1")
  114. # ... use GRASS modules here
  115. # end the session
  116. gscript.setup.finish()
  117. :param gisbase: path to GRASS installation
  118. :param dbase: path to GRASS database (default: '')
  119. :param location: location name (default: 'demolocation')
  120. :param mapset: mapset within given location (default: 'PERMANENT')
  121. :returns: path to ``gisrc`` file (to be deleted later)
  122. """
  123. # Set GISBASE
  124. os.environ['GISBASE'] = gisbase
  125. mswin = sys.platform.startswith('win')
  126. # define PATH
  127. os.environ['PATH'] += os.pathsep + os.path.join(gisbase, 'bin')
  128. os.environ['PATH'] += os.pathsep + os.path.join(gisbase, 'scripts')
  129. if mswin: # added for winGRASS
  130. os.environ['PATH'] += os.pathsep + os.path.join(gisbase, 'extrabin')
  131. # add addons to the PATH
  132. # copied and simplified from lib/init/grass.py
  133. if mswin:
  134. config_dirname = "GRASS7"
  135. config_dir = os.path.join(os.getenv('APPDATA'), config_dirname)
  136. else:
  137. config_dirname = ".grass7"
  138. config_dir = os.path.join(os.getenv('HOME'), config_dirname)
  139. addon_base = os.path.join(config_dir, 'addons')
  140. os.environ['GRASS_ADDON_BASE'] = addon_base
  141. if not mswin:
  142. os.environ['PATH'] += os.pathsep + os.path.join(addon_base, 'scripts')
  143. os.environ['PATH'] += os.pathsep + os.path.join(addon_base, 'bin')
  144. # define LD_LIBRARY_PATH
  145. if '@LD_LIBRARY_PATH_VAR@' not in os.environ:
  146. os.environ['@LD_LIBRARY_PATH_VAR@'] = ''
  147. os.environ['@LD_LIBRARY_PATH_VAR@'] += os.pathsep + os.path.join(gisbase, 'lib')
  148. # TODO: lock the mapset?
  149. os.environ['GIS_LOCK'] = str(os.getpid())
  150. # Set GRASS_PYTHON and PYTHONPATH to find GRASS Python modules
  151. if not os.getenv('GRASS_PYTHON'):
  152. if sys.platform == 'win32':
  153. os.environ['GRASS_PYTHON'] = "python.exe"
  154. else:
  155. os.environ['GRASS_PYTHON'] = "python"
  156. path = os.getenv('PYTHONPATH')
  157. etcpy = os.path.join(gisbase, 'etc', 'python')
  158. if path:
  159. path = etcpy + os.pathsep + path
  160. else:
  161. path = etcpy
  162. os.environ['PYTHONPATH'] = path
  163. # TODO: isn't this contra-productive? may fail soon since we cannot
  164. # write to the installation (applies also to defaults for Location
  165. # and mapset) I don't see what would be the use case here.
  166. if not dbase:
  167. dbase = gisbase
  168. os.environ['GISRC'] = write_gisrc(dbase, location, mapset)
  169. return os.environ['GISRC']
  170. # clean-up functions when terminating a GRASS session
  171. # these fns can only be called within a valid GRASS session
  172. def clean_default_db():
  173. # clean the default db if it is sqlite
  174. from grass.script import db as gdb
  175. from grass.script import core as gcore
  176. conn = gdb.db_connection()
  177. if conn and conn['driver'] == 'sqlite':
  178. # check if db exists
  179. gisenv = gcore.gisenv()
  180. database = conn['database']
  181. database = database.replace('$GISDBASE', gisenv['GISDBASE'])
  182. database = database.replace('$LOCATION_NAME', gisenv['LOCATION_NAME'])
  183. database = database.replace('$MAPSET', gisenv['MAPSET'])
  184. if os.path.exists(database):
  185. gcore.message(_("Cleaning up default sqlite database ..."))
  186. gcore.start_command('db.execute', sql = 'VACUUM')
  187. # give it some time to start
  188. import time
  189. time.sleep(0.1)
  190. def call(cmd, **kwargs):
  191. import subprocess
  192. """Wrapper for subprocess.call to deal with platform-specific issues"""
  193. if windows:
  194. kwargs['shell'] = True
  195. return subprocess.call(cmd, **kwargs)
  196. def clean_temp():
  197. from grass.script import core as gcore
  198. gcore.message(_("Cleaning up temporary files..."))
  199. nul = open(os.devnull, 'w')
  200. gisbase = os.environ['GISBASE']
  201. call([os.path.join(gisbase, "etc", "clean_temp")], stdout=nul)
  202. nul.close()
  203. def finish():
  204. """Terminate the GRASS session and clean up
  205. GRASS commands can no longer be used after this function has been
  206. called
  207. Basic usage::
  208. import grass.script as gscript
  209. gscript.setup.cleanup()
  210. """
  211. clean_default_db()
  212. clean_temp()
  213. # TODO: unlock the mapset?
  214. # unset the GISRC and delete the file
  215. from grass.script import utils as gutils
  216. gutils.try_remove(os.environ['GISRC'])
  217. os.environ.pop('GISRC')
  218. # remove gislock env var (not the gislock itself
  219. os.environ.pop('GIS_LOCK')