django/contrib/gis/geos/collections.py
author Justin Bronn <jbronn@geodjango.org>
Sat Jan 31 15:18:50 2009 -0600 (3 years ago)
branchtrunk
changeset 138 466bece04a15
parent 126 9124703efaf1
child 351 5625e3feb4a7
permissions -rw-r--r--
Merged in patch from Aryeh Leib Taurog for #9877, adapting as necessary.
     1 """
     2  This module houses the Geometry Collection objects:
     3  GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
     4 """
     5 from ctypes import c_int, c_uint, byref
     6 from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
     7 from django.contrib.gis.geos.geometry import GEOSGeometry
     8 from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR, GEOS_PREPARE
     9 from django.contrib.gis.geos.linestring import LineString, LinearRing
    10 from django.contrib.gis.geos.point import Point
    11 from django.contrib.gis.geos.polygon import Polygon
    12 from django.contrib.gis.geos import prototypes as capi
    13 
    14 class GeometryCollection(GEOSGeometry):
    15     _typeid = 7
    16 
    17     def __init__(self, *args, **kwargs):
    18         "Initializes a Geometry Collection from a sequence of Geometry objects."
    19 
    20         # Checking the arguments
    21         if not args:
    22             raise TypeError, 'Must provide at least one Geometry to initialize %s.' % self.__class__.__name__
    23 
    24         if len(args) == 1: 
    25             # If only one geometry provided or a list of geometries is provided
    26             #  in the first argument.
    27             if isinstance(args[0], (tuple, list)):
    28                 init_geoms = args[0]
    29             else:
    30                 init_geoms = args
    31         else:
    32             init_geoms = args
    33 
    34         # Ensuring that only the permitted geometries are allowed in this collection
    35         self._checkAllowedTypes(init_geoms)
    36 
    37         # Creating the geometry pointer array.
    38         collection = self._createCollection(len(init_geoms), iter(init_geoms))
    39         super(GeometryCollection, self).__init__(collection, **kwargs)
    40 
    41     @classmethod
    42     def _createCollection(cls, length, items):
    43         # Creating the geometry pointer array.
    44         geoms = get_pointer_arr(length)
    45         for i, g in enumerate(items):
    46             # this is a little sloppy, but makes life easier
    47             # allow GEOSGeometry types (python wrappers) or pointer types
    48             if hasattr(g, 'ptr'):
    49                 geoms[i] = capi.geom_clone(g.ptr)
    50             else:
    51                 geoms[i] = capi.geom_clone(g)
    52 
    53         return capi.create_collection(c_int(cls._typeid), byref(geoms), c_uint(length))
    54 
    55     def _getItemInternal(self, index):
    56         return capi.get_geomn(self.ptr, index)
    57 
    58     def _getItemExternal(self, index):
    59         "Returns the Geometry from this Collection at the given index (0-based)."
    60         # Checking the index and returning the corresponding GEOS geometry.
    61         self._checkindex(index)
    62         return GEOSGeometry(capi.geom_clone(self._getItemInternal(index)), srid=self.srid)
    63 
    64     def _setCollection(self, length, items):
    65         "Create a new collection, and destroy the contents of the previous pointer."
    66         prev_ptr = self.ptr
    67         srid = self.srid
    68         self.ptr = self._createCollection(length, items)
    69         if srid: self.srid = srid
    70         capi.destroy_geom(prev_ptr)
    71 
    72     def __iter__(self):
    73         "Iterates over each Geometry in the Collection."
    74         for i in xrange(len(self)):
    75             yield self[i]
    76 
    77     def __len__(self):
    78         "Returns the number of geometries in this Collection."
    79         return self.num_geom
    80 
    81     @property
    82     def kml(self):
    83         "Returns the KML for this Geometry Collection."
    84         return '<MultiGeometry>%s</MultiGeometry>' % ''.join([g.kml for g in self])
    85 
    86     @property
    87     def tuple(self):
    88         "Returns a tuple of all the coordinates in this Geometry Collection"
    89         return tuple([g.tuple for g in self])
    90     coords = tuple
    91 
    92 # MultiPoint, MultiLineString, and MultiPolygon class definitions.
    93 class MultiPoint(GeometryCollection): 
    94     _allowed = Point
    95     _typeid = 4
    96 
    97 class MultiLineString(GeometryCollection): 
    98     _allowed = (LineString, LinearRing)
    99     _typeid = 5
   100 
   101 class MultiPolygon(GeometryCollection): 
   102     _allowed = Polygon
   103     _typeid = 6
   104 
   105     @property
   106     def cascaded_union(self):
   107         "Returns a cascaded union of this MultiPolygon."
   108         if GEOS_PREPARE:
   109             return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
   110         else:
   111             raise GEOSException('The cascaded union operation requires GEOS 3.1+.')
   112 
   113 # Setting the allowed types here since GeometryCollection is defined before
   114 # its subclasses.
   115 GeometryCollection._allowed = (Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon)