Permalink
Browse files

Removed some more hardcoded backends in GIS tests

Refs #22632. Thanks Tim Graham for the review.
  • Loading branch information...
1 parent 5675eb3 commit 60428ed5db76f509f4a0eb737d96b4f0ae6b0ad5 @claudep claudep committed Aug 21, 2014
@@ -16,6 +16,9 @@ class BaseSpatialFeatures(object):
# Does the database contain a SpatialRefSys model to store SRID information?
has_spatialrefsys_table = True
+ # Reference implementation of 3D functions is:
+ # http://postgis.net/docs/PostGIS_Special_Functions_Index.html#PostGIS_3D_Functions
+ supports_3d_functions = False
# Does the database support SRID transform operations?
supports_transform = True
# Do geometric relationship operations operate on real shapes (or only on bounding boxes)?
@@ -29,24 +32,34 @@ class BaseSpatialFeatures(object):
# The following properties indicate if the database backend support
# certain lookups (dwithin, left and right, relate, ...)
+ supports_distances_lookups = True
supports_left_right_lookups = False
@property
- def supports_relate_lookup(self):
- return 'relate' in self.connection.ops.geometry_functions
+ def supports_bbcontains_lookup(self):
+ return 'bbcontains' in self.connection.ops.gis_terms
+
+ @property
+ def supports_contained_lookup(self):
+ return 'contained' in self.connection.ops.gis_terms
@property
- def has_dwithin_lookup(self):
+ def supports_dwithin_lookup(self):
return 'dwithin' in self.connection.ops.distance_functions
+ @property
+ def supports_relate_lookup(self):
+ return 'relate' in self.connection.ops.gis_terms
+
# For each of those methods, the class will have a property named
# `has_<name>_method` (defined in __init__) which accesses connection.ops
# to determine GIS method availability.
geoqueryset_methods = (
- 'centroid', 'difference', 'envelope', 'force_rhr', 'geohash', 'gml',
- 'intersection', 'kml', 'num_geom', 'perimeter', 'point_on_surface',
- 'reverse', 'scale', 'snap_to_grid', 'svg', 'sym_difference',
- 'transform', 'translate', 'union', 'unionagg',
+ 'area', 'centroid', 'difference', 'distance', 'distance_spheroid',
+ 'envelope', 'force_rhr', 'geohash', 'gml', 'intersection', 'kml',
+ 'length', 'num_geom', 'perimeter', 'point_on_surface', 'reverse',
+ 'scale', 'snap_to_grid', 'svg', 'sym_difference', 'transform',
+ 'translate', 'union', 'unionagg',
)
# Specifies whether the Collect and Extent aggregates are supported by the database
@@ -10,6 +10,7 @@
class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures):
has_spatialrefsys_table = False
+ supports_distances_lookups = False
supports_transform = False
supports_real_shape_operations = False
supports_null_geometries = False
@@ -11,6 +11,7 @@
class DatabaseFeatures(BaseSpatialFeatures, Psycopg2DatabaseFeatures):
+ supports_3d_functions = True
supports_left_right_lookups = True
@@ -631,8 +631,8 @@ def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, *
u, unit_name, s = get_srid_info(self.query.transformed_srid, connection)
geodetic = unit_name.lower() in geo_field.geodetic_units
- if backend.spatialite and geodetic:
- raise ValueError('SQLite does not support linear distance calculations on geodetic coordinate systems.')
+ if geodetic and not connection.features.supports_distance_geodetic:
+ raise ValueError('This database does not support linear distance calculations on geodetic coordinate systems.')
if distance:
if self.query.transformed_srid:
@@ -690,8 +690,8 @@ def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, *
# works on 3D geometries.
procedure_fmt += ",'%(spheroid)s'"
procedure_args.update({'function': backend.length_spheroid, 'spheroid': params[1]})
- elif geom_3d and backend.postgis:
- # Use 3D variants of perimeter and length routines on PostGIS.
+ elif geom_3d and connection.features.supports_3d_functions:
+ # Use 3D variants of perimeter and length routines on supported backends.
if perimeter:
procedure_args.update({'function': backend.perimeter3d})
elif length:
@@ -1,4 +1,5 @@
from django.contrib.gis.db import models
+from django.contrib.gis.tests.utils import gisfield_may_be_null
from django.utils.encoding import python_2_unicode_compatible
@@ -38,7 +39,7 @@ class CensusZipcode(NamedModel):
class SouthTexasZipcode(NamedModel):
"Model for a few South Texas ZIP codes."
- poly = models.PolygonField(srid=32140, null=True)
+ poly = models.PolygonField(srid=32140, null=gisfield_may_be_null)
class Interstate(NamedModel):
@@ -6,9 +6,7 @@
from django.db.models import Q
from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.measure import D # alias for Distance
-from django.contrib.gis.tests.utils import (
- mysql, oracle, postgis, spatialite, no_oracle
-)
+from django.contrib.gis.tests.utils import oracle, postgis, spatialite, no_oracle
from django.test import TestCase, skipUnlessDBFeature
if HAS_GEOS:
@@ -18,8 +16,6 @@
SouthTexasCity, SouthTexasCityFt, CensusZipcode, SouthTexasZipcode)
-@skipUnless(HAS_GEOS and not mysql,
- "GEOS and spatial db (not mysql) are required.")
@skipUnlessDBFeature("gis_enabled")
class DistanceTest(TestCase):
fixtures = ['initial']
@@ -50,7 +46,7 @@ def test_init(self):
self.assertEqual(1, Interstate.objects.count())
self.assertEqual(1, SouthTexasInterstate.objects.count())
- @skipUnlessDBFeature("has_dwithin_lookup")
+ @skipUnlessDBFeature("supports_dwithin_lookup")
def test_dwithin(self):
"""
Test the `dwithin` lookup type.
@@ -99,6 +95,7 @@ def test_dwithin(self):
else:
self.assertListEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))))
+ @skipUnlessDBFeature("has_distance_method")
def test_distance_projected(self):
"""
Test the `distance` GeoQuerySet method on projected coordinate systems.
@@ -139,7 +136,7 @@ def test_distance_projected(self):
self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
- @skipUnlessDBFeature("supports_distance_geodetic")
+ @skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
def test_distance_geodetic(self):
"""
Test the `distance` GeoQuerySet method on geodetic coordinate systems.
@@ -149,16 +146,16 @@ def test_distance_geodetic(self):
# Testing geodetic distance calculation with a non-point geometry
# (a LineString of Wollongong and Shellharbour coords).
ls = LineString(((150.902, -34.4245), (150.87, -34.5789)))
- if oracle or postgis:
- # Reference query:
- # SELECT ST_distance_sphere(point, ST_GeomFromText('LINESTRING(150.9020 -34.4245,150.8700 -34.5789)', 4326)) FROM distapp_australiacity ORDER BY name;
- distances = [1120954.92533513, 140575.720018241, 640396.662906304,
- 60580.9693849269, 972807.955955075, 568451.8357838,
- 40435.4335201384, 0, 68272.3896586844, 12375.0643697706, 0]
- qs = AustraliaCity.objects.distance(ls).order_by('name')
- for city, distance in zip(qs, distances):
- # Testing equivalence to within a meter.
- self.assertAlmostEqual(distance, city.distance.m, 0)
+
+ # Reference query:
+ # SELECT ST_distance_sphere(point, ST_GeomFromText('LINESTRING(150.9020 -34.4245,150.8700 -34.5789)', 4326)) FROM distapp_australiacity ORDER BY name;
+ distances = [1120954.92533513, 140575.720018241, 640396.662906304,
+ 60580.9693849269, 972807.955955075, 568451.8357838,
+ 40435.4335201384, 0, 68272.3896586844, 12375.0643697706, 0]
+ qs = AustraliaCity.objects.distance(ls).order_by('name')
+ for city, distance in zip(qs, distances):
+ # Testing equivalence to within a meter.
+ self.assertAlmostEqual(distance, city.distance.m, 0)
# Got the reference distances using the raw SQL statements:
# 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));
@@ -197,6 +194,7 @@ def test_distance_geodetic(self):
self.assertAlmostEqual(sphere_distances[i], c.distance.m, tol)
@no_oracle # Oracle already handles geographic distance calculation.
+ @skipUnlessDBFeature("has_distance_method")
def test_distance_transform(self):
"""
Test the `distance` GeoQuerySet method used with `transform` on a geographic field.
@@ -226,6 +224,7 @@ def test_distance_transform(self):
for i, z in enumerate(qs):
self.assertAlmostEqual(z.distance.m, dists_m[i], 5)
+ @skipUnlessDBFeature("supports_distances_lookups")
def test_distance_lookups(self):
"""
Test the `distance_lt`, `distance_gt`, `distance_lte`, and `distance_gte` lookup types.
@@ -255,6 +254,7 @@ def test_distance_lookups(self):
qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300)))
self.assertEqual(['77002', '77025', '77401'], self.get_names(qs))
+ @skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic")
def test_geodetic_distance_lookups(self):
"""
Test distance lookups on geodetic coordinate systems.
@@ -264,23 +264,11 @@ def test_geodetic_distance_lookups(self):
line = GEOSGeometry('LINESTRING(144.9630 -37.8143,151.2607 -33.8870)', 4326)
dist_qs = AustraliaCity.objects.filter(point__distance_lte=(line, D(km=100)))
- if oracle or postgis:
- # Oracle and PostGIS can do distance lookups on arbitrary geometries.
- self.assertEqual(9, dist_qs.count())
- self.assertEqual(['Batemans Bay', 'Canberra', 'Hillsdale',
- 'Melbourne', 'Mittagong', 'Shellharbour',
- 'Sydney', 'Thirroul', 'Wollongong'],
- self.get_names(dist_qs))
- else:
- # spatialite only allow geodetic distance queries (utilizing
- # ST_Distance_Sphere/ST_Distance_Spheroid) from Points to PointFields
- # on geometry columns.
- self.assertRaises(ValueError, dist_qs.count)
-
- # Ensured that a ValueError was raised, none of the rest of the test is
- # support on this backend, so bail now.
- if spatialite:
- return
+ self.assertEqual(9, dist_qs.count())
+ self.assertEqual(['Batemans Bay', 'Canberra', 'Hillsdale',
+ 'Melbourne', 'Mittagong', 'Shellharbour',
+ 'Sydney', 'Thirroul', 'Wollongong'],
+ self.get_names(dist_qs))
# Too many params (4 in this case) should raise a ValueError.
self.assertRaises(ValueError, len,
@@ -309,18 +297,18 @@ def test_geodetic_distance_lookups(self):
# Geodetic distance lookup but telling GeoDjango to use `distance_spheroid`
# instead (we should get the same results b/c accuracy variance won't matter
# in this test case).
- if postgis:
+ querysets = [qs1]
+ if connection.features.has_distance_spheroid_method:
gq3 = Q(point__distance_lte=(wollongong.point, d1, 'spheroid'))
gq4 = Q(point__distance_gte=(wollongong.point, d2, 'spheroid'))
qs2 = AustraliaCity.objects.exclude(name='Wollongong').filter(gq3 | gq4)
- querysets = [qs1, qs2]
- else:
- querysets = [qs1]
+ querysets.append(qs2)
for qs in querysets:
cities = self.get_names(qs)
self.assertEqual(cities, ['Adelaide', 'Hobart', 'Shellharbour', 'Thirroul'])
+ @skipUnlessDBFeature("has_area_method")
def test_area(self):
"""
Test the `area` GeoQuerySet method.
@@ -333,6 +321,7 @@ def test_area(self):
for i, z in enumerate(SouthTexasZipcode.objects.order_by('name').area()):
self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol)
+ @skipUnlessDBFeature("has_length_method")
def test_length(self):
"""
Test the `length` GeoQuerySet method.
@@ -342,13 +331,13 @@ def test_length(self):
len_m1 = 473504.769553813
len_m2 = 4617.668
- if spatialite:
- # Does not support geodetic coordinate systems.
- self.assertRaises(ValueError, Interstate.objects.length)
- else:
+ if connection.features.supports_distance_geodetic:
qs = Interstate.objects.length()
tol = 2 if oracle else 3
self.assertAlmostEqual(len_m1, qs[0].length.m, tol)
+ else:
+ # Does not support geodetic coordinate systems.
+ self.assertRaises(ValueError, Interstate.objects.length)
# Now doing length on a projected coordinate system.
i10 = SouthTexasInterstate.objects.length().get(name='I-10')
@@ -370,6 +359,7 @@ def test_perimeter(self):
for i, c in enumerate(SouthTexasCity.objects.perimeter(model_att='perim')):
self.assertEqual(0, c.perim.m)
+ @skipUnlessDBFeature("has_area_method", "has_distance_method")
def test_measurement_null_fields(self):
"""
Test the measurement GeoQuerySet methods on fields with NULL values.
@@ -6,8 +6,7 @@
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geos import HAS_GEOS
-from django.contrib.gis.tests.utils import postgis
-from django.test import TestCase
+from django.test import TestCase, skipUnlessDBFeature
from django.utils._os import upath
if HAS_GEOS:
@@ -62,15 +61,16 @@
)
-@skipUnless(HAS_GEOS and HAS_GDAL and postgis, "Geos, GDAL and postgis are required.")
+@skipUnless(HAS_GDAL, "GDAL is required for Geo3DTest.")
+@skipUnlessDBFeature("gis_enabled", "supports_3d_functions")
class Geo3DTest(TestCase):
"""
Only a subset of the PostGIS routines are 3D-enabled, and this TestCase
tries to test the features that can handle 3D and that are also
available within GeoDjango. For more information, see the PostGIS docs
on the routines that support 3D:
- http://postgis.refractions.net/documentation/manual-1.5/ch08.html#PostGIS_3D_Functions
+ http://postgis.net/docs/PostGIS_Special_Functions_Index.html#PostGIS_3D_Functions
"""
def _load_interstate_data(self):
@@ -1,10 +1,7 @@
from django.contrib.gis.db import models
-from django.contrib.gis.tests.utils import mysql
+from django.contrib.gis.tests.utils import gisfield_may_be_null
from django.utils.encoding import python_2_unicode_compatible
-# MySQL spatial indices can't handle NULL geometries.
-null_flag = not mysql
-
@python_2_unicode_compatible
class NamedModel(models.Model):
@@ -42,7 +39,7 @@ class Meta:
class State(NamedModel):
- poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here.
+ poly = models.PolygonField(null=gisfield_may_be_null) # Allowing NULL geometries here.
class Track(NamedModel):
Oops, something went wrong. Retry.

0 comments on commit 60428ed

Please sign in to comment.