__init__.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. # -*- coding: utf-8 -*-
  2. """
  3. Fast and exit-safe interface to PyGRASS Raster and Vector layer
  4. using multiprocessing
  5. (C) 2015 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. import time
  12. import threading
  13. import sys
  14. from multiprocessing import Process, Lock, Pipe
  15. from ctypes import *
  16. from grass.exceptions import FatalError
  17. from grass.pygrass.vector import *
  18. from grass.pygrass.raster import *
  19. import grass.lib.gis as libgis
  20. from base import RPCServerBase
  21. from grass.pygrass.gis.region import Region
  22. import grass.pygrass.utils as utils
  23. import logging
  24. ###############################################################################
  25. ###############################################################################
  26. class RPCDefs(object):
  27. # Function identifier and index
  28. STOP = 0
  29. GET_VECTOR_TABLE_AS_DICT = 1
  30. GET_VECTOR_FEATURES_AS_WKB = 2
  31. GET_RASTER_IMAGE_AS_NP = 3
  32. G_FATAL_ERROR = 14
  33. def _get_raster_image_as_np(lock, conn, data):
  34. """Convert a raster map into an image and return
  35. a numpy array with RGB or Gray values.
  36. :param lock: A multiprocessing.Lock instance
  37. :param conn: A multiprocessing.Pipe instance used to send True or False
  38. :param data: The list of data entries [function_id, raster_name, extent, color]
  39. """
  40. array = None
  41. try:
  42. name = data[1]
  43. mapset = data[2]
  44. extent = data[3]
  45. color = data[4]
  46. mapset = utils.get_mapset_raster(name, mapset)
  47. if not mapset:
  48. raise ValueError("Unable to find raster map <%s>"%(name))
  49. rast = RasterRow(name, mapset)
  50. if rast.exist():
  51. reg = Region()
  52. reg.from_rast(name)
  53. if extent is not None:
  54. if "north" in extent:
  55. reg.north = extent["north"]
  56. if "south" in extent:
  57. reg.south = extent["south"]
  58. if "east" in extent:
  59. reg.east = extent["east"]
  60. if "west" in extent:
  61. reg.west = extent["west"]
  62. if "rows" in extent:
  63. reg.rows = extent["rows"]
  64. if "cols" in extent:
  65. reg.cols = extent["cols"]
  66. reg.adjust()
  67. array = raster2numpy_img(name, reg, color)
  68. except:
  69. raise
  70. finally:
  71. conn.send(array)
  72. def _get_vector_table_as_dict(lock, conn, data):
  73. """Get the table of a vector map layer as dictionary
  74. :param lock: A multiprocessing.Lock instance
  75. :param conn: A multiprocessing.Pipe instance used to send True or False
  76. :param data: The list of data entries [function_id, name, mapset, where]
  77. """
  78. ret = None
  79. try:
  80. name = data[1]
  81. mapset = data[2]
  82. where = data[3]
  83. mapset = utils.get_mapset_vector(name, mapset)
  84. if not mapset:
  85. raise ValueError("Unable to find vector map <%s>"%(name))
  86. layer = VectorTopo(name, mapset)
  87. if layer.exist() is True:
  88. layer.open("r")
  89. columns = None
  90. table = None
  91. if layer.table is not None:
  92. columns = layer.table.columns
  93. table = layer.table_to_dict(where=where)
  94. layer.close()
  95. ret = {}
  96. ret["table"] = table
  97. ret["columns"] = columns
  98. except:
  99. raise
  100. finally:
  101. conn.send(ret)
  102. def _get_vector_features_as_wkb_list(lock, conn, data):
  103. """Return vector layer features as wkb list
  104. supported feature types:
  105. point, centroid, line, boundary, area
  106. :param lock: A multiprocessing.Lock instance
  107. :param conn: A multiprocessing.Pipe instance used to send True or False
  108. :param data: The list of data entries [function_id,name,mapset,extent,
  109. feature_type, field]
  110. """
  111. wkb_list = None
  112. try:
  113. name = data[1]
  114. mapset = data[2]
  115. extent = data[3]
  116. feature_type = data[4]
  117. field = data[5]
  118. bbox = None
  119. mapset = utils.get_mapset_vector(name, mapset)
  120. if not mapset:
  121. raise ValueError("Unable to find vector map <%s>"%(name))
  122. layer = VectorTopo(name, mapset)
  123. if layer.exist() is True:
  124. if extent is not None:
  125. bbox = basic.Bbox(north=extent["north"],
  126. south=extent["south"],
  127. east=extent["east"],
  128. west=extent["west"])
  129. layer.open("r")
  130. if feature_type.lower() == "area":
  131. wkb_list = layer.areas_to_wkb_list(bbox=bbox, field=field)
  132. else:
  133. wkb_list = layer.features_to_wkb_list(bbox=bbox,
  134. feature_type=feature_type,
  135. field=field)
  136. layer.close()
  137. except:
  138. raise
  139. finally:
  140. conn.send(wkb_list)
  141. ###############################################################################
  142. def _fatal_error(lock, conn, data):
  143. """Calls G_fatal_error()"""
  144. libgis.G_fatal_error("Fatal Error in C library server")
  145. ###############################################################################
  146. def _stop(lock, conn, data):
  147. conn.close()
  148. lock.release()
  149. sys.exit()
  150. ###############################################################################
  151. def data_provider_server(lock, conn):
  152. """The PyGRASS data provider server designed to be a target for
  153. multiprocessing.Process
  154. :param lock: A multiprocessing.Lock
  155. :param conn: A multiprocessing.Pipe
  156. """
  157. def error_handler(data):
  158. """This function will be called in case of a fatal error in libgis"""
  159. #sys.stderr.write("Error handler was called\n")
  160. # We send an exeption that will be handled in
  161. # the parent process, then close the pipe
  162. # and release any possible lock
  163. conn.send(FatalError())
  164. conn.close()
  165. lock.release()
  166. CALLBACK = CFUNCTYPE(c_void_p, c_void_p)
  167. CALLBACK.restype = c_void_p
  168. CALLBACK.argtypes = c_void_p
  169. cerror_handler = CALLBACK(error_handler)
  170. libgis.G_add_error_handler(cerror_handler, None)
  171. # Crerate the function array
  172. functions = [0]*15
  173. functions[RPCDefs.GET_VECTOR_TABLE_AS_DICT] = _get_vector_table_as_dict
  174. functions[RPCDefs.GET_VECTOR_FEATURES_AS_WKB] = _get_vector_features_as_wkb_list
  175. functions[RPCDefs.GET_RASTER_IMAGE_AS_NP] = _get_raster_image_as_np
  176. functions[RPCDefs.STOP] = _stop
  177. functions[RPCDefs.G_FATAL_ERROR] = _fatal_error
  178. while True:
  179. # Avoid busy waiting
  180. conn.poll(None)
  181. data = conn.recv()
  182. lock.acquire()
  183. functions[data[0]](lock, conn, data)
  184. lock.release()
  185. test_vector_name="data_provider_vector_map"
  186. test_raster_name="data_provider_raster_map"
  187. class DataProvider(RPCServerBase):
  188. """Fast and exit-safe interface to PyGRASS data delivery functions
  189. """
  190. def __init__(self):
  191. RPCServerBase.__init__(self)
  192. def start_server(self):
  193. """This function must be re-implemented in the subclasses
  194. """
  195. self.client_conn, self.server_conn = Pipe(True)
  196. self.lock = Lock()
  197. self.server = Process(target=data_provider_server, args=(self.lock,
  198. self.server_conn))
  199. self.server.daemon = True
  200. self.server.start()
  201. def get_raster_image_as_np(self, name, mapset=None, extent=None, color="RGB"):
  202. """Return the attribute table of a vector map as dictionary.
  203. See documentation of: pygrass.raster.raster2numpy_img
  204. Usage:
  205. .. code-block:: python
  206. >>> from grass.pygrass.rpc import DataProvider
  207. >>> provider = DataProvider()
  208. >>> ret = provider.get_raster_image_as_np(name=test_raster_name)
  209. >>> len(ret)
  210. 64
  211. >>> extent = {"north":30, "south":10, "east":30, "west":10,
  212. ... "rows":2, "cols":2}
  213. >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
  214. ... extent=extent)
  215. >>> len(ret)
  216. 16
  217. >>> extent = {"rows":3, "cols":1}
  218. >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
  219. ... extent=extent)
  220. >>> len(ret)
  221. 12
  222. >>> provider.stop()
  223. ..
  224. """
  225. self.check_server()
  226. self.client_conn.send([RPCDefs.GET_RASTER_IMAGE_AS_NP,
  227. name, mapset, extent, color])
  228. return self.safe_receive("get_raster_image_as_np")
  229. def get_vector_table_as_dict(self, name, mapset=None, where=None):
  230. """Return the attribute table of a vector map as dictionary.
  231. See documentation of: pygrass.vector.VectorTopo::table_to_dict
  232. Usage:
  233. .. code-block:: python
  234. >>> from grass.pygrass.rpc import DataProvider
  235. >>> provider = DataProvider()
  236. >>> ret = provider.get_vector_table_as_dict(name=test_vector_name)
  237. >>> ret["table"]
  238. {1: [1, u'point', 1.0], 2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
  239. >>> ret["columns"]
  240. Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
  241. >>> ret = provider.get_vector_table_as_dict(name=test_vector_name,
  242. ... where="value > 1")
  243. >>> ret["table"]
  244. {2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
  245. >>> ret["columns"]
  246. Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
  247. >>> provider.get_vector_table_as_dict(name="no_map",
  248. ... where="value > 1")
  249. >>> provider.stop()
  250. ..
  251. """
  252. self.check_server()
  253. self.client_conn.send([RPCDefs.GET_VECTOR_TABLE_AS_DICT,
  254. name, mapset, where])
  255. return self.safe_receive("get_vector_table_as_dict")
  256. def get_vector_features_as_wkb_list(self, name, mapset=None, extent=None,
  257. feature_type="point", field=1):
  258. """Return the features of a vector map as wkb list.
  259. :param extent: A dictionary of {"north":double, "south":double,
  260. "east":double, "west":double}
  261. :param feature_type: point, centroid, line, boundary or area
  262. See documentation: pygrass.vector.VectorTopo::features_to_wkb_list
  263. pygrass.vector.VectorTopo::areas_to_wkb_list
  264. Usage:
  265. .. code-block:: python
  266. >>> from grass.pygrass.rpc import DataProvider
  267. >>> provider = DataProvider()
  268. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  269. ... extent=None,
  270. ... feature_type="point")
  271. >>> for entry in wkb:
  272. ... f_id, cat, string = entry
  273. ... print(f_id, cat, len(string))
  274. 1 1 21
  275. 2 1 21
  276. 3 1 21
  277. >>> extent = {"north":6.6, "south":5.5, "east":14.5, "west":13.5}
  278. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  279. ... extent=extent,
  280. ... feature_type="point")
  281. >>> for entry in wkb:
  282. ... f_id, cat, string = entry
  283. ... print(f_id, cat, len(string))
  284. 3 1 21
  285. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  286. ... extent=None,
  287. ... feature_type="line")
  288. >>> for entry in wkb:
  289. ... f_id, cat, string = entry
  290. ... print(f_id, cat, len(string))
  291. 4 2 57
  292. 5 2 57
  293. 6 2 57
  294. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  295. ... extent=None,
  296. ... feature_type="centroid")
  297. >>> for entry in wkb:
  298. ... f_id, cat, string = entry
  299. ... print(f_id, cat, len(string))
  300. 19 3 21
  301. 18 3 21
  302. 20 3 21
  303. 21 3 21
  304. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  305. ... extent=None,
  306. ... feature_type="area")
  307. >>> for entry in wkb:
  308. ... f_id, cat, string = entry
  309. ... print(f_id, cat, len(string))
  310. 1 3 225
  311. 2 3 141
  312. 3 3 93
  313. 4 3 141
  314. >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
  315. ... extent=None,
  316. ... feature_type="boundary")
  317. >>> for entry in wkb:
  318. ... f_id, cat, string = entry
  319. ... print(f_id, cat, len(string))
  320. 10 None 41
  321. 7 None 41
  322. 8 None 41
  323. 9 None 41
  324. 11 None 89
  325. 12 None 41
  326. 14 None 41
  327. 13 None 41
  328. 17 None 41
  329. 15 None 41
  330. 16 None 41
  331. >>> provider.stop()
  332. ..
  333. """
  334. self.check_server()
  335. self.client_conn.send([RPCDefs.GET_VECTOR_FEATURES_AS_WKB,
  336. name, mapset, extent, feature_type, field])
  337. return self.safe_receive("get_vector_features_as_wkb_list")
  338. if __name__ == "__main__":
  339. import doctest
  340. from grass.pygrass import utils
  341. from grass.pygrass.modules import Module
  342. Module("g.region", n=40, s=0, e=40, w=0, res=10)
  343. Module("r.mapcalc", expression="%s = row() + (10 * col())"%(test_raster_name),
  344. overwrite=True)
  345. utils.create_test_vector_map(test_vector_name)
  346. doctest.testmod()
  347. """Remove the generated maps, if exist"""
  348. mset = utils.get_mapset_raster(test_raster_name, mapset='')
  349. if mset:
  350. Module("g.remove", flags='f', type='raster', name=test_raster_name)
  351. mset = utils.get_mapset_vector(test_vector_name, mapset='')
  352. if mset:
  353. Module("g.remove", flags='f', type='vector', name=test_vector_name)