web/lib/django/contrib/gis/geos/polygon.py
changeset 0 0d40e90630ef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/contrib/gis/geos/polygon.py	Wed Jan 20 00:34:04 2010 +0100
@@ -0,0 +1,166 @@
+from ctypes import c_uint, byref
+from django.contrib.gis.geos.error import GEOSIndexError
+from django.contrib.gis.geos.geometry import GEOSGeometry
+from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR
+from django.contrib.gis.geos.linestring import LinearRing
+from django.contrib.gis.geos import prototypes as capi
+
+class Polygon(GEOSGeometry):
+    _minlength = 1
+
+    def __init__(self, *args, **kwargs):
+        """
+        Initializes on an exterior ring and a sequence of holes (both
+        instances may be either LinearRing instances, or a tuple/list
+        that may be constructed into a LinearRing).
+
+        Examples of initialization, where shell, hole1, and hole2 are
+        valid LinearRing geometries:
+        >>> poly = Polygon(shell, hole1, hole2)
+        >>> poly = Polygon(shell, (hole1, hole2))
+
+        Example where a tuple parameters are used:
+        >>> poly = Polygon(((0, 0), (0, 10), (10, 10), (0, 10), (0, 0)),
+                           ((4, 4), (4, 6), (6, 6), (6, 4), (4, 4)))
+        """
+        if not args:
+            raise TypeError('Must provide at least one LinearRing, or a tuple, to initialize a Polygon.')
+
+        # Getting the ext_ring and init_holes parameters from the argument list
+        ext_ring = args[0]
+        init_holes = args[1:]
+        n_holes = len(init_holes)
+
+        # If initialized as Polygon(shell, (LinearRing, LinearRing)) [for backward-compatibility]
+        if n_holes == 1 and isinstance(init_holes[0], (tuple, list)):
+            if len(init_holes[0]) == 0:
+                init_holes  = ()
+                n_holes     = 0
+            elif isinstance(init_holes[0][0], LinearRing):
+                init_holes  = init_holes[0]
+                n_holes     = len(init_holes)
+
+        polygon = self._create_polygon(n_holes + 1, (ext_ring,) + init_holes)
+        super(Polygon, self).__init__(polygon, **kwargs)
+
+    def __iter__(self):
+        "Iterates over each ring in the polygon."
+        for i in xrange(len(self)):
+            yield self[i]
+
+    def __len__(self):
+        "Returns the number of rings in this Polygon."
+        return self.num_interior_rings + 1
+
+    @classmethod
+    def from_bbox(cls, bbox):
+        "Constructs a Polygon from a bounding box (4-tuple)."
+        x0, y0, x1, y1 = bbox
+        return GEOSGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' %  (
+                x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) )
+
+    ### These routines are needed for list-like operation w/ListMixin ###
+    def _create_polygon(self, length, items):
+        # Instantiate LinearRing objects if necessary, but don't clone them yet
+        # _construct_ring will throw a TypeError if a parameter isn't a valid ring
+        # If we cloned the pointers here, we wouldn't be able to clean up
+        # in case of error.
+        rings = []
+        for r in items:
+            if isinstance(r, GEOM_PTR):
+                rings.append(r)
+            else:
+                rings.append(self._construct_ring(r))
+
+        shell = self._clone(rings.pop(0))
+
+        n_holes = length - 1
+        if n_holes:
+            holes = get_pointer_arr(n_holes)
+            for i, r in enumerate(rings):
+                holes[i] = self._clone(r)
+                holes_param = byref(holes)
+        else:
+            holes_param = None
+
+        return capi.create_polygon(shell, holes_param, c_uint(n_holes))
+
+    def _clone(self, g):
+        if isinstance(g, GEOM_PTR):
+            return capi.geom_clone(g)
+        else:
+            return capi.geom_clone(g.ptr)
+
+    def _construct_ring(self, param, msg='Parameter must be a sequence of LinearRings or objects that can initialize to LinearRings'):
+        "Helper routine for trying to construct a ring from the given parameter."
+        if isinstance(param, LinearRing): return param
+        try:
+            ring = LinearRing(param)
+            return ring
+        except TypeError:
+            raise TypeError(msg)
+
+    def _set_list(self, length, items):
+        # Getting the current pointer, replacing with the newly constructed
+        # geometry, and destroying the old geometry.
+        prev_ptr = self.ptr
+        srid = self.srid
+        self.ptr = self._create_polygon(length, items)
+        if srid: self.srid = srid
+        capi.destroy_geom(prev_ptr)
+
+    def _get_single_internal(self, index):
+        """
+        Returns the ring at the specified index.  The first index, 0, will
+        always return the exterior ring.  Indices > 0 will return the
+        interior ring at the given index (e.g., poly[1] and poly[2] would
+        return the first and second interior ring, respectively).
+
+        CAREFUL: Internal/External are not the same as Interior/Exterior!
+        _get_single_internal returns a pointer from the existing geometries for use
+        internally by the object's methods.  _get_single_external returns a clone
+        of the same geometry for use by external code.
+        """
+        if index == 0:
+            return capi.get_extring(self.ptr)
+        else:
+            # Getting the interior ring, have to subtract 1 from the index.
+            return capi.get_intring(self.ptr, index-1)
+
+    def _get_single_external(self, index):
+        return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid)
+
+    _set_single = GEOSGeometry._set_single_rebuild
+    _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
+
+    #### Polygon Properties ####
+    @property
+    def num_interior_rings(self):
+        "Returns the number of interior rings."
+        # Getting the number of rings
+        return capi.get_nrings(self.ptr)
+
+    def _get_ext_ring(self):
+        "Gets the exterior ring of the Polygon."
+        return self[0]
+
+    def _set_ext_ring(self, ring):
+        "Sets the exterior ring of the Polygon."
+        self[0] = ring
+
+    # Properties for the exterior ring/shell.
+    exterior_ring = property(_get_ext_ring, _set_ext_ring)
+    shell = exterior_ring
+
+    @property
+    def tuple(self):
+        "Gets the tuple for each ring in this Polygon."
+        return tuple([self[i].tuple for i in xrange(len(self))])
+    coords = tuple
+
+    @property
+    def kml(self):
+        "Returns the KML representation of this Polygon."
+        inner_kml = ''.join(["<innerBoundaryIs>%s</innerBoundaryIs>" % self[i+1].kml
+                             for i in xrange(self.num_interior_rings)])
+        return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml)