145 def test03b_distance_method(self): |
146 def test03b_distance_method(self): |
146 "Testing the `distance` GeoQuerySet method on geodetic coordnate systems." |
147 "Testing the `distance` GeoQuerySet method on geodetic coordnate systems." |
147 if oracle: tol = 2 |
148 if oracle: tol = 2 |
148 else: tol = 5 |
149 else: tol = 5 |
149 |
150 |
150 # Now testing geodetic distance aggregation. |
151 # Testing geodetic distance calculation with a non-point geometry |
151 hillsdale = AustraliaCity.objects.get(name='Hillsdale') |
152 # (a LineString of Wollongong and Shellharbour coords). |
152 if not oracle: |
153 ls = LineString( ( (150.902, -34.4245), (150.87, -34.5789) ) ) |
153 # PostGIS is limited to disance queries only to/from point geometries, |
154 if oracle or connection.ops.geography: |
154 # ensuring a TypeError is raised if something else is put in. |
155 # Reference query: |
155 self.assertRaises(ValueError, AustraliaCity.objects.distance, 'LINESTRING(0 0, 1 1)') |
156 # SELECT ST_distance_sphere(point, ST_GeomFromText('LINESTRING(150.9020 -34.4245,150.8700 -34.5789)', 4326)) FROM distapp_australiacity ORDER BY name; |
156 self.assertRaises(ValueError, AustraliaCity.objects.distance, LineString((0, 0), (1, 1))) |
157 distances = [1120954.92533513, 140575.720018241, 640396.662906304, |
|
158 60580.9693849269, 972807.955955075, 568451.8357838, |
|
159 40435.4335201384, 0, 68272.3896586844, 12375.0643697706, 0] |
|
160 qs = AustraliaCity.objects.distance(ls).order_by('name') |
|
161 for city, distance in zip(qs, distances): |
|
162 # Testing equivalence to within a meter. |
|
163 self.assertAlmostEqual(distance, city.distance.m, 0) |
|
164 else: |
|
165 # PostGIS 1.4 and below is limited to disance queries only |
|
166 # to/from point geometries, check for raising of ValueError. |
|
167 self.assertRaises(ValueError, AustraliaCity.objects.distance, ls) |
|
168 self.assertRaises(ValueError, AustraliaCity.objects.distance, ls.wkt) |
157 |
169 |
158 # Got the reference distances using the raw SQL statements: |
170 # Got the reference distances using the raw SQL statements: |
159 # SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326), 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11)); |
171 # SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326), 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11)); |
160 spheroid_distances = [60504.0628825298, 77023.948962654, 49154.8867507115, 90847.435881812, 217402.811862568, 709599.234619957, 640011.483583758, 7772.00667666425, 1047861.7859506, 1165126.55237647] |
|
161 # SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326)) FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere |
172 # SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326)) FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere |
162 sphere_distances = [60580.7612632291, 77143.7785056615, 49199.2725132184, 90804.4414289463, 217712.63666124, 709131.691061906, 639825.959074112, 7786.80274606706, 1049200.46122281, 1162619.7297006] |
173 if connection.ops.postgis and connection.ops.proj_version_tuple() >= (4, 7, 0): |
|
174 # PROJ.4 versions 4.7+ have updated datums, and thus different |
|
175 # distance values. |
|
176 spheroid_distances = [60504.0628957201, 77023.9489850262, 49154.8867574404, |
|
177 90847.4358768573, 217402.811919332, 709599.234564757, |
|
178 640011.483550888, 7772.00667991925, 1047861.78619339, |
|
179 1165126.55236034] |
|
180 sphere_distances = [60580.9693849267, 77144.0435286473, 49199.4415344719, |
|
181 90804.7533823494, 217713.384600405, 709134.127242793, |
|
182 639828.157159169, 7786.82949717788, 1049204.06569028, |
|
183 1162623.7238134] |
|
184 |
|
185 else: |
|
186 spheroid_distances = [60504.0628825298, 77023.948962654, 49154.8867507115, |
|
187 90847.435881812, 217402.811862568, 709599.234619957, |
|
188 640011.483583758, 7772.00667666425, 1047861.7859506, |
|
189 1165126.55237647] |
|
190 sphere_distances = [60580.7612632291, 77143.7785056615, 49199.2725132184, |
|
191 90804.4414289463, 217712.63666124, 709131.691061906, |
|
192 639825.959074112, 7786.80274606706, 1049200.46122281, |
|
193 1162619.7297006] |
163 |
194 |
164 # Testing with spheroid distances first. |
195 # Testing with spheroid distances first. |
|
196 hillsdale = AustraliaCity.objects.get(name='Hillsdale') |
165 qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point, spheroid=True) |
197 qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point, spheroid=True) |
166 for i, c in enumerate(qs): |
198 for i, c in enumerate(qs): |
167 self.assertAlmostEqual(spheroid_distances[i], c.distance.m, tol) |
199 self.assertAlmostEqual(spheroid_distances[i], c.distance.m, tol) |
168 if postgis: |
200 if postgis: |
169 # PostGIS uses sphere-only distances by default, testing these as well. |
201 # PostGIS uses sphere-only distances by default, testing these as well. |
173 |
205 |
174 @no_oracle # Oracle already handles geographic distance calculation. |
206 @no_oracle # Oracle already handles geographic distance calculation. |
175 def test03c_distance_method(self): |
207 def test03c_distance_method(self): |
176 "Testing the `distance` GeoQuerySet method used with `transform` on a geographic field." |
208 "Testing the `distance` GeoQuerySet method used with `transform` on a geographic field." |
177 # Normally you can't compute distances from a geometry field |
209 # Normally you can't compute distances from a geometry field |
178 # that is not a PointField (on PostGIS). |
210 # that is not a PointField (on PostGIS 1.4 and below). |
179 self.assertRaises(ValueError, CensusZipcode.objects.distance, self.stx_pnt) |
211 if not connection.ops.geography: |
|
212 self.assertRaises(ValueError, CensusZipcode.objects.distance, self.stx_pnt) |
180 |
213 |
181 # We'll be using a Polygon (created by buffering the centroid |
214 # We'll be using a Polygon (created by buffering the centroid |
182 # of 77005 to 100m) -- which aren't allowed in geographic distance |
215 # of 77005 to 100m) -- which aren't allowed in geographic distance |
183 # queries normally, however our field has been transformed to |
216 # queries normally, however our field has been transformed to |
184 # a non-geographic system. |
217 # a non-geographic system. |
228 self.assertEqual(['77025', '77401'], self.get_names(qs)) |
261 self.assertEqual(['77025', '77401'], self.get_names(qs)) |
229 # If we add a little more distance 77002 should be included. |
262 # If we add a little more distance 77002 should be included. |
230 qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300))) |
263 qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300))) |
231 self.assertEqual(['77002', '77025', '77401'], self.get_names(qs)) |
264 self.assertEqual(['77002', '77025', '77401'], self.get_names(qs)) |
232 |
265 |
233 @no_spatialite |
|
234 def test05_geodetic_distance_lookups(self): |
266 def test05_geodetic_distance_lookups(self): |
235 "Testing distance lookups on geodetic coordinate systems." |
267 "Testing distance lookups on geodetic coordinate systems." |
236 if not oracle: |
268 # Line is from Canberra to Sydney. Query is for all other cities within |
237 # Oracle doesn't have this limitation -- PostGIS only allows geodetic |
269 # a 100km of that line (which should exclude only Hobart & Adelaide). |
238 # distance queries from Points to PointFields. |
270 line = GEOSGeometry('LINESTRING(144.9630 -37.8143,151.2607 -33.8870)', 4326) |
239 mp = GEOSGeometry('MULTIPOINT(0 0, 5 23)') |
271 dist_qs = AustraliaCity.objects.filter(point__distance_lte=(line, D(km=100))) |
240 self.assertRaises(TypeError, |
272 |
241 AustraliaCity.objects.filter(point__distance_lte=(mp, D(km=100)))) |
273 if oracle or connection.ops.geography: |
242 # Too many params (4 in this case) should raise a ValueError. |
274 # Oracle and PostGIS 1.5 can do distance lookups on arbitrary geometries. |
243 self.assertRaises(ValueError, |
275 self.assertEqual(9, dist_qs.count()) |
244 AustraliaCity.objects.filter, point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4')) |
276 self.assertEqual(['Batemans Bay', 'Canberra', 'Hillsdale', |
|
277 'Melbourne', 'Mittagong', 'Shellharbour', |
|
278 'Sydney', 'Thirroul', 'Wollongong'], |
|
279 self.get_names(dist_qs)) |
|
280 else: |
|
281 # PostGIS 1.4 and below only allows geodetic distance queries (utilizing |
|
282 # ST_Distance_Sphere/ST_Distance_Spheroid) from Points to PointFields |
|
283 # on geometry columns. |
|
284 self.assertRaises(ValueError, dist_qs.count) |
|
285 |
|
286 # Ensured that a ValueError was raised, none of the rest of the test is |
|
287 # support on this backend, so bail now. |
|
288 if spatialite: return |
|
289 |
|
290 # Too many params (4 in this case) should raise a ValueError. |
|
291 self.assertRaises(ValueError, len, |
|
292 AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4'))) |
245 |
293 |
246 # Not enough params should raise a ValueError. |
294 # Not enough params should raise a ValueError. |
247 self.assertRaises(ValueError, |
295 self.assertRaises(ValueError, len, |
248 AustraliaCity.objects.filter, point__distance_lte=('POINT(5 23)',)) |
296 AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)',))) |
249 |
297 |
250 # Getting all cities w/in 550 miles of Hobart. |
298 # Getting all cities w/in 550 miles of Hobart. |
251 hobart = AustraliaCity.objects.get(name='Hobart') |
299 hobart = AustraliaCity.objects.get(name='Hobart') |
252 qs = AustraliaCity.objects.exclude(name='Hobart').filter(point__distance_lte=(hobart.point, D(mi=550))) |
300 qs = AustraliaCity.objects.exclude(name='Hobart').filter(point__distance_lte=(hobart.point, D(mi=550))) |
253 cities = self.get_names(qs) |
301 cities = self.get_names(qs) |