Pārlūkot izejas kodu

pygrass vector: Implemented area, boundary, centroid and isle specific unit-tests.
Changed the API a bit and some functions. Buffer methods are not generating areas anymore, since
areas are topological objects.
Implemented the use of GEOS WKT and WKB generation from the vector library (https://trac.osgeo.org/grass/changeset/66046).


git-svn-id: https://svn.osgeo.org/grass/grass/trunk@66047 15284696-431f-4ddb-bdfa-cd5b030d7da7

Soeren Gebbert 9 gadi atpakaļ
vecāks
revīzija
37d4ff496f

+ 36 - 14
lib/python/pygrass/vector/find.py

@@ -16,7 +16,7 @@ test_vector_name = "find_doctest_map"
 
 class AbstractFinder(object):
     def __init__(self, c_mapinfo, table=None, writeable=False):
-        """AbstractFinder
+        """Abstract finder
         -----------------
 
         Find geometry feature around a point.
@@ -136,7 +136,7 @@ class PointFinder(AbstractFinder):
             ...     if f:
             ...         result.append(f)
             >>> for f in result:
-            ...     print(f.to_wkt())    #doctest: +NORMALIZE_WHITESPACE
+            ...     print(f.to_wkt_p())    #doctest: +NORMALIZE_WHITESPACE
             LINESTRING(10.000000 4.000000,
                        10.000000 2.000000,
                        10.000000 0.000000)
@@ -274,12 +274,35 @@ class PointFinder(AbstractFinder):
             [Area(1), Area(2), Area(4)]
             >>> for area in result:
             ...     print(area.to_wkt())         #doctest: +NORMALIZE_WHITESPACE
-            Polygon((0.000000 0.000000, 0.000000 4.000000, 4.000000 4.000000,
-                     4.000000 0.000000, 0.000000 0.000000))
-            Polygon((4.000000 0.000000, 4.000000 4.000000, 6.000000 4.000000,
-                     6.000000 0.000000, 4.000000 0.000000))
-            Polygon((6.000000 0.000000, 6.000000 4.000000, 8.000000 4.000000,
-                     8.000000 0.000000, 6.000000 0.000000))
+            POLYGON ((0.0000000000000000 0.0000000000000000,
+                      0.0000000000000000 4.0000000000000000,
+                      0.0000000000000000 4.0000000000000000,
+                      4.0000000000000000 4.0000000000000000,
+                      4.0000000000000000 4.0000000000000000,
+                      4.0000000000000000 0.0000000000000000,
+                      4.0000000000000000 0.0000000000000000,
+                      0.0000000000000000 0.0000000000000000),
+                     (1.0000000000000000 1.0000000000000000,
+                      3.0000000000000000 1.0000000000000000,
+                      3.0000000000000000 3.0000000000000000,
+                      1.0000000000000000 3.0000000000000000,
+                      1.0000000000000000 1.0000000000000000))
+            POLYGON ((4.0000000000000000 0.0000000000000000,
+                      4.0000000000000000 4.0000000000000000,
+                      4.0000000000000000 4.0000000000000000,
+                      6.0000000000000000 4.0000000000000000,
+                      6.0000000000000000 4.0000000000000000,
+                      6.0000000000000000 0.0000000000000000,
+                      6.0000000000000000 0.0000000000000000,
+                      4.0000000000000000 0.0000000000000000))
+            POLYGON ((6.0000000000000000 0.0000000000000000,
+                      6.0000000000000000 4.0000000000000000,
+                      6.0000000000000000 4.0000000000000000,
+                      8.0000000000000000 4.0000000000000000,
+                      8.0000000000000000 4.0000000000000000,
+                      8.0000000000000000 0.0000000000000000,
+                      8.0000000000000000 0.0000000000000000,
+                      6.0000000000000000 0.0000000000000000))
 
             >>> test_vect.find_by_point.area(Point(20,20))
 
@@ -339,8 +362,7 @@ class BboxFinder(AbstractFinder):
 
     This class provides an interface to search geometry features
     of a vector map that are inside or intersect a boundingbox.
-    The BboxFinder class
-    is part of a topological vector map object.
+    The BboxFinder class is part of a topological vector map object.
 
     """
     def __init__(self, c_mapinfo, table=None, writeable=False):
@@ -360,7 +382,7 @@ class BboxFinder(AbstractFinder):
 
     @must_be_open
     def geos(self, bbox, type='all', bboxlist_only=False):
-        """Find the nearest vector features around a specific point.
+        """Find vector features inside a boundingbox.
 
             :param bbox: The boundingbox to search in
             :type bbox: grass.pygrass.vector.basic.Bbox
@@ -441,7 +463,7 @@ class BboxFinder(AbstractFinder):
 
     @must_be_open
     def nodes(self, bbox):
-        """Find the nodes inside a boundingbox.
+        """Find nodes inside a boundingbox.
 
             :param bbox: The boundingbox to search in
             :type bbox: grass.pygrass.vector.basic.Bbox
@@ -478,7 +500,7 @@ class BboxFinder(AbstractFinder):
 
     @must_be_open
     def areas(self, bbox, boxlist=None, bboxlist_only=False):
-        """Find the areas inside a boundingbox.
+        """Find areas inside a boundingbox.
 
             :param bbox: The boundingbox to search in
             :type bbox: grass.pygrass.vector.basic.Bbox
@@ -536,7 +558,7 @@ class BboxFinder(AbstractFinder):
 
     @must_be_open
     def islands(self, bbox, bboxlist_only=False):
-        """Find the isles inside a boundingbox.
+        """Find isles inside a boundingbox.
 
             :param bbox: The boundingbox to search in
             :type bbox: grass.pygrass.vector.basic.Bbox

+ 103 - 100
lib/python/pygrass/vector/geometry.py

@@ -307,6 +307,40 @@ class Geo(object):
                                                        self.c_points,
                                                        self.c_cats)
 
+    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)'
+
+        .. warning::
+
+            Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with:
+            ``XYZM`` are not supported yet.
+        """
+        return libvect.Vect_line_to_wkt(self.c_points, self.gtype, not self.is2D)
+
+    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
+
+
+        .. warning::
+
+            Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with:
+            ``XYZM`` are not supported yet.
+        """
+        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))
 
 class Point(Geo):
     """Instantiate a Point object that could be 2 or 3D, default
@@ -330,7 +364,7 @@ class Point(Geo):
         >>> pnt
         Point(0.000000, 0.000000, 0.000000)
         >>> print(pnt)
-        POINT(0.000000 0.000000 0.000000)
+        POINT Z (0.0000000000000000 0.0000000000000000 0.0000000000000000)
 
     ..
     """
@@ -430,11 +464,11 @@ class Point(Geo):
         else:
             return self.x, self.y, self.z
 
-    def to_wkt(self):
-        """Return a "well know text" (WKT) geometry string. ::
+    def to_wkt_p(self):
+        """Return a "well know text" (WKT) geometry string Python implementation. ::
 
             >>> pnt = Point(10, 100)
-            >>> pnt.to_wkt()
+            >>> pnt.to_wkt_p()
             'POINT(10.000000 100.000000)'
 
         .. warning::
@@ -445,16 +479,6 @@ class Point(Geo):
         return "POINT(%s)" % ' '.join(['%f' % coord
                                       for coord in self.coords()])
 
-    def to_wkb(self):
-        """Return a "well know binary" (WKB) geometry buffer
-
-        .. warning::
-
-            Not implemented yet.
-
-        """
-        pass
-
     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.
@@ -502,13 +526,11 @@ class Point(Geo):
         :returns: the buffer as Area object
 
         >>> pnt = Point(0, 0)
-        >>> area = pnt.buffer(10)
-        >>> area.boundary                              #doctest: +ELLIPSIS
+        >>> boundary, centroid = pnt.buffer(10)
+        >>> boundary                              #doctest: +ELLIPSIS
         Line([Point(10.000000, 0.000000),...Point(10.000000, 0.000000)])
-        >>> area.centroid
+        >>> centroid
         Point(0.000000, 0.000000)
-        >>> area.isles
-        []
 
         """
         if dist is not None:
@@ -522,7 +544,7 @@ class Point(Geo):
                                    dist_x, dist_y,
                                    angle, int(round_), tol,
                                    p_points)
-        return Area(boundary=bound, centroid=self)
+        return (bound, self)
 
 
 class Line(Geo):
@@ -987,11 +1009,11 @@ class Line(Geo):
         """
         return np.array(self.to_list())
 
-    def to_wkt(self):
+    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()                 #doctest: +ELLIPSIS
+            >>> line.to_wkt_p()                 #doctest: +ELLIPSIS
             'LINESTRING(0.000000 0.000000, ..., 1.000000 2.000000)'
 
         ..
@@ -1023,15 +1045,6 @@ class Line(Geo):
         else:
             return None
 
-    def get_wkb(self):
-        """Return a WKB buffer.
-
-        .. warning::
-
-            Not implemented yet.
-        """
-        pass
-
     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
@@ -1053,12 +1066,12 @@ class Line(Geo):
         :returns: the buffer as Area object
 
         >>> line = Line([(0, 0), (0, 2)])
-        >>> area = line.buffer(10)
-        >>> area.boundary                              #doctest: +ELLIPSIS
+        >>> boundary, centroid, isles = line.buffer(10)
+        >>> boundary                              #doctest: +ELLIPSIS
         Line([Point(-10.000000, 0.000000),...Point(-10.000000, 0.000000)])
-        >>> area.centroid                     #doctest: +NORMALIZE_WHITESPACE
+        >>> centroid                     #doctest: +NORMALIZE_WHITESPACE
         Point(0.000000, 0.000000)
-        >>> area.isles
+        >>> isles
         []
 
         ..
@@ -1076,9 +1089,9 @@ class Line(Geo):
                                   dist_x, dist_y, angle,
                                   int(round_), int(caps), tol,
                                   p_bound, pp_isle, n_isles)
-        return Area(boundary=Line(c_points=p_bound.contents),
-                    centroid=self[0],
-                    isles=[Line(c_points=pp_isle[i].contents)
+        return(Line(c_points=p_bound.contents),
+                    self[0],
+                    [Line(c_points=pp_isle[i].contents)
                            for i in range(n_isles.contents.value)])
 
     def reset(self):
@@ -1176,6 +1189,13 @@ class Node(object):
         return "POINT(%s)" % ' '.join(['%f' % coord
                                       for coord in self.coords()])
 
+    def to_wkb(self):
+        """Return a "well know binary" (WKB) geometry array. ::
+
+           TODO: Must be implemented
+        """
+        raise Exception("Not implemented")
+
     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
@@ -1226,11 +1246,13 @@ class Boundary(Line):
         self.c_right = ctypes.pointer(ctypes.c_int())
 
     @property
-    def left_id(self):
+    def left_area_id(self):
+        """Left side area id, only available after read_area_ids() was called"""
         return self.c_left.contents.value
 
     @property
-    def right_id(self):
+    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):
@@ -1253,7 +1275,7 @@ class Boundary(Line):
         :param idonly: True to return only the cat of feature
         :type idonly: bool
         """
