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