django/contrib/gis/geos/collections.py
author Justin Bronn <jbronn@geodjango.org>
Thu Mar 19 13:08:50 2009 -0500 (3 years ago)
branchtrunk
changeset 362 87b34dce202d
parent 360 fd85d0fb4fb5
child 435 d75ff359e306
permissions -rw-r--r--
Added `merged` property to LineString & MultiLineString, which returns the output of the GEOS line merging operation.
kmtracey@0
     1
"""
kmtracey@0
     2
 This module houses the Geometry Collection objects:
kmtracey@0
     3
 GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
kmtracey@0
     4
"""
kmtracey@0
     5
from ctypes import c_int, c_uint, byref
kmtracey@0
     6
from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
jbronn@1
     7
from django.contrib.gis.geos.geometry import GEOSGeometry
jbronn@126
     8
from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR, GEOS_PREPARE
jbronn@1
     9
from django.contrib.gis.geos.linestring import LineString, LinearRing
jbronn@1
    10
from django.contrib.gis.geos.point import Point
jbronn@1
    11
from django.contrib.gis.geos.polygon import Polygon
jbronn@126
    12
from django.contrib.gis.geos import prototypes as capi
kmtracey@0
    13
kmtracey@0
    14
class GeometryCollection(GEOSGeometry):
kmtracey@0
    15
    _typeid = 7
jbronn@360
    16
    _minlength = 1
kmtracey@0
    17
kmtracey@0
    18
    def __init__(self, *args, **kwargs):
kmtracey@0
    19
        "Initializes a Geometry Collection from a sequence of Geometry objects."
kmtracey@0
    20
kmtracey@0
    21
        # Checking the arguments
kmtracey@0
    22
        if not args:
kmtracey@0
    23
            raise TypeError, 'Must provide at least one Geometry to initialize %s.' % self.__class__.__name__
kmtracey@0
    24
jbronn@353
    25
        if len(args) == 1:
kmtracey@0
    26
            # If only one geometry provided or a list of geometries is provided
kmtracey@0
    27
            #  in the first argument.
jbronn@1
    28
            if isinstance(args[0], (tuple, list)):
kmtracey@0
    29
                init_geoms = args[0]
kmtracey@0
    30
            else:
kmtracey@0
    31
                init_geoms = args
kmtracey@0
    32
        else:
kmtracey@0
    33
            init_geoms = args
kmtracey@0
    34
kmtracey@0
    35
        # Ensuring that only the permitted geometries are allowed in this collection
jbronn@352
    36
        # this is moved to list mixin super class
jbronn@360
    37
        self._check_allowed(init_geoms)
kmtracey@0
    38
kmtracey@0
    39
        # Creating the geometry pointer array.
jbronn@358
    40
        collection = self._create_collection(len(init_geoms), iter(init_geoms))
jbronn@138
    41
        super(GeometryCollection, self).__init__(collection, **kwargs)
kmtracey@0
    42
jbronn@360
    43
    def __iter__(self):
jbronn@360
    44
        "Iterates over each Geometry in the Collection."
jbronn@360
    45
        for i in xrange(len(self)):
jbronn@360
    46
            yield self[i]
jbronn@360
    47
jbronn@360
    48
    def __len__(self):
jbronn@360
    49
        "Returns the number of geometries in this Collection."
jbronn@360
    50
        return self.num_geom
jbronn@360
    51
jbronn@360
    52
    ### Methods for compatibility with ListMixin ###
jbronn@138
    53
    @classmethod
jbronn@358
    54
    def _create_collection(cls, length, items):
jbronn@138
    55
        # Creating the geometry pointer array.
jbronn@138
    56
        geoms = get_pointer_arr(length)
jbronn@138
    57
        for i, g in enumerate(items):
jbronn@138
    58
            # this is a little sloppy, but makes life easier
jbronn@138
    59
            # allow GEOSGeometry types (python wrappers) or pointer types
jbronn@351
    60
            geoms[i] = capi.geom_clone(getattr(g, 'ptr', g))
