__init__.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. from __future__ import print_function
  2. from os.path import join, exists
  3. import grass.lib.gis as libgis
  4. libgis.G_gisinit("")
  5. import grass.lib.vector as libvect
  6. import ctypes
  7. #
  8. # import pygrass modules
  9. #
  10. from grass.pygrass.vector.vector_type import VTYPE
  11. from grass.pygrass.errors import GrassError, must_be_open
  12. from grass.pygrass.gis import Location
  13. from grass.pygrass.vector.geometry import GEOOBJ as _GEOOBJ
  14. from grass.pygrass.vector.geometry import read_line, read_next_line
  15. from grass.pygrass.vector.geometry import Area as _Area
  16. from grass.pygrass.vector.abstract import Info
  17. from grass.pygrass.vector.basic import Bbox, Cats, Ilist
  18. _NUMOF = {
  19. "areas": libvect.Vect_get_num_areas,
  20. "dblinks": libvect.Vect_get_num_dblinks,
  21. "faces": libvect.Vect_get_num_faces,
  22. "holes": libvect.Vect_get_num_holes,
  23. "islands": libvect.Vect_get_num_islands,
  24. "kernels": libvect.Vect_get_num_kernels,
  25. "points": (libvect.Vect_get_num_primitives, libvect.GV_POINT),
  26. "lines": (libvect.Vect_get_num_primitives, libvect.GV_LINE),
  27. "centroids": (libvect.Vect_get_num_primitives, libvect.GV_CENTROID),
  28. "boundaries": (libvect.Vect_get_num_primitives, libvect.GV_BOUNDARY),
  29. "nodes": libvect.Vect_get_num_nodes,
  30. "updated_lines": libvect.Vect_get_num_updated_lines,
  31. "updated_nodes": libvect.Vect_get_num_updated_nodes,
  32. "volumes": libvect.Vect_get_num_volumes,
  33. }
  34. # For test purposes
  35. test_vector_name = "vector_doctest_map"
  36. # =============================================
  37. # VECTOR
  38. # =============================================
  39. class Vector(Info):
  40. """Vector class is the grass vector format without topology
  41. >>> from grass.pygrass.vector import Vector
  42. >>> test_vect = Vector(test_vector_name)
  43. >>> test_vect.is_open()
  44. False
  45. >>> test_vect.mapset
  46. ''
  47. >>> test_vect.exist()
  48. True
  49. >>> test_vect.overwrite
  50. False
  51. """
  52. def __init__(self, name, mapset="", *args, **kwargs):
  53. # Set map name and mapset
  54. super(Vector, self).__init__(name, mapset, *args, **kwargs)
  55. self._topo_level = 1
  56. self._class_name = "Vector"
  57. self.overwrite = False
  58. self._cats = []
  59. def __repr__(self):
  60. if self.exist():
  61. return "%s(%r, %r)" % (self._class_name, self.name, self.mapset)
  62. else:
  63. return "%s(%r)" % (self._class_name, self.name)
  64. def __iter__(self):
  65. """::
  66. >>> test_vect = Vector(test_vector_name)
  67. >>> test_vect.open(mode='r')
  68. >>> features = [feature for feature in test_vect]
  69. >>> features[:3]
  70. [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
  71. >>> test_vect.close()
  72. ..
  73. """
  74. # return (self.read(f_id) for f_id in xrange(self.num_of_features()))
  75. return self
  76. @must_be_open
  77. def __next__(self):
  78. """::
  79. >>> test_vect = Vector(test_vector_name)
  80. >>> test_vect.open(mode='r')
  81. >>> test_vect.next()
  82. Point(10.000000, 6.000000)
  83. >>> test_vect.next()
  84. Point(12.000000, 6.000000)
  85. >>> test_vect.close()
  86. ..
  87. """
  88. return read_next_line(
  89. self.c_mapinfo, self.table, self.writeable, is2D=not self.is_3D()
  90. )
  91. @must_be_open
  92. def next(self):
  93. return self.__next__()
  94. @must_be_open
  95. def rewind(self):
  96. """Rewind vector map to cause reads to start at beginning."""
  97. if libvect.Vect_rewind(self.c_mapinfo) == -1:
  98. raise GrassError("Vect_rewind raise an error.")
  99. @must_be_open
  100. def write(self, geo_obj, cat=None, attrs=None):
  101. """Write geometry features and attributes.
  102. :param geo_obj: a geometry grass object define in
  103. grass.pygrass.vector.geometry
  104. :type geo_obj: geometry GRASS object
  105. :param attrs: a list with the values that will be insert in the
  106. attribute table.
  107. :type attrs: list
  108. :param cat: The category of the geometry feature, otherwise the
  109. c_cats attribute of the geometry object will be used.
  110. :type cat: integer
  111. Open a new vector map ::
  112. >>> new = VectorTopo('newvect')
  113. >>> new.exist()
  114. False
  115. define the new columns of the attribute table ::
  116. >>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
  117. ... (u'name', 'TEXT')]
  118. open the vector map in write mode
  119. >>> new.open('w', tab_name='newvect', tab_cols=cols)
  120. import a geometry feature ::
  121. >>> from grass.pygrass.vector.geometry import Point
  122. create two points ::
  123. >>> point0 = Point(0, 0)
  124. >>> point1 = Point(1, 1)
  125. then write the two points on the map, with ::
  126. >>> new.write(point0, cat=1, attrs=('pub',))
  127. >>> new.write(point1, cat=2, attrs=('resturant',))
  128. commit the db changes ::
  129. >>> new.table.conn.commit()
  130. >>> new.table.execute().fetchall()
  131. [(1, 'pub'), (2, 'resturant')]
  132. close the vector map ::
  133. >>> new.close()
  134. >>> new.exist()
  135. True
  136. then play with the map ::
  137. >>> new.open(mode='r')
  138. >>> new.read(1)
  139. Point(0.000000, 0.000000)
  140. >>> new.read(2)
  141. Point(1.000000, 1.000000)
  142. >>> new.read(1).attrs['name']
  143. 'pub'
  144. >>> new.read(2).attrs['name']
  145. 'resturant'
  146. >>> new.close()
  147. >>> new.remove()
  148. """
  149. self.n_lines += 1
  150. if not isinstance(cat, int) and not isinstance(cat, str):
  151. # likely the case of using 7.0 API
  152. import warnings
  153. warnings.warn(
  154. "Vector.write(geo_obj, attrs=(...)) is"
  155. " deprecated, specify cat explicitly",
  156. DeprecationWarning,
  157. )
  158. # try to accommodate
  159. attrs = cat
  160. cat = None
  161. if attrs and cat is None:
  162. # TODO: this does not work as expected when there are
  163. # already features in the map when we opened it
  164. cat = (self._cats[-1] if self._cats else 0) + 1
  165. if cat is not None and cat not in self._cats:
  166. self._cats.append(cat)
  167. if self.table is not None and attrs is not None:
  168. attr = [
  169. cat,
  170. ]
  171. attr.extend(attrs)
  172. cur = self.table.conn.cursor()
  173. cur.execute(self.table.columns.insert_str, attr)
  174. cur.close()
  175. if cat is not None:
  176. cats = Cats(geo_obj.c_cats)
  177. cats.reset()
  178. cats.set(cat, self.layer)
  179. if geo_obj.gtype == _Area.gtype:
  180. result = self._write_area(geo_obj)
  181. result = libvect.Vect_write_line(
  182. self.c_mapinfo, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats
  183. )
  184. if result == -1:
  185. raise GrassError("Not able to write the vector feature.")
  186. if self._topo_level == 2:
  187. # return new feature id (on level 2)
  188. geo_obj.id = result
  189. else:
  190. # return offset into file where the feature starts (on level 1)
  191. geo_obj.offset = result
  192. @must_be_open
  193. def has_color_table(self):
  194. """Return if vector has color table associated in file system;
  195. Color table stored in the vector's attribute table well be not checked
  196. >>> test_vect = Vector(test_vector_name)
  197. >>> test_vect.open(mode='r')
  198. >>> test_vect.has_color_table()
  199. False
  200. >>> test_vect.close()
  201. >>> from grass.pygrass.utils import copy, remove
  202. >>> copy(test_vector_name,'mytest_vect','vect')
  203. >>> from grass.pygrass.modules.shortcuts import vector as v
  204. >>> v.colors(map='mytest_vect', color='population', column='value')
  205. Module('v.colors')
  206. >>> mytest_vect = Vector('mytest_vect')
  207. >>> mytest_vect.open(mode='r')
  208. >>> mytest_vect.has_color_table()
  209. True
  210. >>> mytest_vect.close()
  211. >>> remove('mytest_vect', 'vect')
  212. """
  213. loc = Location()
  214. path = join(loc.path(), self.mapset, "vector", self.name, "colr")
  215. return True if exists(path) else False
  216. # =============================================
  217. # VECTOR WITH TOPOLOGY
  218. # =============================================
  219. class VectorTopo(Vector):
  220. """Vector class with the support of the GRASS topology.
  221. Open a vector map using the *with statement*: ::
  222. >>> with VectorTopo(test_vector_name, mode='r') as test_vect:
  223. ... for feature in test_vect[:7]:
  224. ... print(feature.attrs['name'])
  225. ...
  226. point
  227. point
  228. point
  229. line
  230. line
  231. line
  232. >>> test_vect.is_open()
  233. False
  234. ..
  235. """
  236. def __init__(self, name, mapset="", *args, **kwargs):
  237. super(VectorTopo, self).__init__(name, mapset, *args, **kwargs)
  238. self._topo_level = 2
  239. self._class_name = "VectorTopo"
  240. def __len__(self):
  241. return libvect.Vect_get_num_lines(self.c_mapinfo)
  242. def __getitem__(self, key):
  243. """::
  244. >>> test_vect = VectorTopo(test_vector_name)
  245. >>> test_vect.open(mode='r')
  246. >>> test_vect[:4]
  247. [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
  248. >>> test_vect.close()
  249. ..
  250. """
  251. if isinstance(key, slice):
  252. return [
  253. self.read(indx)
  254. for indx in range(
  255. key.start if key.start else 1,
  256. key.stop if key.stop else len(self),
  257. key.step if key.step else 1,
  258. )
  259. ]
  260. elif isinstance(key, int):
  261. return self.read(key)
  262. else:
  263. raise ValueError("Invalid argument type: %r." % key)
  264. @must_be_open
  265. def num_primitive_of(self, primitive):
  266. """Return the number of primitive
  267. :param primitive: the name of primitive to query; the supported values are:
  268. * *boundary*,
  269. * *centroid*,
  270. * *face*,
  271. * *kernel*,
  272. * *line*,
  273. * *point*
  274. * *area*
  275. * *volume*
  276. :type primitive: str
  277. ::
  278. >>> test_vect = VectorTopo(test_vector_name)
  279. >>> test_vect.open(mode='r')
  280. >>> test_vect.num_primitive_of('point')
  281. 3
  282. >>> test_vect.num_primitive_of('line')
  283. 3
  284. >>> test_vect.num_primitive_of('centroid')
  285. 4
  286. >>> test_vect.num_primitive_of('boundary')
  287. 11
  288. >>> test_vect.close()
  289. ..
  290. """
  291. return libvect.Vect_get_num_primitives(self.c_mapinfo, VTYPE[primitive])
  292. @must_be_open
  293. def number_of(self, vtype):
  294. """Return the number of the chosen element type
  295. :param vtype: the name of type to query; the supported values are:
  296. *areas*, *dblinks*, *faces*, *holes*, *islands*,
  297. *kernels*, *points*, *lines*, *centroids*, *boundaries*,
  298. *nodes*, *line_points*, *update_lines*, *update_nodes*,
  299. *volumes*
  300. :type vtype: str
  301. >>> test_vect = VectorTopo(test_vector_name)
  302. >>> test_vect.open(mode='r')
  303. >>> test_vect.number_of("areas")
  304. 4
  305. >>> test_vect.number_of("islands")
  306. 2
  307. >>> test_vect.number_of("holes")
  308. 0
  309. >>> test_vect.number_of("lines")
  310. 3
  311. >>> test_vect.number_of("nodes")
  312. 15
  313. >>> test_vect.number_of("pizza")
  314. ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  315. Traceback (most recent call last):
  316. ...
  317. ValueError: vtype not supported, use one of: 'areas', ...
  318. >>> test_vect.close()
  319. ..
  320. """
  321. if vtype in _NUMOF.keys():
  322. if isinstance(_NUMOF[vtype], tuple):
  323. fn, ptype = _NUMOF[vtype]
  324. return fn(self.c_mapinfo, ptype)
  325. else:
  326. return _NUMOF[vtype](self.c_mapinfo)
  327. else:
  328. keys = "', '".join(sorted(_NUMOF.keys()))
  329. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  330. @must_be_open
  331. def num_primitives(self):
  332. """Return dictionary with the number of all primitives"""
  333. output = {}
  334. for prim in VTYPE.keys():
  335. output[prim] = self.num_primitive_of(prim)
  336. return output
  337. @must_be_open
  338. def viter(self, vtype, idonly=False):
  339. """Return an iterator of vector features
  340. :param vtype: the name of type to query; the supported values are:
  341. *areas*, *dblinks*, *faces*, *holes*, *islands*,
  342. *kernels*, *line_points*, *lines*, *nodes*, *points*,
  343. *update_lines*, *update_nodes*, *volumes*
  344. :type vtype: str
  345. :param idonly: variable to return only the id of features instead of
  346. full features
  347. :type idonly: bool
  348. >>> test_vect = VectorTopo(test_vector_name, mode='r')
  349. >>> test_vect.open(mode='r')
  350. >>> areas = [area for area in test_vect.viter('areas')]
  351. >>> areas[:3]
  352. [Area(1), Area(2), Area(3)]
  353. to sort the result in a efficient way, use: ::
  354. >>> from operator import methodcaller as method
  355. >>> areas.sort(key=method('area'), reverse=True) # sort the list
  356. >>> for area in areas[:3]:
  357. ... print(area, area.area())
  358. Area(1) 12.0
  359. Area(2) 8.0
  360. Area(4) 8.0
  361. >>> areas = [area for area in test_vect.viter('areas')]
  362. >>> for area in areas:
  363. ... print(area.centroid().cat)
  364. 3
  365. 3
  366. 3
  367. 3
  368. >>> test_vect.close()
  369. """
  370. if vtype in _GEOOBJ.keys():
  371. if _GEOOBJ[vtype] is not None:
  372. ids = (indx for indx in range(1, self.number_of(vtype) + 1))
  373. if idonly:
  374. return ids
  375. return (
  376. _GEOOBJ[vtype](
  377. v_id=indx,
  378. c_mapinfo=self.c_mapinfo,
  379. table=self.table,
  380. writeable=self.writeable,
  381. )
  382. for indx in ids
  383. )
  384. else:
  385. keys = "', '".join(sorted(_GEOOBJ.keys()))
  386. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  387. @must_be_open
  388. def rewind(self):
  389. """Rewind vector map to cause reads to start at beginning. ::
  390. >>> test_vect = VectorTopo(test_vector_name)
  391. >>> test_vect.open(mode='r')
  392. >>> test_vect.next()
  393. Point(10.000000, 6.000000)
  394. >>> test_vect.next()
  395. Point(12.000000, 6.000000)
  396. >>> test_vect.next()
  397. Point(14.000000, 6.000000)
  398. >>> test_vect.rewind()
  399. >>> test_vect.next()
  400. Point(10.000000, 6.000000)
  401. >>> test_vect.close()
  402. ..
  403. """
  404. libvect.Vect_rewind(self.c_mapinfo)
  405. @must_be_open
  406. def cat(self, cat_id, vtype, layer=None, generator=False, geo=None):
  407. """Return the geometry features with category == cat_id.
  408. :param cat_id: the category number
  409. :type cat_id: int
  410. :param vtype: the type of geometry feature that we are looking for
  411. :type vtype: str
  412. :param layer: the layer number that will be used
  413. :type layer: int
  414. :param generator: if True return a generator otherwise it return a
  415. list of features
  416. :type generator: bool
  417. """
  418. if geo is None and vtype not in _GEOOBJ:
  419. keys = "', '".join(sorted(_GEOOBJ.keys()))
  420. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  421. Obj = _GEOOBJ[vtype] if geo is None else geo
  422. ilist = Ilist()
  423. libvect.Vect_cidx_find_all(
  424. self.c_mapinfo,
  425. layer if layer else self.layer,
  426. Obj.gtype,
  427. cat_id,
  428. ilist.c_ilist,
  429. )
  430. is2D = not self.is_3D()
  431. if generator:
  432. return (
  433. read_line(
  434. feature_id=v_id,
  435. c_mapinfo=self.c_mapinfo,
  436. table=self.table,
  437. writeable=self.writeable,
  438. is2D=is2D,
  439. )
  440. for v_id in ilist
  441. )
  442. else:
  443. return [
  444. read_line(
  445. feature_id=v_id,
  446. c_mapinfo=self.c_mapinfo,
  447. table=self.table,
  448. writeable=self.writeable,
  449. is2D=is2D,
  450. )
  451. for v_id in ilist
  452. ]
  453. @must_be_open
  454. def read(self, feature_id):
  455. """Return a geometry object given the feature id.
  456. :param int feature_id: the id of feature to obtain
  457. >>> test_vect = VectorTopo(test_vector_name)
  458. >>> test_vect.open(mode='r')
  459. >>> feature1 = test_vect.read(0) #doctest: +ELLIPSIS
  460. Traceback (most recent call last):
  461. ...
  462. ValueError: The index must be >0, 0 given.
  463. >>> feature1 = test_vect.read(5)
  464. >>> feature1
  465. Line([Point(12.000000, 4.000000), Point(12.000000, 2.000000), Point(12.000000, 0.000000)])
  466. >>> feature1.length()
  467. 4.0
  468. >>> test_vect.read(-1)
  469. Centroid(7.500000, 3.500000)
  470. >>> len(test_vect)
  471. 21
  472. >>> test_vect.read(21)
  473. Centroid(7.500000, 3.500000)
  474. >>> test_vect.read(22) #doctest: +ELLIPSIS
  475. Traceback (most recent call last):
  476. ...
  477. IndexError: Index out of range
  478. >>> test_vect.close()
  479. """
  480. return read_line(
  481. feature_id,
  482. self.c_mapinfo,
  483. self.table,
  484. self.writeable,
  485. is2D=not self.is_3D(),
  486. )
  487. @must_be_open
  488. def is_empty(self):
  489. """Return if a vector map is empty or not"""
  490. primitives = self.num_primitives()
  491. output = True
  492. for v in primitives.values():
  493. if v != 0:
  494. output = False
  495. break
  496. return output
  497. @must_be_open
  498. def rewrite(self, geo_obj, cat, attrs=None, **kargs):
  499. """Rewrite a geometry features
  500. >>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
  501. ... (u'name', 'TEXT')]
  502. Generate a new vector map
  503. >>> test_vect = VectorTopo('newvect_2')
  504. >>> test_vect.open('w', tab_name='newvect_2', tab_cols=cols,
  505. ... overwrite=True)
  506. import a geometry feature ::
  507. >>> from grass.pygrass.vector.geometry import Point
  508. create two points ::
  509. >>> point0 = Point(0, 0)
  510. >>> point1 = Point(1, 1)
  511. >>> point2 = Point(2, 2)
  512. then write the two points on the map, with ::
  513. >>> test_vect.write(point0, cat=1, attrs=('pub',))
  514. >>> test_vect.write(point1, cat=2, attrs=('resturant',))
  515. >>> test_vect.table.conn.commit() # save changes in the DB
  516. >>> test_vect.table_to_dict()
  517. {1: [1, 'pub'], 2: [2, 'resturant']}
  518. >>> test_vect.close()
  519. Now rewrite one point of the vector map: ::
  520. >>> test_vect.open('rw')
  521. >>> test_vect.rewrite(point2, cat=1, attrs=('Irish Pub',))
  522. >>> test_vect.table.conn.commit() # save changes in the DB
  523. >>> test_vect.close()
  524. Check the output:
  525. >>> test_vect.open('r')
  526. >>> test_vect[1] == point2
  527. True
  528. >>> test_vect[1].attrs['name'] == 'Irish Pub'
  529. True
  530. >>> test_vect.close()
  531. >>> test_vect.remove()
  532. """
  533. if self.table is not None and attrs:
  534. self.table.update(key=cat, values=attrs)
  535. elif self.table is None and attrs:
  536. print(
  537. "Table for vector {name} does not exist, attributes not"
  538. " loaded".format(name=self.name)
  539. )
  540. libvect.Vect_cat_set(geo_obj.c_cats, self.layer, cat)
  541. result = libvect.Vect_rewrite_line(
  542. self.c_mapinfo, cat, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats
  543. )
  544. if result == -1:
  545. raise GrassError("Not able to write the vector feature.")
  546. # return offset into file where the feature starts
  547. geo_obj.offset = result
  548. @must_be_open
  549. def delete(self, feature_id):
  550. """Remove a feature by its id
  551. :param feature_id: the id of the feature
  552. :type feature_id: int
  553. """
  554. if libvect.Vect_rewrite_line(self.c_mapinfo, feature_id) == -1:
  555. raise GrassError("C function: Vect_rewrite_line.")
  556. @must_be_open
  557. def restore(self, geo_obj):
  558. if hasattr(geo_obj, "offset"):
  559. if (
  560. libvect.Vect_restore_line(self.c_mapinfo, geo_obj.offset, geo_obj.id)
  561. == -1
  562. ):
  563. raise GrassError("C function: Vect_restore_line.")
  564. else:
  565. raise ValueError("The value have not an offset attribute.")
  566. @must_be_open
  567. def bbox(self):
  568. """Return the BBox of the vecor map"""
  569. bbox = Bbox()
  570. if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
  571. raise GrassError("I can not find the Bbox.")
  572. return bbox
  573. def close(self, build=True, release=True):
  574. """Close the VectorTopo map, if release is True, the memory
  575. occupied by spatial index is released"""
  576. if release:
  577. libvect.Vect_set_release_support(self.c_mapinfo)
  578. super(VectorTopo, self).close(build=build)
  579. @must_be_open
  580. def table_to_dict(self, where=None):
  581. """Return the attribute table as a dictionary with the category as keys
  582. The columns have the order of the self.table.columns.names() list.
  583. Examples
  584. >>> from grass.pygrass.vector import VectorTopo
  585. >>> from grass.pygrass.vector.basic import Bbox
  586. >>> test_vect = VectorTopo(test_vector_name)
  587. >>> test_vect.open('r')
  588. >>> test_vect.table_to_dict()
  589. {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
  590. >>> test_vect.table_to_dict(where="value > 2")
  591. {3: [3, 'centroid', 3.0]}
  592. >>> test_vect.table_to_dict(where="value > 0")
  593. {1: [1, 'point', 1.0], 2: [2, 'line', 2.0], 3: [3, 'centroid', 3.0]}
  594. >>> test_vect.table.filters.get_sql()
  595. 'SELECT cat,name,value FROM vector_doctest_map WHERE value > 0 ORDER BY cat;'
  596. """
  597. if self.table is not None:
  598. table_dict = {}
  599. # Get the category index
  600. cat_index = self.table.columns.names().index("cat")
  601. # Prepare a filter
  602. if where is not None:
  603. self.table.filters.where(where)
  604. self.table.filters.order_by("cat")
  605. self.table.filters.select(",".join(self.table.columns.names()))
  606. # Execute the query and fetch the result
  607. cur = self.table.execute()
  608. entries = cur.fetchall()
  609. # Generate the dictionary
  610. for entry in entries:
  611. table_dict[entry[cat_index]] = list(entry)
  612. return table_dict
  613. return None
  614. @must_be_open
  615. def features_to_wkb_list(self, bbox=None, feature_type="point", field=1):
  616. """Return all features of type point, line, boundary or centroid
  617. as a list of Well Known Binary representations (WKB)
  618. (id, cat, wkb) triplets located in a specific
  619. bounding box.
  620. :param bbox: The boundingbox to search for features,
  621. if bbox=None the boundingbox of the whole
  622. vector map layer is used
  623. :type bbox: grass.pygrass.vector.basic.Bbox
  624. :param feature_type: The type of feature that should be converted to
  625. the Well Known Binary (WKB) format. Supported are:
  626. 'point' -> libvect.GV_POINT 1
  627. 'line' -> libvect.GV_LINE 2
  628. 'boundary' -> libvect.GV_BOUNDARY 3
  629. 'centroid' -> libvect.GV_CENTROID 4
  630. :type type: string
  631. :param field: The category field
  632. :type field: integer
  633. :return: A list of triplets, or None if nothing was found
  634. The well known binary are stored in byte arrays.
  635. Examples:
  636. >>> from grass.pygrass.vector import VectorTopo
  637. >>> from grass.pygrass.vector.basic import Bbox
  638. >>> test_vect = VectorTopo(test_vector_name)
  639. >>> test_vect.open('r')
  640. >>> bbox = Bbox(north=20, south=-1, east=20, west=-1)
  641. >>> result = test_vect.features_to_wkb_list(bbox=bbox,
  642. ... feature_type="point")
  643. >>> len(result)
  644. 3
  645. >>> for entry in result:
  646. ... f_id, cat, wkb = entry
  647. ... print((f_id, cat, len(wkb)))
  648. (1, 1, 21)
  649. (2, 1, 21)
  650. (3, 1, 21)
  651. >>> result = test_vect.features_to_wkb_list(bbox=None,
  652. ... feature_type="line")
  653. >>> len(result)
  654. 3
  655. >>> for entry in result:
  656. ... f_id, cat, wkb = entry
  657. ... print((f_id, cat, len(wkb)))
  658. (4, 2, 57)
  659. (5, 2, 57)
  660. (6, 2, 57)
  661. >>> result = test_vect.features_to_wkb_list(bbox=bbox,
  662. ... feature_type="boundary")
  663. >>> len(result)
  664. 11
  665. >>> result = test_vect.features_to_wkb_list(bbox=None,
  666. ... feature_type="centroid")
  667. >>> len(result)
  668. 4
  669. >>> for entry in result:
  670. ... f_id, cat, wkb = entry
  671. ... print((f_id, cat, len(wkb)))
  672. (19, 3, 21)
  673. (18, 3, 21)
  674. (20, 3, 21)
  675. (21, 3, 21)
  676. >>> result = test_vect.features_to_wkb_list(bbox=bbox,
  677. ... feature_type="blub")
  678. Traceback (most recent call last):
  679. ...
  680. grass.exceptions.GrassError: Unsupported feature type <blub>, supported are <point,line,boundary,centroid>
  681. >>> test_vect.close()
  682. """
  683. supported = ["point", "line", "boundary", "centroid"]
  684. if feature_type.lower() not in supported:
  685. raise GrassError(
  686. "Unsupported feature type <%s>, "
  687. "supported are <%s>" % (feature_type, ",".join(supported))
  688. )
  689. if bbox is None:
  690. bbox = self.bbox()
  691. bboxlist = self.find_by_bbox.geos(
  692. bbox, type=feature_type.lower(), bboxlist_only=True
  693. )
  694. if bboxlist is not None and len(bboxlist) > 0:
  695. wkb_list = []
  696. line_p = libvect.line_pnts()
  697. line_c = libvect.line_cats()
  698. size = ctypes.c_size_t()
  699. cat = ctypes.c_int()
  700. error = ctypes.c_int()
  701. for f_id in bboxlist.ids:
  702. barray = libvect.Vect_read_line_to_wkb(
  703. self.c_mapinfo,
  704. ctypes.byref(line_p),
  705. ctypes.byref(line_c),
  706. f_id,
  707. ctypes.byref(size),
  708. ctypes.byref(error),
  709. )
  710. if not barray:
  711. if error == -1:
  712. raise GrassError(
  713. _("Unable to read line of feature %i" % (f_id))
  714. )
  715. if error == -2:
  716. print("Empty feature %i" % (f_id))
  717. continue
  718. ok = libvect.Vect_cat_get(
  719. ctypes.byref(line_c), field, ctypes.byref(cat)
  720. )
  721. if ok < 1:
  722. pcat = None
  723. else:
  724. pcat = cat.value
  725. wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value)))
  726. libgis.G_free(barray)
  727. return wkb_list
  728. return None
  729. @must_be_open
  730. def areas_to_wkb_list(self, bbox=None, field=1):
  731. """Return all features of type point, line, boundary or centroid
  732. as a list of Well Known Binary representations (WKB)
  733. (id, cat, wkb) triplets located in a specific
  734. bounding box.
  735. :param bbox: The boundingbox to search for features,
  736. if bbox=None the boundingbox of the whole
  737. vector map layer is used
  738. :type bbox: grass.pygrass.vector.basic.Bbox
  739. :param field: The centroid category field
  740. :type field: integer
  741. :return: A list of triplets, or None if nothing was found
  742. The well known binary are stored in byte arrays.
  743. Examples:
  744. >>> from grass.pygrass.vector import VectorTopo
  745. >>> from grass.pygrass.vector.basic import Bbox
  746. >>> test_vect = VectorTopo(test_vector_name)
  747. >>> test_vect.open('r')
  748. >>> bbox = Bbox(north=20, south=-1, east=20, west=-1)
  749. >>> result = test_vect.areas_to_wkb_list(bbox=bbox)
  750. >>> len(result)
  751. 4
  752. >>> for entry in result:
  753. ... a_id, cat, wkb = entry
  754. ... print((a_id, cat, len(wkb)))
  755. (1, 3, 225)
  756. (2, 3, 141)
  757. (3, 3, 93)
  758. (4, 3, 141)
  759. >>> result = test_vect.areas_to_wkb_list()
  760. >>> len(result)
  761. 4
  762. >>> for entry in result:
  763. ... a_id, cat, wkb = entry
  764. ... print((a_id, cat, len(wkb)))
  765. (1, 3, 225)
  766. (2, 3, 141)
  767. (3, 3, 93)
  768. (4, 3, 141)
  769. >>> test_vect.close()
  770. """
  771. if bbox is None:
  772. bbox = self.bbox()
  773. bboxlist = self.find_by_bbox.areas(bbox, bboxlist_only=True)
  774. if bboxlist is not None and len(bboxlist) > 0:
  775. wkb_list = []
  776. line_c = libvect.line_cats()
  777. size = ctypes.c_size_t()
  778. cat = ctypes.c_int()
  779. for a_id in bboxlist.ids:
  780. barray = libvect.Vect_read_area_to_wkb(
  781. self.c_mapinfo, a_id, ctypes.byref(size)
  782. )
  783. if not barray:
  784. raise GrassError(_("Unable to read area with id %i" % (a_id)))
  785. pcat = None
  786. c_ok = libvect.Vect_get_area_cats(
  787. self.c_mapinfo, a_id, ctypes.byref(line_c)
  788. )
  789. if c_ok == 0: # Centroid found
  790. ok = libvect.Vect_cat_get(
  791. ctypes.byref(line_c), field, ctypes.byref(cat)
  792. )
  793. if ok > 0:
  794. pcat = cat.value
  795. wkb_list.append((a_id, pcat, ctypes.string_at(barray, size.value)))
  796. libgis.G_free(barray)
  797. return wkb_list
  798. return None
  799. if __name__ == "__main__":
  800. import doctest
  801. from grass.pygrass import utils
  802. utils.create_test_vector_map(test_vector_name)
  803. doctest.testmod()
  804. """Remove the generated vector map, if exist"""
  805. from grass.pygrass.utils import get_mapset_vector
  806. from grass.script.core import run_command
  807. mset = get_mapset_vector(test_vector_name, mapset="")
  808. if mset:
  809. run_command("g.remove", flags="f", type="vector", name=test_vector_name)