diff -r 000000000000 -r 0d40e90630ef web/lib/django/contrib/gis/geos/polygon.py --- /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(["%s" % self[i+1].kml + for i in xrange(self.num_interior_rings)]) + return "%s%s" % (self[0].kml, inner_kml)