PageRenderTime 224ms CodeModel.GetById 101ms app.highlight 12ms RepoModel.GetById 108ms app.codeStats 1ms

/django/contrib/gis/geos/polygon.py

https://code.google.com/p/mango-py/
Python | 166 lines | 103 code | 16 blank | 47 comment | 19 complexity | 770e3e984c8395d7cdb077881b766ae9 MD5 | raw file
  1from ctypes import c_uint, byref
  2from django.contrib.gis.geos.error import GEOSIndexError
  3from django.contrib.gis.geos.geometry import GEOSGeometry
  4from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR
  5from django.contrib.gis.geos.linestring import LinearRing
  6from django.contrib.gis.geos import prototypes as capi
  7
  8class Polygon(GEOSGeometry):
  9    _minlength = 1
 10
 11    def __init__(self, *args, **kwargs):
 12        """
 13        Initializes on an exterior ring and a sequence of holes (both
 14        instances may be either LinearRing instances, or a tuple/list
 15        that may be constructed into a LinearRing).
 16
 17        Examples of initialization, where shell, hole1, and hole2 are
 18        valid LinearRing geometries:
 19        >>> poly = Polygon(shell, hole1, hole2)
 20        >>> poly = Polygon(shell, (hole1, hole2))
 21
 22        Example where a tuple parameters are used:
 23        >>> poly = Polygon(((0, 0), (0, 10), (10, 10), (0, 10), (0, 0)),
 24                           ((4, 4), (4, 6), (6, 6), (6, 4), (4, 4)))
 25        """
 26        if not args:
 27            raise TypeError('Must provide at least one LinearRing, or a tuple, to initialize a Polygon.')
 28
 29        # Getting the ext_ring and init_holes parameters from the argument list
 30        ext_ring = args[0]
 31        init_holes = args[1:]
 32        n_holes = len(init_holes)
 33
 34        # If initialized as Polygon(shell, (LinearRing, LinearRing)) [for backward-compatibility]
 35        if n_holes == 1 and isinstance(init_holes[0], (tuple, list)):
 36            if len(init_holes[0]) == 0:
 37                init_holes  = ()
 38                n_holes     = 0
 39            elif isinstance(init_holes[0][0], LinearRing):
 40                init_holes  = init_holes[0]
 41                n_holes     = len(init_holes)
 42
 43        polygon = self._create_polygon(n_holes + 1, (ext_ring,) + init_holes)
 44        super(Polygon, self).__init__(polygon, **kwargs)
 45
 46    def __iter__(self):
 47        "Iterates over each ring in the polygon."
 48        for i in xrange(len(self)):
 49            yield self[i]
 50
 51    def __len__(self):
 52        "Returns the number of rings in this Polygon."
 53        return self.num_interior_rings + 1
 54
 55    @classmethod
 56    def from_bbox(cls, bbox):
 57        "Constructs a Polygon from a bounding box (4-tuple)."
 58        x0, y0, x1, y1 = bbox
 59        return GEOSGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' %  (
 60                x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) )
 61
 62    ### These routines are needed for list-like operation w/ListMixin ###
 63    def _create_polygon(self, length, items):
 64        # Instantiate LinearRing objects if necessary, but don't clone them yet
 65        # _construct_ring will throw a TypeError if a parameter isn't a valid ring
 66        # If we cloned the pointers here, we wouldn't be able to clean up
 67        # in case of error.
 68        rings = []
 69        for r in items:
 70            if isinstance(r, GEOM_PTR):
 71                rings.append(r)
 72            else:
 73                rings.append(self._construct_ring(r))
 74
 75        shell = self._clone(rings.pop(0))
 76
 77        n_holes = length - 1
 78        if n_holes:
 79            holes = get_pointer_arr(n_holes)
 80            for i, r in enumerate(rings):
 81                holes[i] = self._clone(r)
 82                holes_param = byref(holes)
 83        else:
 84            holes_param = None
 85
 86        return capi.create_polygon(shell, holes_param, c_uint(n_holes))
 87
 88    def _clone(self, g):
 89        if isinstance(g, GEOM_PTR):
 90            return capi.geom_clone(g)
 91        else:
 92            return capi.geom_clone(g.ptr)
 93
 94    def _construct_ring(self, param, msg='Parameter must be a sequence of LinearRings or objects that can initialize to LinearRings'):
 95        "Helper routine for trying to construct a ring from the given parameter."
 96        if isinstance(param, LinearRing): return param
 97        try:
 98            ring = LinearRing(param)
 99            return ring
100        except TypeError:
101            raise TypeError(msg)
102
103    def _set_list(self, length, items):
104        # Getting the current pointer, replacing with the newly constructed
105        # geometry, and destroying the old geometry.
106        prev_ptr = self.ptr
107        srid = self.srid
108        self.ptr = self._create_polygon(length, items)
109        if srid: self.srid = srid
110        capi.destroy_geom(prev_ptr)
111
112    def _get_single_internal(self, index):
113        """
114        Returns the ring at the specified index.  The first index, 0, will
115        always return the exterior ring.  Indices > 0 will return the
116        interior ring at the given index (e.g., poly[1] and poly[2] would
117        return the first and second interior ring, respectively).
118
119        CAREFUL: Internal/External are not the same as Interior/Exterior!
120        _get_single_internal returns a pointer from the existing geometries for use
121        internally by the object's methods.  _get_single_external returns a clone
122        of the same geometry for use by external code.
123        """
124        if index == 0:
125            return capi.get_extring(self.ptr)
126        else:
127            # Getting the interior ring, have to subtract 1 from the index.
128            return capi.get_intring(self.ptr, index-1)
129
130    def _get_single_external(self, index):
131        return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid)
132
133    _set_single = GEOSGeometry._set_single_rebuild
134    _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
135
136    #### Polygon Properties ####
137    @property
138    def num_interior_rings(self):
139        "Returns the number of interior rings."
140        # Getting the number of rings
141        return capi.get_nrings(self.ptr)
142
143    def _get_ext_ring(self):
144        "Gets the exterior ring of the Polygon."
145        return self[0]
146
147    def _set_ext_ring(self, ring):
148        "Sets the exterior ring of the Polygon."
149        self[0] = ring
150
151    # Properties for the exterior ring/shell.
152    exterior_ring = property(_get_ext_ring, _set_ext_ring)
153    shell = exterior_ring
154
155    @property
156    def tuple(self):
157        "Gets the tuple for each ring in this Polygon."
158        return tuple([self[i].tuple for i in xrange(len(self))])
159    coords = tuple
160
161    @property
162    def kml(self):
163        "Returns the KML representation of this Polygon."
164        inner_kml = ''.join(["<innerBoundaryIs>%s</innerBoundaryIs>" % self[i+1].kml
165                             for i in xrange(self.num_interior_rings)])
166        return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml)