Commit 65129aac authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #23757 -- Added 3D introspection support to Spatialite backend

Thanks Tim Graham for the review.
parent b4a56ed4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
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'
+8 −2
Original line number Diff line number Diff line
@@ -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()
+7 −1
Original line number Diff line number Diff line
@@ -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.
Loading