Browse Source

Fixed #32575 -- Added support for SpatiaLite 5.

Claude Paroz 4 years ago
parent
commit
30e123ed35

+ 4 - 1
django/contrib/gis/db/backends/spatialite/base.py

@@ -71,4 +71,7 @@ class DatabaseWrapper(SQLiteDatabaseWrapper):
         with self.cursor() as cursor:
             cursor.execute("PRAGMA table_info(geometry_columns);")
             if cursor.fetchall() == []:
-                cursor.execute("SELECT InitSpatialMetaData(1)")
+                if self.ops.spatial_version < (5,):
+                    cursor.execute('SELECT InitSpatialMetaData(1)')
+                else:
+                    cursor.execute('SELECT InitSpatialMetaDataFull(1)')

+ 1 - 1
django/contrib/gis/db/backends/spatialite/features.py

@@ -11,7 +11,7 @@ class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures):
 
     @cached_property
     def supports_area_geodetic(self):
-        return bool(self.connection.ops.lwgeom_version())
+        return bool(self.connection.ops.geom_lib_version())
 
     @cached_property
     def django_test_skips(self):

+ 15 - 1
django/contrib/gis/db/backends/spatialite/operations.py

@@ -81,7 +81,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
     @cached_property
     def unsupported_functions(self):
         unsupported = {'BoundingCircle', 'GeometryDistance', 'MemSize'}
-        if not self.lwgeom_version():
+        if not self.geom_lib_version():
             unsupported |= {'Azimuth', 'GeoHash', 'MakeValid'}
         return unsupported
 
@@ -167,6 +167,20 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
         """Return the version of LWGEOM library used by SpatiaLite."""
         return self._get_spatialite_func('lwgeom_version()')
 
+    def rttopo_version(self):
+        """Return the version of RTTOPO library used by SpatiaLite."""
+        return self._get_spatialite_func('rttopo_version()')
+
+    def geom_lib_version(self):
+        """
+        Return the version of the version-dependant geom library used by
+        SpatiaLite.
+        """
+        if self.spatial_version >= (5,):
+            return self.rttopo_version()
+        else:
+            return self.lwgeom_version()
+
     def spatialite_version(self):
         "Return the SpatiaLite library version as a string."
         return self._get_spatialite_func('spatialite_version()')

+ 6 - 6
docs/ref/contrib/gis/db-api.txt

@@ -344,9 +344,9 @@ functions are available on each spatial backend.
 
 .. currentmodule:: django.contrib.gis.db.models.functions
 
-====================================  =======  ============== ============ =========== ==========
+====================================  =======  ============== ============ =========== =================
 Function                              PostGIS  Oracle         MariaDB      MySQL       SpatiaLite
-====================================  =======  ============== ============ =========== ==========
+====================================  =======  ============== ============ =========== =================
 :class:`Area`                         X        X              X            X           X
 :class:`AsGeoJSON`                    X        X              X (≥ 10.2.4) X (≥ 5.7.5) X
 :class:`AsGML`                        X        X                                       X
@@ -354,19 +354,19 @@ Function                              PostGIS  Oracle         MariaDB      MySQL
 :class:`AsSVG`                        X                                                X
 :class:`AsWKB`                        X        X              X            X           X
 :class:`AsWKT`                        X        X              X            X           X
-:class:`Azimuth`                      X                                                X (LWGEOM)
+:class:`Azimuth`                      X                                                X (LWGEOM/RTTOPO)
 :class:`BoundingCircle`               X        X
 :class:`Centroid`                     X        X              X            X           X
 :class:`Difference`                   X        X              X            X           X
 :class:`Distance`                     X        X              X            X           X
 :class:`Envelope`                     X        X              X            X           X
 :class:`ForcePolygonCW`               X                                                X
-:class:`GeoHash`                      X                                    X (≥ 5.7.5) X (LWGEOM)
+:class:`GeoHash`                      X                                    X (≥ 5.7.5) X (LWGEOM/RTTOPO)
 :class:`Intersection`                 X        X              X            X           X
 :class:`IsValid`                      X        X                           X (≥ 5.7.5) X
 :class:`Length`                       X        X              X            X           X
 :class:`LineLocatePoint`              X                                                X
-:class:`MakeValid`                    X                                                X (LWGEOM)
+:class:`MakeValid`                    X                                                X (LWGEOM/RTTOPO)
 :class:`MemSize`                      X
 :class:`NumGeometries`                X        X              X            X           X
 :class:`NumPoints`                    X        X              X            X           X
@@ -379,7 +379,7 @@ Function                              PostGIS  Oracle         MariaDB      MySQL
 :class:`Transform`                    X        X                                       X
 :class:`Translate`                    X                                                X
 :class:`Union`                        X        X              X            X           X
-====================================  =======  ============== ============ =========== ==========
+====================================  =======  ============== ============ =========== =================
 
 Aggregate Functions
 -------------------

+ 5 - 4
docs/ref/contrib/gis/functions.txt

@@ -44,7 +44,7 @@ Oracle, `PostGIS <https://postgis.net/docs/ST_Area.html>`__, SpatiaLite
 Accepts a single geographic field or expression and returns the area of the
 field as an :class:`~django.contrib.gis.measure.Area` measure.
 
-MySQL and SpatiaLite without LWGEOM don't support area calculations on
+MySQL and SpatiaLite without LWGEOM/RTTOPO don't support area calculations on
 geographic SRSes.
 
 ``AsGeoJSON``
