|
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 # this is moved to list mixin super class |
|
36 self._check_allowed(init_geoms) |
|
37 |
|
38 # Creating the geometry pointer array. |
|
39 collection = self._create_collection(len(init_geoms), iter(init_geoms)) |
|
40 super(GeometryCollection, self).__init__(collection, **kwargs) |
|
41 |
|
42 def __iter__(self): |
|
43 "Iterates over each Geometry in the Collection." |
|
44 for i in xrange(len(self)): |
|
45 yield self[i] |
|
46 |
|
47 def __len__(self): |
|
48 "Returns the number of geometries in this Collection." |
|
49 return self.num_geom |
|
50 |
|
51 ### Methods for compatibility with ListMixin ### |
|
52 def _create_collection(self, length, items): |
|
53 # Creating the geometry pointer array. |
|
54 geoms = get_pointer_arr(length) |
|
55 for i, g in enumerate(items): |
|
56 # this is a little sloppy, but makes life easier |
|
57 # allow GEOSGeometry types (python wrappers) or pointer types |
|
58 geoms[i] = capi.geom_clone(getattr(g, 'ptr', g)) |
|
59 |
|
60 return capi.create_collection(c_int(self._typeid), byref(geoms), c_uint(length)) |
|
61 |
|
62 def _get_single_internal(self, index): |
|
63 return capi.get_geomn(self.ptr, index) |
|
64 |
|
65 def _get_single_external(self, index): |
|
66 "Returns the Geometry from this Collection at the given index (0-based)." |
|
67 # Checking the index and returning the corresponding GEOS geometry. |
|
68 return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid) |
|
69 |
|
70 def _set_list(self, length, items): |
|
71 "Create a new collection, and destroy the contents of the previous pointer." |
|
72 prev_ptr = self.ptr |
|
73 srid = self.srid |
|
74 self.ptr = self._create_collection(length, items) |
|
75 if srid: self.srid = srid |
|
76 capi.destroy_geom(prev_ptr) |
|
77 |
|
78 _set_single = GEOSGeometry._set_single_rebuild |
|
79 _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild |
|
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 @property |
|
102 def merged(self): |
|
103 """ |
|
104 Returns a LineString representing the line merge of this |
|
105 MultiLineString. |
|
106 """ |
|
107 return self._topology(capi.geos_linemerge(self.ptr)) |
|
108 |
|
109 class MultiPolygon(GeometryCollection): |
|
110 _allowed = Polygon |
|
111 _typeid = 6 |
|
112 |
|
113 @property |
|
114 def cascaded_union(self): |
|
115 "Returns a cascaded union of this MultiPolygon." |
|
116 if GEOS_PREPARE: |
|
117 return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid) |
|
118 else: |
|
119 raise GEOSException('The cascaded union operation requires GEOS 3.1+.') |
|
120 |
|
121 # Setting the allowed types here since GeometryCollection is defined before |
|
122 # its subclasses. |
|
123 GeometryCollection._allowed = (Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon) |