123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- # -*- coding: utf-8 -*-
- """
- Created on Tue Jul 31 13:06:20 2012
- @author: pietro
- """
- import ctypes
- import grass.lib.vector as libvect
- from collections.abc import Iterable
- from grass.pygrass.shell.conversion import dict2html
- class Bbox(object):
- """Instantiate a Bounding Box class that contains
- a ctypes pointer to the C struct bound_box, that could be used
- by C GRASS functions.
- >>> bbox = Bbox()
- >>> bbox
- Bbox(0.0, 0.0, 0.0, 0.0)
- The default parameters are 0. It is possible to set or change
- the parameters later, with:
- >>> bbox.north = 10
- >>> bbox.south = -10
- >>> bbox.east = -20
- >>> bbox.west = 20
- >>> bbox
- Bbox(10.0, -10.0, -20.0, 20.0)
- Or directly istantiate the class with the values, with:
- >>> bbox = Bbox(north=100, south=0, east=0, west=100)
- >>> bbox
- Bbox(100.0, 0.0, 0.0, 100.0)
- ..
- """
- def __init__(self, north=0, south=0, east=0, west=0, top=0, bottom=0):
- self.c_bbox = ctypes.pointer(libvect.bound_box())
- self.north = north
- self.south = south
- self.east = east
- self.west = west
- self.top = top
- self.bottom = bottom
- def _get_n(self):
- """Private method to obtain the north value"""
- return self.c_bbox.contents.N
- def _set_n(self, value):
- """Private method to set the north value"""
- self.c_bbox.contents.N = value
- north = property(fget=_get_n, fset=_set_n,
- doc="Set and obtain north value")
- def _get_s(self):
- """Private method to obtain the south value"""
- return self.c_bbox.contents.S
- def _set_s(self, value):
- """Private method to set the south value"""
- self.c_bbox.contents.S = value
- south = property(fget=_get_s, fset=_set_s,
- doc="Set and obtain south value")
- def _get_e(self):
- """Private method to obtain the east value"""
- return self.c_bbox.contents.E
- def _set_e(self, value):
- """Private method to set the east value"""
- self.c_bbox.contents.E = value
- east = property(fget=_get_e, fset=_set_e,
- doc="Set and obtain east value")
- def _get_w(self):
- """Private method to obtain the west value"""
- return self.c_bbox.contents.W
- def _set_w(self, value):
- """Private method to set the west value"""
- self.c_bbox.contents.W = value
- west = property(fget=_get_w, fset=_set_w,
- doc="Set and obtain west value")
- def _get_t(self):
- """Private method to obtain the top value"""
- return self.c_bbox.contents.T
- def _set_t(self, value):
- """Private method to set the top value"""
- self.c_bbox.contents.T = value
- top = property(fget=_get_t, fset=_set_t,
- doc="Set and obtain top value")
- def _get_b(self):
- """Private method to obtain the bottom value"""
- return self.c_bbox.contents.B
- def _set_b(self, value):
- """Private method to set the bottom value"""
- self.c_bbox.contents.B = value
- bottom = property(fget=_get_b, fset=_set_b,
- doc="Set and obtain bottom value")
- def __repr__(self):
- return "Bbox({n}, {s}, {e}, {w})".format(n=self.north, s=self.south,
- e=self.east, w=self.west)
- def _repr_html_(self):
- return dict2html(dict(self.items()), keys=self.keys(),
- border='1', kdec='b')
- def keys(self):
- return ['north', 'south', 'west', 'east', 'top', 'bottom']
- def contains(self, point):
- """Return True if the object is contained by the BoundingBox
- :param point: the point to analyze
- :type point: a Point object or a tuple with the coordinates
- >>> from grass.pygrass.vector.geometry import Point
- >>> poi = Point(5,5)
- >>> bbox = Bbox(north=10, south=0, west=0, east=10)
- >>> bbox.contains(poi)
- True
- """
- return bool(libvect.Vect_point_in_box(point.x, point.y,
- point.z if point.z else 0,
- self.c_bbox))
- def items(self):
- return [(k, self.__getattribute__(k)) for k in self.keys()]
- def nsewtb(self, tb=True):
- """Return a list of values from bounding box
- :param tb: if tb parameter is False return only: north, south, east,
- west and not top and bottom
- :type tb: bool
- """
- if tb:
- return (self.north, self.south, self.east, self.west,
- self.top, self.bottom)
- else:
- return (self.north, self.south, self.east, self.west)
- class BoxList(object):
- """Instantiate a BoxList class to create a list of Bounding Box"""
- def __init__(self, boxlist=None):
- self.c_boxlist = ctypes.pointer(libvect.boxlist())
- # if set to 0, the list will hold only ids and no boxes
- self.c_boxlist.contents.have_boxes = 1
- if boxlist is not None:
- for box in boxlist:
- self.append(box)
- @property
- def ids(self):
- return [self.c_boxlist.contents.id[i] for i in range(self.n_values)]
- @property
- def n_values(self):
- return self.c_boxlist.contents.n_values
- def have_boxes(self):
- return bool(self.c_boxlist.contents.have_boxes)
- def __len__(self):
- return self.c_boxlist.contents.n_values
- def __repr__(self):
- return "Boxlist([%s])" % ", ".join([repr(box)
- for box in self.__iter__()])
- def __getitem__(self, indx):
- bbox = Bbox()
- bbox.c_bbox = ctypes.pointer(self.c_boxlist.contents.box[indx])
- return bbox
- def __setitem__(self, indx, bbox):
- self.c_boxlist.contents.box[indx] = bbox
- def __iter__(self):
- return (self.__getitem__(box_id) for box_id in range(self.__len__()))
- def __str__(self):
- return self.__repr__()
- def append(self, box):
- """Append a Bbox object to a Boxlist object, using the
- ``Vect_boxlist_append`` C function.
- :param bbox: the bounding box to add to the list
- :param bbox: a Bbox object
- >>> box0 = Bbox()
- >>> box1 = Bbox(1,2,3,4)
- >>> box2 = Bbox(5,6,7,8)
- >>> boxlist = BoxList([box0, box1])
- >>> boxlist
- Boxlist([Bbox(0.0, 0.0, 0.0, 0.0), Bbox(1.0, 2.0, 3.0, 4.0)])
- >>> len(boxlist)
- 2
- >>> boxlist.append(box2)
- >>> len(boxlist)
- 3
- """
- indx = self.__len__()
- libvect.Vect_boxlist_append(self.c_boxlist, indx, box.c_bbox)
- # def extend(self, boxlist):
- # """Extend a boxlist with another boxlist or using a list of Bbox, using
- # ``Vect_boxlist_append_boxlist`` c function. ::
- #
- # >>> box0 = Bbox()
- # >>> box1 = Bbox(1,2,3,4)
- # >>> box2 = Bbox(5,6,7,8)
- # >>> box3 = Bbox(9,8,7,6)
- # >>> boxlist0 = BoxList([box0, box1])
- # >>> boxlist0
- # Boxlist([Bbox(0.0, 0.0, 0.0, 0.0), Bbox(1.0, 2.0, 3.0, 4.0)])
- # >>> boxlist1 = BoxList([box2, box3])
- # >>> len(boxlist0)
- # 2
- # >>> boxlist0.extend(boxlist1)
- # >>> len(boxlist0)
- # 4
- # >>> boxlist1.extend([box0, box1])
- # >>> len(boxlist1)
- # 4
- #
- # ..
- # """
- # if hasattr(boxlist, 'c_boxlist'):
- # #import pdb; pdb.set_trace()
- # # FIXME: doesn't work
- # libvect.Vect_boxlist_append_boxlist(self.c_boxlist,
- # boxlist.c_boxlist)
- # else:
- # for box in boxlist:
- # self.append(box)
- def remove(self, indx):
- """Remove Bbox from the boxlist, given an integer or a list of integer
- or a boxlist, using ``Vect_boxlist_delete`` C function or the
- ``Vect_boxlist_delete_boxlist``.
- :param indx: the index value of the Bbox to remove
- :param indx: int
- >>> boxlist = BoxList([Bbox(),
- ... Bbox(1, 0, 0, 1),
- ... Bbox(1, -1, -1, 1)])
- >>> boxlist.remove(0)
- >>> boxlist
- Boxlist([Bbox(1.0, 0.0, 0.0, 1.0), Bbox(1.0, -1.0, -1.0, 1.0)])
- """
- if hasattr(indx, 'c_boxlist'):
- libvect.Vect_boxlist_delete_boxlist(self.c_boxlist, indx.c_boxlist)
- elif isinstance(indx, int):
- libvect.Vect_boxlist_delete(self.c_boxlist, indx)
- else:
- for ind in indx:
- libvect.Vect_boxlist_delete(self.c_boxlist, ind)
- def reset(self):
- """Reset the c_boxlist C struct, using the ``Vect_reset_boxlist`` C
- function.
- >>> boxlist = BoxList([Bbox(),
- ... Bbox(1, 0, 0, 1),
- ... Bbox(1, -1, -1, 1)])
- >>> len(boxlist)
- 3
- >>> boxlist.reset()
- >>> len(boxlist)
- 0
- """
- libvect.Vect_reset_boxlist(self.c_boxlist)
- class Ilist(object):
- """Instantiate a list of integer using the C GRASS struct ``ilist``,
- the class contains this struct as ``c_ilist`` attribute. """
- def __init__(self, integer_list=None):
- self.c_ilist = ctypes.pointer(libvect.struct_ilist())
- if integer_list is not None:
- self.extend(integer_list)
- def __getitem__(self, key):
- if isinstance(key, slice):
- #import pdb; pdb.set_trace()
- #Get the start, stop, and step from the slice
- return [self.c_ilist.contents.value[indx]
- for indx in range(*key.indices(len(self)))]
- elif isinstance(key, int):
- if key < 0: # Handle negative indices
- key += self.c_ilist.contents.n_values
- if key >= self.c_ilist.contents.n_values:
- raise IndexError('Index out of range')
- return self.c_ilist.contents.value[key]
- else:
- raise ValueError("Invalid argument type: %r." % key)
- def __setitem__(self, key, value):
- if self.contains(value):
- raise ValueError('Integer already in the list')
- self.c_ilist.contents.value[key] = int(value)
- def __len__(self):
- return self.c_ilist.contents.n_values
- def __iter__(self):
- return (self.c_ilist.contents.value[i] for i in range(self.__len__()))
- def __repr__(self):
- return "Ilist(%r)" % [i for i in self.__iter__()]
- def __contains__(self, item):
- return item in self.__iter__()
- def append(self, value):
- """Append an integer to the list"""
- if libvect.Vect_list_append(self.c_ilist, value):
- raise # TODO
- def reset(self):
- """Reset the list"""
- libvect.Vect_reset_list(self.c_ilist)
- def extend(self, ilist):
- """Extend the list with another Ilist object or
- with a list of integers
- :param ilist: the ilist to append
- :type ilist: a Ilist object
- """
- if isinstance(ilist, Ilist):
- libvect.Vect_list_append_list(self.c_ilist, ilist.ilist)
- else:
- for i in ilist:
- self.append(i)
- def remove(self, value):
- """Remove a value from a list"""
- if isinstance(value, int):
- libvect.Vect_list_delete(self.c_ilist, value)
- elif isinstance(value, Ilist):
- libvect.Vect_list_delete_list(self.c_ilist, value.ilist)
- elif isinstance(value, Iterable):
- for i in value:
- libvect.Vect_list_delete(self.c_ilist, int(i))
- else:
- raise ValueError('Value: %r, is not supported' % value)
- def contains(self, value):
- """Check if value is in the list"""
- return bool(libvect.Vect_val_in_list(self.c_ilist, value))
- class Cats(object):
- """Instantiate a Category class that contains a ctypes pointer
- to the C line_cats struct.
- >>> cats = Cats()
- >>> for cat in range(100, 110): cats.set(cat, layer=cat-50)
- >>> cats.n_cats
- 10
- >>> cats.cat
- [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
- >>> cats.layer
- [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
- >>> cats.get() # default layer is 1
- (-1, 0)
- >>> cats.get(50)
- (100, 1)
- >>> cats.get(51)
- (101, 1)
- >>> cats.set(1001, 52)
- >>> cats.cat
- [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 1001]
- >>> cats.layer
- [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 52]
- >>> cats.get(52)
- (102, 2)
- >>> cats.reset()
- >>> cats.layer
- []
- >>> cats.cat
- []
- """
- @property
- def layer(self):
- field = self.c_cats.contents.field
- return [field[i] for i in range(self.n_cats)]
- @property
- def cat(self):
- cat = self.c_cats.contents.cat
- return [cat[i] for i in range(self.n_cats)]
- @property
- def n_cats(self):
- """Return the number of categories"""
- return self.c_cats.contents.n_cats
- def __init__(self, c_cats=None):
- self.c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
- def reset(self):
- """Reset the C cats struct from previous values."""
- libvect.Vect_reset_cats(self.c_cats)
- def get(self, layer=1):
- """Return the first found category of given layer
- and the number of category found.
- :param layer: the number of layer
- :type layer: int
- """
- cat = ctypes.c_int()
- n_cats = libvect.Vect_cat_get(self.c_cats, layer, ctypes.byref(cat))
- return cat.value, n_cats
- def set(self, cat, layer=1):
- """Add new field/cat to category structure if doesn't exist yet.
- :param cat: the cat to add
- :type cat: int
- :param layer: the number of layer
- :type layer: int
- """
- libvect.Vect_cat_set(self.c_cats, layer, cat)
- def delete(self, cat=None, layer=1):
- """If cat is given delete cat from line_cats structure
- (using Vect_field_cat_del) else delete all categories of given layer
- (using Vect_cat_del).
- :param cat: the cat to add
- :type cat: int
- :param layer: the number of layer
- :type layer: int
- """
- if cat:
- self.n_del = libvect.Vect_field_cat_del(self.c_cats, layer, cat)
- err_msg = "Layer(%d)/category(%d) number does not exist"
- err_msg = err_msg % (layer, cat)
- else:
- self.n_del = libvect.Vect_cat_del(self.c_cats, layer)
- err_msg = 'Layer: %r does not exist' % layer
- if self.n_del == 0:
- raise ValueError(err_msg)
- def check_cats_constraints(self, cats_list, layer=1):
- """Check if categories match with category constraints
- :param cats_list: a list of categories
- :type cats_list: list
- :param layer: the number of layer
- :type layer: int
- """
- return bool(libvect.Vect_cats_in_constraint(self.c_cats, layer,
- cats_list.c_cat_list))
- def get_list(self, layer=1):
- """Get list of categories of given field.
- :param layer: the number of layer
- :type layer: int
- """
- ilist = Ilist()
- if libvect.Vect_field_cat_get(self.c_cats, layer,
- ilist.c_ilist) < 0:
- raise ValueError('Layer: %r does not exist' % layer)
- return ilist
- class CatsList(object):
- """
- >>> cats_list = CatsList()
- >>> cats_list.min
- []
- >>> cats_list.max
- []
- >>> cats_list.n_ranges
- 0
- >>> cats_list.layer
- 0
- >>> string = "2,3,5-9,20"
- >>> cats_list.from_string(string)
- >>> cats_list.min
- [2, 3, 5, 20]
- >>> cats_list.max
- [2, 3, 9, 20]
- >>> cats_list.n_ranges
- 4
- """
- @property
- def layer(self):
- """Return the layer number"""
- return self.c_cat_list.contents.field
- @property
- def n_ranges(self):
- """Return the ranges number"""
- return self.c_cat_list.contents.n_ranges
- @property
- def min(self):
- """Return the minimum value"""
- min_values = self.c_cat_list.contents.min
- return [min_values[i] for i in range(self.n_ranges)]
- @property
- def max(self):
- """Return the maximum value"""
- max_values = self.c_cat_list.contents.max
- return [max_values[i] for i in range(self.n_ranges)]
- def __init__(self, c_cat_list=None):
- self.c_cat_list = c_cat_list if c_cat_list \
- else ctypes.pointer(libvect.cat_list())
- def from_string(self, string):
- """Converts string of categories and cat ranges separated by commas
- to cat_list.
- :param string: a string containing the cats separated by commas
- :type string: str
- """
- num_errors = libvect.Vect_str_to_cat_list(string, self.c_cat_list)
- if num_errors:
- from grass.pygrass.errors import GrassError
- raise GrassError("%d number of errors in ranges" % num_errors)
- def from_array(self, array):
- """Convert ordered array of integers to cat_list structure.
- :param array: the input array containing the cats
- :type array: array
- """
- # Vect_array_to_cat_list(const int *vals, int nvals, ***)
- # TODO: it's not working
- libvect.Vect_array_to_cat_list(array, len(array), self.c_cat_list)
- def __contains__(self, cat):
- """Check if category number is in list.
- :param cat: the category number
- :type cat: int
- """
- return bool(libvect.Vect_cat_in_cat_list(cat, self.c_cat_list))
- if __name__ == "__main__":
- import doctest
- doctest.testmod()
|