summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2014-11-03 22:03:59 +0100
committerClaude Paroz <claude@2xlibre.net>2015-03-18 19:27:44 +0100
commit65129aac07022f23afa1df7ec7fad2216634cb38 (patch)
treecabff468510d5a964dfaec06ce71205455a5bdeb
parentb4a56ed4f55502239cb11b57f0fa75baa0a97640 (diff)
Fixed #23757 -- Added 3D introspection support to Spatialite backend
Thanks Tim Graham for the review.
-rw-r--r--django/contrib/gis/db/backends/base/features.py2
-rw-r--r--django/contrib/gis/db/backends/postgis/features.py1
-rw-r--r--django/contrib/gis/db/backends/spatialite/features.py5
-rw-r--r--django/contrib/gis/db/backends/spatialite/introspection.py10
-rw-r--r--tests/gis_tests/geo3d/tests.py8
-rw-r--r--tests/gis_tests/inspectapp/models.py8
-rw-r--r--tests/gis_tests/inspectapp/tests.py27
7 files changed, 55 insertions, 6 deletions
diff --git a/django/contrib/gis/db/backends/base/features.py b/django/contrib/gis/db/backends/base/features.py
index ecefde493b..13da494814 100644
--- a/django/contrib/gis/db/backends/base/features.py
+++ b/django/contrib/gis/db/backends/base/features.py
@@ -14,6 +14,8 @@ class BaseSpatialFeatures(object):
# Does the backend introspect GeometryField to its subtypes?
supports_geometry_field_introspection = True
+ # Does the backend support storing 3D geometries?
+ supports_3d_storage = False
# Reference implementation of 3D functions is:
# http://postgis.net/docs/PostGIS_Special_Functions_Index.html#PostGIS_3D_Functions
supports_3d_functions = False
diff --git a/django/contrib/gis/db/backends/postgis/features.py b/django/contrib/gis/db/backends/postgis/features.py
index 1cc15bde38..181c859641 100644
--- a/django/contrib/gis/db/backends/postgis/features.py
+++ b/django/contrib/gis/db/backends/postgis/features.py
@@ -4,5 +4,6 @@ from django.db.backends.postgresql_psycopg2.features import \
class DatabaseFeatures(BaseSpatialFeatures, Psycopg2DatabaseFeatures):
+ supports_3d_storage = True
supports_3d_functions = True
supports_left_right_lookups = True
diff --git a/django/contrib/gis/db/backends/spatialite/features.py b/django/contrib/gis/db/backends/spatialite/features.py
index 613087b51e..5bc4b88de8 100644
--- a/django/contrib/gis/db/backends/spatialite/features.py
+++ b/django/contrib/gis/db/backends/spatialite/features.py
@@ -1,4 +1,5 @@
from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
+from django.contrib.gis.geos import geos_version
from django.db.backends.sqlite3.features import \
DatabaseFeatures as SQLiteDatabaseFeatures
from django.utils.functional import cached_property
@@ -15,3 +16,7 @@ class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures):
# which can result in a significant performance improvement when
# creating the database.
return self.connection.ops.spatial_version >= (4, 1, 0)
+
+ @cached_property
+ def supports_3d_storage(self):
+ return geos_version() >= '3.3'
diff --git a/django/contrib/gis/db/backends/spatialite/introspection.py b/django/contrib/gis/db/backends/spatialite/introspection.py
index c1ed7629b9..4222c04f8b 100644
--- a/django/contrib/gis/db/backends/spatialite/introspection.py
+++ b/django/contrib/gis/db/backends/spatialite/introspection.py
@@ -41,7 +41,13 @@ class SpatiaLiteIntrospection(DatabaseIntrospection):
# OGRGeomType does not require GDAL and makes it easy to convert
# from OGC geom type name to Django field.
- field_type = OGRGeomType(row[2]).django
+ ogr_type = row[2]
+ if isinstance(ogr_type, six.integer_types) and ogr_type > 1000:
+ # Spatialite versions >= 4 use the new SFSQL 1.2 offsets
+ # 1000 (Z), 2000 (M), and 3000 (ZM) to indicate the presence of
+ # higher dimensional coordinates (M not yet supported by Django).
+ ogr_type = ogr_type % 1000 + OGRGeomType.wkb25bit
+ field_type = OGRGeomType(ogr_type).django
# Getting any GeometryField keyword arguments that are not the default.
dim = row[0]
@@ -49,7 +55,7 @@ class SpatiaLiteIntrospection(DatabaseIntrospection):
field_params = {}
if srid != 4326:
field_params['srid'] = srid
- if isinstance(dim, six.string_types) and 'Z' in dim:
+ if (isinstance(dim, six.string_types) and 'Z' in dim) or dim == 3:
field_params['dim'] = 3
finally:
cursor.close()
diff --git a/tests/gis_tests/geo3d/tests.py b/tests/gis_tests/geo3d/tests.py
index 022a8063ab..b05201d0bf 100644
--- a/tests/gis_tests/geo3d/tests.py
+++ b/tests/gis_tests/geo3d/tests.py
@@ -74,7 +74,7 @@ bbox_data = (
@skipUnless(HAS_GDAL, "GDAL is required for Geo3DTest.")
-@skipUnlessDBFeature("gis_enabled", "supports_3d_functions")
+@skipUnlessDBFeature("gis_enabled", "supports_3d_storage")
class Geo3DTest(TestCase):
"""
Only a subset of the PostGIS routines are 3D-enabled, and this TestCase
@@ -189,6 +189,7 @@ class Geo3DTest(TestCase):
ref_json_regex = re.compile(r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$')
self.assertTrue(ref_json_regex.match(h.geojson))
+ @skipUnlessDBFeature("supports_3d_functions")
def test_union(self):
"""
Testing the Union aggregate of 3D models.
@@ -207,6 +208,7 @@ class Geo3DTest(TestCase):
# Ordering of points in the resulting geometry may vary between implementations
self.assertSetEqual({p.ewkt for p in ref_union}, {p.ewkt for p in union})
+ @skipUnlessDBFeature("supports_3d_functions")
@ignore_warnings(category=RemovedInDjango20Warning)
def test_extent(self):
"""
@@ -227,6 +229,7 @@ class Geo3DTest(TestCase):
self.assertIsNone(City3D.objects.none().extent3d())
self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d'])
+ @skipUnlessDBFeature("supports_3d_functions")
def test_perimeter(self):
"""
Testing GeoQuerySet.perimeter() on 3D fields.
@@ -244,6 +247,7 @@ class Geo3DTest(TestCase):
Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m,
tol)
+ @skipUnlessDBFeature("supports_3d_functions")
def test_length(self):
"""
Testing GeoQuerySet.length() on 3D fields.
@@ -276,6 +280,7 @@ class Geo3DTest(TestCase):
InterstateProj3D.objects.length().get(name='I-45').length.m,
tol)
+ @skipUnlessDBFeature("supports_3d_functions")
def test_scale(self):
"""
Testing GeoQuerySet.scale() on Z values.
@@ -287,6 +292,7 @@ class Geo3DTest(TestCase):
for city in City3D.objects.scale(1.0, 1.0, zscale):
self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z)
+ @skipUnlessDBFeature("supports_3d_functions")
def test_translate(self):
"""
Testing GeoQuerySet.translate() on Z values.
diff --git a/tests/gis_tests/inspectapp/models.py b/tests/gis_tests/inspectapp/models.py
index a649605487..fd343b7803 100644
--- a/tests/gis_tests/inspectapp/models.py
+++ b/tests/gis_tests/inspectapp/models.py
@@ -14,3 +14,11 @@ class AllOGRFields(models.Model):
point = models.PointField()
objects = models.GeoManager()
+
+
+class Fields3D(models.Model):
+ point = models.PointField(dim=3)
+ line = models.LineStringField(dim=3)
+ poly = models.PolygonField(dim=3)
+
+ objects = models.GeoManager()
diff --git a/tests/gis_tests/inspectapp/tests.py b/tests/gis_tests/inspectapp/tests.py
index 19d5aa054c..c40ffaa7e8 100644
--- a/tests/gis_tests/inspectapp/tests.py
+++ b/tests/gis_tests/inspectapp/tests.py
@@ -27,9 +27,11 @@ class InspectDbTests(TestCase):
Test the geo-enabled inspectdb command.
"""
out = StringIO()
- call_command('inspectdb',
- table_name_filter=lambda tn: tn.startswith('inspectapp_'),
- stdout=out)
+ call_command(
+ 'inspectdb',
+ table_name_filter=lambda tn: tn == 'inspectapp_allogrfields',
+ stdout=out
+ )
output = out.getvalue()
if connection.features.supports_geometry_field_introspection:
self.assertIn('geom = models.PolygonField()', output)
@@ -39,6 +41,25 @@ class InspectDbTests(TestCase):
self.assertIn('point = models.GeometryField(', output)
self.assertIn('objects = models.GeoManager()', output)
+ @skipUnlessDBFeature("supports_3d_storage")
+ def test_3d_columns(self):
+ out = StringIO()
+ call_command(
+ 'inspectdb',
+ table_name_filter=lambda tn: tn == 'inspectapp_fields3d',
+ stdout=out
+ )
+ output = out.getvalue()
+ if connection.features.supports_geometry_field_introspection:
+ self.assertIn('point = models.PointField(dim=3)', output)
+ self.assertIn('line = models.LineStringField(dim=3)', output)
+ self.assertIn('poly = models.PolygonField(dim=3)', output)
+ else:
+ self.assertIn('point = models.GeometryField(', output)
+ self.assertIn('line = models.GeometryField(', output)
+ self.assertIn('poly = models.GeometryField(', output)
+ self.assertIn('objects = models.GeoManager()', output)
+
@skipUnless(HAS_GDAL, "OGRInspectTest needs GDAL support")
@skipUnlessDBFeature("gis_enabled")