1
# -*- coding: utf-8 -*-
2
from os.path import join, exists
3
import grass.lib.gis as libgis
5
import grass.lib.vector as libvect
8
# import pygrass modules
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
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
21
_NUMOF = {"areas": libvect.Vect_get_num_areas,
22
"dblinks": libvect.Vect_get_num_dblinks,
23
"faces": libvect.Vect_get_num_faces,
24
"holes": libvect.Vect_get_num_holes,
25
"islands": libvect.Vect_get_num_islands,
26
"kernels": libvect.Vect_get_num_kernels,
27
"lines": libvect.Vect_get_num_lines,
28
"nodes": libvect.Vect_get_num_nodes,
29
"updated_lines": libvect.Vect_get_num_updated_lines,
30
"updated_nodes": libvect.Vect_get_num_updated_nodes,
31
"volumes": libvect.Vect_get_num_volumes}
34
#=============================================
36
#=============================================
39
"""Vector class is the grass vector format without topology
41
>>> from grass.pygrass.vector import Vector
42
>>> cens = Vector('census')
55
def __init__(self, name, mapset='', *args, **kwargs):
56
# Set map name and mapset
57
super(Vector, self).__init__(name, mapset, *args, **kwargs)
59
self._class_name = 'Vector'
60
self.overwrite = False
64
return "%s(%r, %r)" % (self._class_name, self.name, self.mapset)
66
return "%s(%r)" % (self._class_name, self.name)
71
>>> cens = Vector('census')
72
>>> cens.open(mode='r')
73
>>> features = [feature for feature in cens]
75
[Boundary(v_id=None), Boundary(v_id=None), Boundary(v_id=None)]
80
#return (self.read(f_id) for f_id in xrange(self.num_of_features()))
87
>>> cens = Vector('census')
88
>>> cens.open(mode='r')
97
return read_next_line(self.c_mapinfo, self.table, self.writable,
98
is2D=not self.is_3D())
102
"""Rewind vector map to cause reads to start at beginning."""
103
if libvect.Vect_rewind(self.c_mapinfo) == -1:
104
raise GrassError("Vect_rewind raise an error.")
107
def write(self, geo_obj, attrs=None, set_cats=True):
108
"""Write geometry features and attributes.
110
:param geo_obj: a geometry grass object define in
111
grass.pygrass.vector.geometry
112
:type geo_obj: geometry GRASS object
113
:param attrs: a list with the values that will be insert in the
116
:param set_cats: if True, the category of the geometry feature is set
117
using the default layer of the vector map and a
118
progressive category value (default), otherwise the
119
c_cats attribute of the geometry object will be used.
122
Open a new vector map ::
124
>>> new = VectorTopo('newvect')
128
define the new columns of the attribute table ::
130
>>> cols = [(u'cat', 'INTEGER PRIMARY KEY'),
131
... (u'name', 'TEXT')]
133
open the vector map in write mode
135
>>> new.open('w', tab_name='newvect', tab_cols=cols)
137
import a geometry feature ::
139
>>> from grass.pygrass.vector.geometry import Point
143
>>> point0 = Point(636981.336043, 256517.602235)
144
>>> point1 = Point(637209.083058, 257970.129540)
146
then write the two points on the map, with ::
148
>>> new.write(point0, ('pub', ))
149
>>> new.write(point1, ('resturnat', ))
151
commit the db changes ::
153
>>> new.table.conn.commit()
154
>>> new.table.execute().fetchall()
155
[(1, u'pub'), (2, u'resturnat')]
157
close the vector map ::
163
then play with the map ::
165
>>> new.open(mode='r')
167
Point(636981.336043, 256517.602235)
169
Point(637209.083058, 257970.129540)
170
>>> new.read(1).attrs['name']
172
>>> new.read(2).attrs['name']
179
if self.table is not None and attrs:
180
attr = [self.n_lines, ]
182
cur = self.table.conn.cursor()
183
cur.execute(self.table.columns.insert_str, attr)
187
cats = Cats(geo_obj.c_cats)
189
cats.set(self.n_lines, self.layer)
191
if geo_obj.gtype == _Area.gtype:
192
result = self._write_area(geo_obj)
193
result = libvect.Vect_write_line(self.c_mapinfo, geo_obj.gtype,
194
geo_obj.c_points, geo_obj.c_cats)
196
raise GrassError("Not able to write the vector feature.")
197
if self._topo_level == 2:
198
# return new feature id (on level 2)
201
# return offset into file where the feature starts (on level 1)
202
geo_obj.offset = result
205
def has_color_table(self):
206
"""Return if vector has color table associated in file system;
207
Color table stored in the vector's attribute table well be not checked
209
>>> cens = Vector('census')
210
>>> cens.open(mode='r')
211
>>> cens.has_color_table()
215
>>> from grass.pygrass.utils import copy, remove
216
>>> copy('census','mycensus','vect')
217
>>> from grass.pygrass.modules.shortcuts import vector as v
218
>>> v.colors(map='mycensus', color='population', column='TOTAL_POP')
220
>>> mycens = Vector('mycensus')
221
>>> mycens.open(mode='r')
222
>>> mycens.has_color_table()
225
>>> remove('mycensus', 'vect')
228
path = join(loc.path(), self.mapset, 'vector', self.name, 'colr')
229
return True if exists(path) else False
232
#=============================================
233
# VECTOR WITH TOPOLOGY
234
#=============================================
236
class VectorTopo(Vector):
237
"""Vector class with the support of the GRASS topology.
239
Open a vector map using the *with statement*: ::
241
>>> with VectorTopo('schools', mode='r') as schools:
242
... for school in schools[:3]:
243
... print school.attrs['NAMESHORT']
248
>>> schools.is_open()
253
def __init__(self, name, mapset='', *args, **kwargs):
254
super(VectorTopo, self).__init__(name, mapset, *args, **kwargs)
256
self._class_name = 'VectorTopo'
259
return libvect.Vect_get_num_lines(self.c_mapinfo)
261
def __getitem__(self, key):
264
>>> cens = VectorTopo('census')
265
>>> cens.open(mode='r')
267
[Boundary(v_id=1), Boundary(v_id=2), Boundary(v_id=3)]
272
if isinstance(key, slice):
273
return [self.read(indx)
274
for indx in range(key.start if key.start else 1,
275
key.stop if key.stop else len(self),
276
key.step if key.step else 1)]
277
elif isinstance(key, int):
278
return self.read(key)
280
raise ValueError("Invalid argument type: %r." % key)
283
def num_primitive_of(self, primitive):
284
"""Return the number of primitive
286
:param primitive: the name of primitive to query; the supported values are:
301
>>> cens = VectorTopo('census')
302
>>> cens.open(mode='r')
303
>>> cens.num_primitive_of('point')
305
>>> cens.num_primitive_of('line')
307
>>> cens.num_primitive_of('centroid')
309
>>> cens.num_primitive_of('boundary')
315
return libvect.Vect_get_num_primitives(self.c_mapinfo,
319
def number_of(self, vtype):
320
"""Return the number of the choosen element type
322
:param vtype: the name of type to query; the supported values are:
323
*areas*, *dblinks*, *faces*, *holes*, *islands*,
324
*kernels*, *line_points*, *lines*, *nodes*,
325
*update_lines*, *update_nodes*, *volumes*
328
>>> cens = VectorTopo('census')
329
>>> cens.open(mode='r')
330
>>> cens.number_of("areas")
332
>>> cens.number_of("islands")
334
>>> cens.number_of("holes")
336
>>> cens.number_of("lines")
338
>>> cens.number_of("nodes")
340
>>> cens.number_of("pizza")
341
... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
342
Traceback (most recent call last):
344
ValueError: vtype not supported, use one of: 'areas', ...
350
if vtype in _NUMOF.keys():
351
return _NUMOF[vtype](self.c_mapinfo)
353
keys = "', '".join(sorted(_NUMOF.keys()))
354
raise ValueError("vtype not supported, use one of: '%s'" % keys)
357
def num_primitives(self):
358
"""Return dictionary with the number of all primitives
361
for prim in VTYPE.keys():
362
output[prim] = self.num_primitive_of(prim)
366
def viter(self, vtype, idonly=False):
367
"""Return an iterator of vector features
369
:param vtype: the name of type to query; the supported values are:
370
*areas*, *dblinks*, *faces*, *holes*, *islands*,
371
*kernels*, *line_points*, *lines*, *nodes*,
372
*update_lines*, *update_nodes*, *volumes*
374
:param idonly: variable to return only the id of features instead of
378
>>> cens = VectorTopo('census', mode='r')
379
>>> cens.open(mode='r')
380
>>> big = [area for area in cens.viter('areas')
381
... if area.alive() and area.area() >= 10000]
383
[Area(5), Area(6), Area(13)]
386
to sort the result in a efficient way, use: ::
388
>>> from operator import methodcaller as method
389
>>> big.sort(key=method('area'), reverse=True) # sort the list
390
>>> for area in big[:3]:
391
... print area, area.area()
392
Area(2099) 5392751.5304
393
Area(2171) 4799921.30863
394
Area(495) 4055812.49695
398
if vtype in _GEOOBJ.keys():
399
if _GEOOBJ[vtype] is not None:
400
ids = (indx for indx in range(1, self.number_of(vtype) + 1))
403
return (_GEOOBJ[vtype](v_id=indx, c_mapinfo=self.c_mapinfo,
405
writable=self.writable)
408
keys = "', '".join(sorted(_GEOOBJ.keys()))
409
raise ValueError("vtype not supported, use one of: '%s'" % keys)
413
"""Rewind vector map to cause reads to start at beginning. ::
415
>>> cens = VectorTopo('census')
416
>>> cens.open(mode='r')
430
libvect.Vect_rewind(self.c_mapinfo)
433
def cat(self, cat_id, vtype, layer=None, generator=False, geo=None):
434
"""Return the geometry features with category == cat_id.
436
:param cat_id: the category number
438
:param vtype: the type of geometry feature that we are looking for
440
:param layer: the layer number that will be used
442
:param generator: if True return a generator otherwise it return a
444
:type generator: bool
446
if geo is None and vtype not in _GEOOBJ:
447
keys = "', '".join(sorted(_GEOOBJ.keys()))
448
raise ValueError("vtype not supported, use one of: '%s'" % keys)
449
Obj = _GEOOBJ[vtype] if geo is None else geo
451
libvect.Vect_cidx_find_all(self.c_mapinfo,
452
layer if layer else self.layer,
453
Obj.gtype, cat_id, ilist.c_ilist)
454
is2D = not self.is_3D()
456
return (read_line(feature_id=v_id, c_mapinfo=self.c_mapinfo,
457
table=self.table, writable=self.writable,
461
return [read_line(feature_id=v_id, c_mapinfo=self.c_mapinfo,
462
table=self.table, writable=self.writable,
467
def read(self, feature_id):
468
"""Return a geometry object given the feature id.
470
:param int feature_id: the id of feature to obtain
472
>>> cens = VectorTopo('census')
473
>>> cens.open(mode='r')
474
>>> feature1 = cens.read(0) #doctest: +ELLIPSIS
475
Traceback (most recent call last):
477
ValueError: The index must be >0, 0 given.
478
>>> feature1 = cens.read(1)
481
>>> feature1.length()
484
Centoid(642963.159711, 214994.016279)
488
Centoid(642963.159711, 214994.016279)
489
>>> cens.read(8921) #doctest: +ELLIPSIS
490
Traceback (most recent call last):
492
IndexError: Index out of range
496
return read_line(feature_id, self.c_mapinfo, self.table, self.writable,
497
is2D=not self.is_3D())
501
"""Return if a vector map is empty or not
503
primitives = self.num_primitives()
505
for v in primitives.values():
512
def rewrite(self, line, geo_obj, attrs=None, **kargs):
513
"""Rewrite a geometry features
515
if self.table is not None and attrs:
518
self.table.update(key=line, values=attr)
519
elif self.table is None and attrs:
520
print "Table for vector {name} does not exist, attributes not" \
521
" loaded".format(name=self.name)
522
libvect.Vect_cat_set(geo_obj.c_cats, self.layer, line)
523
result = libvect.Vect_rewrite_line(self.c_mapinfo,
528
raise GrassError("Not able to write the vector feature.")
530
# return offset into file where the feature starts
531
geo_obj.offset = result
534
def delete(self, feature_id):
535
"""Remove a feature by its id
537
:param feature_id: the id of the feature
538
:type feature_id: int
540
if libvect.Vect_rewrite_line(self.c_mapinfo, feature_id) == -1:
541
raise GrassError("C funtion: Vect_rewrite_line.")
544
def restore(self, geo_obj):
545
if hasattr(geo_obj, 'offset'):
546
if libvect.Vect_restore_line(self.c_mapinfo, geo_obj.id,
547
geo_obj.offset) == -1:
548
raise GrassError("C funtion: Vect_restore_line.")
550
raise ValueError("The value have not an offset attribute.")
554
"""Return the BBox of the vecor map
557
if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
558
raise GrassError("I can not find the Bbox.")
562
def select_by_bbox(self, bbox):
563
"""Return the BBox of the vector map
565
# TODO replace with bbox if bbox else Bbox() ??
567
if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0:
568
raise GrassError("I can not find the Bbox.")
571
def close(self, build=True, release=True):
572
"""Close the VectorTopo map, if release is True, the memory
573
occupied by spatial index is released"""
575
libvect.Vect_set_release_support(self.c_mapinfo)
576
super(VectorTopo, self).close(build=build)