123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990 |
- from __future__ import print_function
- from os.path import join, exists
- import grass.lib.gis as libgis
- libgis.G_gisinit("")
- import grass.lib.vector as libvect
- import ctypes
- #
- # import pygrass modules
- #
- from grass.pygrass.vector.vector_type import VTYPE
- from grass.pygrass.errors import GrassError, must_be_open
- from grass.pygrass.gis import Location
- from grass.pygrass.vector.geometry import GEOOBJ as _GEOOBJ
- from grass.pygrass.vector.geometry import read_line, read_next_line
- from grass.pygrass.vector.geometry import Area as _Area
- from grass.pygrass.vector.abstract import Info
- from grass.pygrass.vector.basic import Bbox, Cats, Ilist
- _NUMOF = {
- "areas": libvect.Vect_get_num_areas,
- "dblinks": libvect.Vect_get_num_dblinks,
- "faces": libvect.Vect_get_num_faces,
- "holes": libvect.Vect_get_num_holes,
- "islands": libvect.Vect_get_num_islands,
- "kernels": libvect.Vect_get_num_kernels,
- "points": (libvect.Vect_get_num_primitives, libvect.GV_POINT),
- "lines": (libvect.Vect_get_num_primitives, libvect.GV_LINE),
- "centroids": (libvect.Vect_get_num_primitives, libvect.GV_CENTROID),
- "boundaries": (libvect.Vect_get_num_primitives, libvect.GV_BOUNDARY),
- "nodes": libvect.Vect_get_num_nodes,
- "updated_lines": libvect.Vect_get_num_updated_lines,
- "updated_nodes": libvect.Vect_get_num_updated_nodes,
- "volumes": libvect.Vect_get_num_volumes,
- }
- # For test purposes
- test_vector_name = "vector_doctest_map"
- # =============================================
- # VECTOR
- # =============================================
- class Vector(Info):
- """Vector class is the grass vector format without topology
- >>> from grass.pygrass.vector import Vector
- >>> test_vect = Vector(test_vector_name)
- >>> test_vect.is_open()
- False
- >>> test_vect.mapset
- ''
- >>> test_vect.exist()
- True
- >>> test_vect.overwrite
- False
- """
- def __init__(self, name, mapset="", *args, **kwargs):
- # Set map name and mapset
- super(Vector, self).__init__(name, mapset, *args, **kwargs)
- self._topo_level = 1
- self._class_name = "Vector"
- self.overwrite = False
- self._cats = []
- def __repr__(self):
- if self.exist():
- return "%s(%r, %r)" % (self._class_name, self.name, self.mapset)
- else:
- return "%s(%r)" % (self._class_name, self.name)
- def __iter__(self):
- """::
- >>> test_vect = Vector(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> features = [feature for feature in test_vect]
- >>> features[:3]
- [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
- >>> test_vect.close()
- ..
- """
- # return (self.read(f_id) for f_id in xrange(self.num_of_features()))
- return self
- @must_be_open
- def __next__(self):
- """::
- >>> test_vect = Vector(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect.next()
- Point(10.000000, 6.000000)
- >>> test_vect.next()
- Point(12.000000, 6.000000)
- >>> test_vect.close()
- ..
- """
- return read_next_line(
- self.c_mapinfo, self.table, self.writeable, is2D=not self.is_3D()
- )
- @must_be_open
- def next(self):
- return self.__next__()
- @must_be_open
- def rewind(self):
- """Rewind vector map to cause reads to start at beginning."""
- if libvect.Vect_rewind(self.c_mapinfo) == -1:
- raise GrassError("Vect_rewind raise an error.")
- @must_be_open
- def write(self, geo_obj, cat=None, attrs=None):
- """Write geometry features and attributes.
- :param geo_obj: a geometry grass object define in
- grass.pygrass.vector.geometry
- :type geo_obj: geometry GRASS object
- :param attrs: a list with the values that will be insert in the
- attribute table.
- :type attrs: list
- :param cat: The category of the geometry feature, otherwise the
- c_cats attribute of the geometry object will be used.
- :type cat: integer
- Open a new vector map ::
- >>> new = VectorTopo('newvect')
- >>> new.exist()
- False
- define the new columns of the attribute table ::
- >>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
- ... (u'name', 'TEXT')]
- open the vector map in write mode
- >>> new.open('w', tab_name='newvect', tab_cols=cols)
- import a geometry feature ::
- >>> from grass.pygrass.vector.geometry import Point
- create two points ::
- >>> point0 = Point(0, 0)
- >>> point1 = Point(1, 1)
- then write the two points on the map, with ::
- >>> new.write(point0, cat=1, attrs=('pub',))
- >>> new.write(point1, cat=2, attrs=('resturant',))
- commit the db changes ::
- >>> new.table.conn.commit()
- >>> new.table.execute().fetchall()
- [(1, 'pub'), (2, 'resturant')]
- close the vector map ::
- >>> new.close()
- >>> new.exist()
- True
- then play with the map ::
- >>> new.open(mode='r')
- >>> new.read(1)
- Point(0.000000, 0.000000)
- >>> new.read(2)
- Point(1.000000, 1.000000)
- >>> new.read(1).attrs['name']
- 'pub'
- >>> new.read(2).attrs['name']
- 'resturant'
- >>> new.close()
- >>> new.remove()
- """
- self.n_lines += 1
- if not isinstance(cat, int) and not isinstance(cat, str):
- # likely the case of using 7.0 API
- import warnings
- warnings.warn(
- "Vector.write(geo_obj, attrs=(...)) is"
- " deprecated, specify cat explicitly",
- DeprecationWarning,
- )
- # try to accommodate
- attrs = cat
- cat = None
- if attrs and cat is None:
- # TODO: this does not work as expected when there are
- # already features in the map when we opened it
- cat = (self._cats[-1] if self._cats else 0) + 1
- if cat is not None and cat not in self._cats:
- self._cats.append(cat)
- if self.table is not None and attrs is not None:
- attr = [
- cat,
- ]
- attr.extend(attrs)
- cur = self.table.conn.cursor()
- cur.execute(self.table.columns.insert_str, attr)
- cur.close()
- if cat is not None:
- cats = Cats(geo_obj.c_cats)
- cats.reset()
- cats.set(cat, self.layer)
- if geo_obj.gtype == _Area.gtype:
- result = self._write_area(geo_obj)
- result = libvect.Vect_write_line(
- self.c_mapinfo, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats
- )
- if result == -1:
- raise GrassError("Not able to write the vector feature.")
- if self._topo_level == 2:
- # return new feature id (on level 2)
- geo_obj.id = result
- else:
- # return offset into file where the feature starts (on level 1)
- geo_obj.offset = result
- @must_be_open
- def has_color_table(self):
- """Return if vector has color table associated in file system;
- Color table stored in the vector's attribute table well be not checked
- >>> test_vect = Vector(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect.has_color_table()
- False
- >>> test_vect.close()
- >>> from grass.pygrass.utils import copy, remove
- >>> copy(test_vector_name,'mytest_vect','vect')
- >>> from grass.pygrass.modules.shortcuts import vector as v
- >>> v.colors(map='mytest_vect', color='population', column='value')
- Module('v.colors')
- >>> mytest_vect = Vector('mytest_vect')
- >>> mytest_vect.open(mode='r')
- >>> mytest_vect.has_color_table()
- True
- >>> mytest_vect.close()
- >>> remove('mytest_vect', 'vect')
- """
- loc = Location()
- path = join(loc.path(), self.mapset, "vector", self.name, "colr")
- return True if exists(path) else False
- # =============================================
- # VECTOR WITH TOPOLOGY
- # =============================================
- class VectorTopo(Vector):
- """Vector class with the support of the GRASS topology.
- Open a vector map using the *with statement*: ::
- >>> with VectorTopo(test_vector_name, mode='r') as test_vect:
- ... for feature in test_vect[:7]:
- ... print(feature.attrs['name'])
- ...
- point
- point
- point
- line
- line
- line
- >>> test_vect.is_open()
- False
- ..
- """
- def __init__(self, name, mapset="", *args, **kwargs):
- super(VectorTopo, self).__init__(name, mapset, *args, **kwargs)
- self._topo_level = 2
- self._class_name = "VectorTopo"
- def __len__(self):
- return libvect.Vect_get_num_lines(self.c_mapinfo)
- def __getitem__(self, key):
- """::
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect[:4]
- [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
- >>> test_vect.close()
- ..
- """
- if isinstance(key, slice):
- return [
- self.read(indx)
- for indx in range(
- key.start if key.start else 1,
- key.stop if key.stop else len(self),
- key.step if key.step else 1,
- )
- ]
- elif isinstance(key, int):
- return self.read(key)
- else:
- raise ValueError("Invalid argument type: %r." % key)
- @must_be_open
- def num_primitive_of(self, primitive):
- """Return the number of primitive
- :param primitive: the name of primitive to query; the supported values are:
- * *boundary*,
- * *centroid*,
- * *face*,
- * *kernel*,
- * *line*,
- * *point*
- * *area*
- * *volume*
- :type primitive: str
- ::
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect.num_primitive_of('point')
- 3
- >>> test_vect.num_primitive_of('line')
- 3
- >>> test_vect.num_primitive_of('centroid')
- 4
- >>> test_vect.num_primitive_of('boundary')
- 11
- >>> test_vect.close()
- ..
- """
- return libvect.Vect_get_num_primitives(self.c_mapinfo, VTYPE[primitive])
- @must_be_open
- def number_of(self, vtype):
- """Return the number of the chosen element type
- :param vtype: the name of type to query; the supported values are:
- *areas*, *dblinks*, *faces*, *holes*, *islands*,
- *kernels*, *points*, *lines*, *centroids*, *boundaries*,
- *nodes*, *line_points*, *update_lines*, *update_nodes*,
- *volumes*
- :type vtype: str
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect.number_of("areas")
- 4
- >>> test_vect.number_of("islands")
- 2
- >>> test_vect.number_of("holes")
- 0
- >>> test_vect.number_of("lines")
- 3
- >>> test_vect.number_of("nodes")
- 15
- >>> test_vect.number_of("pizza")
- ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ValueError: vtype not supported, use one of: 'areas', ...
- >>> test_vect.close()
- ..
- """
- if vtype in _NUMOF.keys():
- if isinstance(_NUMOF[vtype], tuple):
- fn, ptype = _NUMOF[vtype]
- return fn(self.c_mapinfo, ptype)
- else:
- return _NUMOF[vtype](self.c_mapinfo)
- else:
- keys = "', '".join(sorted(_NUMOF.keys()))
- raise ValueError("vtype not supported, use one of: '%s'" % keys)
- @must_be_open
- def num_primitives(self):
- """Return dictionary with the number of all primitives"""
- output = {}
- for prim in VTYPE.keys():
- output[prim] = self.num_primitive_of(prim)
- return output
- @must_be_open
- def viter(self, vtype, idonly=False):
- """Return an iterator of vector features
- :param vtype: the name of type to query; the supported values are:
- *areas*, *dblinks*, *faces*, *holes*, *islands*,
- *kernels*, *line_points*, *lines*, *nodes*, *points*,
- *update_lines*, *update_nodes*, *volumes*
- :type vtype: str
- :param idonly: variable to return only the id of features instead of
- full features
- :type idonly: bool
- >>> test_vect = VectorTopo(test_vector_name, mode='r')
- >>> test_vect.open(mode='r')
- >>> areas = [area for area in test_vect.viter('areas')]
- >>> areas[:3]
- [Area(1), Area(2), Area(3)]
- to sort the result in a efficient way, use: ::
- >>> from operator import methodcaller as method
- >>> areas.sort(key=method('area'), reverse=True) # sort the list
- >>> for area in areas[:3]:
- ... print(area, area.area())
- Area(1) 12.0
- Area(2) 8.0
- Area(4) 8.0
- >>> areas = [area for area in test_vect.viter('areas')]
- >>> for area in areas:
- ... print(area.centroid().cat)
- 3
- 3
- 3
- 3
- >>> test_vect.close()
- """
- if vtype in _GEOOBJ.keys():
- if _GEOOBJ[vtype] is not None:
- ids = (indx for indx in range(1, self.number_of(vtype) + 1))
- if idonly:
- return ids
- return (
- _GEOOBJ[vtype](
- v_id=indx,
- c_mapinfo=self.c_mapinfo,
- table=self.table,
- writeable=self.writeable,
- )
- for indx in ids
- )
- else:
- keys = "', '".join(sorted(_GEOOBJ.keys()))
- raise ValueError("vtype not supported, use one of: '%s'" % keys)
- @must_be_open
- def rewind(self):
- """Rewind vector map to cause reads to start at beginning. ::
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> test_vect.next()
- Point(10.000000, 6.000000)
- >>> test_vect.next()
- Point(12.000000, 6.000000)
- >>> test_vect.next()
- Point(14.000000, 6.000000)
- >>> test_vect.rewind()
- >>> test_vect.next()
- Point(10.000000, 6.000000)
- >>> test_vect.close()
- ..
- """
- libvect.Vect_rewind(self.c_mapinfo)
- @must_be_open
- def cat(self, cat_id, vtype, layer=None, generator=False, geo=None):
- """Return the geometry features with category == cat_id.
- :param cat_id: the category number
- :type cat_id: int
- :param vtype: the type of geometry feature that we are looking for
- :type vtype: str
- :param layer: the layer number that will be used
- :type layer: int
- :param generator: if True return a generator otherwise it return a
- list of features
- :type generator: bool
- """
- if geo is None and vtype not in _GEOOBJ:
- keys = "', '".join(sorted(_GEOOBJ.keys()))
- raise ValueError("vtype not supported, use one of: '%s'" % keys)
- Obj = _GEOOBJ[vtype] if geo is None else geo
- ilist = Ilist()
- libvect.Vect_cidx_find_all(
- self.c_mapinfo,
- layer if layer else self.layer,
- Obj.gtype,
- cat_id,
- ilist.c_ilist,
- )
- is2D = not self.is_3D()
- if generator:
- return (
- read_line(
- feature_id=v_id,
- c_mapinfo=self.c_mapinfo,
- table=self.table,
- writeable=self.writeable,
- is2D=is2D,
- )
- for v_id in ilist
- )
- else:
- return [
- read_line(
- feature_id=v_id,
- c_mapinfo=self.c_mapinfo,
- table=self.table,
- writeable=self.writeable,
- is2D=is2D,
- )
- for v_id in ilist
- ]
- @must_be_open
- def read(self, feature_id):
- """Return a geometry object given the feature id.
- :param int feature_id: the id of feature to obtain
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open(mode='r')
- >>> feature1 = test_vect.read(0) #doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: The index must be >0, 0 given.
- >>> feature1 = test_vect.read(5)
- >>> feature1
- Line([Point(12.000000, 4.000000), Point(12.000000, 2.000000), Point(12.000000, 0.000000)])
- >>> feature1.length()
- 4.0
- >>> test_vect.read(-1)
- Centroid(7.500000, 3.500000)
- >>> len(test_vect)
- 21
- >>> test_vect.read(21)
- Centroid(7.500000, 3.500000)
- >>> test_vect.read(22) #doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- IndexError: Index out of range
- >>> test_vect.close()
- """
- return read_line(
- feature_id,
- self.c_mapinfo,
- self.table,
- self.writeable,
- is2D=not self.is_3D(),
- )
- @must_be_open
- def is_empty(self):
- """Return if a vector map is empty or not"""
- primitives = self.num_primitives()
- output = True
- for v in primitives.values():
- if v != 0:
- output = False
- break
- return output
- @must_be_open
- def rewrite(self, geo_obj, cat, attrs=None, **kargs):
- """Rewrite a geometry features
- >>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
- ... (u'name', 'TEXT')]
- Generate a new vector map
- >>> test_vect = VectorTopo('newvect_2')
- >>> test_vect.open('w', tab_name='newvect_2', tab_cols=cols,
- ... overwrite=True)
- import a geometry feature ::
- >>> from grass.pygrass.vector.geometry import Point
- create two points ::
- >>> point0 = Point(0, 0)
- >>> point1 = Point(1, 1)
- >>> point2 = Point(2, 2)
- then write the two points on the map, with ::
- >>> test_vect.write(point0, cat=1, attrs=('pub',))
- >>> test_vect.write(point1, cat=2, attrs=('resturant',))
- >>> test_vect.table.conn.commit() # save changes in the DB
- >>> test_vect.table_to_dict()
- {1: [1, 'pub'], 2: [2, 'resturant']}
- >>> test_vect.close()
- Now rewrite one point of the vector map: ::
- >>> test_vect.open('rw')
- >>> test_vect.rewrite(point2, cat=1, attrs=('Irish Pub',))
- >>> test_vect.table.conn.commit() # save changes in the DB
- >>> test_vect.close()
- Check the output:
- >>> test_vect.open('r')
- >>> test_vect[1] == point2
- True
- >>> test_vect[1].attrs['name'] == 'Irish Pub'
- True
- >>> test_vect.close()
- >>> test_vect.remove()
- """
- if self.table is not None and attrs:
- self.table.update(key=cat, values=attrs)
- elif self.table is None and attrs:
- print(
- "Table for vector {name} does not exist, attributes not"
- " loaded".format(name=self.name)
- )
- libvect.Vect_cat_set(geo_obj.c_cats, self.layer, cat)
- result = libvect.Vect_rewrite_line(
- self.c_mapinfo, cat, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats
- )
- if result == -1:
- raise GrassError("Not able to write the vector feature.")
- # return offset into file where the feature starts
- geo_obj.offset = result
- @must_be_open
- def delete(self, feature_id):
- """Remove a feature by its id
- :param feature_id: the id of the feature
- :type feature_id: int
- """
- if libvect.Vect_rewrite_line(self.c_mapinfo, feature_id) == -1:
- raise GrassError("C function: Vect_rewrite_line.")
- @must_be_open
- def restore(self, geo_obj):
- if hasattr(geo_obj, "offset"):
- if (
- libvect.Vect_restore_line(self.c_mapinfo, geo_obj.offset, geo_obj.id)
- == -1
- ):
- raise GrassError("C function: Vect_restore_line.")
- else:
- raise ValueError("The value have not an offset attribute.")
- @must_be_open
- def bbox(self):
- """Return the BBox of the vecor map"""
- bbox = Bbox()
- if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
- raise GrassError("I can not find the Bbox.")
- return bbox
- def close(self, build=True, release=True):
- """Close the VectorTopo map, if release is True, the memory
- occupied by spatial index is released"""
- if release:
- libvect.Vect_set_release_support(self.c_mapinfo)
- super(VectorTopo, self).close(build=build)
- @must_be_open
- def table_to_dict(self, where=None):
- """Return the attribute table as a dictionary with the category as keys
- The columns have the order of the self.table.columns.names() list.
- Examples
- >>> from grass.pygrass.vector import VectorTopo
- >>> from grass.pygrass.vector.basic import Bbox
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open('r')
- >>> test_vect.table_to_dict()
- {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
- >>> test_vect.table_to_dict(where="value > 2")
- {3: [3, 'centroid', 3.0]}
- >>> test_vect.table_to_dict(where="value > 0")
- {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
- >>> test_vect.table.filters.get_sql()
- 'SELECT cat,name,value FROM vector_doctest_map WHERE value > 0 ORDER BY cat;'
- """
- if self.table is not None:
- table_dict = {}
- # Get the category index
- cat_index = self.table.columns.names().index("cat")
- # Prepare a filter
- if where is not None:
- self.table.filters.where(where)
- self.table.filters.order_by("cat")
- self.table.filters.select(",".join(self.table.columns.names()))
- # Execute the query and fetch the result
- cur = self.table.execute()
- entries = cur.fetchall()
- # Generate the dictionary
- for entry in entries:
- table_dict[entry[cat_index]] = list(entry)
- return table_dict
- return None
- @must_be_open
- def features_to_wkb_list(self, bbox=None, feature_type="point", field=1):
- """Return all features of type point, line, boundary or centroid
- as a list of Well Known Binary representations (WKB)
- (id, cat, wkb) triplets located in a specific
- bounding box.
- :param bbox: The boundingbox to search for features,
- if bbox=None the boundingbox of the whole
- vector map layer is used
- :type bbox: grass.pygrass.vector.basic.Bbox
- :param feature_type: The type of feature that should be converted to
- the Well Known Binary (WKB) format. Supported are:
- 'point' -> libvect.GV_POINT 1
- 'line' -> libvect.GV_LINE 2
- 'boundary' -> libvect.GV_BOUNDARY 3
- 'centroid' -> libvect.GV_CENTROID 4
- :type type: string
- :param field: The category field
- :type field: integer
- :return: A list of triplets, or None if nothing was found
- The well known binary are stored in byte arrays.
- Examples:
- >>> from grass.pygrass.vector import VectorTopo
- >>> from grass.pygrass.vector.basic import Bbox
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open('r')
- >>> bbox = Bbox(north=20, south=-1, east=20, west=-1)
- >>> result = test_vect.features_to_wkb_list(bbox=bbox,
- ... feature_type="point")
- >>> len(result)
- 3
- >>> for entry in result:
- ... f_id, cat, wkb = entry
- ... print((f_id, cat, len(wkb)))
- (1, 1, 21)
- (2, 1, 21)
- (3, 1, 21)
- >>> result = test_vect.features_to_wkb_list(bbox=None,
- ... feature_type="line")
- >>> len(result)
- 3
- >>> for entry in result:
- ... f_id, cat, wkb = entry
- ... print((f_id, cat, len(wkb)))
- (4, 2, 57)
- (5, 2, 57)
- (6, 2, 57)
- >>> result = test_vect.features_to_wkb_list(bbox=bbox,
- ... feature_type="boundary")
- >>> len(result)
- 11
- >>> result = test_vect.features_to_wkb_list(bbox=None,
- ... feature_type="centroid")
- >>> len(result)
- 4
- >>> for entry in result:
- ... f_id, cat, wkb = entry
- ... print((f_id, cat, len(wkb)))
- (19, 3, 21)
- (18, 3, 21)
- (20, 3, 21)
- (21, 3, 21)
- >>> result = test_vect.features_to_wkb_list(bbox=bbox,
- ... feature_type="blub")
- Traceback (most recent call last):
- ...
- grass.exceptions.GrassError: Unsupported feature type <blub>, supported are <point,line,boundary,centroid>
- >>> test_vect.close()
- """
- supported = ["point", "line", "boundary", "centroid"]
- if feature_type.lower() not in supported:
- raise GrassError(
- "Unsupported feature type <%s>, "
- "supported are <%s>" % (feature_type, ",".join(supported))
- )
- if bbox is None:
- bbox = self.bbox()
- bboxlist = self.find_by_bbox.geos(
- bbox, type=feature_type.lower(), bboxlist_only=True
- )
- if bboxlist is not None and len(bboxlist) > 0:
- wkb_list = []
- line_p = libvect.line_pnts()
- line_c = libvect.line_cats()
- size = ctypes.c_size_t()
- cat = ctypes.c_int()
- error = ctypes.c_int()
- for f_id in bboxlist.ids:
- barray = libvect.Vect_read_line_to_wkb(
- self.c_mapinfo,
- ctypes.byref(line_p),
- ctypes.byref(line_c),
- f_id,
- ctypes.byref(size),
- ctypes.byref(error),
- )
- if not barray:
- if error == -1:
- raise GrassError(
- _("Unable to read line of feature %i" % (f_id))
- )
- if error == -2:
- print("Empty feature %i" % (f_id))
- continue
- ok = libvect.Vect_cat_get(
- ctypes.byref(line_c), field, ctypes.byref(cat)
- )
- if ok < 1:
- pcat = None
- else:
- pcat = cat.value
- wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value)))
- libgis.G_free(barray)
- return wkb_list
- return None
- @must_be_open
- def areas_to_wkb_list(self, bbox=None, field=1):
- """Return all features of type point, line, boundary or centroid
- as a list of Well Known Binary representations (WKB)
- (id, cat, wkb) triplets located in a specific
- bounding box.
- :param bbox: The boundingbox to search for features,
- if bbox=None the boundingbox of the whole
- vector map layer is used
- :type bbox: grass.pygrass.vector.basic.Bbox
- :param field: The centroid category field
- :type field: integer
- :return: A list of triplets, or None if nothing was found
- The well known binary are stored in byte arrays.
- Examples:
- >>> from grass.pygrass.vector import VectorTopo
- >>> from grass.pygrass.vector.basic import Bbox
- >>> test_vect = VectorTopo(test_vector_name)
- >>> test_vect.open('r')
- >>> bbox = Bbox(north=20, south=-1, east=20, west=-1)
- >>> result = test_vect.areas_to_wkb_list(bbox=bbox)
- >>> len(result)
- 4
- >>> for entry in result:
- ... a_id, cat, wkb = entry
- ... print((a_id, cat, len(wkb)))
- (1, 3, 225)
- (2, 3, 141)
- (3, 3, 93)
- (4, 3, 141)
- >>> result = test_vect.areas_to_wkb_list()
- >>> len(result)
- 4
- >>> for entry in result:
- ... a_id, cat, wkb = entry
- ... print((a_id, cat, len(wkb)))
- (1, 3, 225)
- (2, 3, 141)
- (3, 3, 93)
- (4, 3, 141)
- >>> test_vect.close()
- """
- if bbox is None:
- bbox = self.bbox()
- bboxlist = self.find_by_bbox.areas(bbox, bboxlist_only=True)
- if bboxlist is not None and len(bboxlist) > 0:
- wkb_list = []
- line_c = libvect.line_cats()
- size = ctypes.c_size_t()
- cat = ctypes.c_int()
- for a_id in bboxlist.ids:
- barray = libvect.Vect_read_area_to_wkb(
- self.c_mapinfo, a_id, ctypes.byref(size)
- )
- if not barray:
- raise GrassError(_("Unable to read area with id %i" % (a_id)))
- pcat = None
- c_ok = libvect.Vect_get_area_cats(
- self.c_mapinfo, a_id, ctypes.byref(line_c)
- )
- if c_ok == 0: # Centroid found
- ok = libvect.Vect_cat_get(
- ctypes.byref(line_c), field, ctypes.byref(cat)
- )
- if ok > 0:
- pcat = cat.value
- wkb_list.append((a_id, pcat, ctypes.string_at(barray, size.value)))
- libgis.G_free(barray)
- return wkb_list
- return None
- if __name__ == "__main__":
- import doctest
- from grass.pygrass import utils
- utils.create_test_vector_map(test_vector_name)
- doctest.testmod()
- """Remove the generated vector map, if exist"""
- from grass.pygrass.utils import get_mapset_vector
- from grass.script.core import run_command
- mset = get_mapset_vector(test_vector_name, mapset="")
- if mset:
- run_command("g.remove", flags="f", type="vector", name=test_vector_name)
|