|
0
|
1 |
""" |
|
|
2 |
The GDAL/OGR library uses an Envelope structure to hold the bounding |
|
|
3 |
box information for a geometry. The envelope (bounding box) contains |
|
|
4 |
two pairs of coordinates, one for the lower left coordinate and one |
|
|
5 |
for the upper right coordinate: |
|
|
6 |
|
|
|
7 |
+----------o Upper right; (max_x, max_y) |
|
|
8 |
| | |
|
|
9 |
| | |
|
|
10 |
| | |
|
|
11 |
Lower left (min_x, min_y) o----------+ |
|
|
12 |
""" |
|
|
13 |
from ctypes import Structure, c_double |
|
|
14 |
from django.contrib.gis.gdal.error import OGRException |
|
|
15 |
|
|
|
16 |
# The OGR definition of an Envelope is a C structure containing four doubles. |
|
|
17 |
# See the 'ogr_core.h' source file for more information: |
|
|
18 |
# http://www.gdal.org/ogr/ogr__core_8h-source.html |
|
|
19 |
class OGREnvelope(Structure): |
|
|
20 |
"Represents the OGREnvelope C Structure." |
|
|
21 |
_fields_ = [("MinX", c_double), |
|
|
22 |
("MaxX", c_double), |
|
|
23 |
("MinY", c_double), |
|
|
24 |
("MaxY", c_double), |
|
|
25 |
] |
|
|
26 |
|
|
|
27 |
class Envelope(object): |
|
|
28 |
""" |
|
|
29 |
The Envelope object is a C structure that contains the minimum and |
|
|
30 |
maximum X, Y coordinates for a rectangle bounding box. The naming |
|
|
31 |
of the variables is compatible with the OGR Envelope structure. |
|
|
32 |
""" |
|
|
33 |
|
|
|
34 |
def __init__(self, *args): |
|
|
35 |
""" |
|
|
36 |
The initialization function may take an OGREnvelope structure, 4-element |
|
|
37 |
tuple or list, or 4 individual arguments. |
|
|
38 |
""" |
|
|
39 |
|
|
|
40 |
if len(args) == 1: |
|
|
41 |
if isinstance(args[0], OGREnvelope): |
|
|
42 |
# OGREnvelope (a ctypes Structure) was passed in. |
|
|
43 |
self._envelope = args[0] |
|
|
44 |
elif isinstance(args[0], (tuple, list)): |
|
|
45 |
# A tuple was passed in. |
|
|
46 |
if len(args[0]) != 4: |
|
|
47 |
raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0])) |
|
|
48 |
else: |
|
|
49 |
self._from_sequence(args[0]) |
|
|
50 |
else: |
|
|
51 |
raise TypeError('Incorrect type of argument: %s' % str(type(args[0]))) |
|
|
52 |
elif len(args) == 4: |
|
|
53 |
# Individiual parameters passed in. |
|
|
54 |
# Thanks to ww for the help |
|
|
55 |
self._from_sequence(map(float, args)) |
|
|
56 |
else: |
|
|
57 |
raise OGRException('Incorrect number (%d) of arguments.' % len(args)) |
|
|
58 |
|
|
|
59 |
# Checking the x,y coordinates |
|
|
60 |
if self.min_x > self.max_x: |
|
|
61 |
raise OGRException('Envelope minimum X > maximum X.') |
|
|
62 |
if self.min_y > self.max_y: |
|
|
63 |
raise OGRException('Envelope minimum Y > maximum Y.') |
|
|
64 |
|
|
|
65 |
def __eq__(self, other): |
|
|
66 |
""" |
|
|
67 |
Returns True if the envelopes are equivalent; can compare against |
|
|
68 |
other Envelopes and 4-tuples. |
|
|
69 |
""" |
|
|
70 |
if isinstance(other, Envelope): |
|
|
71 |
return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ |
|
|
72 |
(self.max_x == other.max_x) and (self.max_y == other.max_y) |
|
|
73 |
elif isinstance(other, tuple) and len(other) == 4: |
|
|
74 |
return (self.min_x == other[0]) and (self.min_y == other[1]) and \ |
|
|
75 |
(self.max_x == other[2]) and (self.max_y == other[3]) |
|
|
76 |
else: |
|
|
77 |
raise OGRException('Equivalence testing only works with other Envelopes.') |
|
|
78 |
|
|
|
79 |
def __str__(self): |
|
|
80 |
"Returns a string representation of the tuple." |
|
|
81 |
return str(self.tuple) |
|
|
82 |
|
|
|
83 |
def _from_sequence(self, seq): |
|
|
84 |
"Initializes the C OGR Envelope structure from the given sequence." |
|
|
85 |
self._envelope = OGREnvelope() |
|
|
86 |
self._envelope.MinX = seq[0] |
|
|
87 |
self._envelope.MinY = seq[1] |
|
|
88 |
self._envelope.MaxX = seq[2] |
|
|
89 |
self._envelope.MaxY = seq[3] |
|
|
90 |
|
|
|
91 |
def expand_to_include(self, *args): |
|
|
92 |
""" |
|
|
93 |
Modifies the envelope to expand to include the boundaries of |
|
|
94 |
the passed-in 2-tuple (a point), 4-tuple (an extent) or |
|
|
95 |
envelope. |
|
|
96 |
""" |
|
|
97 |
# We provide a number of different signatures for this method, |
|
|
98 |
# and the logic here is all about converting them into a |
|
|
99 |
# 4-tuple single parameter which does the actual work of |
|
|
100 |
# expanding the envelope. |
|
|
101 |
if len(args) == 1: |
|
|
102 |
if isinstance(args[0], Envelope): |
|
|
103 |
return self.expand_to_include(args[0].tuple) |
|
|
104 |
elif hasattr(args[0], 'x') and hasattr(args[0], 'y'): |
|
|
105 |
return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y) |
|
|
106 |
elif isinstance(args[0], (tuple, list)): |
|
|
107 |
# A tuple was passed in. |
|
|
108 |
if len(args[0]) == 2: |
|
|
109 |
return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1])) |
|
|
110 |
elif len(args[0]) == 4: |
|
|
111 |
(minx, miny, maxx, maxy) = args[0] |
|
|
112 |
if minx < self._envelope.MinX: |
|
|
113 |
self._envelope.MinX = minx |
|
|
114 |
if miny < self._envelope.MinY: |
|
|
115 |
self._envelope.MinY = miny |
|
|
116 |
if maxx > self._envelope.MaxX: |
|
|
117 |
self._envelope.MaxX = maxx |
|
|
118 |
if maxy > self._envelope.MaxY: |
|
|
119 |
self._envelope.MaxY = maxy |
|
|
120 |
else: |
|
|
121 |
raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0])) |
|
|
122 |
else: |
|
|
123 |
raise TypeError('Incorrect type of argument: %s' % str(type(args[0]))) |
|
|
124 |
elif len(args) == 2: |
|
|
125 |
# An x and an y parameter were passed in |
|
|
126 |
return self.expand_to_include((args[0], args[1], args[0], args[1])) |
|
|
127 |
elif len(args) == 4: |
|
|
128 |
# Individiual parameters passed in. |
|
|
129 |
return self.expand_to_include(args) |
|
|
130 |
else: |
|
|
131 |
raise OGRException('Incorrect number (%d) of arguments.' % len(args[0])) |
|
|
132 |
|
|
|
133 |
@property |
|
|
134 |
def min_x(self): |
|
|
135 |
"Returns the value of the minimum X coordinate." |
|
|
136 |
return self._envelope.MinX |
|
|
137 |
|
|
|
138 |
@property |
|
|
139 |
def min_y(self): |
|
|
140 |
"Returns the value of the minimum Y coordinate." |
|
|
141 |
return self._envelope.MinY |
|
|
142 |
|
|
|
143 |
@property |
|
|
144 |
def max_x(self): |
|
|
145 |
"Returns the value of the maximum X coordinate." |
|
|
146 |
return self._envelope.MaxX |
|
|
147 |
|
|
|
148 |
@property |
|
|
149 |
def max_y(self): |
|
|
150 |
"Returns the value of the maximum Y coordinate." |
|
|
151 |
return self._envelope.MaxY |
|
|
152 |
|
|
|
153 |
@property |
|
|
154 |
def ur(self): |
|
|
155 |
"Returns the upper-right coordinate." |
|
|
156 |
return (self.max_x, self.max_y) |
|
|
157 |
|
|
|
158 |
@property |
|
|
159 |
def ll(self): |
|
|
160 |
"Returns the lower-left coordinate." |
|
|
161 |
return (self.min_x, self.min_y) |
|
|
162 |
|
|
|
163 |
@property |
|
|
164 |
def tuple(self): |
|
|
165 |
"Returns a tuple representing the envelope." |
|
|
166 |
return (self.min_x, self.min_y, self.max_x, self.max_y) |
|
|
167 |
|
|
|
168 |
@property |
|
|
169 |
def wkt(self): |
|
|
170 |
"Returns WKT representing a Polygon for this envelope." |
|
|
171 |
# TODO: Fix significant figures. |
|
|
172 |
return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \ |
|
|
173 |
(self.min_x, self.min_y, self.min_x, self.max_y, |
|
|
174 |
self.max_x, self.max_y, self.max_x, self.min_y, |
|
|
175 |
self.min_x, self.min_y) |