GRASS GIS logo

Source code for pygrass.vector.geometry

# -*- coding: utf-8 -*-
"""
Created on Wed Jul 18 10:46:25 2012

@author: pietro

"""
import ctypes
import re
from collections import namedtuple

import numpy as np

import grass.lib.gis as libgis
import grass.lib.vector as libvect

from grass.pygrass.errors import GrassError, mapinfo_must_be_set

from grass.pygrass.vector.basic import Ilist, Bbox, Cats
from grass.pygrass.vector import sql

# For test purposes
test_vector_name = "geometry_doctest_map"

LineDist = namedtuple('LineDist', 'point dist spdist sldist')

WKT = {'POINT\((.*)\)': 'point',  # 'POINT\(\s*([+-]*\d+\.*\d*)+\s*\)'
       'LINESTRING\((.*)\)': 'line'}


[docs]def read_WKT(string): """Read the string and return a geometry object **WKT**: :: POINT(0 0) LINESTRING(0 0,1 1,1 2) POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)) MULTIPOINT(0 0,1 2) MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4)) MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1))) GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4)) **EWKT**: :: POINT(0 0 0) -- XYZ SRID=32632;POINT(0 0) -- XY with SRID POINTM(0 0 0) -- XYM POINT(0 0 0 0) -- XYZM SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM with SRID MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1)) POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)) MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0), (1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)), ((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0))) GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) ) MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) ) POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) ) TRIANGLE ((0 0, 0 9, 9 0, 0 0)) TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) ) """ for regexp, obj in WKT.items(): if re.match(regexp, string): geo = 10 return obj(geo)
[docs]def read_WKB(buff): """Read the binary buffer and return a geometry object""" pass
[docs]def intersects(lineA, lineB, with_z=False): """Return a list of points >>> lineA = Line([(0, 0), (4, 0)]) >>> lineB = Line([(2, 2), (2, -2)]) >>> intersects(lineA, lineB) Line([Point(2.000000, 0.000000)]) """ line = Line() if libvect.Vect_line_get_intersections(lineA.c_points, lineB.c_points, line.c_points, int(with_z)): return line else: return [] #============================================= # GEOMETRY #=============================================
[docs]def get_xyz(pnt): """Return a tuple with: x, y, z. >>> pnt = Point(0, 0) >>> get_xyz(pnt) (0.0, 0.0, 0.0) >>> get_xyz((1, 1)) (1, 1, 0.0) >>> get_xyz((1, 1, 2)) (1, 1, 2) >>> get_xyz((1, 1, 2, 2)) #doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: The the format of the point is not supported: (1, 1, 2, 2) """ if isinstance(pnt, Point): if pnt.is2D: x, y = pnt.x, pnt.y z = 0. else: x, y, z = pnt.x, pnt.y, pnt.z else: if len(pnt) == 2: x, y = pnt z = 0. elif len(pnt) == 3: x, y, z = pnt else: str_error = "The the format of the point is not supported: {0!r}" raise ValueError(str_error.format(pnt)) return x, y, z
[docs]class Attrs(object): def __init__(self, cat, table, writeable=False): self._cat = None self.cond = '' self.table = table self.cat = cat self.writeable = writeable def _get_cat(self): return self._cat def _set_cat(self, value): self._cat = value if value: # update condition self.cond = "%s=%d" % (self.table.key, value) cat = property(fget=_get_cat, fset=_set_cat, doc="Set and obtain cat value") def __getitem__(self, keys): """Return the value stored in the attribute table. >>> from grass.pygrass.vector import VectorTopo >>> test_vect = VectorTopo(test_vector_name) >>> test_vect.open('r') >>> v1 = test_vect[1] >>> v1.attrs['name'] u'point' >>> v1.attrs['name', 'value'] (u'point', 1.0) >>> test_vect.close() """ sqlcode = sql.SELECT_WHERE.format(cols=(keys if np.isscalar(keys) else ', '.join(keys)), tname=self.table.name, condition=self.cond) cur = self.table.execute(sqlcode) results = cur.fetchone() if results is not None: return results[0] if len(results) == 1 else results def __setitem__(self, keys, values): """Set value of a given column of a table attribute. >>> from grass.pygrass.vector import VectorTopo >>> test_vect = VectorTopo(test_vector_name) >>> test_vect.open('r') >>> v1 = test_vect[1] >>> v1.attrs['name'] u'point' >>> v1.attrs['name'] = "new_point_1" >>> v1.attrs['name'] u'new_point_1' >>> v1.attrs['name', 'value'] = "new_point_2", 100. >>> v1.attrs['name', 'value'] (u'new_point_2', 100.0) >>> v1.attrs['name', 'value'] = "point", 1. >>> v1.attrs.table.conn.commit() >>> test_vect.close() """ if self.writeable: if np.isscalar(keys): keys, values = (keys, ), (values, ) # check if key is a column of the table or not for key in keys: if key not in self.table.columns: raise KeyError('Column: %s not in table' % key) # prepare the string using as paramstyle: qmark vals = ','.join(['%s=?' % k for k in keys]) # "UPDATE {tname} SET {values} WHERE {condition};" sqlcode = sql.UPDATE_WHERE.format(tname=self.table.name, values=vals, condition=self.cond) self.table.execute(sqlcode, values=values) #self.table.conn.commit() else: str_err = "You can only read the attributes if the map is in another mapset" raise GrassError(str_err) def __dict__(self): """Return a dict of the attribute table row.""" dic = {} for key, val in zip(self.keys(), self.values()): dic[key] = val return dic
[docs] def values(self): """Return the values of the attribute table row. >>> from grass.pygrass.vector import VectorTopo >>> test_vect = VectorTopo(test_vector_name) >>> test_vect.open('r') >>> v1 = test_vect[1] >>> v1.attrs.values() (1, u'point', 1.0) >>> test_vect.close() """ #SELECT {cols} FROM {tname} WHERE {condition} cur = self.table.execute(sql.SELECT_WHERE.format(cols='*', tname=self.table.name, condition=self.cond)) return cur.fetchone()
[docs] def keys(self): """Return the column name of the attribute table. >>> from grass.pygrass.vector import VectorTopo >>> test_vect = VectorTopo(test_vector_name) >>> test_vect.open('r') >>> v1 = test_vect[1] >>> v1.attrs.keys() [u'cat', u'name', u'value'] >>> test_vect.close() """ return self.table.columns.names()
[docs] def commit(self): """Save the changes""" self.table.conn.commit()
[docs]class Geo(object): """ Base object for different feature types """ gtype = None def __init__(self, v_id=0, c_mapinfo=None, c_points=None, c_cats=None, table=None, writeable=False, is2D=True, free_points=False, free_cats=False): """Constructor of a geometry object :param v_id: The vector feature id :param c_mapinfo: A pointer to the vector mapinfo structure :param c_points: A pointer to a libvect.line_pnts structure, this is optional, if not set an internal structure will be allocated and free'd at object destruction :param c_cats: A pointer to a libvect.line_cats structure, this is optional, if not set an internal structure will be allocated and free'd at object destruction :param table: The attribute table to select attributes for this feature :param writeable: Not sure what this is for? :param is2D: If True this feature has two dimensions, False if this feature has three dimensions :param free_points: Set this True if the provided c_points structure should be free'd at object destruction, be aware that no other object should free them, otherwise you can expect a double free corruption segfault :param free_cats: Set this True if the provided c_cats structure should be free'd at object destruction, be aware that no other object should free them, otherwise you can expect a double free corruption segfault """ self.id = v_id # vector id self.c_mapinfo = c_mapinfo self.is2D = (is2D if is2D is not None else bool(libvect.Vect_is_3d(self.c_mapinfo) != 1)) # Set True if cats and points are allocated by this object # to free the cats and points structures on destruction self._free_points = False self._free_cats = False read = False # set c_points if c_points is None: self.c_points = ctypes.pointer(libvect.line_pnts()) self._free_points = True read = True else: self.c_points = c_points self._free_points = free_points # set c_cats if c_cats is None: self.c_cats = ctypes.pointer(libvect.line_cats()) self._free_cats = free_cats read = True else: self.c_cats = c_cats self._free_cats = True if self.id and self.c_mapinfo is not None and read: self.read() # set the attributes as last thing to do self.attrs = None if table is not None and self.cat is not None: self.attrs = Attrs(self.cat, table, writeable) def __del__(self): """Take care of the allocated line_pnts and line_cats allocation """ if self._free_points == True and self.c_points: if self.c_points.contents.alloc_points > 0: #print("G_free(points) [%i]"%(self.c_points.contents.alloc_points)) libgis.G_free(self.c_points.contents.x) libgis.G_free(self.c_points.contents.y) if self.c_points.contents.z: libgis.G_free(self.c_points.contents.z) if self._free_cats == True and self.c_cats: if self.c_cats.contents.alloc_cats > 0: #print("G_free(cats) [%i]"%(self.c_cats.contents.alloc_cats)) libgis.G_free(self.c_cats.contents.cat) @property
[docs] def cat(self): if self.c_cats.contents.cat: return self.c_cats.contents.cat.contents.value
[docs] def has_topology(self): if self.c_mapinfo is not None: return self.c_mapinfo.contents.level == 2 else: return False
@mapinfo_must_be_set
[docs] def read(self): """Read and set the coordinates of the centroid from the vector map, using the centroid_id and calling the Vect_read_line C function""" self.id, ftype, c_points, c_cats = c_read_line(self.id, self.c_mapinfo, self.c_points, self.c_cats)
[docs] def to_wkt(self): """Return a "well know text" (WKT) geometry string, this method uses the GEOS implementation in the vector library. :: >>> pnt = Point(10, 100) >>> pnt.to_wkt() 'POINT (10.0000000000000000 100.0000000000000000)' """ return libvect.Vect_line_to_wkt(self.c_points, self.gtype, not self.is2D)
[docs] def to_wkb(self): """Return a "well know binary" (WKB) geometry byte array, this method uses the GEOS implementation in the vector library. :: >>> pnt = Point(10, 100) >>> wkb = pnt.to_wkb() >>> len(wkb) 21 """ size = ctypes.c_size_t() barray = libvect.Vect_line_to_wkb(self.c_points, self.gtype, not self.is2D, ctypes.byref(size)) return(ctypes.string_at(barray, size.value))
[docs]class Point(Geo): """Instantiate a Point object that could be 2 or 3D, default parameters are 0. :: >>> pnt = Point() >>> pnt.x 0.0 >>> pnt.y 0.0 >>> pnt.z >>> pnt.is2D True >>> pnt Point(0.000000, 0.000000) >>> pnt.z = 0 >>> pnt.is2D False >>> pnt Point(0.000000, 0.000000, 0.000000) >>> print(pnt) POINT Z (0.0000000000000000 0.0000000000000000 0.0000000000000000) >>> c_points = ctypes.pointer(libvect.line_pnts()) >>> c_cats = ctypes.pointer(libvect.line_cats()) >>> p = Point(c_points = c_points, c_cats=c_cats) >>> del p >>> c_points = ctypes.pointer(libvect.line_pnts()) >>> c_cats = ctypes.pointer(libvect.line_cats()) >>> p = Point(c_points=c_points, c_cats=c_cats, free_points=True, ... free_cats=True) >>> del p .. """ # geometry type gtype = libvect.GV_POINT def __init__(self, x=0, y=0, z=None, **kargs): super(Point, self).__init__(**kargs) if self.id and self.c_mapinfo: self.read() else: self.is2D = True if z is None else False z = z if z is not None else 0 libvect.Vect_append_point(self.c_points, x, y, z) def _get_x(self): return self.c_points.contents.x[0] def _set_x(self, value): self.c_points.contents.x[0] = value x = property(fget=_get_x, fset=_set_x, doc="Set and obtain x coordinate") def _get_y(self): return self.c_points.contents.y[0] def _set_y(self, value): self.c_points.contents.y[0] = value y = property(fget=_get_y, fset=_set_y, doc="Set and obtain y coordinate") def _get_z(self): if self.is2D: return None return self.c_points.contents.z[0] def _set_z(self, value): if value is None: self.is2D = True self.c_points.contents.z[0] = 0 else: self.c_points.contents.z[0] = value self.is2D = False z = property(fget=_get_z, fset=_set_z, doc="Set and obtain z coordinate") def __str__(self): return self.to_wkt() def __repr__(self): return "Point(%s)" % ', '.join(['%f' % coor for coor in self.coords()]) def __eq__(self, pnt): """Return True if the coordinates are the same. >>> p0 = Point() >>> p1 = Point() >>> p2 = Point(1, 1) >>> p0 == p1 True >>> p1 == p2 False """ if isinstance(pnt, Point): return pnt.coords() == self.coords() return Point(*pnt).coords() == self.coords() def __ne__(self, other): return not self == other # Restore Python 2 hashing beaviour on Python 3 __hash__ = object.__hash__
[docs] def coords(self): """Return a tuple with the point coordinates. :: >>> pnt = Point(10, 100) >>> pnt.coords() (10.0, 100.0) If the point is 2D return a x, y tuple. But if we change the ``z`` the Point object become a 3D point, therefore the method return a x, y, z tuple. :: >>> pnt.z = 1000. >>> pnt.coords() (10.0, 100.0, 1000.0) .. """ if self.is2D: return self.x, self.y else: return self.x, self.y, self.z
[docs] def to_wkt_p(self): """Return a "well know text" (WKT) geometry string Python implementation. :: >>> pnt = Point(10, 100) >>> pnt.to_wkt_p() 'POINT(10.000000 100.000000)' .. warning:: Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with: ``XYZM`` are not supported yet. """ return "POINT(%s)" % ' '.join(['%f' % coord for coord in self.coords()])
[docs] def distance(self, pnt): """Calculate distance of 2 points, using the Vect_points_distance C function, If one of the point have z == None, return the 2D distance. :param pnt: the point for calculate the distance :type pnt: a Point object or a tuple with the coordinates >>> pnt0 = Point(0, 0, 0) >>> pnt1 = Point(1, 0) >>> pnt0.distance(pnt1) 1.0 >>> pnt1.z = 1 >>> pnt1 Point(1.000000, 0.000000, 1.000000) >>> pnt0.distance(pnt1) 1.4142135623730951 """ if self.is2D or pnt.is2D: return libvect.Vect_points_distance(self.x, self.y, 0, pnt.x, pnt.y, 0, 0) else: return libvect.Vect_points_distance(self.x, self.y, self.z, pnt.x, pnt.y, pnt.z, 1)
[docs] def buffer(self, dist=None, dist_x=None, dist_y=None, angle=0, round_=True, tol=0.1): """Return the buffer area around the point, using the ``Vect_point_buffer2`` C function. :param dist: the distance around the point :type dist: num :param dist_x: the distance along x :type dist_x: num :param dist_y: the distance along y :type dist_y: num :param angle: the angle between 0x and major axis :type angle: num :param round_: to make corners round :type round_: bool :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as Area object >>> pnt = Point(0, 0) >>> boundary, centroid = pnt.buffer(10) >>> boundary #doctest: +ELLIPSIS Line([Point(10.000000, 0.000000),...Point(10.000000, 0.000000)]) >>> centroid Point(0.000000, 0.000000) """ if dist is not None: dist_x = dist dist_y = dist elif not dist_x or not dist_y: raise TypeError('TypeError: buffer expected 1 arguments, got 0') bound = Line() p_points = ctypes.pointer(bound.c_points) libvect.Vect_point_buffer2(self.x, self.y, dist_x, dist_y, angle, int(round_), tol, p_points) return (bound, self)
[docs]class Line(Geo): """Instantiate a new Line with a list of tuple, or with a list of Point. :: >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)]) >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000), Point(2.000000, 0.000000), Point(1.000000, -1.000000)]) .. """ # geometry type gtype = libvect.GV_LINE def __init__(self, points=None, **kargs): super(Line, self).__init__(**kargs) if points is not None: for pnt in points: self.append(pnt) def __getitem__(self, key): """Get line point of given index, slice allowed. :: >>> line = Line([(0, 0), (1, 1), (2, 2), (3, 3)]) >>> line[1] Point(1.000000, 1.000000) >>> line[-1] Point(3.000000, 3.000000) >>> line[:2] [Point(0.000000, 0.000000), Point(1.000000, 1.000000)] .. """ #TODO: # line[0].x = 10 is not working #pnt.c_px = ctypes.pointer(self.c_points.contents.x[indx]) # pnt.c_px = ctypes.cast(id(self.c_points.contents.x[indx]), # ctypes.POINTER(ctypes.c_double)) if isinstance(key, slice): #import pdb; pdb.set_trace() #Get the start, stop, and step from the slice return [Point(self.c_points.contents.x[indx], self.c_points.contents.y[indx], None if self.is2D else self.c_points.contents.z[indx]) for indx in range(*key.indices(len(self)))] elif isinstance(key, int): if key < 0: # Handle negative indices key += self.c_points.contents.n_points if key >= self.c_points.contents.n_points: raise IndexError('Index out of range') return Point(self.c_points.contents.x[key], self.c_points.contents.y[key], None if self.is2D else self.c_points.contents.z[key]) else: raise ValueError("Invalid argument type: %r." % key) def __setitem__(self, indx, pnt): """Change the coordinate of point. :: >>> line = Line([(0, 0), (1, 1)]) >>> line[0] = (2, 2) >>> line Line([Point(2.000000, 2.000000), Point(1.000000, 1.000000)]) .. """ x, y, z = get_xyz(pnt) self.c_points.contents.x[indx] = x self.c_points.contents.y[indx] = y self.c_points.contents.z[indx] = z def __iter__(self): """Return a Point generator of the Line""" return (self.__getitem__(i) for i in range(self.__len__())) def __len__(self): """Return the number of points of the line.""" return self.c_points.contents.n_points def __str__(self): return self.to_wkt() def __repr__(self): return "Line([%s])" % ', '.join([repr(pnt) for pnt in self.__iter__()])
[docs] def point_on_line(self, distance, angle=0, slope=0): """Return a Point object on line in the specified distance, using the `Vect_point_on_line` C function. Raise a ValueError If the distance exceed the Line length. :: >>> line = Line([(0, 0), (1, 1)]) >>> line.point_on_line(5) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ValueError: The distance exceed the length of the line, that is: 1.414214 >>> line.point_on_line(1) Point(0.707107, 0.707107) .. """ # instantiate an empty Point object maxdist = self.length() if distance > maxdist: str_err = "The distance exceed the length of the line, that is: %f" raise ValueError(str_err % maxdist) pnt = Point(0, 0, -9999) if not libvect.Vect_point_on_line(self.c_points, distance, pnt.c_points.contents.x, pnt.c_points.contents.y, pnt.c_points.contents.z, angle, slope): raise ValueError("Vect_point_on_line give an error.") pnt.is2D = self.is2D return pnt
@mapinfo_must_be_set
[docs] def alive(self): """Return True if this line is alive or False if this line is dead or its index is out of range. """ return(bool(libvect.Vect_line_alive(self.c_mapinfo, self.id)))
[docs] def append(self, pnt): """Appends one point to the end of a line, using the ``Vect_append_point`` C function. :param pnt: the point to add to line :type pnt: a Point object or a tuple with the coordinates >>> line = Line() >>> line.append((10, 100)) >>> line Line([Point(10.000000, 100.000000)]) >>> line.append((20, 200)) >>> line Line([Point(10.000000, 100.000000), Point(20.000000, 200.000000)]) Like python list. """ x, y, z = get_xyz(pnt) libvect.Vect_append_point(self.c_points, x, y, z)
[docs] def bbox(self, bbox=None): """Return the bounding box of the line, using ``Vect_line_box`` C function. :: >>> line = Line([(0, 0), (0, 1), (2, 1), (2, 0)]) >>> bbox = line.bbox() >>> bbox Bbox(1.0, 0.0, 2.0, 0.0) .. """ bbox = bbox if bbox else Bbox() libvect.Vect_line_box(self.c_points, bbox.c_bbox) return bbox
[docs] def extend(self, line, forward=True): """Appends points to the end of a line. :param line: it is possible to extend a line, give a list of points, or directly with a line_pnts struct. :type line: Line object ot list of points :param forward: if forward is True the line is extend forward otherwise is extend backward. The method use the `Vect_append_points` C function. :type forward: bool >>> line = Line([(0, 0), (1, 1)]) >>> line.extend( Line([(2, 2), (3, 3)]) ) >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000), Point(2.000000, 2.000000), Point(3.000000, 3.000000)]) """ # set direction if forward: direction = libvect.GV_FORWARD else: direction = libvect.GV_BACKWARD # check if is a Line object if isinstance(line, Line): c_points = line.c_points else: # instantiate a Line object lin = Line() for pnt in line: # add the points to the line lin.append(pnt) c_points = lin.c_points libvect.Vect_append_points(self.c_points, c_points, direction)
[docs] def insert(self, indx, pnt): """Insert new point at index position and move all old points at that position and above up, using ``Vect_line_insert_point`` C function. :param indx: the index where add new point :type indx: int :param pnt: the point to add :type pnt: a Point object >>> line = Line([(0, 0), (1, 1)]) >>> line.insert(0, Point(1.000000, -1.000000) ) >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(1.000000, -1.000000), Point(0.000000, 0.000000), Point(1.000000, 1.000000)]) """ if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: raise IndexError('Index out of range') x, y, z = get_xyz(pnt) libvect.Vect_line_insert_point(self.c_points, indx, x, y, z)
[docs] def length(self): """Calculate line length, 3D-length in case of 3D vector line, using `Vect_line_length` C function. :: >>> line = Line([(0, 0), (1, 1), (0, 1)]) >>> line.length() 2.414213562373095 .. """ return libvect.Vect_line_length(self.c_points)
[docs] def length_geodesic(self): """Calculate line length, usig `Vect_line_geodesic_length` C function. :: >>> line = Line([(0, 0), (1, 1), (0, 1)]) >>> line.length_geodesic() 2.414213562373095 .. """ return libvect.Vect_line_geodesic_length(self.c_points)
[docs] def distance(self, pnt): """Calculate the distance between line and a point. :param pnt: the point to calculate distance :type pnt: a Point object or a tuple with the coordinates Return a namedtuple with: * point: the closest point on the line, * dist: the distance between these two points, * spdist: distance to point on line from segment beginning * sldist: distance to point on line form line beginning along line The distance is compute using the ``Vect_line_distance`` C function. >>> point = Point(2.3, 0.5) >>> line = Line([(0, 0), (2, 0), (3, 0)]) >>> line.distance(point) #doctest: +NORMALIZE_WHITESPACE LineDist(point=Point(2.300000, 0.000000), dist=0.5, spdist=0.2999999999999998, sldist=2.3) """ # instantite outputs cx = ctypes.c_double(0) cy = ctypes.c_double(0) cz = ctypes.c_double(0) dist = ctypes.c_double(0) sp_dist = ctypes.c_double(0) lp_dist = ctypes.c_double(0) libvect.Vect_line_distance(self.c_points, pnt.x, pnt.y, 0 if pnt.is2D else pnt.z, 0 if self.is2D else 1, ctypes.byref(cx), ctypes.byref(cy), ctypes.byref(cz), ctypes.byref(dist), ctypes.byref(sp_dist), ctypes.byref(lp_dist)) # instantiate the Point class point = Point(cx.value, cy.value, cz.value) point.is2D = self.is2D return LineDist(point, dist.value, sp_dist.value, lp_dist.value)
@mapinfo_must_be_set
[docs] def first_cat(self): """Fetches FIRST category number for given vector line and field, using the ``Vect_get_line_cat`` C function. .. warning:: Not implemented yet. """ # TODO: add this method. # libvect.Vect_get_line_cat(self.c_mapinfo, self.id, self.field) pass
[docs] def pop(self, indx): """Return the point in the index position and remove from the Line. :param indx: the index where add new point :type indx: int >>> line = Line([(0, 0), (1, 1), (2, 2)]) >>> midle_pnt = line.pop(1) >>> midle_pnt #doctest: +NORMALIZE_WHITESPACE Point(1.000000, 1.000000) >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(2.000000, 2.000000)]) """ if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: raise IndexError('Index out of range') pnt = self.__getitem__(indx) libvect.Vect_line_delete_point(self.c_points, indx) return pnt
[docs] def delete(self, indx): """Remove the point in the index position. :param indx: the index where add new point :type indx: int >>> line = Line([(0, 0), (1, 1), (2, 2)]) >>> line.delete(-1) >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000)]) """ if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: raise IndexError('Index out of range') libvect.Vect_line_delete_point(self.c_points, indx)
[docs] def prune(self): """Remove duplicate points, i.e. zero length segments, using `Vect_line_prune` C function. :: >>> line = Line([(0, 0), (1, 1), (1, 1), (2, 2)]) >>> line.prune() >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000), Point(2.000000, 2.000000)]) .. """ libvect.Vect_line_prune(self.c_points)
[docs] def prune_thresh(self, threshold): """Remove points in threshold, using the ``Vect_line_prune_thresh`` C function. :param threshold: the threshold value where prune points :type threshold: num >>> line = Line([(0, 0), (1.0, 1.0), (1.2, 0.9), (2, 2)]) >>> line.prune_thresh(0.5) >>> line #doctest: +SKIP +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000), Point(2.000000, 2.000000)]) .. warning :: prune_thresh is not working yet. """ libvect.Vect_line_prune(self.c_points, ctypes.c_double(threshold))
[docs] def remove(self, pnt): """Delete point at given index and move all points above down, using `Vect_line_delete_point` C function. :param pnt: the point to remove :type pnt: a Point object or a tuple with the coordinates >>> line = Line([(0, 0), (1, 1), (2, 2)]) >>> line.remove((2, 2)) >>> line[-1] #doctest: +NORMALIZE_WHITESPACE Point(1.000000, 1.000000) .. """ for indx, point in enumerate(self.__iter__()): if pnt == point: libvect.Vect_line_delete_point(self.c_points, indx) return raise ValueError('list.remove(x): x not in list')
[docs] def reverse(self): """Reverse the order of vertices, using `Vect_line_reverse` C function. :: >>> line = Line([(0, 0), (1, 1), (2, 2)]) >>> line.reverse() >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(2.000000, 2.000000), Point(1.000000, 1.000000), Point(0.000000, 0.000000)]) .. """ libvect.Vect_line_reverse(self.c_points)
[docs] def segment(self, start, end): """Create line segment. using the ``Vect_line_segment`` C function. :param start: distance from the beginning of the line where the segment start :type start: float :param end: distance from the beginning of the line where the segment end :type end: float :: # x (1, 1) # | # |- # | # x--------x (1, 0) # (0, 0) ^ >>> line = Line([(0, 0), (1, 0), (1, 1)]) >>> line.segment(0.5, 1.5) #doctest: +NORMALIZE_WHITESPACE Line([Point(0.500000, 0.000000), Point(1.000000, 0.000000), Point(1.000000, 0.500000)]) """ line = Line() libvect.Vect_line_segment(self.c_points, start, end, line.c_points) return line
[docs] def to_list(self): """Return a list of tuple. :: >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)]) >>> line.to_list() [(0.0, 0.0), (1.0, 1.0), (2.0, 0.0), (1.0, -1.0)] .. """ return [pnt.coords() for pnt in self.__iter__()]
[docs] def to_array(self): """Return an array of coordinates. :: >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)]) >>> line.to_array() #doctest: +NORMALIZE_WHITESPACE array([[ 0., 0.], [ 1., 1.], [ 2., 0.], [ 1., -1.]]) .. """ return np.array(self.to_list())
[docs] def to_wkt_p(self): """Return a Well Known Text string of the line. :: >>> line = Line([(0, 0), (1, 1), (1, 2)]) >>> line.to_wkt_p() #doctest: +ELLIPSIS 'LINESTRING(0.000000 0.000000, ..., 1.000000 2.000000)' .. """ return "LINESTRING(%s)" % ', '.join([ ' '.join(['%f' % coord for coord in pnt.coords()]) for pnt in self.__iter__()])
[docs] def from_wkt(self, wkt): """Create a line reading a WKT string. :param wkt: the WKT string containing the LINESTRING :type wkt: str >>> line = Line() >>> line.from_wkt("LINESTRING(0 0,1 1,1 2)") >>> line #doctest: +NORMALIZE_WHITESPACE Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000), Point(1.000000, 2.000000)]) .. """ match = re.match('LINESTRING\((.*)\)', wkt) if match: self.reset() for coord in match.groups()[0].strip().split(','): self.append(tuple([float(e) for e in coord.split(' ')])) else: return None
[docs] def buffer(self, dist=None, dist_x=None, dist_y=None, angle=0, round_=True, caps=True, tol=0.1): """Return the buffer area around the line, using the ``Vect_line_buffer2`` C function. :param dist: the distance around the line :type dist: num :param dist_x: the distance along x :type dist_x: num :param dist_y: the distance along y :type dist_y: num :param angle: the angle between 0x and major axis :type angle: num :param round_: to make corners round :type round_: bool :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as Area object >>> line = Line([(0, 0), (0, 2)]) >>> boundary, centroid, isles = line.buffer(10) >>> boundary #doctest: +ELLIPSIS Line([Point(-10.000000, 0.000000),...Point(-10.000000, 0.000000)]) >>> centroid #doctest: +NORMALIZE_WHITESPACE Point(0.000000, 0.000000) >>> isles [] .. """ if dist is not None: dist_x = dist dist_y = dist elif not dist_x or not dist_y: raise TypeError('TypeError: buffer expected 1 arguments, got 0') p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts())) pp_isle = ctypes.pointer(ctypes.pointer( ctypes.pointer(libvect.line_pnts()))) n_isles = ctypes.pointer(ctypes.c_int()) libvect.Vect_line_buffer2(self.c_points, dist_x, dist_y, angle, int(round_), int(caps), tol, p_bound, pp_isle, n_isles) boundary = Line(c_points=p_bound.contents) isles = [Line(c_points=pp_isle[i].contents) for i in range(n_isles.contents.value) if pp_isle[i]] return(boundary, self[0], isles)
[docs] def reset(self): """Reset line, using `Vect_reset_line` C function. :: >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)]) >>> len(line) 4 >>> line.reset() >>> len(line) 0 >>> line Line([]) .. """ libvect.Vect_reset_line(self.c_points)
@mapinfo_must_be_set
[docs] def nodes(self): """Return the start and end nodes of the line This method requires topology build. return: A tuple of Node objects that represent the start and end point of this line. """ if self.has_topology(): n1 = ctypes.c_int() n2 = ctypes.c_int() libvect.Vect_get_line_nodes(self.c_mapinfo, self.id, ctypes.byref(n1), ctypes.byref(n2)) return (Node(n1.value, self.c_mapinfo), Node(n2.value, self.c_mapinfo))
[docs]class Node(object): """Node class for topological analysis of line neighbors. Objects of this class will be returned by the node() function of a Line object. All methods in this class require a proper setup of the Node objects. Hence, the correct id and a valid pointer to a mapinfo object must be provided in the constructions. Otherwise a segfault may happen. """ def __init__(self, v_id, c_mapinfo, **kwords): """Construct a Node object param v_id: The unique node id param c_mapinfo: A valid pointer to the mapinfo object param **kwords: Ignored """ self.id = v_id # vector id self.c_mapinfo = c_mapinfo self._setup() @mapinfo_must_be_set def _setup(self): self.is2D = bool(libvect.Vect_is_3d(self.c_mapinfo) != 1) self.nlines = libvect.Vect_get_node_n_lines(self.c_mapinfo, self.id) def __len__(self): return self.nlines def __iter__(self): return self.ilines() def __repr__(self): return "Node(%d)" % self.id @mapinfo_must_be_set
[docs] def alive(self): """Return True if this node is alive or False if this node is dead or its index is out of range. """ return(bool(libvect.Vect_node_alive(self.c_mapinfo, self.id)))
@mapinfo_must_be_set
[docs] def coords(self): """Return a tuple with the node coordinates.""" x = ctypes.c_double() y = ctypes.c_double() z = ctypes.c_double() libvect.Vect_get_node_coor(self.c_mapinfo, self.id, ctypes.byref(x), ctypes.byref(y), ctypes.byref(z)) return (x.value, y.value) if self.is2D else (x.value, y.value, z.value)
[docs] def to_wkt(self): """Return a "well know text" (WKT) geometry string. :: """ return "POINT(%s)" % ' '.join(['%f' % coord for coord in self.coords()])
[docs] def to_wkb(self): """Return a "well know binary" (WKB) geometry array. :: TODO: Must be implemented """ raise Exception("Not implemented")
[docs] def ilines(self, only_in=False, only_out=False): """Return a generator with all lines id connected to a node. The line id is negative if line is ending on the node and positive if starting from the node. :param only_in: Return only the lines that are ending in the node :type only_in: bool :param only_out: Return only the lines that are starting in the node :type only_out: bool """ for iline in range(self.nlines): lid = libvect.Vect_get_node_line(self.c_mapinfo, self.id, iline) if (not only_in and lid > 0) or (not only_out and lid < 0): yield lid
@mapinfo_must_be_set
[docs] def lines(self, only_in=False, only_out=False): """Return a generator with all lines connected to a node. :param only_in: Return only the lines that are ending in the node :type only_in: bool :param only_out: Return only the lines that are starting in the node :type only_out: bool """ for iline in self.ilines(only_in, only_out): yield Line(v_id=abs(iline), c_mapinfo=self.c_mapinfo)
@mapinfo_must_be_set
[docs] def angles(self): """Return a generator with all lines angles in a node.""" for iline in range(self.nlines): yield libvect.Vect_get_node_line_angle(self.c_mapinfo, self.id, iline)
[docs]class Boundary(Line): """ """ # geometry type gtype = libvect.GV_BOUNDARY def __init__(self, **kargs): super(Boundary, self).__init__(**kargs) v_id = kargs.get('v_id', 0) self.dir = libvect.GV_FORWARD if v_id > 0 else libvect.GV_BACKWARD self.c_left = ctypes.pointer(ctypes.c_int()) self.c_right = ctypes.pointer(ctypes.c_int()) @property
[docs] def left_area_id(self): """Left side area id, only available after read_area_ids() was called""" return self.c_left.contents.value
@property
[docs] def right_area_id(self): """Right side area id, only available after read_area_ids() was called""" return self.c_right.contents.value
def __repr__(self): return "Boundary([%s])" % ', '.join([repr(pnt) for pnt in self.__iter__()]) @mapinfo_must_be_set def _centroid(self, side, idonly=False): if side > 0: v_id = libvect.Vect_get_area_centroid(self.c_mapinfo, side) v_id = v_id if v_id else None if idonly: return v_id else: cntr = Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo) return cntr
[docs] def left_centroid(self, idonly=False): """Return left centroid :param idonly: True to return only the cat of feature :type idonly: bool """ return self._centroid(self.c_left.contents.value, idonly)
[docs] def right_centroid(self, idonly=False): """Return right centroid :param idonly: True to return only the cat of feature :type idonly: bool """ return self._centroid(self.c_right.contents.value, idonly)
@mapinfo_must_be_set
[docs] def read_area_ids(self): """Read and return left and right area ids of the boundary""" libvect.Vect_get_line_areas(self.c_mapinfo, self.id, self.c_left, self.c_right) return self.c_left.contents.value, self.c_right.contents.value
[docs] def area(self): """Return the area of the polygon. >>> bound = Boundary(points=[(0, 0), (0, 2), (2, 2), (2, 0), ... (0, 0)]) >>> bound.area() 4.0 """ libgis.G_begin_polygon_area_calculations() return libgis.G_area_of_polygon(self.c_points.contents.x, self.c_points.contents.y, self.c_points.contents.n_points)
[docs]class Centroid(Point): """The Centroid class inherit from the Point class. Centroid contains an attribute with the C Map_info struct, and attributes with the id of the Area. :: >>> centroid = Centroid(x=0, y=10) >>> centroid Centroid(0.000000, 10.000000) >>> from grass.pygrass.vector import VectorTopo >>> test_vect = VectorTopo(test_vector_name) >>> test_vect.open(mode='r') >>> centroid = Centroid(v_id=18, c_mapinfo=test_vect.c_mapinfo) >>> centroid Centroid(3.500000, 3.500000) >>> test_vect.close() .. """ # geometry type gtype = libvect.GV_CENTROID def __init__(self, area_id=None, **kargs): super(Centroid, self).__init__(**kargs) self.area_id = area_id if self.id and self.c_mapinfo and self.area_id is None: self.area_id = self._area_id() elif self.c_mapinfo and self.area_id and self.id is None: self.id = self._centroid_id() if self.area_id is not None: self.read() #self.c_pline = ctypes.pointer(libvect.P_line()) if topology else None def __repr__(self): return "Centroid(%s)" % ', '.join(['%f' % co for co in self.coords()]) @mapinfo_must_be_set def _centroid_id(self): """Return the centroid_id, using the c_mapinfo and an area_id attributes of the class, and calling the Vect_get_area_centroid C function, if no centroid_id were found return None""" centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo, self.area_id) return centroid_id if centroid_id != 0 else None @mapinfo_must_be_set def _area_id(self): """Return the area_id, using the c_mapinfo and an centroid_id attributes of the class, and calling the Vect_centroid_area C function, if no area_id were found return None""" area_id = libvect.Vect_get_centroid_area(self.c_mapinfo, self.id) return area_id if area_id != 0 else None
[docs]class Isle(Geo): """An Isle is an area contained by another area. """ def __init__(self, **kargs): super(Isle, self).__init__(**kargs) #self.area_id = area_id def __repr__(self): return "Isle(%d)" % (self.id) @mapinfo_must_be_set
[docs] def boundaries(self): """Return a list of boundaries""" ilist = Ilist() libvect.Vect_get_isle_boundaries(self.c_mapinfo, self.id, ilist.c_ilist) return ilist
@mapinfo_must_be_set
[docs] def bbox(self, bbox=None): """Return bounding box of Isle""" bbox = bbox if bbox else Bbox() libvect.Vect_get_isle_box(self.c_mapinfo, self.id, bbox.c_bbox) return bbox
@mapinfo_must_be_set
[docs] def points(self): """Return a Line object with the outer ring points""" line = Line() libvect.Vect_get_isle_points(self.c_mapinfo, self.id, line.c_points) return line
[docs] def to_wkt(self): """Return a Well Known Text string of the isle. :: For now the outer ring is returned TODO: Implement inner rings detected from isles """ line = self.points() return "Polygon((%s))" % ', '.join([ ' '.join(['%f' % coord for coord in pnt]) for pnt in line.to_list()])
[docs] def to_wkb(self): """Return a "well know text" (WKB) geometry array. :: """ raise Exception("Not implemented")
@mapinfo_must_be_set
[docs] def points_geos(self): """Return a Line object with the outer ring points """ return libvect.Vect_get_isle_points_geos(self.c_mapinfo, self.id)
@mapinfo_must_be_set
[docs] def area_id(self): """Returns area id for isle.""" return libvect.Vect_get_isle_area(self.c_mapinfo, self.id)
@mapinfo_must_be_set
[docs] def alive(self): """Check if isle is alive or dead (topology required)""" return bool(libvect.Vect_isle_alive(self.c_mapinfo, self.id))
@mapinfo_must_be_set
[docs] def contain_pnt(self, pnt): """Check if point is in area. :param pnt: the point to remove :type pnt: a Point object or a tuple with the coordinates """ bbox = self.bbox() return bool(libvect.Vect_point_in_island(pnt.x, pnt.y, self.c_mapinfo, self.id, bbox.c_bbox.contents))
[docs] def area(self): """Return the area value of an Isle""" border = self.points() return libgis.G_area_of_polygon(border.c_points.contents.x, border.c_points.contents.y, border.c_points.contents.n_points)
[docs] def perimeter(self): """Return the perimeter value of an Isle. """ border = self.points() return libvect.Vect_line_geodesic_length(border.c_points)
[docs]class Isles(object): def __init__(self, c_mapinfo, area_id=None): self.c_mapinfo = c_mapinfo self.area_id = area_id self._isles_id = None self._isles = None if area_id: self._isles_id = self.isles_ids() self._isles = self.isles() @mapinfo_must_be_set def __len__(self): return libvect.Vect_get_area_num_isles(self.c_mapinfo, self.area_id) def __repr__(self): return "Isles(%r)" % self.area_id def __getitem__(self, key): if self._isles is None: self.isles() return self._isles[key] @mapinfo_must_be_set
[docs] def isles_ids(self): """Return the id of isles""" return [libvect.Vect_get_area_isle(self.c_mapinfo, self.area_id, i) for i in range(self.__len__())]
@mapinfo_must_be_set
[docs] def isles(self): """Return isles""" return [Isle(v_id=isle_id, c_mapinfo=self.c_mapinfo) for isle_id in self._isles_id]
[docs]class Area(Geo): """ Vect_build_line_area, Vect_find_area, Vect_get_area_box, Vect_get_area_points_geos, Vect_centroid_area, Vect_get_isle_area, Vect_get_line_areas, Vect_get_num_areas, Vect_get_point_in_area, Vect_isle_find_area, Vect_point_in_area, Vect_point_in_area_outer_ring, Vect_read_area_geos, Vect_remove_small_areas, Vect_select_areas_by_box, Vect_select_areas_by_polygon """ # geometry type gtype = libvect.GV_AREA def __init__(self, **kargs): super(Area, self).__init__(**kargs) # set the attributes #if self.attrs and self.cat: # self.attrs.cat = self.cat def __repr__(self): return "Area(%d)" % self.id if self.id else "Area( )" @property
[docs] def cat(self): centroid = self.centroid() return centroid.cat if centroid else None
@mapinfo_must_be_set
[docs] def points(self, line=None): """Return a Line object with the outer ring :param line: a Line object to fill with info from points of area :type line: a Line object """ line = Line() if line is None else line libvect.Vect_get_area_points(self.c_mapinfo, self.id, line.c_points) return line
@mapinfo_must_be_set
[docs] def centroid(self): """Return the centroid :param centroid: a Centroid object to fill with info from centroid of area :type centroid: a Centroid object """ centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo, self.id) if centroid_id: return Centroid(v_id=centroid_id, c_mapinfo=self.c_mapinfo, area_id=self.id)
@mapinfo_must_be_set
[docs] def num_isles(self): return libvect.Vect_get_area_num_isles(self.c_mapinfo, self.id)
@mapinfo_must_be_set
[docs] def isles(self, isles=None): """Return a list of islands located in this area""" if isles is not None: isles.area_id = self.id return isles return Isles(self.c_mapinfo, self.id)
@mapinfo_must_be_set
[docs] def area(self): """Returns area of area without areas of isles. double Vect_get_area_area (const struct Map_info \*Map, int area) """ return libvect.Vect_get_area_area(self.c_mapinfo, self.id)
@mapinfo_must_be_set
[docs] def alive(self): """Check if area is alive or dead (topology required) """ return bool(libvect.Vect_area_alive(self.c_mapinfo, self.id))
@mapinfo_must_be_set
[docs] def bbox(self, bbox=None): """Return the Bbox of area :param bbox: a Bbox object to fill with info from bounding box of area :type bbox: a Bbox object """ bbox = bbox if bbox else Bbox() libvect.Vect_get_area_box(self.c_mapinfo, self.id, bbox.c_bbox) return bbox
@mapinfo_must_be_set
[docs] def buffer(self, dist=None, dist_x=None, dist_y=None, angle=0, round_=True, caps=True, tol=0.1): """Return the buffer area around the area, using the ``Vect_area_buffer2`` C function. :param dist: the distance around the area :type dist: num :param dist_x: the distance along x :type dist_x: num :param dist_y: the distance along y :type dist_y: num :param angle: the angle between 0x and major axis :type angle: num :param round_: to make corners round :type round_: bool :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as line, centroid, isles object tuple """ if dist is not None: dist_x = dist dist_y = dist elif not dist_x or not dist_y: raise TypeError('TypeError: buffer expected 1 arguments, got 0') p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts())) pp_isle = ctypes.pointer(ctypes.pointer( ctypes.pointer(libvect.line_pnts()))) n_isles = ctypes.pointer(ctypes.c_int()) libvect.Vect_area_buffer2(self.c_mapinfo, self.id, dist_x, dist_y, angle, int(round_), int(caps), tol, p_bound, pp_isle, n_isles) return (Line(c_points=p_bound.contents), self.centroid, [Line(c_points=pp_isle[i].contents) for i in range(n_isles.contents.value)])
@mapinfo_must_be_set
[docs] def boundaries(self, ilist=False): """Creates list of boundaries for given area. int Vect_get_area_boundaries(const struct Map_info \*Map, int area, struct ilist \*List) """ ilst = Ilist() libvect.Vect_get_area_boundaries(self.c_mapinfo, self.id, ilst.c_ilist) if ilist: return ilist return [Boundary(v_id=abs(v_id), c_mapinfo=self.c_mapinfo) for v_id in ilst]
[docs] def to_wkt(self): """Return a "well know text" (WKT) area string, this method uses the GEOS implementation in the vector library. :: """ return libvect.Vect_read_area_to_wkt(self.c_mapinfo, self.id)
[docs] def to_wkb(self): """Return a "well know binary" (WKB) area byte array, this method uses the GEOS implementation in the vector library. :: """ size = ctypes.c_size_t() barray = libvect.Vect_read_area_to_wkb(self.c_mapinfo, self.id, ctypes.byref(size)) return(ctypes.string_at(barray, size.value))
@mapinfo_must_be_set
[docs] def cats(self, cats=None): """Get area categories. :param cats: a Cats object to fill with info with area categories :type cats: a Cats object """ cats = cats if cats else Cats() libvect.Vect_get_area_cats(self.c_mapinfo, self.id, cats.c_cats) return cats
[docs] def get_first_cat(self): """Find FIRST category of given field and area. int Vect_get_area_cat(const struct Map_info \*Map, int area, int field) ..warning: Not implemented """ pass
@mapinfo_must_be_set
[docs] def contains_point(self, point, bbox=None): """Check if point is in area. :param point: the point to analyze :type point: a Point object or a tuple with the coordinates :param bbox: the bounding box where run the analysis :type bbox: a Bbox object """ bbox = bbox if bbox else self.bbox() return bool(libvect.Vect_point_in_area(point.x, point.y, self.c_mapinfo, self.id, bbox.c_bbox))
@mapinfo_must_be_set
[docs] def perimeter(self): """Calculate area perimeter. :return: double Vect_area_perimeter (const struct line_pnts \*Points) """ border = self.points() return libvect.Vect_line_geodesic_length(border.c_points)
[docs] def read(self): pass # # Define a dictionary to convert the feature type to name and or object #
GV_TYPE = {libvect.GV_POINT: {'label': 'point', 'obj': Point}, libvect.GV_LINE: {'label': 'line', 'obj': Line}, libvect.GV_BOUNDARY: {'label': 'boundary', 'obj': Boundary}, libvect.GV_CENTROID: {'label': 'centroid', 'obj': Centroid}, libvect.GV_FACE: {'label': 'face', 'obj': None}, libvect.GV_KERNEL: {'label': 'kernel', 'obj': None}, libvect.GV_AREA: {'label': 'area', 'obj': Area}, libvect.GV_VOLUME: {'label': 'volume', 'obj': None}, } GEOOBJ = {"areas": Area, "dblinks": None, "faces": None, "holes": None, "boundaries": Boundary, "islands": Isle, "kernels": None, "line_points": None, "points": Point, "lines": Line, "nodes": Node, "volumes": None}
[docs]def c_read_next_line(c_mapinfo, c_points, c_cats): v_id = c_mapinfo.contents.next_line v_id = v_id if v_id != 0 else None ftype = libvect.Vect_read_next_line(c_mapinfo, c_points, c_cats) if ftype == -2: raise StopIteration() if ftype == -1: raise return ftype, v_id, c_points, c_cats
[docs]def read_next_line(c_mapinfo, table=None, writeable=False, c_points=None, c_cats=None, is2D=True): """Return the next geometry feature of a vector map.""" # Take care of good memory management free_points = False if c_points == None: free_points = True free_cats = False if c_cats == None: free_cats = True c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts()) c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats()) ftype, v_id, c_points, c_cats = c_read_next_line(c_mapinfo, c_points, c_cats) return GV_TYPE[ftype]['obj'](v_id=v_id, c_mapinfo=c_mapinfo, c_points=c_points, c_cats=c_cats, table=table, writeable=writeable, is2D=is2D, free_points=free_points, free_cats=free_cats)
[docs]def c_read_line(feature_id, c_mapinfo, c_points, c_cats): nmax = libvect.Vect_get_num_lines(c_mapinfo) if feature_id < 0: # Handle negative indices feature_id += nmax + 1 if feature_id > nmax: raise IndexError('Index out of range') if feature_id > 0: ftype = libvect.Vect_read_line(c_mapinfo, c_points, c_cats, feature_id) return feature_id, ftype, c_points, c_cats else: raise ValueError('The index must be >0, %r given.' % feature_id)
[docs]def read_line(feature_id, c_mapinfo, table=None, writeable=False, c_points=None, c_cats=None, is2D=True): """Return a geometry object given the feature id and the c_mapinfo. """ # Take care of good memory management free_points = False if c_points == None: free_points = True free_cats = False if c_cats == None: free_cats = True c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts()) c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats()) feature_id, ftype, c_points, c_cats = c_read_line(feature_id, c_mapinfo, c_points, c_cats) if GV_TYPE[ftype]['obj'] is not None: return GV_TYPE[ftype]['obj'](v_id=feature_id, c_mapinfo=c_mapinfo, c_points=c_points, c_cats=c_cats, table=table, writeable=writeable, is2D=is2D, free_points=free_points, free_cats=free_cats)
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)

Help Index | Topics Index | Keywords Index | Full Index

© 2003-2020 GRASS Development Team, GRASS GIS 7.6.2dev Reference Manual