utils.py 18 KB

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