jbronn@138
    61
jbronn@138
    62
        return capi.create_collection(c_int(cls._typeid), byref(geoms), c_uint(length))
jbronn@138
    63
jbronn@358
    64
    def _getitem_internal(self, index):
jbronn@138
    65
        return capi.get_geomn(self.ptr, index)
jbronn@138
    66
jbronn@358
    67
    def _getitem_external(self, index):
kmtracey@0
    68
        "Returns the Geometry from this Collection at the given index (0-based)."
kmtracey@0
    69
        # Checking the index and returning the corresponding GEOS geometry.
jbronn@358
    70
        return GEOSGeometry(capi.geom_clone(self._getitem_internal(index)), srid=self.srid)
kmtracey@0
    71
jbronn@359
    72
    def _set_collection(self, length, items):
jbronn@138
    73
        "Create a new collection, and destroy the contents of the previous pointer."
kmtracey@0
    74
        prev_ptr = self.ptr
kmtracey@0
    75
        srid = self.srid
jbronn@358
    76
        self.ptr = self._create_collection(length, items)
kmtracey@0
    77
        if srid: self.srid = srid
jbronn@126
    78
        capi.destroy_geom(prev_ptr)
kmtracey@0
    79
jbronn@360
    80
    # Because GeometryCollections need to be rebuilt upon the changing of a
jbronn@360
    81
    # component geometry, these routines are set to their counterparts that
jbronn@360
    82
    # rebuild the entire geometry.
jbronn@359
    83
    _set_single = GEOSGeometry._set_single_rebuild
jbronn@360
    84
    _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
jbronn@353
    85
kmtracey@0
    86
    @property
kmtracey@0
    87
    def kml(self):
kmtracey@0
    88
        "Returns the KML for this Geometry Collection."
kmtracey@0
    89
        return '<MultiGeometry>%s</MultiGeometry>' % ''.join([g.kml for g in self])
kmtracey@0
    90
kmtracey@0
    91
    @property
kmtracey@0
    92
    def tuple(self):
kmtracey@0
    93
        "Returns a tuple of all the coordinates in this Geometry Collection"
kmtracey@0
    94
        return tuple([g.tuple for g in self])
kmtracey@0
    95
    coords = tuple
kmtracey@0
    96
kmtracey@0
    97
# MultiPoint, MultiLineString, and MultiPolygon class definitions.
jbronn@353
    98
class MultiPoint(GeometryCollection):
kmtracey@0
    99
    _allowed = Point
kmtracey@0
   100
    _typeid = 4
jbronn@126
   101
jbronn@353
   102
class MultiLineString(GeometryCollection):
kmtracey@0
   103
    _allowed = (LineString, LinearRing)
kmtracey@0
   104
    _typeid = 5
jbronn@126
   105
jbronn@362
   106
    @property
jbronn@362
   107
    def merged(self):
jbronn@362
   108
        """ 
jbronn@362
   109
        Returns a LineString representing the line merge of this 
jbronn@362
   110
        MultiLineString.
jbronn@362
   111
        """ 
jbronn@362
   112
        return self._topology(capi.geos_linemerge(self.ptr))         
jbronn@362
   113
jbronn@353
   114
class MultiPolygon(GeometryCollection):
kmtracey@0
   115
    _allowed = Polygon
kmtracey@0
   116
    _typeid = 6
jbronn@94
   117
jbronn@126
   118
    @property
jbronn@126
   119
    def cascaded_union(self):
jbronn@126
   120
        "Returns a cascaded union of this MultiPolygon."
jbronn@126
   121
        if GEOS_PREPARE:
jbronn@126
   122
            return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
jbronn@126
   123
        else:
jbronn@126
   124
            raise GEOSException('The cascaded union operation requires GEOS 3.1+.')
jbronn@126
   125
jbronn@126
   126
# Setting the allowed types here since GeometryCollection is defined before
jbronn@126
   127
# its subclasses.
jbronn@94
   128
GeometryCollection._allowed = (Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon)