utils.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. # -*- coding: utf-8 -*-
  2. import itertools
  3. import fnmatch
  4. import os
  5. from sqlite3 import OperationalError
  6. import grass.lib.gis as libgis
  7. libgis.G_gisinit('')
  8. import grass.lib.raster as libraster
  9. from grass.script import core as grasscore
  10. from grass.pygrass.errors import GrassError
  11. test_vector_name="Utils_test_vector"
  12. test_raster_name="Utils_test_raster"
  13. def looking(obj, filter_string):
  14. """
  15. >>> import grass.lib.vector as libvect
  16. >>> sorted(looking(libvect, '*by_box*')) # doctest: +NORMALIZE_WHITESPACE
  17. ['Vect_select_areas_by_box', 'Vect_select_isles_by_box',
  18. 'Vect_select_lines_by_box', 'Vect_select_nodes_by_box']
  19. """
  20. word_list = dir(obj)
  21. word_list.sort()
  22. return fnmatch.filter(word_list, filter_string)
  23. def findfiles(dirpath, match=None):
  24. """Return a list of the files"""
  25. res = []
  26. for f in sorted(os.listdir(dirpath)):
  27. abspath = os.path.join(dirpath, f)
  28. if os.path.isdir(abspath):
  29. res.extend(findfiles(abspath, match))
  30. if match:
  31. if fnmatch.fnmatch(abspath, match):
  32. res.append(abspath)
  33. else:
  34. res.append(abspath)
  35. return res
  36. def findmaps(type, pattern=None, mapset='', location='', gisdbase=''):
  37. """Return a list of tuple contining the names of the:
  38. * map
  39. * mapset,
  40. * location,
  41. * gisdbase
  42. """
  43. from grass.pygrass.gis import Gisdbase, Location, Mapset
  44. def find_in_location(type, pattern, location):
  45. res = []
  46. for msetname in location.mapsets():
  47. mset = Mapset(msetname, location.name, location.gisdbase)
  48. res.extend([(m, mset.name, mset.location, mset.gisdbase)
  49. for m in mset.glist(type, pattern)])
  50. return res
  51. def find_in_gisdbase(type, pattern, gisdbase):
  52. res = []
  53. for loc in gisdbase.locations():
  54. res.extend(find_in_location(type, pattern,
  55. Location(loc, gisdbase.name)))
  56. return res
  57. if gisdbase and location and mapset:
  58. mset = Mapset(mapset, location, gisdbase)
  59. return [(m, mset.name, mset.location, mset.gisdbase)
  60. for m in mset.glist(type, pattern)]
  61. elif gisdbase and location:
  62. loc = Location(location, gisdbase)
  63. return find_in_location(type, pattern, loc)
  64. elif gisdbase:
  65. gis = Gisdbase(gisdbase)
  66. return find_in_gisdbase(type, pattern, gis)
  67. elif location:
  68. loc = Location(location)
  69. return find_in_location(type, pattern, loc)
  70. elif mapset:
  71. mset = Mapset(mapset)
  72. return [(m, mset.name, mset.location, mset.gisdbase)
  73. for m in mset.glist(type, pattern)]
  74. else:
  75. gis = Gisdbase()
  76. return find_in_gisdbase(type, pattern, gis)
  77. def remove(oldname, maptype):
  78. """Remove a map"""
  79. grasscore.run_command('g.remove', quiet=True, flags='f',
  80. type=maptype, name=oldname)
  81. def rename(oldname, newname, maptype, **kwargs):
  82. """Rename a map"""
  83. kwargs.update({maptype: '{old},{new}'.format(old=oldname, new=newname), })
  84. grasscore.run_command('g.rename', quiet=True, **kwargs)
  85. def copy(existingmap, newmap, maptype, **kwargs):
  86. """Copy a map
  87. >>> copy(test_vector_name, 'mycensus', 'vector')
  88. >>> rename('mycensus', 'mynewcensus', 'vector')
  89. >>> remove('mynewcensus', 'vector')
  90. """
  91. kwargs.update({maptype: '{old},{new}'.format(old=existingmap, new=newmap)})
  92. grasscore.run_command('g.copy', quiet=True, **kwargs)
  93. def getenv(env):
  94. """Return the current grass environment variables
  95. >>> from grass.script.core import gisenv
  96. >>> getenv("MAPSET") == gisenv()["MAPSET"]
  97. True
  98. """
  99. return libgis.G_getenv_nofatal(env)
  100. def get_mapset_raster(mapname, mapset=''):
  101. """Return the mapset of the raster map
  102. >>> get_mapset_raster(test_raster_name) == getenv("MAPSET")
  103. True
  104. """
  105. return libgis.G_find_raster2(mapname, mapset)
  106. def get_mapset_vector(mapname, mapset=''):
  107. """Return the mapset of the vector map
  108. >>> get_mapset_vector(test_vector_name) == getenv("MAPSET")
  109. True
  110. """
  111. return libgis.G_find_vector2(mapname, mapset)
  112. def is_clean_name(name):
  113. """Return if the name is valid
  114. >>> is_clean_name('census')
  115. True
  116. >>> is_clean_name('0census')
  117. True
  118. >>> is_clean_name('census?')
  119. True
  120. >>> is_clean_name('cénsus')
  121. False
  122. """
  123. if libgis.G_legal_filename(name) < 0:
  124. return False
  125. return True
  126. def coor2pixel(coord, region):
  127. """Convert coordinates into a pixel row and col
  128. >>> from grass.pygrass.gis.region import Region
  129. >>> reg = Region()
  130. >>> coor2pixel((reg.west, reg.north), reg)
  131. (0.0, 0.0)
  132. >>> coor2pixel((reg.east, reg.south), reg) == (reg.rows, reg.cols)
  133. True
  134. """
  135. (east, north) = coord
  136. return (libraster.Rast_northing_to_row(north, region.byref()),
  137. libraster.Rast_easting_to_col(east, region.byref()))
  138. def pixel2coor(pixel, region):
  139. """Convert row and col of a pixel into a coordinates
  140. >>> from grass.pygrass.gis.region import Region
  141. >>> reg = Region()
  142. >>> pixel2coor((0, 0), reg) == (reg.north, reg.west)
  143. True
  144. >>> pixel2coor((reg.cols, reg.rows), reg) == (reg.south, reg.east)
  145. True
  146. """
  147. (col, row) = pixel
  148. return (libraster.Rast_row_to_northing(row, region.byref()),
  149. libraster.Rast_col_to_easting(col, region.byref()))
  150. def get_raster_for_points(poi_vector, raster, column=None, region=None):
  151. """Query a raster map for each point feature of a vector
  152. test_vector_name="Utils_test_vector"
  153. test_raster_name="Utils_test_raster"
  154. Example
  155. >>> from grass.pygrass.vector import VectorTopo
  156. >>> from grass.pygrass.raster import RasterRow
  157. >>> from grass.pygrass.gis.region import Region
  158. >>> region = Region()
  159. >>> region.from_rast(test_raster_name)
  160. >>> region.set_raster_region()
  161. >>> ele = RasterRow(test_raster_name)
  162. >>> copy(test_vector_name,'test_vect_2','vector')
  163. >>> fire = VectorTopo('test_vect_2')
  164. >>> fire.open(mode='r')
  165. >>> l = get_raster_for_points(fire, ele, region=region)
  166. >>> l[0] # doctest: +ELLIPSIS
  167. (1, 620856.9585876337, 230066.3831321055, 111.2153883384)
  168. >>> l[1] # doctest: +ELLIPSIS
  169. (2, 625331.9185974908, 229990.82160762616, 89.978796115200012)
  170. >>> fire.table.columns.add(test_raster_name,'double precision')
  171. >>> test_raster_name in fire.table.columns
  172. True
  173. >>> get_raster_for_points(fire, ele, column=test_raster_name, region=region)
  174. True
  175. >>> fire.table.filters.select('name', test_raster_name)
  176. Filters(u'SELECT name, Utils_test_raster FROM test_vect_2;')
  177. >>> cur = fire.table.execute()
  178. >>> r = cur.fetchall()
  179. >>> r[0] # doctest: +ELLIPSIS
  180. (u'Morrisville #3', 111.2153883384)
  181. >>> r[1] # doctest: +ELLIPSIS
  182. (u'Morrisville #1', 89.97879611520001)
  183. >>> remove('test_vect_2','vect')
  184. :param point: point vector object
  185. :param raster: raster object
  186. :param str column: column name to update
  187. """
  188. from math import isnan
  189. if not column:
  190. result = []
  191. if region is None:
  192. from grass.pygrass.gis.region import Region
  193. region = Region()
  194. if not poi_vector.is_open():
  195. poi_vector.open()
  196. if not raster.is_open():
  197. raster.open()
  198. if poi_vector.num_primitive_of('point') == 0:
  199. raise GrassError(_("Vector doesn't contain points"))
  200. for poi in poi_vector.viter('points'):
  201. val = raster.get_value(poi, region)
  202. if column:
  203. if val is not None and not isnan(val):
  204. poi.attrs[column] = val
  205. else:
  206. if val is not None and not isnan(val):
  207. result.append((poi.id, poi.x, poi.y, val))
  208. else:
  209. result.append((poi.id, poi.x, poi.y, None))
  210. if not column:
  211. return result
  212. else:
  213. poi.attrs.commit()
  214. return True
  215. def r_export(rast, output='', fmt='png', **kargs):
  216. from grass.pygrass.modules import Module
  217. if rast.exist():
  218. output = output if output else "%s_%s.%s" % (rast.name, rast.mapset,
  219. fmt)
  220. Module('r.out.%s' % fmt, input=rast.fullname(), output=output,
  221. overwrite=True, **kargs)
  222. return output
  223. else:
  224. raise ValueError('Raster map does not exist.')
  225. def get_lib_path(modname, libname=None):
  226. """Return the path of the libname contained in the module.
  227. """
  228. from os.path import isdir, join
  229. from os import getenv
  230. if isdir(join(getenv('GISBASE'), 'etc', modname)):
  231. path = join(os.getenv('GISBASE'), 'etc', modname)
  232. elif getenv('GRASS_ADDON_BASE') and libname and \
  233. isdir(join(getenv('GRASS_ADDON_BASE'), 'etc', modname, libname)):
  234. path = join(getenv('GRASS_ADDON_BASE'), 'etc', modname, libname)
  235. elif getenv('GRASS_ADDON_BASE') and \
  236. isdir(join(getenv('GRASS_ADDON_BASE'), 'etc', modname)):
  237. path = join(getenv('GRASS_ADDON_BASE'), 'etc', modname)
  238. elif getenv('GRASS_ADDON_BASE') and \
  239. isdir(join(getenv('GRASS_ADDON_BASE'), modname, modname)):
  240. path = join(os.getenv('GRASS_ADDON_BASE'), modname, modname)
  241. elif libname and isdir(join('..', libname)): # used by g.extension compilation process
  242. path = join('..', libname)
  243. elif isdir(join('..', 'etc', modname)): # used by g.extension compilation process
  244. path = join('..', 'etc', modname)
  245. elif isdir(join('etc', modname)): # used by g.extension compilation process
  246. path = join('etc', modname)
  247. else:
  248. path = None
  249. return path
  250. def set_path(modulename, dirname=None, path='.'):
  251. """Set sys.path looking in the the local directory GRASS directories.
  252. :param modulename: string with the name of the GRASS module
  253. :param dirname: string with the directory name containing the python
  254. libraries, default None
  255. :param path: string with the path to reach the dirname locally.
  256. Example
  257. --------
  258. "set_path" example working locally with the source code of a module
  259. (r.green) calling the function with all the parameters. Below it is
  260. reported the directory structure on the r.green module.
  261. ::
  262. grass_prompt> pwd
  263. ~/Download/r.green/r.green.hydro/r.green.hydro.financial
  264. grass_prompt> tree ../../../r.green
  265. ../../../r.green
  266. ├── ...
  267. ├── libgreen
  268. │ ├── pyfile1.py
  269. │ └── pyfile2.py
  270. └── r.green.hydro
  271. ├── Makefile
  272. ├── libhydro
  273. │ ├── pyfile1.py
  274. │ └── pyfile2.py
  275. ├── r.green.hydro.*
  276. └── r.green.hydro.financial
  277. ├── Makefile
  278. ├── ...
  279. └── r.green.hydro.financial.py
  280. 21 directories, 125 files
  281. in the source code the function is called with the following parameters: ::
  282. set_path('r.green', 'libhydro', '..')
  283. set_path('r.green', 'libgreen', os.path.join('..', '..'))
  284. when we are executing the module: r.green.hydro.financial locally from
  285. the command line: ::
  286. grass_prompt> python r.green.hydro.financial.py --ui
  287. In this way we are executing the local code even if the module was already
  288. installed as grass-addons and it is available in GRASS standards path.
  289. The function is cheching if the dirname is provided and if the
  290. directory exists and it is available using the path
  291. provided as third parameter, if yes add the path to sys.path to be
  292. importable, otherwise it will check on GRASS GIS standard paths.
  293. """
  294. import sys
  295. # TODO: why dirname is checked first - the logic should be revised
  296. pathlib = None
  297. if dirname:
  298. pathlib = os.path.join(path, dirname)
  299. if pathlib and os.path.exists(pathlib):
  300. # we are running the script from the script directory, therefore
  301. # we add the path to sys.path to reach the directory (dirname)
  302. sys.path.append(os.path.abspath(path))
  303. else:
  304. # running from GRASS GIS session
  305. path = get_lib_path(modulename, dirname)
  306. if path is None:
  307. pathname = os.path.join(modulename, dirname) if dirname else modulename
  308. raise ImportError("Not able to find the path '%s' directory "
  309. "(current dir '%s')." % (pathname, os.getcwd()))
  310. sys.path.append(path)
  311. def split_in_chunk(iterable, length=10):
  312. """Split a list in chunk.
  313. >>> for chunk in split_in_chunk(range(25)): print chunk
  314. (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  315. (10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
  316. (20, 21, 22, 23, 24)
  317. >>> for chunk in split_in_chunk(range(25), 3): print chunk
  318. (0, 1, 2)
  319. (3, 4, 5)
  320. (6, 7, 8)
  321. (9, 10, 11)
  322. (12, 13, 14)
  323. (15, 16, 17)
  324. (18, 19, 20)
  325. (21, 22, 23)
  326. (24,)
  327. """
  328. it = iter(iterable)
  329. while True:
  330. chunk = tuple(itertools.islice(it, length))
  331. if not chunk:
  332. return
  333. yield chunk
  334. def table_exist(cursor, table_name):
  335. """Return True if the table exist False otherwise"""
  336. try:
  337. # sqlite
  338. cursor.execute("SELECT name FROM sqlite_master"
  339. " WHERE type='table' AND name='%s';" % table_name)
  340. except OperationalError:
  341. try:
  342. # pg
  343. cursor.execute("SELECT EXISTS(SELECT * FROM "
  344. "information_schema.tables "
  345. "WHERE table_name=%s)" % table_name)
  346. except OperationalError:
  347. return False
  348. one = cursor.fetchone() if cursor else None
  349. return True if one and one[0] else False
  350. def create_test_vector_map(map_name="test_vector"):
  351. """This functions creates a vector map layer with points, lines, boundaries,
  352. centroids, areas, isles and attributes for testing purposes
  353. This should be used in doc and unit tests to create location/mapset
  354. independent vector map layer. This map includes 3 points, 3 lines,
  355. 11 boundaries and 4 centroids. The attribute table contains cat, name
  356. and value columns.
  357. param map_name: The vector map name that should be used
  358. P1 P2 P3
  359. 6 * * *
  360. 5
  361. 4 _______ ___ ___ L1 L2 L3
  362. Y 3 |A1___ *| *| *| | | |
  363. 2 | |A2*| | | | | | |
  364. 1 | |___| |A3 |A4 | | | |
  365. 0 |_______|___|___| | | |
  366. -1
  367. -1 0 1 2 3 4 5 6 7 8 9 10 12 14
  368. X
  369. """
  370. from grass.pygrass.vector import VectorTopo
  371. from grass.pygrass.vector.geometry import Point, Line, Centroid, Boundary
  372. cols = [(u'cat', 'INTEGER PRIMARY KEY'),
  373. (u'name','varchar(50)'),
  374. (u'value', 'double precision')]
  375. with VectorTopo(map_name, mode='w', tab_name=map_name,
  376. tab_cols=cols) as vect:
  377. # Write 3 points
  378. vect.write(Point(10, 6), cat=1, attrs=("point", 1))
  379. vect.write(Point(12, 6), cat=1)
  380. vect.write(Point(14, 6), cat=1)
  381. # Write 3 lines
  382. vect.write(Line([(10, 4), (10, 2), (10,0)]), cat=2, attrs=("line", 2))
  383. vect.write(Line([(12, 4), (12, 2), (12,0)]), cat=2)
  384. vect.write(Line([(14, 4), (14, 2), (14,0)]), cat=2)
  385. # boundaries 1 - 4
  386. vect.write(Boundary(points=[(0, 0), (0,4)]))
  387. vect.write(Boundary(points=[(0, 4), (4,4)]))
  388. vect.write(Boundary(points=[(4, 4), (4,0)]))
  389. vect.write(Boundary(points=[(4, 0), (0,0)]))
  390. # 5. boundary (Isle)
  391. vect.write(Boundary(points=[(1, 1), (1,3), (3, 3), (3,1), (1,1)]))
  392. # boundaries 6 - 8
  393. vect.write(Boundary(points=[(4, 4), (6,4)]))
  394. vect.write(Boundary(points=[(6, 4), (6,0)]))
  395. vect.write(Boundary(points=[(6, 0), (4,0)]))
  396. # boundaries 9 - 11
  397. vect.write(Boundary(points=[(6, 4), (8,4)]))
  398. vect.write(Boundary(points=[(8, 4), (8,0)]))
  399. vect.write(Boundary(points=[(8, 0), (6,0)]))
  400. # Centroids, all have the same cat and attribute
  401. vect.write(Centroid(x=3.5, y=3.5), cat=3, attrs=("centroid", 3))
  402. vect.write(Centroid(x=2.5, y=2.5), cat=3)
  403. vect.write(Centroid(x=5.5, y=3.5), cat=3)
  404. vect.write(Centroid(x=7.5, y=3.5), cat=3)
  405. vect.organization = 'Thuenen Institut'
  406. vect.person = 'Soeren Gebbert'
  407. vect.title = 'Test dataset'
  408. vect.comment = 'This is a comment'
  409. vect.table.conn.commit()
  410. vect.organization = "Thuenen Institut"
  411. vect.person = "Soeren Gebbert"
  412. vect.title = "Test dataset"
  413. vect.comment = "This is a comment"
  414. vect.close()
  415. def create_test_stream_network_map(map_name="streams"):
  416. """
  417. This functions creates a vector map layer with lines that represent
  418. a stream network with two different graphs. The first graph
  419. contains a loop, the second can be used as directed graph.
  420. This should be used in doc and unit tests to create location/mapset
  421. independent vector map layer.
  422. param map_name: The vector map name that should be used
  423. 1(0,2) 3(2,2)
  424. \ /
  425. 1 \ / 2
  426. \ /
  427. 2(1,1)
  428. 6(0,1) || 5(2,1)
  429. 5 \ || / 4
  430. \||/
  431. 4(1,0)
  432. |
  433. | 6
  434. |7(1,-1)
  435. 7(0,-1) 8(2,-1)
  436. \ /
  437. 8 \ / 9
  438. \ /
  439. 9(1, -2)
  440. |
  441. | 10
  442. |
  443. 10(1,-3)
  444. """
  445. from grass.pygrass.vector import VectorTopo
  446. from grass.pygrass.vector.geometry import Line
  447. cols = [(u'cat', 'INTEGER PRIMARY KEY'), (u'id', 'INTEGER')]
  448. with VectorTopo(map_name, mode='w', tab_name=map_name,
  449. tab_cols=cols) as streams:
  450. # First flow graph
  451. l = Line([(0,2), (0.22, 1.75), (0.55, 1.5), (1,1)])
  452. streams.write(l, cat=1, attrs=(1,))
  453. l = Line([(2,2),(1,1)])
  454. streams.write(l, cat=2, attrs=(2,))
  455. l = Line([(1,1), (0.85, 0.5), (1,0)])
  456. streams.write(l, cat=3, attrs=(3,))
  457. l = Line([(2,1),(1,0)])
  458. streams.write(l, cat=4, attrs=(4,))
  459. l = Line([(0,1),(1,0)])
  460. streams.write(l, cat=5, attrs=(5,))
  461. l = Line([(1,0),(1,-1)])
  462. streams.write(l, cat=6, attrs=(6,))
  463. # Reverse line 3
  464. l = Line([(1,0), (1.15, 0.5),(1,1)])
  465. streams.write(l, cat=7, attrs=(7,))
  466. # second flow graph
  467. l = Line([(0,-1),(1,-2)])
  468. streams.write(l, cat=8, attrs=(8,))
  469. l = Line([(2,-1),(1,-2)])
  470. streams.write(l, cat=9, attrs=(9,))
  471. l = Line([(1,-2),(1,-3)])
  472. streams.write(l, cat=10, attrs=(10,))
  473. streams.organization = 'Thuenen Institut'
  474. streams.person = 'Soeren Gebbert'
  475. streams.title = 'Test dataset for stream networks'
  476. streams.comment = 'This is a comment'
  477. streams.table.conn.commit()
  478. streams.close()
  479. if __name__ == "__main__":
  480. import doctest
  481. from grass.pygrass import utils
  482. from grass.script.core import run_command
  483. utils.create_test_vector_map(test_vector_name)
  484. run_command("g.region", n=50, s=0, e=60, w=0, res=1)
  485. run_command("r.mapcalc", expression="%s = 1"%(test_raster_name),
  486. overwrite=True)
  487. doctest.testmod()
  488. """Remove the generated vector map, if exist"""
  489. mset = utils.get_mapset_vector(test_vector_name, mapset='')
  490. if mset:
  491. run_command("g.remove", flags='f', type='vector', name=test_vector_name)
  492. mset = utils.get_mapset_raster(test_raster_name, mapset='')
  493. if mset:
  494. run_command("g.remove", flags='f', type='raster', name=test_raster_name)