|
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) |