|
1 import os, unittest |
|
2 from models import Country, City, State, Feature, MinusOneSRID |
|
3 from django.contrib.gis import gdal |
|
4 from django.contrib.gis.db.backend import SpatialBackend |
|
5 from django.contrib.gis.geos import * |
|
6 from django.contrib.gis.measure import Distance |
|
7 from django.contrib.gis.tests.utils import no_oracle, no_postgis, oracle, postgis |
|
8 |
|
9 # TODO: Some tests depend on the success/failure of previous tests, these should |
|
10 # be decoupled. This flag is an artifact of this problem, and makes debugging easier; |
|
11 # specifically, the DISABLE flag will disables all tests, allowing problem tests to |
|
12 # be examined individually. |
|
13 DISABLE = False |
|
14 |
|
15 class GeoModelTest(unittest.TestCase): |
|
16 |
|
17 def test01_initial_sql(self): |
|
18 "Testing geographic initial SQL." |
|
19 if DISABLE: return |
|
20 if oracle: |
|
21 # Oracle doesn't allow strings longer than 4000 characters |
|
22 # in SQL files, and I'm stumped on how to use Oracle BFILE's |
|
23 # in PLSQL, so we set up the larger geometries manually, rather |
|
24 # than relying on the initial SQL. |
|
25 |
|
26 # Routine for returning the path to the data files. |
|
27 data_dir = os.path.join(os.path.dirname(__file__), 'sql') |
|
28 def get_file(wkt_file): |
|
29 return os.path.join(data_dir, wkt_file) |
|
30 |
|
31 State(name='Colorado', poly=fromfile(get_file('co.wkt'))).save() |
|
32 State(name='Kansas', poly=fromfile(get_file('ks.wkt'))).save() |
|
33 Country(name='Texas', mpoly=fromfile(get_file('tx.wkt'))).save() |
|
34 Country(name='New Zealand', mpoly=fromfile(get_file('nz.wkt'))).save() |
|
35 |
|
36 # Ensuring that data was loaded from initial SQL. |
|
37 self.assertEqual(2, Country.objects.count()) |
|
38 self.assertEqual(8, City.objects.count()) |
|
39 |
|
40 # Oracle cannot handle NULL geometry values w/certain queries. |
|
41 if oracle: n_state = 2 |
|
42 else: n_state = 3 |
|
43 self.assertEqual(n_state, State.objects.count()) |
|
44 |
|
45 def test02_proxy(self): |
|
46 "Testing Lazy-Geometry support (using the GeometryProxy)." |
|
47 if DISABLE: return |
|
48 ## Testing on a Point |
|
49 pnt = Point(0, 0) |
|
50 nullcity = City(name='NullCity', point=pnt) |
|
51 nullcity.save() |
|
52 |
|
53 # Making sure TypeError is thrown when trying to set with an |
|
54 # incompatible type. |
|
55 for bad in [5, 2.0, LineString((0, 0), (1, 1))]: |
|
56 try: |
|
57 nullcity.point = bad |
|
58 except TypeError: |
|
59 pass |
|
60 else: |
|
61 self.fail('Should throw a TypeError') |
|
62 |
|
63 # Now setting with a compatible GEOS Geometry, saving, and ensuring |
|
64 # the save took, notice no SRID is explicitly set. |
|
65 new = Point(5, 23) |
|
66 nullcity.point = new |
|
67 |
|
68 # Ensuring that the SRID is automatically set to that of the |
|
69 # field after assignment, but before saving. |
|
70 self.assertEqual(4326, nullcity.point.srid) |
|
71 nullcity.save() |
|
72 |
|
73 # Ensuring the point was saved correctly after saving |
|
74 self.assertEqual(new, City.objects.get(name='NullCity').point) |
|
75 |
|
76 # Setting the X and Y of the Point |
|
77 nullcity.point.x = 23 |
|
78 nullcity.point.y = 5 |
|
79 # Checking assignments pre & post-save. |
|
80 self.assertNotEqual(Point(23, 5), City.objects.get(name='NullCity').point) |
|
81 nullcity.save() |
|
82 self.assertEqual(Point(23, 5), City.objects.get(name='NullCity').point) |
|
83 nullcity.delete() |
|
84 |
|
85 ## Testing on a Polygon |
|
86 shell = LinearRing((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)) |
|
87 inner = LinearRing((40, 40), (40, 60), (60, 60), (60, 40), (40, 40)) |
|
88 |
|
89 # Creating a State object using a built Polygon |
|
90 ply = Polygon(shell, inner) |
|
91 nullstate = State(name='NullState', poly=ply) |
|
92 self.assertEqual(4326, nullstate.poly.srid) # SRID auto-set from None |
|
93 nullstate.save() |
|
94 |
|
95 ns = State.objects.get(name='NullState') |
|
96 self.assertEqual(ply, ns.poly) |
|
97 |
|
98 # Testing the `ogr` and `srs` lazy-geometry properties. |
|
99 if gdal.HAS_GDAL: |
|
100 self.assertEqual(True, isinstance(ns.poly.ogr, gdal.OGRGeometry)) |
|
101 self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb) |
|
102 self.assertEqual(True, isinstance(ns.poly.srs, gdal.SpatialReference)) |
|
103 self.assertEqual('WGS 84', ns.poly.srs.name) |
|
104 |
|
105 # Changing the interior ring on the poly attribute. |
|
106 new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30)) |
|
107 ns.poly[1] = new_inner |
|
108 ply[1] = new_inner |
|
109 self.assertEqual(4326, ns.poly.srid) |
|
110 ns.save() |
|
111 self.assertEqual(ply, State.objects.get(name='NullState').poly) |
|
112 ns.delete() |
|
113 |
|
114 @no_oracle # Oracle does not support KML. |
|
115 def test03a_kml(self): |
|
116 "Testing KML output from the database using GeoManager.kml()." |
|
117 if DISABLE: return |
|
118 # Should throw a TypeError when trying to obtain KML from a |
|
119 # non-geometry field. |
|
120 qs = City.objects.all() |
|
121 self.assertRaises(TypeError, qs.kml, 'name') |
|
122 |
|
123 # The reference KML depends on the version of PostGIS used |
|
124 # (the output stopped including altitude in 1.3.3). |
|
125 major, minor1, minor2 = SpatialBackend.version |
|
126 ref_kml1 = '<Point><coordinates>-104.609252,38.255001,0</coordinates></Point>' |
|
127 ref_kml2 = '<Point><coordinates>-104.609252,38.255001</coordinates></Point>' |
|
128 if major == 1: |
|
129 if minor1 > 3 or (minor1 == 3 and minor2 >= 3): ref_kml = ref_kml2 |
|
130 else: ref_kml = ref_kml1 |
|
131 else: |
|
132 ref_kml = ref_kml2 |
|
133 |
|
134 # Ensuring the KML is as expected. |
|
135 ptown1 = City.objects.kml(field_name='point', precision=9).get(name='Pueblo') |
|
136 ptown2 = City.objects.kml(precision=9).get(name='Pueblo') |
|
137 for ptown in [ptown1, ptown2]: |
|
138 self.assertEqual(ref_kml, ptown.kml) |
|
139 |
|
140 def test03b_gml(self): |
|
141 "Testing GML output from the database using GeoManager.gml()." |
|
142 if DISABLE: return |
|
143 # Should throw a TypeError when tyring to obtain GML from a |
|
144 # non-geometry field. |
|
145 qs = City.objects.all() |
|
146 self.assertRaises(TypeError, qs.gml, field_name='name') |
|
147 ptown1 = City.objects.gml(field_name='point', precision=9).get(name='Pueblo') |
|
148 ptown2 = City.objects.gml(precision=9).get(name='Pueblo') |
|
149 |
|
150 if oracle: |
|
151 # No precision parameter for Oracle :-/ |
|
152 import re |
|
153 gml_regex = re.compile(r'<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="\." cs="," ts=" ">-104.60925199\d+,38.25500\d+ </gml:coordinates></gml:Point>') |
|
154 for ptown in [ptown1, ptown2]: |
|
155 self.assertEqual(True, bool(gml_regex.match(ptown.gml))) |
|
156 else: |
|
157 for ptown in [ptown1, ptown2]: |
|
158 self.assertEqual('<gml:Point srsName="EPSG:4326"><gml:coordinates>-104.609252,38.255001</gml:coordinates></gml:Point>', ptown.gml) |
|
159 |
|
160 def test04_transform(self): |
|
161 "Testing the transform() GeoManager method." |
|
162 if DISABLE: return |
|
163 # Pre-transformed points for Houston and Pueblo. |
|
164 htown = fromstr('POINT(1947516.83115183 6322297.06040572)', srid=3084) |
|
165 ptown = fromstr('POINT(992363.390841912 481455.395105533)', srid=2774) |
|
166 prec = 3 # Precision is low due to version variations in PROJ and GDAL. |
|
167 |
|
168 # Asserting the result of the transform operation with the values in |
|
169 # the pre-transformed points. Oracle does not have the 3084 SRID. |
|
170 if not oracle: |
|
171 h = City.objects.transform(htown.srid).get(name='Houston') |
|
172 self.assertEqual(3084, h.point.srid) |
|
173 self.assertAlmostEqual(htown.x, h.point.x, prec) |
|
174 self.assertAlmostEqual(htown.y, h.point.y, prec) |
|
175 |
|
176 p1 = City.objects.transform(ptown.srid, field_name='point').get(name='Pueblo') |
|
177 p2 = City.objects.transform(srid=ptown.srid).get(name='Pueblo') |
|
178 for p in [p1, p2]: |
|
179 self.assertEqual(2774, p.point.srid) |
|
180 self.assertAlmostEqual(ptown.x, p.point.x, prec) |
|
181 self.assertAlmostEqual(ptown.y, p.point.y, prec) |
|
182 |
|
183 @no_oracle # Most likely can do this in Oracle, however, it is not yet implemented (patches welcome!) |
|
184 def test05_extent(self): |
|
185 "Testing the `extent` GeoQuerySet method." |
|
186 if DISABLE: return |
|
187 # Reference query: |
|
188 # `SELECT ST_extent(point) FROM geoapp_city WHERE (name='Houston' or name='Dallas');` |
|
189 # => BOX(-96.8016128540039 29.7633724212646,-95.3631439208984 32.7820587158203) |
|
190 expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820) |
|
191 |
|
192 qs = City.objects.filter(name__in=('Houston', 'Dallas')) |
|
193 extent = qs.extent() |
|
194 |
|
195 for val, exp in zip(extent, expected): |
|
196 self.assertAlmostEqual(exp, val, 8) |
|
197 |
|
198 @no_oracle |
|
199 def test06_make_line(self): |
|
200 "Testing the `make_line` GeoQuerySet method." |
|
201 if DISABLE: return |
|
202 # Ensuring that a `TypeError` is raised on models without PointFields. |
|
203 self.assertRaises(TypeError, State.objects.make_line) |
|
204 self.assertRaises(TypeError, Country.objects.make_line) |
|
205 # Reference query: |
|
206 # SELECT AsText(ST_MakeLine(geoapp_city.point)) FROM geoapp_city; |
|
207 self.assertEqual(GEOSGeometry('LINESTRING(-95.363151 29.763374,-96.801611 32.782057,-97.521157 34.464642,174.783117 -41.315268,-104.609252 38.255001,-95.23506 38.971823,-87.650175 41.850385,-123.305196 48.462611)', srid=4326), |
|
208 City.objects.make_line()) |
|
209 |
|
210 def test09_disjoint(self): |
|
211 "Testing the `disjoint` lookup type." |
|
212 if DISABLE: return |
|
213 ptown = City.objects.get(name='Pueblo') |
|
214 qs1 = City.objects.filter(point__disjoint=ptown.point) |
|
215 self.assertEqual(7, qs1.count()) |
|
216 |
|
217 if not postgis: |
|
218 # TODO: Do NULL columns bork queries on PostGIS? The following |
|
219 # error is encountered: |
|
220 # psycopg2.ProgrammingError: invalid memory alloc request size 4294957297 |
|
221 qs2 = State.objects.filter(poly__disjoint=ptown.point) |
|
222 self.assertEqual(1, qs2.count()) |
|
223 self.assertEqual('Kansas', qs2[0].name) |
|
224 |
|
225 def test10_contains_contained(self): |
|
226 "Testing the 'contained', 'contains', and 'bbcontains' lookup types." |
|
227 if DISABLE: return |
|
228 # Getting Texas, yes we were a country -- once ;) |
|
229 texas = Country.objects.get(name='Texas') |
|
230 |
|
231 # Seeing what cities are in Texas, should get Houston and Dallas, |
|
232 # and Oklahoma City because 'contained' only checks on the |
|
233 # _bounding box_ of the Geometries. |
|
234 if not oracle: |
|
235 qs = City.objects.filter(point__contained=texas.mpoly) |
|
236 self.assertEqual(3, qs.count()) |
|
237 cities = ['Houston', 'Dallas', 'Oklahoma City'] |
|
238 for c in qs: self.assertEqual(True, c.name in cities) |
|
239 |
|
240 # Pulling out some cities. |
|
241 houston = City.objects.get(name='Houston') |
|
242 wellington = City.objects.get(name='Wellington') |
|
243 pueblo = City.objects.get(name='Pueblo') |
|
244 okcity = City.objects.get(name='Oklahoma City') |
|
245 lawrence = City.objects.get(name='Lawrence') |
|
246 |
|
247 # Now testing contains on the countries using the points for |
|
248 # Houston and Wellington. |
|
249 tx = Country.objects.get(mpoly__contains=houston.point) # Query w/GEOSGeometry |
|
250 nz = Country.objects.get(mpoly__contains=wellington.point.hex) # Query w/EWKBHEX |
|
251 ks = State.objects.get(poly__contains=lawrence.point) |
|
252 self.assertEqual('Texas', tx.name) |
|
253 self.assertEqual('New Zealand', nz.name) |
|
254 self.assertEqual('Kansas', ks.name) |
|
255 |
|
256 # Pueblo and Oklahoma City (even though OK City is within the bounding box of Texas) |
|
257 # are not contained in Texas or New Zealand. |
|
258 self.assertEqual(0, len(Country.objects.filter(mpoly__contains=pueblo.point))) # Query w/GEOSGeometry object |
|
259 self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT |
|
260 |
|
261 # OK City is contained w/in bounding box of Texas. |
|
262 if not oracle: |
|
263 qs = Country.objects.filter(mpoly__bbcontains=okcity.point) |
|
264 self.assertEqual(1, len(qs)) |
|
265 self.assertEqual('Texas', qs[0].name) |
|
266 |
|
267 def test11_lookup_insert_transform(self): |
|
268 "Testing automatic transform for lookups and inserts." |
|
269 if DISABLE: return |
|
270 # San Antonio in 'WGS84' (SRID 4326) |
|
271 sa_4326 = 'POINT (-98.493183 29.424170)' |
|
272 wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84 |
|
273 |
|
274 # Oracle doesn't have SRID 3084, using 41157. |
|
275 if oracle: |
|
276 # San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157) |
|
277 # Used the following Oracle SQL to get this value: |
|
278 # SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(SDO_GEOMETRY('POINT (-98.493183 29.424170)', 4326), 41157)) FROM DUAL; |
|
279 nad_wkt = 'POINT (300662.034646583 5416427.45974934)' |
|
280 nad_srid = 41157 |
|
281 else: |
|
282 # San Antonio in 'NAD83(HARN) / Texas Centric Lambert Conformal' (SRID 3084) |
|
283 nad_wkt = 'POINT (1645978.362408288754523 6276356.025927528738976)' # Used ogr.py in gdal 1.4.1 for this transform |
|
284 nad_srid = 3084 |
|
285 |
|
286 # Constructing & querying with a point from a different SRID. Oracle |
|
287 # `SDO_OVERLAPBDYINTERSECT` operates differently from |
|
288 # `ST_Intersects`, so contains is used instead. |
|
289 nad_pnt = fromstr(nad_wkt, srid=nad_srid) |
|
290 if oracle: |
|
291 tx = Country.objects.get(mpoly__contains=nad_pnt) |
|
292 else: |
|
293 tx = Country.objects.get(mpoly__intersects=nad_pnt) |
|
294 self.assertEqual('Texas', tx.name) |
|
295 |
|
296 # Creating San Antonio. Remember the Alamo. |
|
297 sa = City(name='San Antonio', point=nad_pnt) |
|
298 sa.save() |
|
299 |
|
300 # Now verifying that San Antonio was transformed correctly |
|
301 sa = City.objects.get(name='San Antonio') |
|
302 self.assertAlmostEqual(wgs_pnt.x, sa.point.x, 6) |
|
303 self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 6) |
|
304 |
|
305 # If the GeometryField SRID is -1, then we shouldn't perform any |
|
306 # transformation if the SRID of the input geometry is different. |
|
307 m1 = MinusOneSRID(geom=Point(17, 23, srid=4326)) |
|
308 m1.save() |
|
309 self.assertEqual(-1, m1.geom.srid) |
|
310 |
|
311 # Oracle does not support NULL geometries in its spatial index for |
|
312 # some routines (e.g., SDO_GEOM.RELATE). |
|
313 @no_oracle |
|
314 def test12_null_geometries(self): |
|
315 "Testing NULL geometry support, and the `isnull` lookup type." |
|
316 if DISABLE: return |
|
317 # Querying for both NULL and Non-NULL values. |
|
318 nullqs = State.objects.filter(poly__isnull=True) |
|
319 validqs = State.objects.filter(poly__isnull=False) |
|
320 |
|
321 # Puerto Rico should be NULL (it's a commonwealth unincorporated territory) |
|
322 self.assertEqual(1, len(nullqs)) |
|
323 self.assertEqual('Puerto Rico', nullqs[0].name) |
|
324 |
|
325 # The valid states should be Colorado & Kansas |
|
326 self.assertEqual(2, len(validqs)) |
|
327 state_names = [s.name for s in validqs] |
|
328 self.assertEqual(True, 'Colorado' in state_names) |
|
329 self.assertEqual(True, 'Kansas' in state_names) |
|
330 |
|
331 # Saving another commonwealth w/a NULL geometry. |
|
332 if not oracle: |
|
333 # TODO: Fix saving w/NULL geometry on Oracle. |
|
334 State(name='Northern Mariana Islands', poly=None).save() |
|
335 |
|
336 @no_oracle # No specific `left` or `right` operators in Oracle. |
|
337 def test13_left_right(self): |
|
338 "Testing the 'left' and 'right' lookup types." |
|
339 if DISABLE: return |
|
340 # Left: A << B => true if xmax(A) < xmin(B) |
|
341 # Right: A >> B => true if xmin(A) > xmax(B) |
|
342 # See: BOX2D_left() and BOX2D_right() in lwgeom_box2dfloat4.c in PostGIS source. |
|
343 |
|
344 # Getting the borders for Colorado & Kansas |
|
345 co_border = State.objects.get(name='Colorado').poly |
|
346 ks_border = State.objects.get(name='Kansas').poly |
|
347 |
|
348 # Note: Wellington has an 'X' value of 174, so it will not be considered |
|
349 # to the left of CO. |
|
350 |
|
351 # These cities should be strictly to the right of the CO border. |
|
352 cities = ['Houston', 'Dallas', 'San Antonio', 'Oklahoma City', |
|
353 'Lawrence', 'Chicago', 'Wellington'] |
|
354 qs = City.objects.filter(point__right=co_border) |
|
355 self.assertEqual(7, len(qs)) |
|
356 for c in qs: self.assertEqual(True, c.name in cities) |
|
357 |
|
358 # These cities should be strictly to the right of the KS border. |
|
359 cities = ['Chicago', 'Wellington'] |
|
360 qs = City.objects.filter(point__right=ks_border) |
|
361 self.assertEqual(2, len(qs)) |
|
362 for c in qs: self.assertEqual(True, c.name in cities) |
|
363 |
|
364 # Note: Wellington has an 'X' value of 174, so it will not be considered |
|
365 # to the left of CO. |
|
366 vic = City.objects.get(point__left=co_border) |
|
367 self.assertEqual('Victoria', vic.name) |
|
368 |
|
369 cities = ['Pueblo', 'Victoria'] |
|
370 qs = City.objects.filter(point__left=ks_border) |
|
371 self.assertEqual(2, len(qs)) |
|
372 for c in qs: self.assertEqual(True, c.name in cities) |
|
373 |
|
374 def test14_equals(self): |
|
375 "Testing the 'same_as' and 'equals' lookup types." |
|
376 if DISABLE: return |
|
377 pnt = fromstr('POINT (-95.363151 29.763374)', srid=4326) |
|
378 c1 = City.objects.get(point=pnt) |
|
379 c2 = City.objects.get(point__same_as=pnt) |
|
380 c3 = City.objects.get(point__equals=pnt) |
|
381 for c in [c1, c2, c3]: self.assertEqual('Houston', c.name) |
|
382 |
|
383 def test15_relate(self): |
|
384 "Testing the 'relate' lookup type." |
|
385 if DISABLE: return |
|
386 # To make things more interesting, we will have our Texas reference point in |
|
387 # different SRIDs. |
|
388 pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847) |
|
389 pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326) |
|
390 |
|
391 # Not passing in a geometry as first param shoud |
|
392 # raise a type error when initializing the GeoQuerySet |
|
393 self.assertRaises(TypeError, Country.objects.filter, mpoly__relate=(23, 'foo')) |
|
394 # Making sure the right exception is raised for the given |
|
395 # bad arguments. |
|
396 for bad_args, e in [((pnt1, 0), TypeError), ((pnt2, 'T*T***FF*', 0), ValueError)]: |
|
397 qs = Country.objects.filter(mpoly__relate=bad_args) |
|
398 self.assertRaises(e, qs.count) |
|
399 |
|
400 # Relate works differently for the different backends. |
|
401 if postgis: |
|
402 contains_mask = 'T*T***FF*' |
|
403 within_mask = 'T*F**F***' |
|
404 intersects_mask = 'T********' |
|
405 elif oracle: |
|
406 contains_mask = 'contains' |
|
407 within_mask = 'inside' |
|
408 # TODO: This is not quite the same as the PostGIS mask above |
|
409 intersects_mask = 'overlapbdyintersect' |
|
410 |
|
411 # Testing contains relation mask. |
|
412 self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, contains_mask)).name) |
|
413 self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, contains_mask)).name) |
|
414 |
|
415 # Testing within relation mask. |
|
416 ks = State.objects.get(name='Kansas') |
|
417 self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, within_mask)).name) |
|
418 |
|
419 # Testing intersection relation mask. |
|
420 if not oracle: |
|
421 self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name) |
|
422 self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name) |
|
423 self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name) |
|
424 |
|
425 def test16_createnull(self): |
|
426 "Testing creating a model instance and the geometry being None" |
|
427 if DISABLE: return |
|
428 c = City() |
|
429 self.assertEqual(c.point, None) |
|
430 |
|
431 def test17_unionagg(self): |
|
432 "Testing the `unionagg` (aggregate union) GeoManager method." |
|
433 if DISABLE: return |
|
434 tx = Country.objects.get(name='Texas').mpoly |
|
435 # Houston, Dallas, San Antonio -- Oracle has different order. |
|
436 union1 = fromstr('MULTIPOINT(-98.493183 29.424170,-96.801611 32.782057,-95.363151 29.763374)') |
|
437 union2 = fromstr('MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374,-98.493183 29.424170)') |
|
438 qs = City.objects.filter(point__within=tx) |
|
439 self.assertRaises(TypeError, qs.unionagg, 'name') |
|
440 # Using `field_name` keyword argument in one query and specifying an |
|
441 # order in the other (which should not be used because this is |
|
442 # an aggregate method on a spatial column) |
|
443 u1 = qs.unionagg(field_name='point') |
|
444 u2 = qs.order_by('name').unionagg() |
|
445 tol = 0.00001 |
|
446 if SpatialBackend.oracle: |
|
447 union = union2 |
|
448 else: |
|
449 union = union1 |
|
450 self.assertEqual(True, union.equals_exact(u1, tol)) |
|
451 self.assertEqual(True, union.equals_exact(u2, tol)) |
|
452 qs = City.objects.filter(name='NotACity') |
|
453 self.assertEqual(None, qs.unionagg(field_name='point')) |
|
454 |
|
455 def test18_geometryfield(self): |
|
456 "Testing GeometryField." |
|
457 if DISABLE: return |
|
458 Feature(name='Point', geom=Point(1, 1)).save() |
|
459 Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5))).save() |
|
460 Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))).save() |
|
461 Feature(name='GeometryCollection', |
|
462 geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)), |
|
463 Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))).save() |
|
464 |
|
465 f_1 = Feature.objects.get(name='Point') |
|
466 self.assertEqual(True, isinstance(f_1.geom, Point)) |
|
467 self.assertEqual((1.0, 1.0), f_1.geom.tuple) |
|
468 f_2 = Feature.objects.get(name='LineString') |
|
469 self.assertEqual(True, isinstance(f_2.geom, LineString)) |
|
470 self.assertEqual(((0.0, 0.0), (1.0, 1.0), (5.0, 5.0)), f_2.geom.tuple) |
|
471 |
|
472 f_3 = Feature.objects.get(name='Polygon') |
|
473 self.assertEqual(True, isinstance(f_3.geom, Polygon)) |
|
474 f_4 = Feature.objects.get(name='GeometryCollection') |
|
475 self.assertEqual(True, isinstance(f_4.geom, GeometryCollection)) |
|
476 self.assertEqual(f_3.geom, f_4.geom[2]) |
|
477 |
|
478 def test19_centroid(self): |
|
479 "Testing the `centroid` GeoQuerySet method." |
|
480 if DISABLE: return |
|
481 qs = State.objects.exclude(poly__isnull=True).centroid() |
|
482 if oracle: tol = 0.1 |
|
483 else: tol = 0.000000001 |
|
484 for s in qs: |
|
485 self.assertEqual(True, s.poly.centroid.equals_exact(s.centroid, tol)) |
|
486 |
|
487 def test20_pointonsurface(self): |
|
488 "Testing the `point_on_surface` GeoQuerySet method." |
|
489 if DISABLE: return |
|
490 # Reference values. |
|
491 if SpatialBackend.oracle: |
|
492 # SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_GEOM.SDO_POINTONSURFACE(GEOAPP_COUNTRY.MPOLY, 0.05)) FROM GEOAPP_COUNTRY; |
|
493 ref = {'New Zealand' : fromstr('POINT (174.616364 -36.100861)', srid=4326), |
|
494 'Texas' : fromstr('POINT (-103.002434 36.500397)', srid=4326), |
|
495 } |
|
496 elif SpatialBackend.postgis: |
|
497 # Using GEOSGeometry to compute the reference point on surface values |
|
498 # -- since PostGIS also uses GEOS these should be the same. |
|
499 ref = {'New Zealand' : Country.objects.get(name='New Zealand').mpoly.point_on_surface, |
|
500 'Texas' : Country.objects.get(name='Texas').mpoly.point_on_surface |
|
501 } |
|
502 for cntry in Country.objects.point_on_surface(): |
|
503 self.assertEqual(ref[cntry.name], cntry.point_on_surface) |
|
504 |
|
505 @no_oracle |
|
506 def test21_scale(self): |
|
507 "Testing the `scale` GeoQuerySet method." |
|
508 if DISABLE: return |
|
509 xfac, yfac = 2, 3 |
|
510 qs = Country.objects.scale(xfac, yfac, model_att='scaled') |
|
511 for c in qs: |
|
512 for p1, p2 in zip(c.mpoly, c.scaled): |
|
513 for r1, r2 in zip(p1, p2): |
|
514 for c1, c2 in zip(r1.coords, r2.coords): |
|
515 self.assertEqual(c1[0] * xfac, c2[0]) |
|
516 self.assertEqual(c1[1] * yfac, c2[1]) |
|
517 |
|
518 @no_oracle |
|
519 def test22_translate(self): |
|
520 "Testing the `translate` GeoQuerySet method." |
|
521 if DISABLE: return |
|
522 xfac, yfac = 5, -23 |
|
523 qs = Country.objects.translate(xfac, yfac, model_att='translated') |
|
524 for c in qs: |
|
525 for p1, p2 in zip(c.mpoly, c.translated): |
|
526 for r1, r2 in zip(p1, p2): |
|
527 for c1, c2 in zip(r1.coords, r2.coords): |
|
528 self.assertEqual(c1[0] + xfac, c2[0]) |
|
529 self.assertEqual(c1[1] + yfac, c2[1]) |
|
530 |
|
531 def test23_numgeom(self): |
|
532 "Testing the `num_geom` GeoQuerySet method." |
|
533 if DISABLE: return |
|
534 # Both 'countries' only have two geometries. |
|
535 for c in Country.objects.num_geom(): self.assertEqual(2, c.num_geom) |
|
536 for c in City.objects.filter(point__isnull=False).num_geom(): |
|
537 # Oracle will return 1 for the number of geometries on non-collections, |
|
538 # whereas PostGIS will return None. |
|
539 if postgis: self.assertEqual(None, c.num_geom) |
|
540 else: self.assertEqual(1, c.num_geom) |
|
541 |
|
542 def test24_numpoints(self): |
|
543 "Testing the `num_points` GeoQuerySet method." |
|
544 if DISABLE: return |
|
545 for c in Country.objects.num_points(): self.assertEqual(c.mpoly.num_points, c.num_points) |
|
546 if postgis: |
|
547 # Oracle cannot count vertices in Point geometries. |
|
548 for c in City.objects.num_points(): self.assertEqual(1, c.num_points) |
|
549 |
|
550 @no_oracle |
|
551 def test25_geoset(self): |
|
552 "Testing the `difference`, `intersection`, `sym_difference`, and `union` GeoQuerySet methods." |
|
553 if DISABLE: return |
|
554 geom = Point(5, 23) |
|
555 for c in Country.objects.all().intersection(geom).difference(geom).sym_difference(geom).union(geom): |
|
556 self.assertEqual(c.mpoly.difference(geom), c.difference) |
|
557 self.assertEqual(c.mpoly.intersection(geom), c.intersection) |
|
558 self.assertEqual(c.mpoly.sym_difference(geom), c.sym_difference) |
|
559 self.assertEqual(c.mpoly.union(geom), c.union) |
|
560 |
|
561 from test_feeds import GeoFeedTest |
|
562 from test_sitemaps import GeoSitemapTest |
|
563 def suite(): |
|
564 s = unittest.TestSuite() |
|
565 s.addTest(unittest.makeSuite(GeoModelTest)) |
|
566 s.addTest(unittest.makeSuite(GeoFeedTest)) |
|
567 s.addTest(unittest.makeSuite(GeoSitemapTest)) |
|
568 return s |