c_libraries_interface.py 37 KB

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