__init__.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. # -*- coding: utf-8 -*-
  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. #
  7. # import pygrass modules
  8. #
  9. from grass.pygrass.vector.vector_type import VTYPE
  10. from grass.pygrass.errors import GrassError, must_be_open
  11. from grass.pygrass.gis import Location
  12. from grass.pygrass.vector.geometry import GEOOBJ as _GEOOBJ
  13. from grass.pygrass.vector.geometry import read_line, read_next_line
  14. from grass.pygrass.vector.geometry import Area as _Area
  15. from grass.pygrass.vector.abstract import Info
  16. from grass.pygrass.vector.basic import Bbox, Cats, Ilist
  17. _NUMOF = {"areas": libvect.Vect_get_num_areas,
  18. "dblinks": libvect.Vect_get_num_dblinks,
  19. "faces": libvect.Vect_get_num_faces,
  20. "holes": libvect.Vect_get_num_holes,
  21. "islands": libvect.Vect_get_num_islands,
  22. "kernels": libvect.Vect_get_num_kernels,
  23. "points": libvect.Vect_get_num_lines,
  24. "lines": libvect.Vect_get_num_lines,
  25. "nodes": libvect.Vect_get_num_nodes,
  26. "updated_lines": libvect.Vect_get_num_updated_lines,
  27. "updated_nodes": libvect.Vect_get_num_updated_nodes,
  28. "volumes": libvect.Vect_get_num_volumes}
  29. # For test purposes
  30. test_vector_name = "vector_doctest_map"
  31. #=============================================
  32. # VECTOR
  33. #=============================================
  34. class Vector(Info):
  35. """Vector class is the grass vector format without topology
  36. >>> from grass.pygrass.vector import Vector
  37. >>> test_vect = Vector(test_vector_name)
  38. >>> test_vect.is_open()
  39. False
  40. >>> test_vect.mapset
  41. ''
  42. >>> test_vect.exist()
  43. True
  44. >>> test_vect.overwrite
  45. False
  46. """
  47. def __init__(self, name, mapset='', *args, **kwargs):
  48. # Set map name and mapset
  49. super(Vector, self).__init__(name, mapset, *args, **kwargs)
  50. self._topo_level = 1
  51. self._class_name = 'Vector'
  52. self.overwrite = False
  53. self._cats = []
  54. def __repr__(self):
  55. if self.exist():
  56. return "%s(%r, %r)" % (self._class_name, self.name, self.mapset)
  57. else:
  58. return "%s(%r)" % (self._class_name, self.name)
  59. def __iter__(self):
  60. """::
  61. >>> test_vect = Vector(test_vector_name)
  62. >>> test_vect.open(mode='r')
  63. >>> features = [feature for feature in test_vect]
  64. >>> features[:3]
  65. [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
  66. >>> test_vect.close()
  67. ..
  68. """
  69. #return (self.read(f_id) for f_id in xrange(self.num_of_features()))
  70. return self
  71. @must_be_open
  72. def next(self):
  73. """::
  74. >>> test_vect = Vector(test_vector_name)
  75. >>> test_vect.open(mode='r')
  76. >>> test_vect.next()
  77. Point(10.000000, 6.000000)
  78. >>> test_vect.next()
  79. Point(12.000000, 6.000000)
  80. >>> test_vect.close()
  81. ..
  82. """
  83. return read_next_line(self.c_mapinfo, self.table, self.writeable,
  84. is2D=not self.is_3D())
  85. @must_be_open
  86. def rewind(self):
  87. """Rewind vector map to cause reads to start at beginning."""
  88. if libvect.Vect_rewind(self.c_mapinfo) == -1:
  89. raise GrassError("Vect_rewind raise an error.")
  90. @must_be_open
  91. def write(self, geo_obj, cat=None, attrs=None):
  92. """Write geometry features and attributes.
  93. :param geo_obj: a geometry grass object define in
  94. grass.pygrass.vector.geometry
  95. :type geo_obj: geometry GRASS object
  96. :param attrs: a list with the values that will be insert in the
  97. attribute table.
  98. :type attrs: list
  99. :param cat: The category of the geometry feature, otherwise the
  100. c_cats attribute of the geometry object will be used.
  101. :type cat: integer
  102. Open a new vector map ::
  103. >>> new = VectorTopo('newvect')
  104. >>> new.exist()
  105. False
  106. define the new columns of the attribute table ::
  107. >>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
  108. ... (u'name', 'TEXT')]
  109. open the vector map in write mode
  110. >>> new.open('w', tab_name='newvect', tab_cols=cols)
  111. import a geometry feature ::
  112. >>> from grass.pygrass.vector.geometry import Point
  113. create two points ::
  114. >>> point0 = Point(0, 0)
  115. >>> point1 = Point(1, 1)
  116. then write the two points on the map, with ::
  117. >>> new.write(point0, cat=1, attrs=('pub',))
  118. >>> new.write(point1, cat=2, attrs=('resturant',))
  119. commit the db changes ::
  120. >>> new.table.conn.commit()
  121. >>> new.table.execute().fetchall()
  122. [(1, u'pub'), (2, u'resturant')]
  123. close the vector map ::
  124. >>> new.close()
  125. >>> new.exist()
  126. True
  127. then play with the map ::
  128. >>> new.open(mode='r')
  129. >>> new.read(1)
  130. Point(0.000000, 0.000000)
  131. >>> new.read(2)
  132. Point(1.000000, 1.000000)
  133. >>> new.read(1).attrs['name']
  134. u'pub'
  135. >>> new.read(2).attrs['name']
  136. u'resturant'
  137. >>> new.close()
  138. >>> new.remove()
  139. """
  140. self.n_lines += 1
  141. if self.table is not None and attrs and cat is not None:
  142. if cat not in self._cats:
  143. self._cats.append(cat)
  144. attr = [cat, ]
  145. attr.extend(attrs)
  146. cur = self.table.conn.cursor()
  147. cur.execute(self.table.columns.insert_str, attr)
  148. cur.close()
  149. if cat is not None:
  150. cats = Cats(geo_obj.c_cats)
  151. cats.reset()
  152. cats.set(cat, self.layer)
  153. if geo_obj.gtype == _Area.gtype:
  154. result = self._write_area(geo_obj)
  155. result = libvect.Vect_write_line(self.c_mapinfo, geo_obj.gtype,
  156. geo_obj.c_points, geo_obj.c_cats)
  157. if result == -1:
  158. raise GrassError("Not able to write the vector feature.")
  159. if self._topo_level == 2:
  160. # return new feature id (on level 2)
  161. geo_obj.id = result
  162. else:
  163. # return offset into file where the feature starts (on level 1)
  164. geo_obj.offset = result
  165. @must_be_open
  166. def has_color_table(self):
  167. """Return if vector has color table associated in file system;
  168. Color table stored in the vector's attribute table well be not checked
  169. >>> test_vect = Vector(test_vector_name)
  170. >>> test_vect.open(mode='r')
  171. >>> test_vect.has_color_table()
  172. False
  173. >>> test_vect.close()
  174. >>> from grass.pygrass.utils import copy, remove
  175. >>> copy(test_vector_name,'mytest_vect','vect')
  176. >>> from grass.pygrass.modules.shortcuts import vector as v
  177. >>> v.colors(map='mytest_vect', color='population', column='value')
  178. Module('v.colors')
  179. >>> mytest_vect = Vector('mytest_vect')
  180. >>> mytest_vect.open(mode='r')
  181. >>> mytest_vect.has_color_table()
  182. True
  183. >>> mytest_vect.close()
  184. >>> remove('mytest_vect', 'vect')
  185. """
  186. loc = Location()
  187. path = join(loc.path(), self.mapset, 'vector', self.name, 'colr')
  188. return True if exists(path) else False
  189. #=============================================
  190. # VECTOR WITH TOPOLOGY
  191. #=============================================
  192. class VectorTopo(Vector):
  193. """Vector class with the support of the GRASS topology.
  194. Open a vector map using the *with statement*: ::
  195. >>> with VectorTopo(test_vector_name, mode='r') as test_vect:
  196. ... for feature in test_vect[:7]:
  197. ... print feature.attrs['name']
  198. ...
  199. point
  200. point
  201. point
  202. line
  203. line
  204. line
  205. >>> test_vect.is_open()
  206. False
  207. ..
  208. """
  209. def __init__(self, name, mapset='', *args, **kwargs):
  210. super(VectorTopo, self).__init__(name, mapset, *args, **kwargs)
  211. self._topo_level = 2
  212. self._class_name = 'VectorTopo'
  213. def __len__(self):
  214. return libvect.Vect_get_num_lines(self.c_mapinfo)
  215. def __getitem__(self, key):
  216. """::
  217. >>> test_vect = VectorTopo(test_vector_name)
  218. >>> test_vect.open(mode='r')
  219. >>> test_vect[:4]
  220. [Point(10.000000, 6.000000), Point(12.000000, 6.000000), Point(14.000000, 6.000000)]
  221. >>> test_vect.close()
  222. ..
  223. """
  224. if isinstance(key, slice):
  225. return [self.read(indx)
  226. for indx in range(key.start if key.start else 1,
  227. key.stop if key.stop else len(self),
  228. key.step if key.step else 1)]
  229. elif isinstance(key, int):
  230. return self.read(key)
  231. else:
  232. raise ValueError("Invalid argument type: %r." % key)
  233. @must_be_open
  234. def num_primitive_of(self, primitive):
  235. """Return the number of primitive
  236. :param primitive: the name of primitive to query; the supported values are:
  237. * *boundary*,
  238. * *centroid*,
  239. * *face*,
  240. * *kernel*,
  241. * *line*,
  242. * *point*
  243. * *area*
  244. * *volume*
  245. :type primitive: str
  246. ::
  247. >>> test_vect = VectorTopo(test_vector_name)
  248. >>> test_vect.open(mode='r')
  249. >>> test_vect.num_primitive_of('point')
  250. 3
  251. >>> test_vect.num_primitive_of('line')
  252. 3
  253. >>> test_vect.num_primitive_of('centroid')
  254. 4
  255. >>> test_vect.num_primitive_of('boundary')
  256. 11
  257. >>> test_vect.close()
  258. ..
  259. """
  260. return libvect.Vect_get_num_primitives(self.c_mapinfo,
  261. VTYPE[primitive])
  262. @must_be_open
  263. def number_of(self, vtype):
  264. """Return the number of the choosen element type
  265. :param vtype: the name of type to query; the supported values are:
  266. *areas*, *dblinks*, *faces*, *holes*, *islands*,
  267. *kernels*, *line_points*, *lines*, *nodes*,
  268. *update_lines*, *update_nodes*, *volumes*
  269. :type vtype: str
  270. >>> test_vect = VectorTopo(test_vector_name)
  271. >>> test_vect.open(mode='r')
  272. >>> test_vect.number_of("areas")
  273. 4
  274. >>> test_vect.number_of("islands")
  275. 2
  276. >>> test_vect.number_of("holes")
  277. 0
  278. >>> test_vect.number_of("lines")
  279. 21
  280. >>> test_vect.number_of("nodes")
  281. 15
  282. >>> test_vect.number_of("pizza")
  283. ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  284. Traceback (most recent call last):
  285. ...
  286. ValueError: vtype not supported, use one of: 'areas', ...
  287. >>> test_vect.close()
  288. ..
  289. """
  290. if vtype in _NUMOF.keys():
  291. return _NUMOF[vtype](self.c_mapinfo)
  292. else:
  293. keys = "', '".join(sorted(_NUMOF.keys()))
  294. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  295. @must_be_open
  296. def num_primitives(self):
  297. """Return dictionary with the number of all primitives
  298. """
  299. output = {}
  300. for prim in VTYPE.keys():
  301. output[prim] = self.num_primitive_of(prim)
  302. return output
  303. @must_be_open
  304. def viter(self, vtype, idonly=False):
  305. """Return an iterator of vector features
  306. :param vtype: the name of type to query; the supported values are:
  307. *areas*, *dblinks*, *faces*, *holes*, *islands*,
  308. *kernels*, *line_points*, *lines*, *nodes*,
  309. *update_lines*, *update_nodes*, *volumes*
  310. :type vtype: str
  311. :param idonly: variable to return only the id of features instead of
  312. full features
  313. :type idonly: bool
  314. >>> test_vect = VectorTopo(test_vector_name, mode='r')
  315. >>> test_vect.open(mode='r')
  316. >>> areas = [area for area in test_vect.viter('areas')]
  317. >>> areas[:3]
  318. [Area(1), Area(2), Area(3)]
  319. to sort the result in a efficient way, use: ::
  320. >>> from operator import methodcaller as method
  321. >>> areas.sort(key=method('area'), reverse=True) # sort the list
  322. >>> for area in areas[:3]:
  323. ... print area, area.area()
  324. Area(1) 12.0
  325. Area(2) 8.0
  326. Area(4) 8.0
  327. >>> test_vect.close()
  328. """
  329. if vtype in _GEOOBJ.keys():
  330. if _GEOOBJ[vtype] is not None:
  331. ids = (indx for indx in range(1, self.number_of(vtype) + 1))
  332. if idonly:
  333. return ids
  334. return (_GEOOBJ[vtype](v_id=indx, c_mapinfo=self.c_mapinfo,
  335. table=self.table,
  336. writeable=self.writeable)
  337. for indx in ids)
  338. else:
  339. keys = "', '".join(sorted(_GEOOBJ.keys()))
  340. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  341. @must_be_open
  342. def rewind(self):
  343. """Rewind vector map to cause reads to start at beginning. ::
  344. >>> test_vect = VectorTopo(test_vector_name)
  345. >>> test_vect.open(mode='r')
  346. >>> test_vect.next()
  347. Point(10.000000, 6.000000)
  348. >>> test_vect.next()
  349. Point(12.000000, 6.000000)
  350. >>> test_vect.next()
  351. Point(14.000000, 6.000000)
  352. >>> test_vect.rewind()
  353. >>> test_vect.next()
  354. Point(10.000000, 6.000000)
  355. >>> test_vect.close()
  356. ..
  357. """
  358. libvect.Vect_rewind(self.c_mapinfo)
  359. @must_be_open
  360. def cat(self, cat_id, vtype, layer=None, generator=False, geo=None):
  361. """Return the geometry features with category == cat_id.
  362. :param cat_id: the category number
  363. :type cat_id: int
  364. :param vtype: the type of geometry feature that we are looking for
  365. :type vtype: str
  366. :param layer: the layer number that will be used
  367. :type layer: int
  368. :param generator: if True return a generator otherwise it return a
  369. list of features
  370. :type generator: bool
  371. """
  372. if geo is None and vtype not in _GEOOBJ:
  373. keys = "', '".join(sorted(_GEOOBJ.keys()))
  374. raise ValueError("vtype not supported, use one of: '%s'" % keys)
  375. Obj = _GEOOBJ[vtype] if geo is None else geo
  376. ilist = Ilist()
  377. libvect.Vect_cidx_find_all(self.c_mapinfo,
  378. layer if layer else self.layer,
  379. Obj.gtype, cat_id, ilist.c_ilist)
  380. is2D = not self.is_3D()
  381. if generator:
  382. return (read_line(feature_id=v_id, c_mapinfo=self.c_mapinfo,
  383. table=self.table, writeable=self.writeable,
  384. is2D=is2D)
  385. for v_id in ilist)
  386. else:
  387. return [read_line(feature_id=v_id, c_mapinfo=self.c_mapinfo,
  388. table=self.table, writeable=self.writeable,
  389. is2D=is2D)
  390. for v_id in ilist]
  391. @must_be_open
  392. def read(self, feature_id):
  393. """Return a geometry object given the feature id.
  394. :param int feature_id: the id of feature to obtain
  395. >>> test_vect = VectorTopo(test_vector_name)
  396. >>> test_vect.open(mode='r')
  397. >>> feature1 = test_vect.read(0) #doctest: +ELLIPSIS
  398. Traceback (most recent call last):
  399. ...
  400. ValueError: The index must be >0, 0 given.
  401. >>> feature1 = test_vect.read(5)
  402. >>> feature1
  403. Line([Point(12.000000, 4.000000), Point(12.000000, 2.000000), Point(12.000000, 0.000000)])
  404. >>> feature1.length()
  405. 4.0
  406. >>> test_vect.read(-1)
  407. Centoid(7.500000, 3.500000)
  408. >>> len(test_vect)
  409. 21
  410. >>> test_vect.read(21)
  411. Centoid(7.500000, 3.500000)
  412. >>> test_vect.read(22) #doctest: +ELLIPSIS
  413. Traceback (most recent call last):
  414. ...
  415. IndexError: Index out of range
  416. >>> test_vect.close()
  417. """
  418. return read_line(feature_id, self.c_mapinfo, self.table, self.writeable,
  419. is2D=not self.is_3D())
  420. @must_be_open
  421. def is_empty(self):
  422. """Return if a vector map is empty or not
  423. """
  424. primitives = self.num_primitives()
  425. output = True
  426. for v in primitives.values():
  427. if v != 0:
  428. output = False
  429. break
  430. return output
  431. @must_be_open
  432. def rewrite(self, line, geo_obj, attrs=None, **kargs):
  433. """Rewrite a geometry features
  434. """
  435. if self.table is not None and attrs:
  436. attr = [line, ]
  437. attr.extend(attrs)
  438. self.table.update(key=line, values=attr)
  439. elif self.table is None and attrs:
  440. print "Table for vector {name} does not exist, attributes not" \
  441. " loaded".format(name=self.name)
  442. libvect.Vect_cat_set(geo_obj.c_cats, self.layer, line)
  443. result = libvect.Vect_rewrite_line(self.c_mapinfo,
  444. line, geo_obj.gtype,
  445. geo_obj.c_points,
  446. geo_obj.c_cats)
  447. if result == -1:
  448. raise GrassError("Not able to write the vector feature.")
  449. # return offset into file where the feature starts
  450. geo_obj.offset = result
  451. @must_be_open
  452. def delete(self, feature_id):
  453. """Remove a feature by its id
  454. :param feature_id: the id of the feature
  455. :type feature_id: int
  456. """
  457. if libvect.Vect_rewrite_line(self.c_mapinfo, feature_id) == -1:
  458. raise GrassError("C funtion: Vect_rewrite_line.")
  459. @must_be_open
  460. def restore(self, geo_obj):
  461. if hasattr(geo_obj, 'offset'):
  462. if libvect.Vect_restore_line(self.c_mapinfo, geo_obj.id,
  463. geo_obj.offset) == -1:
  464. raise GrassError("C funtion: Vect_restore_line.")
  465. else:
  466. raise ValueError("The value have not an offset attribute.")
  467. @must_be_open
  468. def bbox(self):
  469. """Return the BBox of the vecor map
  470. """
  471. bbox = Bbox()
  472. if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
  473. raise GrassError("I can not find the Bbox.")
  474. return bbox
  475. @must_be_open
  476. def select_by_bbox(self, bbox):
  477. """Return the BBox of the vector map
  478. """
  479. # TODO replace with bbox if bbox else Bbox() ??
  480. bbox = Bbox()
  481. if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
  482. raise GrassError("I can not find the Bbox.")
  483. return bbox
  484. def close(self, build=True, release=True):
  485. """Close the VectorTopo map, if release is True, the memory
  486. occupied by spatial index is released"""
  487. if release:
  488. libvect.Vect_set_release_support(self.c_mapinfo)
  489. super(VectorTopo, self).close(build=build)
  490. if __name__ == "__main__":
  491. import doctest
  492. from grass.pygrass import utils
  493. utils.create_test_vector_map(test_vector_name)
  494. doctest.testmod()