-        return self._centroid(self.left_id, idonly)
+        return self._centroid(self.c_left.contents.value, idonly)
 
     def right_centroid(self, idonly=False):
         """Return right centroid
@@ -1261,11 +1283,11 @@ class Boundary(Line):
         :param idonly: True to return only the cat of feature
         :type idonly: bool
         """
-        return self._centroid(self.left_id, idonly)
+        return self._centroid(self.c_right.contents.value, idonly)
 
     @mapinfo_must_be_set
-    def get_left_right(self):
-        """Return left and right value"""
+    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)
@@ -1297,9 +1319,9 @@ class Centroid(Point):
         >>> from grass.pygrass.vector import VectorTopo
         >>> test_vect = VectorTopo(test_vector_name)
         >>> test_vect.open(mode='r')
-        >>> centroid = Centroid(v_id=1, c_mapinfo=test_vect.c_mapinfo)
+        >>> centroid = Centroid(v_id=18, c_mapinfo=test_vect.c_mapinfo)
         >>> centroid
-        Centoid(10.000000, 6.000000)
+        Centoid(3.500000, 3.500000)
         >>> test_vect.close()
 
     ..
@@ -1311,9 +1333,9 @@ class Centroid(Point):
         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.get_area_id()
+            self.area_id = self._area_id()
         elif self.c_mapinfo and self.area_id and self.id is None:
-            self.id = self.centroid_id()
+            self.id = self._centroid_id()
         if self.area_id is not None:
             self.read()
 
@@ -1323,7 +1345,7 @@ class Centroid(Point):
         return "Centoid(%s)" % ', '.join(['%f' % co for co in self.coords()])
 
     @mapinfo_must_be_set
-    def centroid_id(self):
+    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"""
@@ -1332,7 +1354,7 @@ class Centroid(Point):
         return centroid_id if centroid_id != 0 else None
 
     @mapinfo_must_be_set
-    def get_area_id(self):
+    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"""
@@ -1386,6 +1408,11 @@ class Isle(Geo):
                ' '.join(['%f' % coord for coord in pnt])
                for pnt in line.to_list()])
 
