basic.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Tue Jul 31 13:06:20 2012
  4. @author: pietro
  5. """
  6. import ctypes
  7. import grass.lib.vector as libvect
  8. from collections import Iterable
  9. class Bbox(object):
  10. """Instantiate a Bounding Box class that contains
  11. a ctypes pointer to the C struct bound_box, that could be used
  12. by C GRASS functions. ::
  13. >>> bbox = Bbox()
  14. >>> bbox
  15. Bbox(0.0, 0.0, 0.0, 0.0)
  16. The default parameters are 0. It is possible to set or change
  17. the parameters later, with: ::
  18. >>> bbox.north = 10
  19. >>> bbox.south = -10
  20. >>> bbox.east = -20
  21. >>> bbox.west = 20
  22. >>> bbox
  23. Bbox(10.0, -10.0, -20.0, 20.0)
  24. Or directly istantiate the class with the values, with: ::
  25. >>> bbox = Bbox(north=100, south=0, east=0, west=100)
  26. >>> bbox
  27. Bbox(100.0, 0.0, 0.0, 100.0)
  28. ..
  29. """
  30. def __init__(self, north=0, south=0, east=0, west=0, top=0, bottom=0):
  31. self.c_bbox = ctypes.pointer(libvect.bound_box())
  32. self.north = north
  33. self.south = south
  34. self.east = east
  35. self.west = west
  36. self.top = top
  37. self.bottom = bottom
  38. def _get_n(self):
  39. return self.c_bbox.contents.N
  40. def _set_n(self, value):
  41. self.c_bbox.contents.N = value
  42. north = property(fget=_get_n, fset=_set_n)
  43. def _get_s(self):
  44. return self.c_bbox.contents.S
  45. def _set_s(self, value):
  46. self.c_bbox.contents.S = value
  47. south = property(fget=_get_s, fset=_set_s)
  48. def _get_e(self):
  49. return self.c_bbox.contents.E
  50. def _set_e(self, value):
  51. self.c_bbox.contents.E = value
  52. east = property(fget=_get_e, fset=_set_e)
  53. def _get_w(self):
  54. return self.c_bbox.contents.W
  55. def _set_w(self, value):
  56. self.c_bbox.contents.W = value
  57. west = property(fget=_get_w, fset=_set_w)
  58. def _get_t(self):
  59. return self.c_bbox.contents.T
  60. def _set_t(self, value):
  61. self.c_bbox.contents.T = value
  62. top = property(fget=_get_t, fset=_set_t)
  63. def _get_b(self):
  64. return self.c_bbox.contents.B
  65. def _set_b(self, value):
  66. self.c_bbox.contents.B = value
  67. bottom = property(fget=_get_b, fset=_set_b)
  68. def __repr__(self):
  69. return "Bbox({n}, {s}, {e}, {w})".format(n=self.north, s=self.south,
  70. e=self.east, w=self.west)
  71. def contains(self, point):
  72. """Return True if the object is contained by the BoundingBox. ::
  73. >>> bbox = Bbox(north=10, south=0, west=0, east=10)
  74. >>> bbox.contains(point)
  75. True
  76. ..
  77. """
  78. return bool(libvect.Vect_point_in_box(point.x, point.y,
  79. point.z if point.z else 0,
  80. self.c_bbox))
  81. class BoxList(object):
  82. def __init__(self, boxlist=None):
  83. self.c_boxlist = ctypes.pointer(libvect.boxlist())
  84. # if set to 0, the list will hold only ids and no boxes
  85. self.c_boxlist.contents.have_boxes = 1
  86. if boxlist is not None:
  87. for box in boxlist:
  88. self.append(box)
  89. def __len__(self):
  90. return self.c_boxlist.contents.n_values
  91. def __repr__(self):
  92. return "Boxlist([%s])" % ", ".join([repr(box)
  93. for box in self.__iter__()])
  94. def __getitem__(self, indx):
  95. bbox = Bbox()
  96. bbox.c_bbox = ctypes.pointer(self.c_boxlist.contents.box[indx])
  97. return bbox
  98. def __setitem__(self, indx, bbox):
  99. self.c_boxlist.contents.box[indx] = bbox
  100. def __iter__(self):
  101. return (self.__getitem__(box_id) for box_id in xrange(self.__len__()))
  102. def __str__(self):
  103. return self.__repr__()
  104. def append(self, box):
  105. """Append a Bbox object to a Boxlist object, using the
  106. ``Vect_boxlist_append`` C fuction. ::
  107. >>> box0 = Bbox()
  108. >>> box1 = Bbox(1,2,3,4)
  109. >>> box2 = Bbox(5,6,7,8)
  110. >>> boxlist = BoxList([box0, box1])
  111. >>> boxlist
  112. Boxlist([Bbox(0.0, 0.0, 0.0, 0.0), Bbox(1.0, 2.0, 3.0, 4.0)])
  113. >>> len(boxlist)
  114. 2
  115. >>> boxlist.append(box2)
  116. >>> len(boxlist)
  117. 3
  118. ..
  119. """
  120. indx = self.__len__()
  121. libvect.Vect_boxlist_append(self.c_boxlist, indx, box.c_bbox)
  122. # def extend(self, boxlist):
  123. # """Extend a boxlist with another boxlist or using a list of Bbox, using
  124. # ``Vect_boxlist_append_boxlist`` c function. ::
  125. #
  126. # >>> box0 = Bbox()
  127. # >>> box1 = Bbox(1,2,3,4)
  128. # >>> box2 = Bbox(5,6,7,8)
  129. # >>> box3 = Bbox(9,8,7,6)
  130. # >>> boxlist0 = BoxList([box0, box1])
  131. # >>> boxlist0
  132. # Boxlist([Bbox(0.0, 0.0, 0.0, 0.0), Bbox(1.0, 2.0, 3.0, 4.0)])
  133. # >>> boxlist1 = BoxList([box2, box3])
  134. # >>> len(boxlist0)
  135. # 2
  136. # >>> boxlist0.extend(boxlist1)
  137. # >>> len(boxlist0)
  138. # 4
  139. # >>> boxlist1.extend([box0, box1])
  140. # >>> len(boxlist1)
  141. # 4
  142. #
  143. # ..
  144. # """
  145. # if hasattr(boxlist, 'c_boxlist'):
  146. # #import pdb; pdb.set_trace()
  147. # # FIXME: doesn't work
  148. # libvect.Vect_boxlist_append_boxlist(self.c_boxlist,
  149. # boxlist.c_boxlist)
  150. # else:
  151. # for box in boxlist:
  152. # self.append(box)
  153. def remove(self, indx):
  154. """Remove Bbox from the boxlist, given an integer or a list of integer
  155. or a boxlist, using ``Vect_boxlist_delete`` C function or the
  156. ``Vect_boxlist_delete_boxlist``. ::
  157. >>> boxlist = BoxList([Bbox(),
  158. ... Bbox(1, 0, 0, 1),
  159. ... Bbox(1, -1, -1, 1)])
  160. >>> boxlist.remove(0)
  161. >>> boxlist
  162. Boxlist([Bbox(1.0, 0.0, 0.0, 1.0), Bbox(1.0, -1.0, -1.0, 1.0)])
  163. ..
  164. """
  165. if hasattr(indx, 'c_boxlist'):
  166. libvect.Vect_boxlist_delete_boxlist(self.c_boxlist, indx.c_boxlist)
  167. elif isinstance(indx, int):
  168. libvect.Vect_boxlist_delete(self.c_boxlist, indx)
  169. else:
  170. for ind in indx:
  171. libvect.Vect_boxlist_delete(self.c_boxlist, ind)
  172. def reset(self):
  173. """Reset the c_boxlist C struct, using the ``Vect_reset_boxlist`` C
  174. function. ::
  175. >>> boxlist = BoxList([Bbox(),
  176. ... Bbox(1, 0, 0, 1),
  177. ... Bbox(1, -1, -1, 1)])
  178. >>> len(boxlist)
  179. 3
  180. >>> boxlist.reset()
  181. >>> len(boxlist)
  182. 0
  183. ..
  184. """
  185. libvect.Vect_reset_boxlist(self.c_boxlist)
  186. class Ilist(object):
  187. """Instantiate a list of integer using the C GRASS struct ``ilist``,
  188. the class contains this struct as ``c_ilist`` attribute. """
  189. def __init__(self, integer_list=None):
  190. self.c_ilist = ctypes.pointer(libvect.struct_ilist())
  191. if integer_list is not None:
  192. self.extend(integer_list)
  193. def __getitem__(self, key):
  194. if isinstance(key, slice):
  195. #import pdb; pdb.set_trace()
  196. #Get the start, stop, and step from the slice
  197. return [self.c_ilist.contents.value[indx]
  198. for indx in xrange(*key.indices(len(self)))]
  199. elif isinstance(key, int):
  200. if key < 0: # Handle negative indices
  201. key += self.c_ilist.contents.n_values
  202. if key >= self.c_ilist.contents.n_values:
  203. raise IndexError('Index out of range')
  204. return self.c_ilist.contents.value[key]
  205. else:
  206. raise ValueError("Invalid argument type: %r." % key)
  207. def __setitem__(self, key, value):
  208. if self.contains(value):
  209. raise ValueError('Integer already in the list')
  210. self.c_ilist.contents.value[key] = int(value)
  211. def __len__(self):
  212. return self.c_ilist.contents.n_values
  213. def __iter__(self):
  214. return [self.c_ilist.contents.value[i] for i in xrange(self.__len__())]
  215. def __repr__(self):
  216. return "Ilist(%r)" % repr(self.__iter__())
  217. def __contains__(self, item):
  218. return item in self.__iter__()
  219. def append(self, value):
  220. """Append an integer to the list"""
  221. if libvect.Vect_list_append(self.c_ilist, value):
  222. raise # TODO
  223. def reset(self):
  224. """Reset the list"""
  225. libvect.Vect_reset_list(self.c_ilist)
  226. def extend(self, ilist):
  227. """Extend the list with another Ilist object or
  228. with a list of integers"""
  229. if isinstance(ilist, Ilist):
  230. libvect.Vect_list_append_list(self.c_ilist, ilist.ilist)
  231. else:
  232. for i in ilist:
  233. self.append(i)
  234. def remove(self, value):
  235. """Remove a value from a list"""
  236. if isinstance(value, int):
  237. libvect.Vect_list_delete(self.c_ilist, value)
  238. elif isinstance(value, Ilist):
  239. libvect.Vect_list_delete_list(self.c_ilist, value.ilist)
  240. elif isinstance(value, Iterable):
  241. for i in value:
  242. libvect.Vect_list_delete(self.c_ilist, int(i))
  243. else:
  244. raise ValueError('Value: %r, is not supported' % value)
  245. def contains(self, value):
  246. """Check if value is in the list"""
  247. return bool(libvect.Vect_val_in_list(self.c_ilist, value))
  248. class Cats(object):
  249. """Instantiate a Category class that contains a ctypes pointer
  250. to the C line_cats struct. ::
  251. >>> cats = Cats()
  252. >>> for cat in xrange(100, 110): cats.set(cat, layer=cat-50)
  253. >>> cats.n_cats
  254. 10
  255. >>> cats.cat
  256. [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
  257. >>> cats.layer
  258. [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
  259. >>> cats.get() # default layer is 1
  260. (-1, 0)
  261. >>> cats.get(50)
  262. (100, 1)
  263. >>> cats.get(51)
  264. (101, 1)
  265. >>> cats.set(1001, 52)
  266. >>> cats.cat
  267. [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 1001]
  268. >>> cats.layer
  269. [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 52]
  270. >>> cats.get(52)
  271. (102, 2)
  272. >>> cats.reset()
  273. >>> cats.layer
  274. []
  275. >>> cats.cat
  276. []
  277. """
  278. @property
  279. def layer(self):
  280. field = self.c_cats.contents.field
  281. return [field[i] for i in xrange(self.n_cats)]
  282. @property
  283. def cat(self):
  284. cat = self.c_cats.contents.cat
  285. return [cat[i] for i in xrange(self.n_cats)]
  286. @property
  287. def n_cats(self):
  288. return self.c_cats.contents.n_cats
  289. def __init__(self, c_cats=None):
  290. self.c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
  291. def reset(self):
  292. """Reset the C cats struct from previous values."""
  293. libvect.Vect_reset_cats(self.c_cats)
  294. def get(self, layer=1):
  295. """Return the first found category of given layer
  296. and the number of category found. """
  297. cat = ctypes.c_int()
  298. n_cats = libvect.Vect_cat_get(self.c_cats, layer, ctypes.byref(cat))
  299. return cat.value, n_cats
  300. def set(self, cat, layer=1):
  301. """Add new field/cat to category structure if doesn't exist yet."""
  302. libvect.Vect_cat_set(self.c_cats, layer, cat)
  303. def delete(self, cat=None, layer=1):
  304. """If cat is given delete cat from line_cats structure
  305. (using Vect_field_cat_del) else delete all categories of given layer
  306. (using Vect_cat_del).
  307. """
  308. if cat:
  309. self.n_del = libvect.Vect_field_cat_del(self.c_cats, layer, cat)
  310. err_msg = "Layer(%d)/category(%d) number does not exist"
  311. err_msg = err_msg % (layer, cat)
  312. else:
  313. self.n_del = libvect.Vect_cat_del(self.c_cats, layer)
  314. err_msg = 'Layer: %r does not exist' % layer
  315. if self.n_del == 0:
  316. raise ValueError(err_msg)
  317. def check_cats_constraints(self, cats_list, layer=1):
  318. """Check if categories match with category constraints"""
  319. return bool(libvect.Vect_cats_in_constraint(self.c_cats, layer,
  320. cats_list.c_cat_list))
  321. def get_list(self, layer=1):
  322. """Get list of categories of given field."""
  323. ilist = Ilist()
  324. if libvect.Vect_field_cat_get(self.c_cats, layer,
  325. ilist.c_ilist) < 0:
  326. raise ValueError('Layer: %r does not exist' % layer)
  327. return ilist
  328. class CatsList(object):
  329. """::
  330. >>> cats_list = CatsList()
  331. >>> cats_list.min
  332. []
  333. >>> cats_list.max
  334. []
  335. >>> cats_list.n_ranges
  336. 0
  337. >>> cats_list.layer
  338. 0
  339. >>> string = "2,3,5-9,20"
  340. >>> cats_list.from_string(string)
  341. >>> cats_list.min
  342. [2, 3, 5, 20]
  343. >>> cats_list.max
  344. [2, 3, 9, 20]
  345. >>> cats_list.n_ranges
  346. 4
  347. """
  348. @property
  349. def layer(self):
  350. return self.c_cat_list.contents.field
  351. @property
  352. def n_ranges(self):
  353. return self.c_cat_list.contents.n_ranges
  354. @property
  355. def min(self):
  356. min_values = self.c_cat_list.contents.min
  357. return [min_values[i] for i in xrange(self.n_ranges)]
  358. @property
  359. def max(self):
  360. max_values = self.c_cat_list.contents.max
  361. return [max_values[i] for i in xrange(self.n_ranges)]
  362. def __init__(self, c_cat_list=None):
  363. self.c_cat_list = c_cat_list if c_cat_list \
  364. else ctypes.pointer(libvect.cat_list())
  365. def from_string(self, string):
  366. """Converts string of categories and cat ranges separated by commas
  367. to cat_list."""
  368. num_errors = libvect.Vect_str_to_cat_list(string, self.c_cat_list)
  369. if num_errors:
  370. from grass.pygrass.errors import GrassError
  371. raise GrassError("%d number of errors in ranges" % num_errors)
  372. def from_array(self, array):
  373. """Convert ordered array of integers to cat_list structure."""
  374. # Vect_array_to_cat_list(const int *vals, int nvals, ***)
  375. # TODO: it's not working
  376. libvect.Vect_array_to_cat_list(array, len(array), self.c_cat_list)
  377. def __contains__(self, cat):
  378. """Check if category number is in list.
  379. int Vect_cat_in_cat_list (int cat, const struct cat_list *list)"""
  380. return bool(libvect.Vect_cat_in_cat_list(cat, self.c_cat_list))