web/lib/django/contrib/gis/geos/polygon.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from ctypes import c_uint, byref
       
     2 from django.contrib.gis.geos.error import GEOSIndexError
       
     3 from django.contrib.gis.geos.geometry import GEOSGeometry
       
     4 from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR
       
     5 from django.contrib.gis.geos.linestring import LinearRing
       
     6 from django.contrib.gis.geos import prototypes as capi
       
     7 
       
     8 class 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)