utils.py 18 KB

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