c_libraries_interface.py 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  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 >= 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 == 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. sys.exit()
  472. ###############################################################################
  473. def c_library_server(lock, conn):
  474. """!The GRASS C-libraries server function designed to be a target for
  475. multiprocessing.Process
  476. @param lock A multiprocessing.Lock
  477. @param conn A multiprocessing.Pipe
  478. """
  479. # Crerate the function array
  480. functions = [0]*8
  481. functions[RPCDefs.STOP] = _stop
  482. functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp
  483. functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp
  484. functions[RPCDefs.READ_TIMESTAMP] = _read_timestamp
  485. functions[RPCDefs.REMOVE_TIMESTAMP] = _remove_timestamp
  486. functions[RPCDefs.READ_MAP_INFO] = _read_map_info
  487. functions[RPCDefs.MAP_EXISTS] = _map_exists
  488. libgis.G_gisinit("c_library_server")
  489. while True:
  490. # Avoid busy waiting
  491. conn.poll(4)
  492. data = conn.recv()
  493. lock.acquire()
  494. functions[data[0]](lock, conn, data)
  495. lock.release()
  496. class CLibrariesInterface(object):
  497. """!Fast and exit-safe interface to GRASS C-libraries functions
  498. This class implements a fast and exit-safe interface to the GRASS
  499. gis, raster, 3D raster and vector C-libraries functions.
  500. The C-libraries functions are called via ctypes in a subprocess
  501. using a pipe (multiprocessing.Pipe) to transfer the text messages.
  502. Hence, the process that uses the CLibrariesInterface will not be
  503. exited, if a G_fatal_error() was invoked in the subprocess.
  504. In this case the CLibrariesInterface object will simply start a
  505. new subprocess and restarts the pipeline.
  506. Usage:
  507. @code
  508. >>> import grass.script as grass
  509. >>> import grass.temporal as tgis
  510. >>> grass.use_temp_region()
  511. >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  512. ... t=1.0, b=0.0, res=10.0, res3=10.0)
  513. 0
  514. >>> tgis.init()
  515. >>> grass.run_command("r.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  516. 0
  517. >>> grass.run_command("r3.mapcalc", expression="test = 1", overwrite=True, quiet=True)
  518. 0
  519. >>> grass.run_command("v.random", output="test", n=10, overwrite=True, quiet=True)
  520. 0
  521. >>> grass.run_command("r.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  522. 0
  523. >>> grass.run_command("r3.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  524. 0
  525. >>> grass.run_command("v.timestamp", map="test", date='12 Mar 1995 10:34:40', overwrite=True, quiet=True)
  526. 0
  527. # Raster map
  528. >>> ciface = tgis.CLibrariesInterface()
  529. >>> check = ciface.raster_map_exists("test", tgis.get_current_mapset())
  530. >>> print check
  531. True
  532. >>> ciface.read_raster_info("test", tgis.get_current_mapset())
  533. {'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}
  534. >>> check = ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  535. >>> print check
  536. True
  537. >>> if check:
  538. ... res = ciface.read_raster_timestamp("test", tgis.get_current_mapset())
  539. ... if res[0]:
  540. ... print str(res[1][0]), str(res[1][0])
  541. ... ciface.remove_raster_timestamp("test", tgis.get_current_mapset())
  542. 1995-03-12 10:34:40 1995-03-12 10:34:40
  543. 1
  544. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  545. False
  546. >>> ciface.write_raster_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  547. 1
  548. >>> ciface.has_raster_timestamp("test", tgis.get_current_mapset())
  549. True
  550. # 3D raster map
  551. >>> check = ciface.raster3d_map_exists("test", tgis.get_current_mapset())
  552. >>> print check
  553. True
  554. >>> ciface.read_raster3d_info("test", tgis.get_current_mapset())
  555. {'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}
  556. >>> check = ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  557. >>> print check
  558. True
  559. >>> if check:
  560. ... res = ciface.read_raster3d_timestamp("test", tgis.get_current_mapset())
  561. ... if res[0]:
  562. ... print str(res[1][0]), str(res[1][0])
  563. ... ciface.remove_raster3d_timestamp("test", tgis.get_current_mapset())
  564. 1995-03-12 10:34:40 1995-03-12 10:34:40
  565. 1
  566. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  567. False
  568. >>> ciface.write_raster3d_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  569. 1
  570. >>> ciface.has_raster3d_timestamp("test", tgis.get_current_mapset())
  571. True
  572. # Vector map
  573. >>> check = ciface.vector_map_exists("test", tgis.get_current_mapset())
  574. >>> print check
  575. True
  576. >>> kvp = ciface.read_vector_info("test", tgis.get_current_mapset())
  577. >>> print kvp['points']
  578. 10
  579. >>> check = ciface.has_vector_timestamp("test", tgis.get_current_mapset(), None)
  580. >>> print check
  581. True
  582. >>> if check:
  583. ... res = ciface.read_vector_timestamp("test", tgis.get_current_mapset())
  584. ... if res[0]:
  585. ... print str(res[1][0]), str(res[1][0])
  586. ... ciface.remove_vector_timestamp("test", tgis.get_current_mapset())
  587. 1995-03-12 10:34:40 1995-03-12 10:34:40
  588. 1
  589. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  590. False
  591. >>> ciface.write_vector_timestamp("test", tgis.get_current_mapset(), "13 Jan 1999 14:30:05")
  592. 1
  593. >>> ciface.has_vector_timestamp("test", tgis.get_current_mapset())
  594. True
  595. >>> grass.del_temp_region()
  596. @endcode
  597. """
  598. def __init__(self):
  599. self.client_conn = None
  600. self.server_conn = None
  601. self.server = None
  602. self.start_server()
  603. def __del__(self):
  604. self.stop()
  605. def start_server(self):
  606. self.client_conn, self.server_conn = Pipe()
  607. self.lock = Lock()
  608. self.server = Process(target=c_library_server, args=(self.lock,
  609. self.server_conn))
  610. self.server.daemon = True
  611. self.server.start()
  612. def _check_restart_server(self):
  613. """!Restart the server if it was terminated
  614. """
  615. if self.server.is_alive() is True:
  616. return
  617. self.client_conn.close()
  618. self.server_conn.close()
  619. self.start_server()
  620. logging.warning("Needed to restart the libgis server")
  621. def raster_map_exists(self, name, mapset):
  622. """!Check if a raster map exists in the spatial database
  623. @param name The name of the map
  624. @param mapset The mapset of the map
  625. @return True if exists, False if not
  626. """
  627. self._check_restart_server()
  628. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER,
  629. name, mapset, None])
  630. return self.client_conn.recv()
  631. def read_raster_info(self, name, mapset):
  632. """!Read the raster map info from the file system and store the content
  633. into a dictionary
  634. @param name The name of the map
  635. @param mapset The mapset of the map
  636. @return The key value pairs of the map specific metadata, or None in case of an error
  637. """
  638. self._check_restart_server()
  639. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER,
  640. name, mapset, None])
  641. return self.client_conn.recv()
  642. def has_raster_timestamp(self, name, mapset):
  643. """!Check if a file based raster timetamp exists
  644. @param name The name of the map
  645. @param mapset The mapset of the map
  646. @return True if exists, False if not
  647. """
  648. self._check_restart_server()
  649. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER,
  650. name, mapset, None])
  651. return self.client_conn.recv()
  652. def remove_raster_timestamp(self, name, mapset):
  653. """!Remove a file based raster timetamp
  654. Please have a look at the documentation G_remove_raster_timestamp
  655. for the return values description.
  656. @param name The name of the map
  657. @param mapset The mapset of the map
  658. @return The return value of G_remove_raster_timestamp
  659. """
  660. self._check_restart_server()
  661. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  662. name, mapset, None])
  663. return self.client_conn.recv()
  664. def read_raster_timestamp(self, name, mapset):
  665. """!Read a file based raster timetamp
  666. Please have a look at the documentation G_read_raster_timestamp
  667. for the return values description.
  668. The timestamps to be send are tuples of values:
  669. - relative time (start, end, unit), start and end are of type
  670. integer, unit is of type string.
  671. - absolute time (start, end), start and end are of type datetime
  672. The end time may be None in case of a time instance.
  673. @param name The name of the map
  674. @param mapset The mapset of the map
  675. @return The return value of G_read_raster_timestamp
  676. """
  677. self._check_restart_server()
  678. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER,
  679. name, mapset, None])
  680. return self.client_conn.recv()
  681. def write_raster_timestamp(self, name, mapset, timestring):
  682. """!Write a file based raster timetamp
  683. Please have a look at the documentation G_write_raster_timestamp
  684. for the return values description.
  685. Note:
  686. Only timestamps of maps from the current mapset can written.
  687. @param name The name of the map
  688. @param mapset The mapset of the map
  689. @param timestring A GRASS datetime C-library compatible string
  690. @return The return value of G_write_raster_timestamp
  691. """
  692. self._check_restart_server()
  693. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER,
  694. name, mapset, None, timestring])
  695. return self.client_conn.recv()
  696. def raster3d_map_exists(self, name, mapset):
  697. """!Check if a 3D raster map exists in the spatial database
  698. @param name The name of the map
  699. @param mapset The mapset of the map
  700. @return True if exists, False if not
  701. """
  702. self._check_restart_server()
  703. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_RASTER3D,
  704. name, mapset, None])
  705. return self.client_conn.recv()
  706. def read_raster3d_info(self, name, mapset):
  707. """!Read the 3D raster map info from the file system and store the content
  708. into a dictionary
  709. @param name The name of the map
  710. @param mapset The mapset of the map
  711. @return The key value pairs of the map specific metadata, or None in case of an error
  712. """
  713. self._check_restart_server()
  714. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_RASTER3D,
  715. name, mapset, None])
  716. return self.client_conn.recv()
  717. def has_raster3d_timestamp(self, name, mapset):
  718. """!Check if a file based 3D raster timetamp exists
  719. @param name The name of the map
  720. @param mapset The mapset of the map
  721. @return True if exists, False if not
  722. """
  723. self._check_restart_server()
  724. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  725. name, mapset, None])
  726. return self.client_conn.recv()
  727. def remove_raster3d_timestamp(self, name, mapset):
  728. """!Remove a file based 3D raster timetamp
  729. Please have a look at the documentation G_remove_raster3d_timestamp
  730. for the return values description.
  731. @param name The name of the map
  732. @param mapset The mapset of the map
  733. @return The return value of G_remove_raster3d_timestamp
  734. """
  735. self._check_restart_server()
  736. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  737. name, mapset, None])
  738. return self.client_conn.recv()
  739. def read_raster3d_timestamp(self, name, mapset):
  740. """!Read a file based 3D raster timetamp
  741. Please have a look at the documentation G_read_raster3d_timestamp
  742. for the return values description.
  743. The timestamps to be send are tuples of values:
  744. - relative time (start, end, unit), start and end are of type
  745. integer, unit is of type string.
  746. - absolute time (start, end), start and end are of type datetime
  747. The end time may be None in case of a time instance.
  748. @param name The name of the map
  749. @param mapset The mapset of the map
  750. @return The return value of G_read_raster3d_timestamp
  751. """
  752. self._check_restart_server()
  753. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  754. name, mapset, None])
  755. return self.client_conn.recv()
  756. def write_raster3d_timestamp(self, name, mapset, timestring):
  757. """!Write a file based 3D raster timetamp
  758. Please have a look at the documentation G_write_raster3d_timestamp
  759. for the return values description.
  760. Note:
  761. Only timestamps of maps from the current mapset can written.
  762. @param name The name of the map
  763. @param mapset The mapset of the map
  764. @param timestring A GRASS datetime C-library compatible string
  765. @return The return value of G_write_raster3d_timestamp
  766. """
  767. self._check_restart_server()
  768. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_RASTER3D,
  769. name, mapset, None, timestring])
  770. return self.client_conn.recv()
  771. def vector_map_exists(self, name, mapset):
  772. """!Check if a vector map exists in the spatial database
  773. @param name The name of the map
  774. @param mapset The mapset of the map
  775. @return True if exists, False if not
  776. """
  777. self._check_restart_server()
  778. self.client_conn.send([RPCDefs.MAP_EXISTS, RPCDefs.TYPE_VECTOR,
  779. name, mapset, None])
  780. return self.client_conn.recv()
  781. def read_vector_info(self, name, mapset):
  782. """!Read the vector map info from the file system and store the content
  783. into a dictionary
  784. @param name The name of the map
  785. @param mapset The mapset of the map
  786. @return The key value pairs of the map specific metadata, or None in case of an error
  787. """
  788. self._check_restart_server()
  789. self.client_conn.send([RPCDefs.READ_MAP_INFO, RPCDefs.TYPE_VECTOR,
  790. name, mapset, None])
  791. return self.client_conn.recv()
  792. def has_vector_timestamp(self, name, mapset, layer=None):
  793. """!Check if a file based vector timetamp exists
  794. @param name The name of the map
  795. @param mapset The mapset of the map
  796. @param layer The layer of the vector map
  797. @return True if exists, False if not
  798. """
  799. self._check_restart_server()
  800. self.client_conn.send([RPCDefs.HAS_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  801. name, mapset, layer])
  802. return self.client_conn.recv()
  803. def remove_vector_timestamp(self, name, mapset, layer=None):
  804. """!Remove a file based vector timetamp
  805. Please have a look at the documentation G_remove_vector_timestamp
  806. for the return values description.
  807. @param name The name of the map
  808. @param mapset The mapset of the map
  809. @param layer The layer of the vector map
  810. @return The return value of G_remove_vector_timestamp
  811. """
  812. self._check_restart_server()
  813. self.client_conn.send([RPCDefs.REMOVE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  814. name, mapset, layer])
  815. return self.client_conn.recv()
  816. def read_vector_timestamp(self, name, mapset, layer=None):
  817. """!Read a file based vector timetamp
  818. Please have a look at the documentation G_read_vector_timestamp
  819. for the return values description.
  820. The timestamps to be send are tuples of values:
  821. - relative time (start, end, unit), start and end are of type
  822. integer, unit is of type string.
  823. - absolute time (start, end), start and end are of type datetime
  824. The end time may be None in case of a time instance.
  825. @param name The name of the map
  826. @param mapset The mapset of the map
  827. @param layer The layer of the vector map
  828. @return The return value ofG_read_vector_timestamp and the timestamps
  829. """
  830. self._check_restart_server()
  831. self.client_conn.send([RPCDefs.READ_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  832. name, mapset, layer])
  833. return self.client_conn.recv()
  834. def write_vector_timestamp(self, name, mapset, timestring, layer=None):
  835. """!Write a file based vector timetamp
  836. Please have a look at the documentation G_write_vector_timestamp
  837. for the return values description.
  838. Note:
  839. Only timestamps pf maps from the current mapset can written.
  840. @param name The name of the map
  841. @param mapset The mapset of the map
  842. @param timestring A GRASS datetime C-library compatible string
  843. @param layer The layer of the vector map
  844. @return The return value of G_write_vector_timestamp
  845. """
  846. self._check_restart_server()
  847. self.client_conn.send([RPCDefs.WRITE_TIMESTAMP, RPCDefs.TYPE_VECTOR,
  848. name, mapset, layer, timestring])
  849. return self.client_conn.recv()
  850. def stop(self):
  851. """!Stop the messenger server and close the pipe
  852. """
  853. if self.server is not None and self.server.is_alive():
  854. self.client_conn.send([0,])
  855. self.server.join(5)
  856. self.server.terminate()
  857. if self.client_conn is not None:
  858. self.client_conn.close()
  859. if __name__ == "__main__":
  860. import doctest
  861. doctest.testmod()