@@ -208,7 +208,7 @@ __ https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry
 .. class:: Azimuth(point_a, point_b, **extra)
 
 *Availability*: `PostGIS <https://postgis.net/docs/ST_Azimuth.html>`__,
-SpatiaLite (LWGEOM)
+SpatiaLite (LWGEOM/RTTOPO)
 
 Returns the azimuth in radians of the segment defined by the given point
 geometries, or ``None`` if the two points are coincident. The azimuth is angle
@@ -334,7 +334,8 @@ are returned unchanged.
 
 *Availability*: `MySQL
 <https://dev.mysql.com/doc/refman/en/spatial-geohash-functions.html#function_st-geohash>`__ (≥ 5.7.5),
-`PostGIS <https://postgis.net/docs/ST_GeoHash.html>`__, SpatiaLite (LWGEOM)
+`PostGIS <https://postgis.net/docs/ST_GeoHash.html>`__, SpatiaLite
+(LWGEOM/RTTOPO)
 
 Accepts a single geographic field or expression and returns a `GeoHash`__
 representation of the geometry.
@@ -416,7 +417,7 @@ Returns a float between 0 and 1 representing the location of the closest point o
 .. class:: MakeValid(expr)
 
 *Availability*: `PostGIS <https://postgis.net/docs/ST_MakeValid.html>`__,
-SpatiaLite (LWGEOM)
+SpatiaLite (LWGEOM/RTTOPO)
 
 Accepts a geographic field or expression and attempts to convert the value into
 a valid geometry without losing any of the input vertices. Geometries that are

+ 2 - 1
docs/ref/contrib/gis/install/geolibs.txt

@@ -13,7 +13,7 @@ Program                   Description                           Required
 :doc:`GDAL <../gdal>`     Geospatial Data Abstraction Library   Yes                               3.2, 3.1, 3.0, 2.4, 2.3, 2.2, 2.1
 :doc:`GeoIP <../geoip2>`  IP-based geolocation library          No                                2
 `PostGIS`__               Spatial extensions for PostgreSQL     Yes (PostgreSQL only)             3.0, 2.5, 2.4
-`SpatiaLite`__            Spatial extensions for SQLite         Yes (SQLite only)                 4.3
+`SpatiaLite`__            Spatial extensions for SQLite         Yes (SQLite only)                 5.0, 4.3
 ========================  ====================================  ================================  =================================
 
 Note that older or more recent versions of these libraries *may* also work
@@ -36,6 +36,7 @@ totally fine with GeoDjango. Your mileage may vary.
     PostGIS 2.5.0 2018-09-23
     PostGIS 3.0.0 2019-10-20
     SpatiaLite 4.3.0 2015-09-07
+    SpatiaLite 5.0.0 2020-08-23
 
 .. note::
 

+ 1 - 1
docs/releases/4.0.txt

@@ -100,7 +100,7 @@ Minor features
 :mod:`django.contrib.gis`
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* Added support for SpatiaLite 5.
 
 :mod:`django.contrib.messages`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 2 - 6
tests/gis_tests/distapp/tests.py

@@ -367,16 +367,12 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase):
         dist2 = SouthTexasCityFt.objects.annotate(distance=Distance('point', lagrange)).order_by('id')
         dist_qs = [dist1, dist2]
 
-        # Original query done on PostGIS, have to adjust AlmostEqual tolerance
-        # for Oracle.
-        tol = 2 if connection.ops.oracle else 5
-
         # Ensuring expected distances are returned for each distance queryset.
         for qs in dist_qs:
             for i, c in enumerate(qs):
                 with self.subTest(c=c):
-                    self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
-                    self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
+                    self.assertAlmostEqual(m_distances[i], c.distance.m, -1)
+                    self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, -1)
 
     @skipUnlessDBFeature("has_Distance_function", "supports_distance_geodetic")
     def test_distance_geodetic(self):

+ 5 - 1
tests/gis_tests/geoapp/test_functions.py

@@ -183,7 +183,11 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
     def test_azimuth(self):
         # Returns the azimuth in radians.
         azimuth_expr = functions.Azimuth(Point(0, 0, srid=4326), Point(1, 1, srid=4326))
-        self.assertAlmostEqual(City.objects.annotate(azimuth=azimuth_expr).first().azimuth, math.pi / 4)
+        self.assertAlmostEqual(
+            City.objects.annotate(azimuth=azimuth_expr).first().azimuth,
+            math.pi / 4,
+            places=2,
+        )
         # Returns None if the two points are coincident.
         azimuth_expr = functions.Azimuth(Point(0, 0, srid=4326), Point(0, 0, srid=4326))
         self.assertIsNone(City.objects.annotate(azimuth=azimuth_expr).first().azimuth)

+ 6 - 3
tests/gis_tests/geogapp/tests.py

@@ -111,9 +111,12 @@ class GeographyFunctionTests(FuncTestMixin, TestCase):
         if connection.ops.oracle:
             ref_dists = [0, 4899.68, 8081.30, 9115.15]
         elif connection.ops.spatialite:
-            # SpatiaLite returns non-zero distance for polygons and points
-            # covered by that polygon.
-            ref_dists = [326.61, 4899.68, 8081.30, 9115.15]
+            if connection.ops.spatial_version < (5,):
+                # SpatiaLite < 5 returns non-zero distance for polygons and points
+                # covered by that polygon.
+                ref_dists = [326.61, 4899.68, 8081.30, 9115.15]
+            else:
+                ref_dists = [0, 4899.68, 8081.30, 9115.15]
         else:
             ref_dists = [0, 4891.20, 8071.64, 9123.95]
         htown = City.objects.get(name='Houston')