utils.py 18 KB

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