c_libraries_interface.py 49 KB

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