c_libraries_interface.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. # -*- coding: utf-8 -*-
  2. """@package grass.pygrass.massages
  3. @brief Temporal Framework GRASS C-library interface
  4. Fast and exit-safe interface to GRASS C-library functions
  5. using ctypes and multiprocessing
  6. (C) 2013 by the GRASS Development Team
  7. This program is free software under the GNU General Public
  8. License (>=v2). Read the file COPYING that comes with GRASS
  9. for details.
  10. @author Soeren Gebbert
  11. """
  12. import sys
  13. from multiprocessing import Process, Lock, Pipe
  14. import logging
  15. from ctypes import *
  16. from core import *
  17. import core as corefunc
  18. import grass.lib.gis as libgis
  19. import grass.lib.raster as libraster
  20. import grass.lib.vector as libvector
  21. import grass.lib.date as libdate
  22. import grass.lib.raster3d as libraster3d
  23. import grass.lib.temporal as libtgis
  24. ###############################################################################
  25. class RPCDefs(object):
  26. # Function identifier and index
  27. STOP=0
  28. HAS_TIMESTAMP=1
  29. WRITE_TIMESTAMP=2
  30. READ_TIMESTAMP=3
  31. REMOVE_TIMESTAMP=4
  32. READ_MAP_INFO=5
  33. MAP_EXISTS=6
  34. READ_MAP_INFO=7
  35. AVAILABLE_MAPSETS = 8
  36. GET_DRIVER_NAME = 9
  37. GET_DATABASE_NAME = 10
  38. TYPE_RASTER=0
  39. TYPE_RASTER3D=1
  40. TYPE_VECTOR=2
  41. ###############################################################################
  42. def _get_driver_name(lock, conn, data):
  43. """Return the temporal database driver of a specific mapset
  44. :param lock: A multiprocessing.Lock instance
  45. :param conn: A multiprocessing.Pipe instance used to send True or False
  46. :param data: The mapset as list entry 1 [function_id, mapset]
  47. :returns: Name of the driver or None if no temporal database present
  48. """
  49. drstring = libtgis.tgis_get_mapset_driver_name(data[1])
  50. conn.send(drstring)
  51. ###############################################################################
  52. def _get_database_name(lock, conn, data):
  53. """Return the temporal database name of a specific mapset
  54. :param lock: A multiprocessing.Lock instance
  55. :param conn: A multiprocessing.Pipe instance used to send True or False
  56. :param data: The mapset as list entry 1 [function_id, mapset]
  57. :returns: Name of the database or None if no temporal database present
  58. """
  59. dbstring = libtgis.tgis_get_mapset_database_name(data[1])
  60. if dbstring:
  61. # We substitute GRASS variables if they are located in the database string
  62. # This behavior is in conjunction with db.connect
  63. dbstring = dbstring.replace("$GISDBASE", corefunc.current_gisdbase)
  64. dbstring = dbstring.replace("$LOCATION_NAME", corefunc.current_location)
  65. dbstring = dbstring.replace("$MAPSET", corefunc.current_mapset)
  66. conn.send(dbstring)
  67. ###############################################################################
  68. def _available_mapsets(lock, conn, data):
  69. """Return all available mapsets the user can access as a list of strings
  70. :param lock: A multiprocessing.Lock instance
  71. :param conn: A multiprocessing.Pipe instance used to send True or False
  72. :param data: The list of data entries [function_id]
  73. :returns: Names of available mapsets as list of strings
  74. """
  75. mapsets = libgis.G_get_available_mapsets()
  76. count = 0
  77. mapset_list = []
  78. while mapsets[count]:
  79. char_list = ""
  80. mapset = mapsets[count]
  81. if libgis.G__mapset_permissions(mapset) > 0:
  82. count += 1
  83. c = 0
  84. while mapset[c] != "\x00":
  85. char_list += mapset[c]
  86. c += 1
  87. mapset_list.append(char_list)
  88. # We need to sort the mapset list, but the first one should be
  89. # the current mapset
  90. current_mapset = libgis.G_mapset()
  91. mapset_list.remove(current_mapset)
  92. mapset_list.sort()
  93. mapset_list.reverse()
  94. mapset_list.append(current_mapset)
  95. mapset_list.reverse()
  96. conn.send(mapset_list)
  97. def _has_timestamp(lock, conn, data):
  98. """Check if the file based GRASS timestamp is present and send
  99. True or False using the provided pipe.
  100. :param lock: A multiprocessing.Lock instance
  101. :param conn: A multiprocessing.Pipe instance used to send True or False
  102. :param data: The list of data entries [function_id, maptype, name, mapset, layer]
  103. """
  104. maptype = data[1]
  105. name = data[2]
  106. mapset = data[3]
  107. layer= data[4]
  108. check = False
  109. if maptype == RPCDefs.TYPE_RASTER:
  110. if libgis.G_has_raster_timestamp(name, mapset) == 1:
  111. check = True
  112. elif maptype == RPCDefs.TYPE_VECTOR:
  113. if libgis.G_has_vector_timestamp(name, layer, mapset) == 1:
  114. check = True
  115. elif maptype == RPCDefs.TYPE_RASTER3D:
  116. if libgis.G_has_raster3d_timestamp(name, mapset) == 1:
  117. check = True
  118. conn.send(check)
  119. ###############################################################################
  120. def _read_timestamp(lock, conn, data):
  121. """Read the file based GRASS timestamp and send
  122. the result using the provided pipe.
  123. The tuple to be send via pipe: (return value of G_read_*_timestamp, timestamps).
  124. Please have a look at the documentation of G_read_raster_timestamp,
  125. G_read_vector_timestamp and G_read_raster3d_timestamp for the return
  126. values description.
  127. The timestamps to be send are tuples of values:
  128. - relative time (start, end, unit), start and end are of type
  129. integer, unit is of type string.
  130. - absolute time (start, end), start and end are of type datetime
  131. The end time may be None in case of a time instance.
  132. :param lock: A multiprocessing.Lock instance
  133. :param conn: A multiprocessing.Pipe instance used to send the result
  134. :param data: The list of data entries [function_id, maptype, name, mapset, layer]
  135. """
  136. maptype = data[1]
  137. name = data[2]
  138. mapset = data[3]
  139. layer= data[4]
  140. check = False
  141. ts = libgis.TimeStamp()
  142. if maptype == RPCDefs.TYPE_RASTER:
  143. check = libgis.G_read_raster_timestamp(name, mapset, byref(ts))
  144. elif maptype == RPCDefs.TYPE_VECTOR:
  145. check = libgis.G_read_vector_timestamp(name, layer, mapset, byref(ts))
  146. elif maptype == RPCDefs.TYPE_RASTER3D:
  147. check = libgis.G_read_raster3d_timestamp(name, mapset, byref(ts))
  148. dates = _convert_timestamp_from_grass(ts)
  149. conn.send((check, dates))
  150. ###############################################################################
  151. def _write_timestamp(lock, conn, data):
  152. """Write the file based GRASS timestamp
  153. the return values of the called C-functions using the provided pipe.
  154. The value to be send via pipe is the return value of G_write_*_timestamp.
  155. Please have a look at the documentation of G_write_raster_timestamp,
  156. G_write_vector_timestamp and G_write_raster3d_timestamp for the return
  157. values description.
  158. :param lock: A multiprocessing.Lock instance
  159. :param conn: A multiprocessing.Pipe instance used to send True or False
  160. :param data: The list of data entries [function_id, maptype, name, mapset, layer, timestring]
  161. """
  162. maptype = data[1]
  163. name = data[2]
  164. mapset = data[3]
  165. layer= data[4]
  166. timestring = data[5]
  167. check = -3
  168. ts = libgis.TimeStamp()
  169. check = libgis.G_scan_timestamp(byref(ts), timestring)
  170. if check != 1:
  171. logging.error("Unable to convert the timestamp: "+ timestring)
  172. return -2
  173. if maptype == RPCDefs.TYPE_RASTER:
  174. check = libgis.G_write_raster_timestamp(name, byref(ts))
  175. elif maptype == RPCDefs.TYPE_VECTOR:
  176. check = libgis.G_write_vector_timestamp(name, layer, byref(ts))
  177. elif maptype == RPCDefs.TYPE_RASTER3D:
  178. check = libgis.G_write_raster3d_timestamp(name, byref(ts))
  179. conn.send(check)
  180. ###############################################################################
  181. def _remove_timestamp(lock, conn, data):
  182. """Remove the file based GRASS timestamp
  183. the return values of the called C-functions using the provided pipe.
  184. The value to be send via pipe is the return value of G_remove_*_timestamp.
  185. Please have a look at the documentation of G_remove_raster_timestamp,
  186. G_remove_vector_timestamp and G_remove_raster3d_timestamp for the return
  187. values description.
  188. :param lock: A multiprocessing.Lock instance
  189. :param conn: A multiprocessing.Pipe instance used to send True or False
  190. :param data: The list of data entries [function_id, maptype, name, mapset, layer]
  191. """
  192. maptype = data[1]
  193. name = data[2]
  194. mapset = data[3]
  195. layer= data[4]
  196. check = False
  197. if maptype == RPCDefs.TYPE_RASTER:
  198. check = libgis.G_remove_raster_timestamp(name, mapset)
  199. elif maptype == RPCDefs.TYPE_VECTOR:
  200. check = libgis.G_remove_vector_timestamp(name, layer, mapset)
  201. elif maptype == RPCDefs.TYPE_RASTER3D:
  202. check = libgis.G_remove_raster3d_timestamp(name, mapset)
  203. conn.send(check)
  204. ###############################################################################
  205. def _map_exists(lock, conn, data):
  206. """Check if a map exists in the spatial database
  207. The value to be send via pipe is True in case the map exists and False
  208. if not.
  209. :param lock: A multiprocessing.Lock instance
  210. :param conn: A multiprocessing.Pipe instance used to send True or False
  211. :param data: The list of data entries [function_id, maptype, name, mapset]
  212. """
  213. maptype = data[1]
  214. name = data[2]
  215. mapset = data[3]
  216. check = False
  217. if maptype == RPCDefs.TYPE_RASTER:
  218. mapset = libgis.G_find_raster(name, mapset)
  219. elif maptype == RPCDefs.TYPE_VECTOR:
  220. mapset = libgis.G_find_vector(name, mapset)
  221. elif maptype == RPCDefs.TYPE_RASTER3D:
  222. mapset = libgis.G_find_raster3d(name, mapset)
  223. if mapset:
  224. check = True
  225. conn.send(check)
  226. ###############################################################################
  227. def _read_map_info(lock, conn, data):
  228. """Read map specific metadata from the spatial database using C-library
  229. functions
  230. :param lock: A multiprocessing.Lock instance
  231. :param conn: A multiprocessing.Pipe instance used to send True or False
  232. :param data: The list of data entries [function_id, maptype, name, mapset]
  233. """
  234. maptype = data[1]
  235. name = data[2]
  236. mapset = data[3]
  237. if maptype == RPCDefs.TYPE_RASTER:
  238. kvp = _read_raster_info(name, mapset)
  239. elif maptype == RPCDefs.TYPE_VECTOR:
  240. kvp = _read_vector_info(name, mapset)
  241. elif maptype == RPCDefs.TYPE_RASTER3D:
  242. kvp = _read_raster3d_info(name, mapset)
  243. conn.send(kvp)
  244. ###############################################################################
  245. def _read_raster_info(name, mapset):
  246. """Read the raster map info from the file system and store the content
  247. into a dictionary
  248. This method uses the ctypes interface to the gis and raster libraries
  249. to read the map metadata information
  250. :param name: The name of the map
  251. :param mapset: The mapset of the map
  252. :returns: The key value pairs of the map specific metadata, or None in case of an error
  253. """
  254. kvp = {}
  255. if not libgis.G_find_raster(name, mapset):
  256. return None
  257. # Read the region information
  258. region = libgis.Cell_head()
  259. libraster.Rast_get_cellhd(name, mapset, byref(region))
  260. kvp["north"] = region.north
  261. kvp["south"] = region.south
  262. kvp["east"] = region.east
  263. kvp["west"] = region.west
  264. kvp["nsres"] = region.ns_res
  265. kvp["ewres"] = region.ew_res
  266. kvp["rows"] = region.cols
  267. kvp["cols"] = region.rows
  268. maptype = libraster.Rast_map_type(name, mapset)
  269. if maptype == libraster.DCELL_TYPE:
  270. kvp["datatype"] = "DCELL"
  271. elif maptype == libraster.FCELL_TYPE:
  272. kvp["datatype"] = "FCELL"
  273. elif maptype == libraster.CELL_TYPE:
  274. kvp["datatype"] = "CELL"
  275. # Read range
  276. if libraster.Rast_map_is_fp(name, mapset):
  277. range = libraster.FPRange()
  278. libraster.Rast_init_fp_range(byref(range))
  279. ret = libraster.Rast_read_fp_range(name, mapset, byref(range))
  280. if ret < 0:
  281. logging.error(_("Unable to read range file"))
  282. return None
  283. if ret == 2:
  284. kvp["min"] = None
  285. kvp["max"] = None
  286. else:
  287. min = libgis.DCELL()
  288. max = libgis.DCELL()
  289. libraster.Rast_get_fp_range_min_max(
  290. byref(range), byref(min), byref(max))
  291. kvp["min"] = min.value
  292. kvp["max"] = max.value
  293. else:
  294. range = libraster.Range()
  295. libraster.Rast_init_range(byref(range))
  296. ret = libraster.Rast_read_range(name, mapset, byref(range))
  297. if ret < 0:
  298. logging.error(_("Unable to read range file"))
  299. return None
  300. if ret == 2:
  301. kvp["min"] = None
  302. kvp["max"] = None
  303. else:
  304. min = libgis.CELL()
  305. max = libgis.CELL()
  306. libraster.Rast_get_range_min_max(
  307. byref(range), byref(min), byref(max))
  308. kvp["min"] = min.value
  309. kvp["max"] = max.value
  310. return kvp
  311. ###############################################################################
  312. def _read_raster3d_info(name, mapset):
  313. """Read the 3D raster map info from the file system and store the content
  314. into a dictionary
  315. This method uses the ctypes interface to the gis and raster3d libraries
  316. to read the map metadata information
  317. :param name: The name of the map
  318. :param mapset: The mapset of the map
  319. :returns: The key value pairs of the map specific metadata, or None in case of an error
  320. """
  321. kvp = {}
  322. if not libgis.G_find_raster3d(name, mapset):
  323. return None
  324. # Read the region information
  325. region = libraster3d.RASTER3D_Region()
  326. libraster3d.Rast3d_read_region_map(name, mapset, byref(region))
  327. kvp["north"] = region.north
  328. kvp["south"] = region.south
  329. kvp["east"] = region.east
  330. kvp["west"] = region.west
  331. kvp["nsres"] = region.ns_res
  332. kvp["ewres"] = region.ew_res
  333. kvp["tbres"] = region.tb_res
  334. kvp["rows"] = region.cols
  335. kvp["cols"] = region.rows
  336. kvp["depths"] = region.depths
  337. kvp["top"] = region.top
  338. kvp["bottom"] = region.bottom
  339. # We need to open the map, this function returns a void pointer
  340. # but we may need the correct type which is RASTER3D_Map, hence
  341. # the casting
  342. g3map = cast(libraster3d.Rast3d_open_cell_old(name, mapset,
  343. libraster3d.RASTER3D_DEFAULT_WINDOW,
  344. libraster3d.RASTER3D_TILE_SAME_AS_FILE,
  345. libraster3d.RASTER3D_NO_CACHE),
  346. POINTER(libraster3d.RASTER3D_Map))
  347. if not g3map:
  348. logging.error(_("Unable to open 3D raster map <%s>" % (name)))
  349. return None
  350. maptype = libraster3d.Rast3d_file_type_map(g3map)
  351. if maptype == libraster.DCELL_TYPE:
  352. kvp["datatype"] = "DCELL"
  353. elif maptype == libraster.FCELL_TYPE:
  354. kvp["datatype"] = "FCELL"
  355. # Read range
  356. min = libgis.DCELL()
  357. max = libgis.DCELL()
  358. ret = libraster3d.Rast3d_range_load(g3map)
  359. if not ret:
  360. logging.error(_("Unable to load range of 3D raster map <%s>" %
  361. (name)))
  362. return None
  363. libraster3d.Rast3d_range_min_max(g3map, byref(min), byref(max))
  364. if min.value != min.value:
  365. kvp["min"] = None
  366. else:
  367. kvp["min"] = float(min.value)
  368. if max.value != max.value:
  369. kvp["max"] = None
  370. else:
  371. kvp["max"] = float(max.value)
  372. if not libraster3d.Rast3d_close(g3map):
  373. logging.error(_("Unable to close 3D raster map <%s>" % (name)))
  374. return None
  375. return kvp
  376. ###############################################################################
  377. def _read_vector_info(name, mapset):
  378. """Read the vector map info from the file system and store the content
  379. into a dictionary
  380. This method uses the ctypes interface to the vector libraries
  381. to read the map metadata information
  382. :param name: The name of the map
  383. :param mapset: The mapset of the map
  384. :returns: The key value pairs of the map specific metadata, or None in case of an error
  385. """
  386. kvp = {}
  387. if not libgis.G_find_vector(name, mapset):
  388. return None
  389. # The vector map structure
  390. Map = libvector.Map_info()
  391. # We open the maps always in topology mode first
  392. libvector.Vect_set_open_level(2)
  393. with_topo = True
  394. # Code lend from v.info main.c
  395. if libvector.Vect_open_old_head2(byref(Map), name, mapset, "1") < 2:
  396. # force level 1, open fully
  397. # NOTE: number of points, lines, boundaries, centroids,
  398. # faces, kernels is still available
  399. libvector.Vect_set_open_level(1) # no topology
  400. with_topo = False
  401. if libvector.Vect_open_old2(byref(Map), name, mapset, "1") < 1:
  402. logging.error(_("Unable to open vector map <%s>" %
  403. (libvector.Vect_get_full_name(byref(Map)))))
  404. return None
  405. # Release the vector spatial index memory when closed
  406. libvector.Vect_set_release_support(byref(Map))
  407. # Read the extent information
  408. bbox = libvector.bound_box()
  409. libvector.Vect_get_map_box(byref(Map), byref(bbox))
  410. kvp["north"] = bbox.N
  411. kvp["south"] = bbox.S
  412. kvp["east"] = bbox.E
  413. kvp["west"] = bbox.W
  414. kvp["top"] = bbox.T
  415. kvp["bottom"] = bbox.B
  416. kvp["map3d"] = bool(libvector.Vect_is_3d(byref(Map)))
  417. # Read number of features
  418. if with_topo:
  419. kvp["points"] = libvector.Vect_get_num_primitives(
  420. byref(Map), libvector.GV_POINT)
  421. kvp["lines"] = libvector.Vect_get_num_primitives(
  422. byref(Map), libvector.GV_LINE)
  423. kvp["boundaries"] = libvector.Vect_get_num_primitives(
  424. byref(Map), libvector.GV_BOUNDARY)
  425. kvp["centroids"] = libvector.Vect_get_num_primitives(
  426. byref(Map), libvector.GV_CENTROID)
  427. kvp["faces"] = libvector.Vect_get_num_primitives(
  428. byref(Map), libvector.GV_FACE)
  429. kvp["kernels"] = libvector.Vect_get_num_primitives(
  430. byref(Map), libvector.GV_KERNEL)
  431. # Summarize the primitives
  432. kvp["primitives"] = kvp["points"] + kvp["lines"] + \
  433. kvp["boundaries"] + kvp["centroids"]
  434. if kvp["map3d"]:
  435. kvp["primitives"] += kvp["faces"] + kvp["kernels"]
  436. # Read topology information
  437. kvp["nodes"] = libvector.Vect_get_num_nodes(byref(Map))
  438. kvp["areas"] = libvector.Vect_get_num_areas(byref(Map))
  439. kvp["islands"] = libvector.Vect_get_num_islands(byref(Map))
  440. kvp["holes"] = libvector.Vect_get_num_holes(byref(Map))
  441. kvp["volumes"] = libvector.Vect_get_num_primitives(
  442. byref(Map), libvector.GV_VOLUME)
  443. else:
  444. kvp["points"] = None
  445. kvp["lines"] = None
  446. kvp["boundaries"] = None
  447. kvp["centroids"] = None
  448. kvp["faces"] = None
  449. kvp["kernels"] = None
  450. kvp["primitives"] = None
  451. kvp["nodes"] = None
  452. kvp["areas"] = None
  453. kvp["islands"] = None
  454. kvp["holes"] = None
  455. kvp["volumes"] = None
  456. libvector.Vect_close(byref(Map))
  457. return kvp
  458. ###############################################################################
  459. def _convert_timestamp_from_grass(ts):
  460. """Convert a GRASS file based timestamp into the temporal framework
  461. format datetime or integer.
  462. A tuple of two datetime objects (start, end) is returned in case of absolute time.
  463. In case of relative time a tuple with start time, end time and the
  464. relative unit (start, end, unit) will be returned.
  465. Note:
  466. The end time will be set to None in case of a time instance.
  467. :param ts grass.lib.gis.TimeStamp object created by G_read_*_timestamp
  468. """
  469. dt1 = libgis.DateTime()
  470. dt2 = libgis.DateTime()
  471. count = c_int()
  472. libgis.G_get_timestamps(byref(ts),
  473. byref(dt1),
  474. byref(dt2),
  475. byref(count))
  476. if dt1.mode == libdate.DATETIME_ABSOLUTE:
  477. pdt1 = None
  478. pdt2 = None
  479. if count.value >= 1:
  480. pdt1 = datetime(int(dt1.year), int(dt1.month), int(dt1.day),
  481. int(dt1.hour), int(dt1.minute),
  482. int(dt1.second))
  483. if count.value == 2:
  484. pdt2 = datetime(int(dt2.year), int(dt2.month), int(dt2.day),
  485. int(dt2.hour), int(dt2.minute),
  486. int(dt2.second))
  487. # ATTENTION: We ignore the time zone
  488. # TODO: Write time zone support
  489. return (pdt1, pdt2)
  490. else:
  491. unit = None
  492. start = None
  493. end = None
  494. if count.value >= 1:
  495. if dt1.year > 0:
  496. unit = "years"
  497. start = dt1.year
  498. elif dt1.month > 0:
  499. unit = "months"
  500. start = dt1.month
  501. elif dt1.day > 0:
  502. unit = "days"
  503. start = dt1.day
  504. elif dt1.hour > 0:
  505. unit = "hours"
  506. start = dt1.hour
  507. elif dt1.minute > 0:
  508. unit = "minutes"
  509. start = dt1.minute
  510. elif dt1.second > 0:
  511. unit = "seconds"
  512. start = dt1.second
  513. if count.value == 2:
  514. if dt2.year > 0:
  515. end = dt2.year
  516. elif dt2.month > 0:
  517. end = dt2.month
  518. elif dt2.day > 0:
  519. end = dt2.day
  520. elif dt2.hour > 0:
  521. end = dt2.hour
  522. elif dt2.minute > 0:
  523. end = dt2.minute
  524. elif dt2.second > 0:
  525. end = dt2.second
  526. return (start, end, unit)
  527. ###############################################################################
  528. def _stop(lock, conn, data):
  529. conn.close()
  530. lock.release()
  531. libgis.G_debug(1, "Stop C-interface server")
  532. sys.exit()
  533. ###############################################################################
  534. def c_library_server(lock, conn):
  535. """The GRASS C-libraries server function designed to be a target for
  536. multiprocessing.Process
  537. :param lock: A multiprocessing.Lock
  538. :param conn: A multiprocessing.Pipe
  539. """
  540. # Crerate the function array
  541. functions = [0]*11
  542. functions[RPCDefs.STOP] = _stop
  543. functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp
  544. functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp
  545. functions[RPCDefs.READ_TIMESTAMP] = _read_timestamp
  546. functions[RPCDefs.REMOVE_TIMESTAMP] = _remove_timestamp
  547. functions[RPCDefs.READ_MAP_INFO] = _read_map_info
  548. functions[RPCDefs.MAP_EXISTS] = _map_exists
  549. functions[RPCDefs.AVAILABLE_MAPSETS] = _available_mapsets
  550. functions[RPCDefs.GET_DRIVER_NAME] = _get_driver_name
  551. functions[RPCDefs.GET_DATABASE_NAME] = _get_database_name
  552. libgis.G_gisinit("c_library_server")
  553. libgis.G_debug(1, "Start C-interface server")
  554. while True:
  555. # Avoid busy waiting
  556. conn.poll(4)
  557. data = conn.recv()
  558. lock.acquire()
  559. functions[data[0]](lock, conn, data)
  560. lock.release()
  561. class CLibrariesInterface(object):
  562. """Fast and exit-safe interface to GRASS C-libraries functions
  563. This class implements a fast and exit-safe interface to the GRASS
  564. gis, raster, 3D raster and vector C-libraries functions.
  565. The C-libraries functions are called via ctypes in a subprocess
  566. using a pipe (multiprocessing.Pipe) to transfer the text messages.
  567. Hence, the process that uses the CLibrariesInterface will not be
  568. exited, if a G_fatal_error() was invoked in the subprocess.
  569. In this case the CLibrariesInterface object will simply start a
  570. new subprocess and restarts the pipeline.
  571. Usage:
  572. @code
  573. >>> import grass.script as gscript
  574. >>> import grass.temporal as tgis
  575. >>> gscript.use_temp_region()
  576. >>> gscript.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  577. ... t=1.0, b=0.0, res=10.0, res3=10.0)
  578. 0
  579. >>> tgis.init()
  580. >>> gscript.run_command("r.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  581. 0
  582. >>> gscript.run_command("r3.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  583. 0
  584. >>> gscript.run_command("v.random", output="test", n=10, overwrite=True, quiet=True)
  585. 0
  586. >>> gscript.run_command("r.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  587. 0
  588. >>> gscript.run_command("r3.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  589. 0
  590. >>> gscript.run_command("v.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  591. 0
  592. # Check mapsets
  593. >>> ciface = tgis.CLibrariesInterface()
  594. >>> mapsets = ciface.available_mapsets()
  595. >>> mapsets[0] == tgis.get_current_mapset()
  596. True
  597. # Raster map
  598. >>> ciface = tgis.CLibrariesInterface()
  599. >>> check = ciface.raster_map_exists("test", tgis.get_current_mapset())
  600. >>> print check
  601. True
  602. >>> ciface.read_raster_info("test", tgis.get_current_mapset())
  603. {'rows': 12, 'north': 80.0, 'min': 1, 'datatype': 'CELL', 'max': 1, 'ewres': 10.0, 'cols': 8, 'west': 0.0, 'east': 120.0, 'nsres': 10.0, 'south': 0.0}
  604. >>> check = ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  605. >>> print check
  606. True
  607. >>> if check:
  608. ... res = ciface.read_raster_timestamp("test", tgis.get_current_mapset())
  609. ... if res[0]:
  610. ... print str(res[1][0]), str(res[1][0])
  611. ... ciface.remove_raster_timestamp("test", tgis.get_current_mapset())
  612. 1995-03-12 10:34:40 1995-03-12 10:34:40
  613. 1
  614. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  615. False
  616. >>> ciface.write_raster_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  617. 1
  618. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  619. True
  620. # 3D raster map
  621. >>> check = ciface.raster3d_map_exists("test", tgis.get_current_mapset())
  622. >>> print check
  623. True
  624. >>> ciface.read_raster3d_info("test", tgis.get_current_mapset())
  625. {'tbres': 1.0, 'rows': 12, 'north': 80.0, 'bottom': 0.0, 'datatype': 'DCELL', 'max': 1.0, 'top': 1.0, 'min': 1.0, 'cols': 8, 'depths': 1, 'west': 0.0, 'ewres': 10.0, 'east': 120.0, 'nsres': 10.0, 'south': 0.0}
  626. >>> check = ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  627. >>> print check
  628. True
  629. >>> if check:
  630. ... res = ciface.read_raster3d_timestamp("test", tgis.get_current_mapset())
  631. ... if res[0]:
  632. ... print str(res[1][0]), str(res[1][0])
  633. ... ciface.remove_raster3d_timestamp("test", tgis.get_current_mapset())
  634. 1995-03-12 10:34:40 1995-03-12 10:34:40
  635. 1
  636. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  637. False
  638. >>> ciface.write_raster3d_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  639. 1
  640. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  641. True
  642. # Vector map
  643. >>> check = ciface.vector_map_exists("test", tgis.get_current_mapset())
  644. >>> print check
  645. True
  646. >>> kvp = ciface.read_vector_info("test", tgis.get_current_mapset())
  647. >>> print kvp['points']
  648. 10
  649. >>> check = ciface.has_vector_timestamp("test", tgis.get_current_mapset(), None)
  650. >>> print check
  651. True
  652. >>> if check:
  653. ... res = ciface.read_vector_timestamp("test", tgis.get_current_mapset())
  654. ... if res[0]:
  655. ... print str(res[1][0]), str(res[1][0])
  656. ... ciface.remove_vector_timestamp("test", tgis.get_current_mapset())
  657. 1995-03-12 10:34:40 1995-03-12 10:34:40
  658. 1
  659. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  660. False
  661. >>> ciface.write_vector_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  662. 1
  663. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  664. True
  665. >>> ciface.get_driver_name()
  666. 'sqlite'
  667. >>> ciface.get_database_name().split("/")[-1]
  668. 'sqlite.db'
  669. >>> gscript.del_temp_region()
  670. @endcode
  671. """
  672. def __init__(self):
  673. self.client_conn = None
  674. self.server_conn = None
  675. self.server = None
  676. self.start_server()
  677. def start_server(self):
  678. self.client_conn, self.server_conn = Pipe()
  679. self.lock = Lock()
  680. self.server = Process(target=c_library_server, args=(self.lock,
  681. self.server_conn))
  682. self.server.daemon = True
  683. self.server.start()
  684. def _check_restart_server(self):
  685. """Restart the server if it was terminated
  686. """
  687. if self.server.is_alive() is True:
  688. return
  689. self.client_conn.close()
  690. self.server_conn.close()
  691. self.start_server()
  692. logging.warning("Needed to restart the libgis server")
  693. def raster_map_exists(self, name, mapset):
  694. """Check if a raster map exists in the spatial database
  695. :param name: The name of the map
  696. :param mapset: The mapset of the map
  697. :returns: True if exists, False if not
  698. """
  699. self._check_restart_server()
  700. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER,
  701. name, mapset, None])
  702. return self.client_conn.recv()
  703. def read_raster_info(self, name, mapset):
  704. """Read the raster map info from the file system and store the content
  705. into a dictionary
  706. :param name: The name of the map
  707. :param mapset: The mapset of the map
  708. :returns: The key value pairs of the map specific metadata, or None in case of an error
  709. """
  710. self._check_restart_server()
  711. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER,
  712. name, mapset, None])
  713. return self.client_conn.recv()
  714. def has_raster_timestamp(self, name, mapset):
  715. """Check if a file based raster timetamp exists
  716. :param name: The name of the map
  717. :param mapset: The mapset of the map
  718. :returns: True if exists, False if not
  719. """
  720. self._check_restart_server()
  721. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER,
  722. name, mapset, None])
  723. return self.client_conn.recv()
  724. def remove_raster_timestamp(self, name, mapset):
  725. """Remove a file based raster timetamp
  726. Please have a look at the documentation G_remove_raster_timestamp
  727. for the return values description.
  728. :param name: The name of the map
  729. :param mapset: The mapset of the map
  730. :returns: The return value of G_remove_raster_timestamp
  731. """
  732. self._check_restart_server()
  733. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  734. name, mapset, None])
  735. return self.client_conn.recv()
  736. def read_raster_timestamp(self, name, mapset):
  737. """Read a file based raster timetamp
  738. Please have a look at the documentation G_read_raster_timestamp
  739. for the return values description.
  740. The timestamps to be send are tuples of values:
  741. - relative time (start, end, unit), start and end are of type
  742. integer, unit is of type string.
  743. - absolute time (start, end), start and end are of type datetime
  744. The end time may be None in case of a time instance.
  745. :param name: The name of the map
  746. :param mapset: The mapset of the map
  747. :returns: The return value of G_read_raster_timestamp
  748. """
  749. self._check_restart_server()
  750. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER,
  751. name, mapset, None])
  752. return self.client_conn.recv()
  753. def write_raster_timestamp(self, name, mapset, timestring):
  754. """Write a file based raster timetamp
  755. Please have a look at the documentation G_write_raster_timestamp
  756. for the return values description.
  757. Note:
  758. Only timestamps of maps from the current mapset can written.
  759. :param name: The name of the map
  760. :param mapset: The mapset of the map
  761. :param timestring: A GRASS datetime C-library compatible string
  762. :returns: The return value of G_write_raster_timestamp
  763. """
  764. self._check_restart_server()
  765. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  766. name, mapset, None, timestring])
  767. return self.client_conn.recv()
  768. def raster3d_map_exists(self, name, mapset):
  769. """Check if a 3D raster map exists in the spatial database
  770. :param name: The name of the map
  771. :param mapset: The mapset of the map
  772. :returns: True if exists, False if not
  773. """
  774. self._check_restart_server()
  775. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER3D,
  776. name, mapset, None])
  777. return self.client_conn.recv()
  778. def read_raster3d_info(self, name, mapset):
  779. """Read the 3D raster map info from the file system and store the content
  780. into a dictionary
  781. :param name: The name of the map
  782. :param mapset: The mapset of the map
  783. :returns: The key value pairs of the map specific metadata, or None in case of an error
  784. """
  785. self._check_restart_server()
  786. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER3D,
  787. name, mapset, None])
  788. return self.client_conn.recv()
  789. def has_raster3d_timestamp(self, name, mapset):
  790. """Check if a file based 3D raster timetamp exists
  791. :param name: The name of the map
  792. :param mapset: The mapset of the map
  793. :returns: True if exists, False if not
  794. """
  795. self._check_restart_server()
  796. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  797. name, mapset, None])
  798. return self.client_conn.recv()
  799. def remove_raster3d_timestamp(self, name, mapset):
  800. """Remove a file based 3D raster timetamp
  801. Please have a look at the documentation G_remove_raster3d_timestamp
  802. for the return values description.
  803. :param name: The name of the map
  804. :param mapset: The mapset of the map
  805. :returns: The return value of G_remove_raster3d_timestamp
  806. """
  807. self._check_restart_server()
  808. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  809. name, mapset, None])
  810. return self.client_conn.recv()
  811. def read_raster3d_timestamp(self, name, mapset):
  812. """Read a file based 3D raster timetamp
  813. Please have a look at the documentation G_read_raster3d_timestamp
  814. for the return values description.
  815. The timestamps to be send are tuples of values:
  816. - relative time (start, end, unit), start and end are of type
  817. integer, unit is of type string.
  818. - absolute time (start, end), start and end are of type datetime
  819. The end time may be None in case of a time instance.
  820. :param name: The name of the map
  821. :param mapset: The mapset of the map
  822. :returns: The return value of G_read_raster3d_timestamp
  823. """
  824. self._check_restart_server()
  825. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  826. name, mapset, None])
  827. return self.client_conn.recv()
  828. def write_raster3d_timestamp(self, name, mapset, timestring):
  829. """Write a file based 3D raster timetamp
  830. Please have a look at the documentation G_write_raster3d_timestamp
  831. for the return values description.
  832. Note:
  833. Only timestamps of maps from the current mapset can written.
  834. :param name: The name of the map
  835. :param mapset: The mapset of the map
  836. :param timestring: A GRASS datetime C-library compatible string
  837. :returns: The return value of G_write_raster3d_timestamp
  838. """
  839. self._check_restart_server()
  840. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  841. name, mapset, None, timestring])
  842. return self.client_conn.recv()
  843. def vector_map_exists(self, name, mapset):
  844. """Check if a vector map exists in the spatial database
  845. :param name: The name of the map
  846. :param mapset: The mapset of the map
  847. :returns: True if exists, False if not
  848. """
  849. self._check_restart_server()
  850. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_VECTOR,
  851. name, mapset, None])
  852. return self.client_conn.recv()
  853. def read_vector_info(self, name, mapset):
  854. """Read the vector map info from the file system and store the content
  855. into a dictionary
  856. :param name: The name of the map
  857. :param mapset: The mapset of the map
  858. :returns: The key value pairs of the map specific metadata, or None in case of an error
  859. """
  860. self._check_restart_server()
  861. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_VECTOR,
  862. name, mapset, None])
  863. return self.client_conn.recv()
  864. def has_vector_timestamp(self, name, mapset, layer=None):
  865. """Check if a file based vector timetamp exists
  866. :param name: The name of the map
  867. :param mapset: The mapset of the map
  868. :param layer: The layer of the vector map
  869. :returns: True if exists, False if not
  870. """
  871. self._check_restart_server()
  872. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  873. name, mapset, layer])
  874. return self.client_conn.recv()
  875. def remove_vector_timestamp(self, name, mapset, layer=None):
  876. """Remove a file based vector timetamp
  877. Please have a look at the documentation G_remove_vector_timestamp
  878. for the return values description.
  879. :param name: The name of the map
  880. :param mapset: The mapset of the map
  881. :param layer The layer of the vector map
  882. :returns: The return value of G_remove_vector_timestamp
  883. """
  884. self._check_restart_server()
  885. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  886. name, mapset, layer])
  887. return self.client_conn.recv()
  888. def read_vector_timestamp(self, name, mapset, layer=None):
  889. """Read a file based vector timetamp
  890. Please have a look at the documentation G_read_vector_timestamp
  891. for the return values description.
  892. The timestamps to be send are tuples of values:
  893. - relative time (start, end, unit), start and end are of type
  894. integer, unit is of type string.
  895. - absolute time (start, end), start and end are of type datetime
  896. The end time may be None in case of a time instance.
  897. :param name: The name of the map
  898. :param mapset: The mapset of the map
  899. :param layer: The layer of the vector map
  900. :returns: The return value ofG_read_vector_timestamp and the timestamps
  901. """
  902. self._check_restart_server()
  903. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  904. name, mapset, layer])
  905. return self.client_conn.recv()
  906. def write_vector_timestamp(self, name, mapset, timestring, layer=None):
  907. """Write a file based vector timestamp
  908. Please have a look at the documentation G_write_vector_timestamp
  909. for the return values description.
  910. Note:
  911. Only timestamps pf maps from the current mapset can written.
  912. :param name: The name of the map
  913. :param mapset: The mapset of the map
  914. :param timestring: A GRASS datetime C-library compatible string
  915. :param layer: The layer of the vector map
  916. :returns: The return value of G_write_vector_timestamp
  917. """
  918. self._check_restart_server()
  919. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  920. name, mapset, layer, timestring])
  921. return self.client_conn.recv()
  922. def available_mapsets(self):
  923. """Return all available mapsets the user can access as a list of strings
  924. :returns: Names of available mapsets as list of strings
  925. """
  926. self._check_restart_server()
  927. self.client_conn.send([RPCDefs.AVAILABLE_MAPSETS, ])
  928. return self.client_conn.recv()
  929. def get_driver_name(self, mapset=None):
  930. """Return the temporal database driver of a specific mapset
  931. :param mapset: Name of the mapset
  932. :returns: Name of the driver or None if no temporal database present
  933. """
  934. if mapset is None or mapset is "":
  935. mapset = corefunc.get_current_mapset()
  936. self._check_restart_server()
  937. self.client_conn.send([RPCDefs.GET_DRIVER_NAME, mapset])
  938. return self.client_conn.recv()
  939. def get_database_name(self, mapset=None):
  940. """Return the temporal database name of a specific mapset
  941. :param mapset: Name of the mapset
  942. :returns: Name of the database or None if no temporal database present
  943. """
  944. if mapset is None or mapset is "":
  945. mapset = corefunc.get_current_mapset()
  946. self._check_restart_server()
  947. self.client_conn.send([RPCDefs.GET_DATABASE_NAME, mapset])
  948. return self.client_conn.recv()
  949. def stop(self):
  950. """Stop the messenger server and close the pipe
  951. This method should be called at exit using the package atexit
  952. """
  953. if self.server is not None and self.server.is_alive():
  954. self.client_conn.send([0,])
  955. self.server.join(5)
  956. self.server.terminate()
  957. if self.client_conn is not None:
  958. self.client_conn.close()
  959. if __name__ == "__main__":
  960. import doctest
  961. doctest.testmod()