__init__.py 19 KB

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