|
0
|
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: |
|
29
|
22 |
raise TypeError('Must provide at least one Geometry to initialize %s.' % self.__class__.__name__) |
|
0
|
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) |