Sfoglia il codice sorgente

Fixed #27602 -- Added Oracle support for BoundingCircle GIS function.

Sergey Fedoseev 8 anni fa
parent
commit
38a6df555f

+ 11 - 5
django/contrib/gis/db/backends/oracle/operations.py

@@ -18,6 +18,7 @@ from django.contrib.gis.geometry.backend import Geometry
 from django.contrib.gis.measure import Distance
 from django.db.backends.oracle.operations import DatabaseOperations
 from django.utils import six
+from django.utils.functional import cached_property
 
 DEFAULT_TOLERANCE = '0.05'
 
@@ -85,6 +86,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
 
     function_names = {
         'Area': 'SDO_GEOM.SDO_AREA',
+        'BoundingCircle': 'SDO_GEOM.SDO_MBC',
         'Centroid': 'SDO_GEOM.SDO_CENTROID',
         'Difference': 'SDO_GEOM.SDO_DIFFERENCE',
         'Distance': 'SDO_GEOM.SDO_DISTANCE',
@@ -131,11 +133,15 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
 
     truncate_params = {'relate': None}
 
-    unsupported_functions = {
-        'AsGeoJSON', 'AsKML', 'AsSVG', 'BoundingCircle', 'Envelope',
-        'ForceRHR', 'GeoHash', 'MakeValid', 'MemSize', 'Scale',
-        'SnapToGrid', 'Translate',
-    }
+    @cached_property
+    def unsupported_functions(self):
+        unsupported = {
+            'AsGeoJSON', 'AsKML', 'AsSVG', 'Envelope', 'ForceRHR', 'GeoHash',
+            'MakeValid', 'MemSize', 'Scale', 'SnapToGrid', 'Translate',
+        }
+        if self.connection.oracle_full_version < '12.1.0.2':
+            unsupported.add('BoundingCircle')
+        return unsupported
 
     def geo_quote_name(self, name):
         return super(OracleOperations, self).geo_quote_name(name).upper()

+ 6 - 1
django/contrib/gis/db/models/functions.py

@@ -201,10 +201,15 @@ class AsSVG(GeoFunc):
         super(AsSVG, self).__init__(*expressions, **extra)
 
 
-class BoundingCircle(GeoFunc):
+class BoundingCircle(OracleToleranceMixin, GeoFunc):
     def __init__(self, expression, num_seg=48, **extra):
         super(BoundingCircle, self).__init__(*[expression, num_seg], **extra)
 
+    def as_oracle(self, compiler, connection):
+        clone = self.copy()
+        clone.set_source_expressions([self.get_source_expressions()[0]])
+        return super(BoundingCircle, clone).as_oracle(compiler, connection)
+
 
 class Centroid(OracleToleranceMixin, GeoFunc):
     arity = 1

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

@@ -374,38 +374,38 @@ Database functions
 The following table provides a summary of what geography-specific database
 functions are available on each spatial backend.
 
-====================================  =======  ======  ===========  ==========
-Function                              PostGIS  Oracle  MySQL        SpatiaLite
-====================================  =======  ======  ===========  ==========
-:class:`Area`                         X        X       X            X
-:class:`AsGeoJSON`                    X                             X
-:class:`AsGML`                        X        X                    X
-:class:`AsKML`                        X                             X
-:class:`AsSVG`                        X                             X
-:class:`BoundingCircle`               X
-:class:`Centroid`                     X        X       X            X
-:class:`Difference`                   X        X       X (≥ 5.6.1)  X
-:class:`Distance`                     X        X       X (≥ 5.6.1)  X
-:class:`Envelope`                     X                X            X
+====================================  =======  ==============  ===========  ==========
+Function                              PostGIS  Oracle          MySQL        SpatiaLite
+====================================  =======  ==============  ===========  ==========
+:class:`Area`                         X        X               X            X
+:class:`AsGeoJSON`                    X                                     X
+:class:`AsGML`                        X        X                            X
+:class:`AsKML`                        X                                     X
+:class:`AsSVG`                        X                                     X
+:class:`BoundingCircle`               X        X (≥ 12.1.0.2)
+:class:`Centroid`                     X        X               X            X
+:class:`Difference`                   X        X               X (≥ 5.6.1)  X
+:class:`Distance`                     X        X               X (≥ 5.6.1)  X
+:class:`Envelope`                     X                        X            X
 :class:`ForceRHR`                     X
-:class:`GeoHash`                      X                             X (LWGEOM)
-:class:`Intersection`                 X        X       X (≥ 5.6.1)  X
-:class:`IsValid`                      X        X                    X (LWGEOM)
-:class:`Length`                       X        X       X            X
-:class:`MakeValid`                    X                             X (LWGEOM)
+:class:`GeoHash`                      X                                     X (LWGEOM)
+:class:`Intersection`                 X        X               X (≥ 5.6.1)  X
+:class:`IsValid`                      X        X                            X (LWGEOM)
+:class:`Length`                       X        X               X            X
+:class:`MakeValid`                    X                                     X (LWGEOM)
 :class:`MemSize`                      X
-:class:`NumGeometries`                X        X       X            X
-:class:`NumPoints`                    X        X       X            X
-:class:`Perimeter`                    X        X                    X
-:class:`PointOnSurface`               X        X                    X
-:class:`Reverse`                      X        X                    X
-:class:`Scale`                        X                             X
-:class:`SnapToGrid`                   X                             X
-:class:`SymDifference`                X        X       X (≥ 5.6.1)  X
-:class:`Transform`                    X        X                    X
-:class:`Translate`                    X                             X
-:class:`Union`                        X        X       X (≥ 5.6.1)  X
-====================================  =======  ======  ===========  ==========
+:class:`NumGeometries`                X        X               X            X
+:class:`NumPoints`                    X        X               X            X
+:class:`Perimeter`                    X        X                            X
+:class:`PointOnSurface`               X        X                            X
+:class:`Reverse`                      X        X                            X
+:class:`Scale`                        X                                     X
+:class:`SnapToGrid`                   X                                     X
+:class:`SymDifference`                X        X               X (≥ 5.6.1)  X
+:class:`Transform`                    X        X                            X
+:class:`Translate`                    X                                     X
+:class:`Union`                        X        X               X (≥ 5.6.1)  X
+====================================  =======  ==============  ===========  ==========
 
 Aggregate Functions
 -------------------

+ 8 - 1
docs/ref/contrib/gis/functions.txt

@@ -165,11 +165,18 @@ __ http://www.w3.org/Graphics/SVG/
 
 .. class:: BoundingCircle(expression, num_seg=48, **extra)
 
-*Availability*: `PostGIS <http://postgis.net/docs/ST_MinimumBoundingCircle.html>`__
+*Availability*: `PostGIS <http://postgis.net/docs/ST_MinimumBoundingCircle.html>`__,
+`Oracle (≥ 12.1.0.2) <https://docs.oracle.com/database/121/SPATL/GUID-82A61626-BB64-4793-B53D-A0DBEC91831A.htm#SPATL1554>`_
 
 Accepts a single geographic field or expression and returns the smallest circle
 polygon that can fully contain the geometry.
 
+The ``num_seg`` parameter is used only on PostGIS.
+
+.. versionchanged:: 1.11
+
+    Oracle support was added.
+
 ``Centroid``
 ============
 

+ 1 - 0
docs/releases/1.11.txt

@@ -165,6 +165,7 @@ Minor features
 
 * Added Oracle support for the
   :class:`~django.contrib.gis.db.models.functions.AsGML` function,
+  :class:`~django.contrib.gis.db.models.functions.BoundingCircle` function,
   :class:`~django.contrib.gis.db.models.functions.IsValid` function, and
   :lookup:`isvalid` lookup.
 

+ 17 - 13
tests/gis_tests/geoapp/test_functions.py

@@ -11,7 +11,7 @@ from django.db.models import Sum
 from django.test import TestCase, skipUnlessDBFeature
 from django.utils import six
 
-from ..utils import mysql, oracle, spatialite
+from ..utils import mysql, oracle, postgis, spatialite
 from .models import City, Country, CountryWebMercator, State, Track
 
 
@@ -148,21 +148,25 @@ class GISFunctionsTests(TestCase):
             # num_seg is the number of segments per quarter circle.
             return (4 * num_seg) + 1
 
-        # The weak precision in the assertions is because the BoundingCircle
-        # calculation changed on PostGIS 2.3.
+        expected_areas = (169, 136) if postgis else (171, 126)
         qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly')).order_by('name')
-        self.assertAlmostEqual(qs[0].circle.area, 169, 0)
-        self.assertAlmostEqual(qs[1].circle.area, 136, 0)
-        # By default num_seg=48.
-        self.assertEqual(qs[0].circle.num_points, circle_num_points(48))
-        self.assertEqual(qs[1].circle.num_points, circle_num_points(48))
+        self.assertAlmostEqual(qs[0].circle.area, expected_areas[0], 0)
+        self.assertAlmostEqual(qs[1].circle.area, expected_areas[1], 0)
+        if postgis:
+            # By default num_seg=48.
+            self.assertEqual(qs[0].circle.num_points, circle_num_points(48))
+            self.assertEqual(qs[1].circle.num_points, circle_num_points(48))
 
         qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly', num_seg=12)).order_by('name')
-        self.assertGreater(qs[0].circle.area, 168.4, 0)
-        self.assertLess(qs[0].circle.area, 169.5, 0)
-        self.assertAlmostEqual(qs[1].circle.area, 136, 0)
-        self.assertEqual(qs[0].circle.num_points, circle_num_points(12))
-        self.assertEqual(qs[1].circle.num_points, circle_num_points(12))
+        if postgis:
+            self.assertGreater(qs[0].circle.area, 168.4, 0)
+            self.assertLess(qs[0].circle.area, 169.5, 0)
+            self.assertAlmostEqual(qs[1].circle.area, 136, 0)
+            self.assertEqual(qs[0].circle.num_points, circle_num_points(12))
+            self.assertEqual(qs[1].circle.num_points, circle_num_points(12))
+        else:
+            self.assertAlmostEqual(qs[0].circle.area, expected_areas[0], 0)
+            self.assertAlmostEqual(qs[1].circle.area, expected_areas[1], 0)
 
     @skipUnlessDBFeature("has_Centroid_function")
     def test_centroid(self):