text: from ctypes import byref, c_uint
  
  from django.contrib.gis.geos import prototypes as capi
  from django.contrib.gis.geos.geometry import GEOSGeometry
  from django.contrib.gis.geos.libgeos import GEOM_PTR, get_pointer_arr
  from django.contrib.gis.geos.linestring import LinearRing
  from django.utils import six
  from django.utils.six.moves import range
  
  
  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:
          >>> from django.contrib.gis.geos import LinearRing, Polygon
          >>> shell = hole1 = hole2 = LinearRing()
          >>> 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:
              super(Polygon, self).__init__(self._create_polygon(0, None), **kwargs)
              return
  
          # 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 range(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
          for z in bbox:
              if not isinstance(z, six.integer_types + (float,)):
                  return GEOSGeometry('POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' %
                                      (x0, y0, x0, y1, x1, y1, x1, y0, x0, y0))
          return Polygon(((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.
          if not length:
              return capi.create_empty_polygon()
  
          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 range(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 range(self.num_interior_rings)
          )
          return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml)
  