+    def to_wkb(self):
+        """Return a "well know text" (WKB) geometry array. ::
+        """
+        raise Exception("Not implemented")
+
     @mapinfo_must_be_set
     def points_geos(self):
         """Return a Line object with the outer ring points
@@ -1487,15 +1514,8 @@ class Area(Geo):
     # geometry type
     gtype = libvect.GV_AREA
 
-    def __init__(self, boundary=None, centroid=None, isles=None, **kargs):
+    def __init__(self, **kargs):
         super(Area, self).__init__(**kargs)
-        self.boundary = None
-        self.centroid = None
-        self.isles = None
-        if boundary and centroid:
-            self.boundary = boundary
-            self.centroid = centroid
-            self.isles = isles if isles else []
 
         # set the attributes
         if self.attrs and self.cat:
@@ -1504,16 +1524,6 @@ class Area(Geo):
     def __repr__(self):
         return "Area(%d)" % self.id if self.id else "Area( )"
 
-    def init_from_id(self, area_id=None):
-        """Return an Area object"""
-        if area_id is None and self.id is None:
-            raise ValueError("You need to give or set the area_id")
-        self.id = area_id if area_id is not None else self.id
-        # get boundary
-        self.points()
-        # get isles
-        self.isles()
-
     @mapinfo_must_be_set
     def points(self, line=None):
         """Return a Line object with the outer ring
@@ -1526,7 +1536,7 @@ class Area(Geo):
         return line
 
     @mapinfo_must_be_set
-    def centroid(self, centroid=None):
+    def centroid(self):
         """Return the centroid
 
         :param centroid: a Centroid object to fill with info from centroid of area
@@ -1534,10 +1544,6 @@ class Area(Geo):
         """
         centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo, self.id)
         if centroid_id:
-            if centroid:
-                centroid.id = centroid_id
-                centroid.read()
-                return centroid
             return Centroid(v_id=centroid_id, c_mapinfo=self.c_mapinfo,
                             area_id=self.id)
 
@@ -1547,7 +1553,7 @@ class Area(Geo):
 
     @mapinfo_must_be_set
     def isles(self, isles=None):
-        """Instantiate the boundary attribute reading area_id"""
+        """Return a list of islands located in this area"""
         if isles is not None:
             isles.area_id = self.id
             return isles
@@ -1596,9 +1602,10 @@ class Area(Geo):
         :param tol: fix the maximum distance between theoretical arc and
                     output segments
         :type tol: float
-        :returns: the buffer as Area object
+        :returns: the buffer as line, centroid, isles object tuple
 
         """
+
         if dist is not None:
             dist_x = dist
             dist_y = dist
@@ -1612,10 +1619,10 @@ class Area(Geo):
                                   dist_x, dist_y, angle,
                                   int(round_), int(caps), tol,
                                   p_bound, pp_isle, n_isles)
-        return Area(boundary=Line(c_points=p_bound.contents),
-                    centroid=self.centroid,
-                    isles=[Line(c_points=pp_isle[i].contents)
-                           for i in range(n_isles.contents.value)])
+        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
     def boundaries(self, ilist=False):
@@ -1631,19 +1638,20 @@ class Area(Geo):
             return ilist
         return [Boundary(v_id=abs(v_id), c_mapinfo=self.c_mapinfo) for v_id in ilst]
 
-
     def to_wkt(self):
-        """Return a Well Known Text string of the Area. ::
-
-            For now the outer ring is returned
-
-            TODO: Implement inner rings detected from isles
+        """Return a "well know text" (WKT) area string, this method uses
+           the GEOS implementation in the vector library. ::
         """
-        line = self.points()
+        return libvect.Vect_read_area_to_wkt(self.c_mapinfo, self.id)
 
-        return "Polygon((%s))" % ', '.join([
-               ' '.join(['%f' % coord for coord in pnt])
-               for pnt in line.to_list()])
+    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
     def cats(self, cats=None):
@@ -1666,16 +1674,16 @@ class Area(Geo):
         pass
 
     @mapinfo_must_be_set
-    def contain_pnt(self, pnt, bbox=None):
+    def contains_point(self, point, bbox=None):
         """Check if point is in area.
 
-        :param pnt: the point to analyze
-        :type pnt: a Point object or a tuple with the coordinates
+        :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(pnt.x, pnt.y,
+        return bool(libvect.Vect_point_in_area(point.x, point.y,
                                                self.c_mapinfo, self.id,
                                                bbox.c_bbox))
 
@@ -1689,13 +1697,8 @@ class Area(Geo):
         border = self.points()
         return libvect.Vect_line_geodesic_length(border.c_points)
 
-    def read(self, line=None, centroid=None, isles=None):
-        self.boundary = self.points(line)
-        self.centroid = self.centroid(centroid)
-        #self.isles = self.isles(isles)
-        if self.centroid:
-            libvect.Vect_read_line(self.c_mapinfo, None, self.c_cats,
-                                   self.centroid.id)
+    def read(self):
+        pass
 
 
 #

+ 161 - 4
lib/python/pygrass/vector/testsuite/test_geometry.py

@@ -14,9 +14,10 @@ from grass.gunittest.main import test
 import grass.lib.vector as libvect
 from grass.script.core import run_command
 
-from grass.pygrass.vector import VectorTopo
+from grass.pygrass.vector import Vector, VectorTopo
 from grass.pygrass.vector.geometry import Point, Line, Node
-
+from grass.pygrass.vector.geometry import Area, Boundary, Centroid
+from grass.pygrass.vector.basic import Bbox
 
 class PointTestCase(TestCase):
 
@@ -52,11 +53,21 @@ class PointTestCase(TestCase):
         self.assertEqual(Point(1, 2).coords(), (1, 2))
         self.assertEqual(Point(1, 2, 3).coords(), (1, 2, 3))
 
+    def test_to_wkt_p(self):
+        """Test coords method"""
+        self.assertEqual(Point(1, 2).to_wkt_p(), 'POINT(1.000000 2.000000)')
+        self.assertEqual(Point(1, 2, 3).to_wkt_p(),
+                         'POINT(1.000000 2.000000 3.000000)')
+
     def test_to_wkt(self):
         """Test coords method"""
-        self.assertEqual(Point(1, 2).to_wkt(), 'POINT(1.000000 2.000000)')
+        self.assertEqual(Point(1, 2).to_wkt(), 'POINT (1.0000000000000000 2.0000000000000000)')
         self.assertEqual(Point(1, 2, 3).to_wkt(),
-                         'POINT(1.000000 2.000000 3.000000)')
+                         'POINT Z (1.0000000000000000 2.0000000000000000 3.0000000000000000)')
+
+    def test_to_wkb(self):
+        """Test to_wkb method"""
+        self.assertEqual(len(Point(1, 2).to_wkb()), 21)
 
     def test_distance(self):
         """Test distance method"""
@@ -151,6 +162,15 @@ class LineTestCase(TestCase):
         vals = (0.7071067811865475, 0.7071067811865475)
         self.assertTupleEqual(line.point_on_line(1).coords(), vals)
 
+    def test_to_wkt(self):
+        """Test to_wkt method"""
+        string = 'LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 1.0000000000000000)'
+        self.assertEqual(Line([(0, 0), (1, 1)]).to_wkt(), string)
+
+    def test_to_wkb(self):
+        """Test to_wkb method"""
+        self.assertEqual(len(Line([(0, 0), (1, 1)]).to_wkb()), 41)
+
     def test_bbox(self):
         """Test bbox method"""
         line = Line([(0, 10), (0, 11), (1, 11), (1, 10)])
@@ -227,5 +247,142 @@ class NodeTestCase(TestCase):
                    2.356194496154785)
         self.assertTupleEqual(angles, tuple(node.angles()))
 
+class AreaTestCase(TestCase):
+
+    tmpname = "AreaTestCase_map"
+
+    @classmethod
+    def setUpClass(cls):
+
+        # Tests are based on a stream network
+        from grass.pygrass import utils
+        utils.create_test_vector_map(cls.tmpname)
+
+        cls.vect = None
+        cls.vect = VectorTopo(cls.tmpname)
+        cls.vect.open('r')
+        cls.c_mapinfo = cls.vect.c_mapinfo
+
+    @classmethod
+    def tearDownClass(cls):
+        if cls.vect is not None:
+            cls.vect.close()
+            cls.c_mapinfo = None
+
+        """Remove the generated vector map, if exist"""
+        from grass.pygrass.utils import get_mapset_vector
+        mset = get_mapset_vector(cls.tmpname, mapset='')
+        if mset:
+            run_command("g.remove", flags='f', type='vector', name=cls.tmpname)
+
+    def test_init(self):
+        """Test area __init__ and basic functions"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(1, area.id)
+        self.assertTrue(area.is2D)
+        self.assertTrue(area.alive())
+        self.assertEqual(area.area(), 12.0)
+
+    def test_to_wkt(self):
+        """Test to_wkt method"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        # Outer and inner ring!!
+        string = "POLYGON ((0.0000000000000000 0.0000000000000000, "\
+                           "0.0000000000000000 4.0000000000000000, "\
+                           "0.0000000000000000 4.0000000000000000, "\
+                           "4.0000000000000000 4.0000000000000000, "\
+                           "4.0000000000000000 4.0000000000000000, "\
+                           "4.0000000000000000 0.0000000000000000, "\
+                           "4.0000000000000000 0.0000000000000000, "\
+                           "0.0000000000000000 0.0000000000000000), "\
+                           "(1.0000000000000000 1.0000000000000000, "\
+                           "3.0000000000000000 1.0000000000000000, "\
+                           "3.0000000000000000 3.0000000000000000, "\
+                           "1.0000000000000000 3.0000000000000000, "\
+                           "1.0000000000000000 1.0000000000000000))"
+        self.assertEqual(area.to_wkt(), string)
+
+    def test_to_wkb(self):
+        """Test to_wkt method"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(len(area.to_wkb()), 225)
+
+    def test_contains_point(self):
+        """Test contain_point method"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        p = Point(0.5, 0.5)
+        bbox = Bbox(4.0, 0.0, 4.0, 0.0)
+        self.assertTrue(area.contains_point(p, bbox))
+        self.assertTrue(area.contains_point(p))
+
+    def test_bbox(self):
+        """Test contain_point method"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(str(area.bbox()), "Bbox(4.0, 0.0, 4.0, 0.0)")
+
+    def test_centroid(self):
+        """Test centroid access"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        centroid = area.centroid()
+
+        self.assertEqual(centroid.id, 18)
+        self.assertEqual(centroid.area_id, 1)
+        self.assertEqual(centroid.to_wkt(), 'POINT (3.5000000000000000 3.5000000000000000)')
+
+    def test_boundaries_1(self):
+        """Test boundary access"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        boundaries = area.boundaries()
+        self.assertEqual(len(boundaries), 4)
+
+        string_list = []
+        string_list.append("LINESTRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 4.0000000000000000)")
+        string_list.append("LINESTRING (0.0000000000000000 4.0000000000000000, 4.0000000000000000 4.0000000000000000)")
+        string_list.append("LINESTRING (4.0000000000000000 4.0000000000000000, 4.0000000000000000 0.0000000000000000)")
+        string_list.append("LINESTRING (4.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000)")
+
+        for boundary, i in zip(boundaries, range(4)):
+            self.assertEqual(len(boundary.to_wkb()), 41)
+            self.assertEqual(boundary.to_wkt(), string_list[i])
+
+    def test_boundaries_2(self):
+        """Test boundary access"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        boundaries = area.boundaries()
+        boundary = boundaries[2]
+        boundary.read_area_ids()
+        self.assertEqual(boundary.left_area_id, 2)
+        self.assertEqual(boundary.right_area_id, 1)
+
+        self.assertEqual(boundary.left_centroid().to_wkt(), 'POINT (5.5000000000000000 3.5000000000000000)')
+        self.assertEqual(boundary.right_centroid().to_wkt(), 'POINT (3.5000000000000000 3.5000000000000000)')
+
+    def test_isles_1(self):
+        """Test centroid access"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(area.num_isles(), 1)
+
+        isles = area.isles()
+        isle = isles[0]
+
+        self.assertEqual(isle.area(), 4.0)
+        self.assertEqual(isle.points().to_wkt(), "LINESTRING (1.0000000000000000 1.0000000000000000, "\
+                                                             "3.0000000000000000 1.0000000000000000, "\
+                                                             "3.0000000000000000 3.0000000000000000, "\
+                                                             "1.0000000000000000 3.0000000000000000, "\
+                                                             "1.0000000000000000 1.0000000000000000)")
+    def test_isles_2(self):
+        """Test centroid access"""
+        area = Area(v_id=1, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(area.num_isles(), 1)
+
+        isles = area.isles()
+        isle = isles[0]
+        self.assertEqual(isle.area_id(), 1)
+        self.assertTrue(isle.alive())
+
+        self.assertEqual(str(isle.bbox()), "Bbox(3.0, 1.0, 3.0, 1.0)")
+
+
 if __name__ == '__main__':
     test()