PageRenderTime 37ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

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