Fixed #27602 -- Added Oracle support for BoundingCircle GIS function.
This commit is contained in:
parent
5a23cc00f5
commit
38a6df555f
|
@ -18,6 +18,7 @@ from django.contrib.gis.geometry.backend import Geometry
|
||||||
from django.contrib.gis.measure import Distance
|
from django.contrib.gis.measure import Distance
|
||||||
from django.db.backends.oracle.operations import DatabaseOperations
|
from django.db.backends.oracle.operations import DatabaseOperations
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
DEFAULT_TOLERANCE = '0.05'
|
DEFAULT_TOLERANCE = '0.05'
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
|
|
||||||
function_names = {
|
function_names = {
|
||||||
'Area': 'SDO_GEOM.SDO_AREA',
|
'Area': 'SDO_GEOM.SDO_AREA',
|
||||||
|
'BoundingCircle': 'SDO_GEOM.SDO_MBC',
|
||||||
'Centroid': 'SDO_GEOM.SDO_CENTROID',
|
'Centroid': 'SDO_GEOM.SDO_CENTROID',
|
||||||
'Difference': 'SDO_GEOM.SDO_DIFFERENCE',
|
'Difference': 'SDO_GEOM.SDO_DIFFERENCE',
|
||||||
'Distance': 'SDO_GEOM.SDO_DISTANCE',
|
'Distance': 'SDO_GEOM.SDO_DISTANCE',
|
||||||
|
@ -131,11 +133,15 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
|
|
||||||
truncate_params = {'relate': None}
|
truncate_params = {'relate': None}
|
||||||
|
|
||||||
unsupported_functions = {
|
@cached_property
|
||||||
'AsGeoJSON', 'AsKML', 'AsSVG', 'BoundingCircle', 'Envelope',
|
def unsupported_functions(self):
|
||||||
'ForceRHR', 'GeoHash', 'MakeValid', 'MemSize', 'Scale',
|
unsupported = {
|
||||||
'SnapToGrid', 'Translate',
|
'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):
|
def geo_quote_name(self, name):
|
||||||
return super(OracleOperations, self).geo_quote_name(name).upper()
|
return super(OracleOperations, self).geo_quote_name(name).upper()
|
||||||
|
|
|
@ -201,10 +201,15 @@ class AsSVG(GeoFunc):
|
||||||
super(AsSVG, self).__init__(*expressions, **extra)
|
super(AsSVG, self).__init__(*expressions, **extra)
|
||||||
|
|
||||||
|
|
||||||
class BoundingCircle(GeoFunc):
|
class BoundingCircle(OracleToleranceMixin, GeoFunc):
|
||||||
def __init__(self, expression, num_seg=48, **extra):
|
def __init__(self, expression, num_seg=48, **extra):
|
||||||
super(BoundingCircle, self).__init__(*[expression, num_seg], **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):
|
class Centroid(OracleToleranceMixin, GeoFunc):
|
||||||
arity = 1
|
arity = 1
|
||||||
|
|
|
@ -374,15 +374,15 @@ Database functions
|
||||||
The following table provides a summary of what geography-specific database
|
The following table provides a summary of what geography-specific database
|
||||||
functions are available on each spatial backend.
|
functions are available on each spatial backend.
|
||||||
|
|
||||||
==================================== ======= ====== =========== ==========
|
==================================== ======= ============== =========== ==========
|
||||||
Function PostGIS Oracle MySQL SpatiaLite
|
Function PostGIS Oracle MySQL SpatiaLite
|
||||||
==================================== ======= ====== =========== ==========
|
==================================== ======= ============== =========== ==========
|
||||||
:class:`Area` X X X X
|
:class:`Area` X X X X
|
||||||
:class:`AsGeoJSON` X X
|
:class:`AsGeoJSON` X X
|
||||||
:class:`AsGML` X X X
|
:class:`AsGML` X X X
|
||||||
:class:`AsKML` X X
|
:class:`AsKML` X X
|
||||||
:class:`AsSVG` X X
|
:class:`AsSVG` X X
|
||||||
:class:`BoundingCircle` X
|
:class:`BoundingCircle` X X (≥ 12.1.0.2)
|
||||||
:class:`Centroid` X X X X
|
:class:`Centroid` X X X X
|
||||||
:class:`Difference` X X X (≥ 5.6.1) X
|
:class:`Difference` X X X (≥ 5.6.1) X
|
||||||
:class:`Distance` X X X (≥ 5.6.1) X
|
:class:`Distance` X X X (≥ 5.6.1) X
|
||||||
|
@ -405,7 +405,7 @@ Function PostGIS Oracle MySQL SpatiaLite
|
||||||
:class:`Transform` X X X
|
:class:`Transform` X X X
|
||||||
:class:`Translate` X X
|
:class:`Translate` X X
|
||||||
:class:`Union` X X X (≥ 5.6.1) X
|
:class:`Union` X X X (≥ 5.6.1) X
|
||||||
==================================== ======= ====== =========== ==========
|
==================================== ======= ============== =========== ==========
|
||||||
|
|
||||||
Aggregate Functions
|
Aggregate Functions
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
@ -165,11 +165,18 @@ __ http://www.w3.org/Graphics/SVG/
|
||||||
|
|
||||||
.. class:: BoundingCircle(expression, num_seg=48, **extra)
|
.. 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
|
Accepts a single geographic field or expression and returns the smallest circle
|
||||||
polygon that can fully contain the geometry.
|
polygon that can fully contain the geometry.
|
||||||
|
|
||||||
|
The ``num_seg`` parameter is used only on PostGIS.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.11
|
||||||
|
|
||||||
|
Oracle support was added.
|
||||||
|
|
||||||
``Centroid``
|
``Centroid``
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
@ -165,6 +165,7 @@ Minor features
|
||||||
|
|
||||||
* Added Oracle support for the
|
* Added Oracle support for the
|
||||||
:class:`~django.contrib.gis.db.models.functions.AsGML` function,
|
: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
|
:class:`~django.contrib.gis.db.models.functions.IsValid` function, and
|
||||||
:lookup:`isvalid` lookup.
|
:lookup:`isvalid` lookup.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.db.models import Sum
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, skipUnlessDBFeature
|
||||||
from django.utils import six
|
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
|
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.
|
# num_seg is the number of segments per quarter circle.
|
||||||
return (4 * num_seg) + 1
|
return (4 * num_seg) + 1
|
||||||
|
|
||||||
# The weak precision in the assertions is because the BoundingCircle
|
expected_areas = (169, 136) if postgis else (171, 126)
|
||||||
# calculation changed on PostGIS 2.3.
|
|
||||||
qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly')).order_by('name')
|
qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly')).order_by('name')
|
||||||
self.assertAlmostEqual(qs[0].circle.area, 169, 0)
|
self.assertAlmostEqual(qs[0].circle.area, expected_areas[0], 0)
|
||||||
self.assertAlmostEqual(qs[1].circle.area, 136, 0)
|
self.assertAlmostEqual(qs[1].circle.area, expected_areas[1], 0)
|
||||||
|
if postgis:
|
||||||
# By default num_seg=48.
|
# By default num_seg=48.
|
||||||
self.assertEqual(qs[0].circle.num_points, circle_num_points(48))
|
self.assertEqual(qs[0].circle.num_points, circle_num_points(48))
|
||||||
self.assertEqual(qs[1].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')
|
qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly', num_seg=12)).order_by('name')
|
||||||
|
if postgis:
|
||||||
self.assertGreater(qs[0].circle.area, 168.4, 0)
|
self.assertGreater(qs[0].circle.area, 168.4, 0)
|
||||||
self.assertLess(qs[0].circle.area, 169.5, 0)
|
self.assertLess(qs[0].circle.area, 169.5, 0)
|
||||||
self.assertAlmostEqual(qs[1].circle.area, 136, 0)
|
self.assertAlmostEqual(qs[1].circle.area, 136, 0)
|
||||||
self.assertEqual(qs[0].circle.num_points, circle_num_points(12))
|
self.assertEqual(qs[0].circle.num_points, circle_num_points(12))
|
||||||
self.assertEqual(qs[1].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")
|
@skipUnlessDBFeature("has_Centroid_function")
|
||||||
def test_centroid(self):
|
def test_centroid(self):
|
||||||
|
|
Loading…
Reference in New Issue