basic.py 17 KB

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