Fixed #28841 -- Added ForcePolygonCW GIS function and deprecated ForceRHR.
This commit is contained in:
parent
44908d4d93
commit
aefe624c62
|
@ -57,10 +57,10 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
@cached_property
|
@cached_property
|
||||||
def unsupported_functions(self):
|
def unsupported_functions(self):
|
||||||
unsupported = {
|
unsupported = {
|
||||||
'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle', 'ForceRHR',
|
'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle',
|
||||||
'LineLocatePoint', 'MakeValid', 'MemSize', 'Perimeter',
|
'ForcePolygonCW', 'ForceRHR', 'LineLocatePoint', 'MakeValid',
|
||||||
'PointOnSurface', 'Reverse', 'Scale', 'SnapToGrid', 'Transform',
|
'MemSize', 'Perimeter', 'PointOnSurface', 'Reverse', 'Scale',
|
||||||
'Translate',
|
'SnapToGrid', 'Transform', 'Translate',
|
||||||
}
|
}
|
||||||
if self.connection.mysql_version < (5, 7, 5):
|
if self.connection.mysql_version < (5, 7, 5):
|
||||||
unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'})
|
unsupported.update({'AsGeoJSON', 'GeoHash', 'IsValid'})
|
||||||
|
|
|
@ -105,9 +105,9 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported_functions = {
|
unsupported_functions = {
|
||||||
'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'Envelope', 'ForceRHR',
|
'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'Envelope',
|
||||||
'GeoHash', 'LineLocatePoint', 'MakeValid', 'MemSize', 'Scale',
|
'ForcePolygonCW', 'ForceRHR', 'GeoHash', 'LineLocatePoint',
|
||||||
'SnapToGrid', 'Translate',
|
'MakeValid', 'MemSize', 'Scale', 'SnapToGrid', 'Translate',
|
||||||
}
|
}
|
||||||
|
|
||||||
def geo_quote_name(self, name):
|
def geo_quote_name(self, name):
|
||||||
|
|
|
@ -158,6 +158,8 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
'LengthSpheroid': 'ST_length_spheroid',
|
'LengthSpheroid': 'ST_length_spheroid',
|
||||||
'MemSize': 'ST_mem_size',
|
'MemSize': 'ST_mem_size',
|
||||||
})
|
})
|
||||||
|
if self.spatial_version < (2, 4, 0):
|
||||||
|
function_names['ForcePolygonCW'] = 'ST_ForceRHR'
|
||||||
return function_names
|
return function_names
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -78,7 +78,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def unsupported_functions(self):
|
def unsupported_functions(self):
|
||||||
unsupported = {'BoundingCircle', 'ForceRHR', 'MemSize'}
|
unsupported = {'BoundingCircle', 'ForcePolygonCW', 'ForceRHR', 'MemSize'}
|
||||||
if not self.lwgeom_version():
|
if not self.lwgeom_version():
|
||||||
unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'}
|
unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'}
|
||||||
return unsupported
|
return unsupported
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import warnings
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.contrib.gis.db.models.fields import BaseSpatialField, GeometryField
|
from django.contrib.gis.db.models.fields import BaseSpatialField, GeometryField
|
||||||
|
@ -10,6 +11,7 @@ from django.db.models import (
|
||||||
from django.db.models.expressions import Func, Value
|
from django.db.models.expressions import Func, Value
|
||||||
from django.db.models.functions import Cast
|
from django.db.models.functions import Cast
|
||||||
from django.db.utils import NotSupportedError
|
from django.db.utils import NotSupportedError
|
||||||
|
from django.utils.deprecation import RemovedInDjango30Warning
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
NUMERIC_TYPES = (int, float, Decimal)
|
NUMERIC_TYPES = (int, float, Decimal)
|
||||||
|
@ -274,9 +276,20 @@ class Envelope(GeomOutputGeoFunc):
|
||||||
arity = 1
|
arity = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ForcePolygonCW(GeomOutputGeoFunc):
|
||||||
|
arity = 1
|
||||||
|
|
||||||
|
|
||||||
class ForceRHR(GeomOutputGeoFunc):
|
class ForceRHR(GeomOutputGeoFunc):
|
||||||
arity = 1
|
arity = 1
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
warnings.warn(
|
||||||
|
'ForceRHR is deprecated in favor of ForcePolygonCW.',
|
||||||
|
RemovedInDjango30Warning, stacklevel=2,
|
||||||
|
)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GeoHash(GeoFunc):
|
class GeoHash(GeoFunc):
|
||||||
output_field = TextField()
|
output_field = TextField()
|
||||||
|
|
|
@ -29,6 +29,8 @@ details on these changes.
|
||||||
* The ``field_name`` keyword argument of ``QuerySet.earliest()`` and
|
* The ``field_name`` keyword argument of ``QuerySet.earliest()`` and
|
||||||
``latest()`` will be removed.
|
``latest()`` will be removed.
|
||||||
|
|
||||||
|
* ``django.contrib.gis.db.models.functions.ForceRHR`` will be removed.
|
||||||
|
|
||||||
See the :ref:`Django 2.1 release notes <deprecated-features-2.1>` for more
|
See the :ref:`Django 2.1 release notes <deprecated-features-2.1>` for more
|
||||||
details on these changes.
|
details on these changes.
|
||||||
|
|
||||||
|
|
|
@ -20,17 +20,18 @@ get a ``NotImplementedError`` exception.
|
||||||
|
|
||||||
Function's summary:
|
Function's summary:
|
||||||
|
|
||||||
================== ======================== ====================== =================== ================== =====================
|
================== ======================== ====================== ======================= ================== =====================
|
||||||
Measurement Relationships Operations Editors Output format Miscellaneous
|
Measurement Relationships Operations Editors Output format Miscellaneous
|
||||||
================== ======================== ====================== =================== ================== =====================
|
================== ======================== ====================== ======================= ================== =====================
|
||||||
:class:`Area` :class:`Azimuth` :class:`Difference` :class:`ForceRHR` :class:`AsGeoJSON` :class:`IsValid`
|
:class:`Area` :class:`Azimuth` :class:`Difference` :class:`ForcePolygonCW` :class:`AsGeoJSON` :class:`IsValid`
|
||||||
:class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`MakeValid` :class:`AsGML` :class:`MemSize`
|
:class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`ForceRHR` :class:`AsGML` :class:`MemSize`
|
||||||
:class:`Length` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`NumGeometries`
|
:class:`Length` :class:`Centroid` :class:`SymDifference` :class:`MakeValid` :class:`AsKML` :class:`NumGeometries`
|
||||||
:class:`Perimeter` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumPoints`
|
:class:`Perimeter` :class:`Envelope` :class:`Union` :class:`Reverse` :class:`AsSVG` :class:`NumPoints`
|
||||||
.. :class:`LineLocatePoint` :class:`SnapToGrid` :class:`GeoHash`
|
.. :class:`LineLocatePoint` :class:`Scale` :class:`GeoHash`
|
||||||
.. :class:`PointOnSurface` :class:`Transform`
|
.. :class:`PointOnSurface` :class:`SnapToGrid`
|
||||||
|
.. :class:`Transform`
|
||||||
.. :class:`Translate`
|
.. :class:`Translate`
|
||||||
================== ======================== ====================== =================== ================== =====================
|
================== ======================== ====================== ======================= ================== =====================
|
||||||
|
|
||||||
``Area``
|
``Area``
|
||||||
========
|
========
|
||||||
|
@ -271,11 +272,29 @@ SpatiaLite
|
||||||
Accepts a single geographic field or expression and returns the geometry
|
Accepts a single geographic field or expression and returns the geometry
|
||||||
representing the bounding box of the geometry.
|
representing the bounding box of the geometry.
|
||||||
|
|
||||||
|
``ForcePolygonCW``
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. class:: ForcePolygonCW(expression, **extra)
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
*Availability*: `PostGIS <https://postgis.net/docs/ST_ForcePolygonCW.html>`__
|
||||||
|
|
||||||
|
Accepts a single geographic field or expression and returns a modified version
|
||||||
|
of the polygon/multipolygon in which all exterior rings are oriented clockwise
|
||||||
|
and all interior rings are oriented counterclockwise. Non-polygonal geometries
|
||||||
|
are returned unchanged.
|
||||||
|
|
||||||
``ForceRHR``
|
``ForceRHR``
|
||||||
============
|
============
|
||||||
|
|
||||||
.. class:: ForceRHR(expression, **extra)
|
.. class:: ForceRHR(expression, **extra)
|
||||||
|
|
||||||
|
.. deprecated:: 2.1
|
||||||
|
|
||||||
|
Use :class:`ForcePolygonCW` instead.
|
||||||
|
|
||||||
*Availability*: `PostGIS <https://postgis.net/docs/ST_ForceRHR.html>`__
|
*Availability*: `PostGIS <https://postgis.net/docs/ST_ForceRHR.html>`__
|
||||||
|
|
||||||
Accepts a single geographic field or expression and returns a modified version
|
Accepts a single geographic field or expression and returns a modified version
|
||||||
|
|
|
@ -239,7 +239,8 @@ Features deprecated in 2.1
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
* ...
|
* The ``ForceRHR`` GIS function is deprecated in favor of the new
|
||||||
|
:class:`~django.contrib.gis.db.models.functions.ForcePolygonCW` function.
|
||||||
|
|
||||||
.. _removed-features-2.1:
|
.. _removed-features-2.1:
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ from django.contrib.gis.geos import (
|
||||||
from django.contrib.gis.measure import Area
|
from django.contrib.gis.measure import Area
|
||||||
from django.db import NotSupportedError, connection
|
from django.db import NotSupportedError, connection
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||||
|
from django.utils.deprecation import RemovedInDjango30Warning
|
||||||
|
|
||||||
from ..utils import FuncTestMixin, mysql, oracle, postgis, spatialite
|
from ..utils import FuncTestMixin, mysql, oracle, postgis, spatialite
|
||||||
from .models import City, Country, CountryWebMercator, State, Track
|
from .models import City, Country, CountryWebMercator, State, Track
|
||||||
|
@ -215,7 +216,22 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
|
||||||
for country in countries:
|
for country in countries:
|
||||||
self.assertIsInstance(country.envelope, Polygon)
|
self.assertIsInstance(country.envelope, Polygon)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("has_ForcePolygonCW_function")
|
||||||
|
def test_force_polygon_cw(self):
|
||||||
|
rings = (
|
||||||
|
((0, 0), (5, 0), (0, 5), (0, 0)),
|
||||||
|
((1, 1), (1, 3), (3, 1), (1, 1)),
|
||||||
|
)
|
||||||
|
rhr_rings = (
|
||||||
|
((0, 0), (0, 5), (5, 0), (0, 0)),
|
||||||
|
((1, 1), (3, 1), (1, 3), (1, 1)),
|
||||||
|
)
|
||||||
|
State.objects.create(name='Foo', poly=Polygon(*rings))
|
||||||
|
st = State.objects.annotate(force_polygon_cw=functions.ForcePolygonCW('poly')).get(name='Foo')
|
||||||
|
self.assertEqual(rhr_rings, st.force_polygon_cw.coords)
|
||||||
|
|
||||||
@skipUnlessDBFeature("has_ForceRHR_function")
|
@skipUnlessDBFeature("has_ForceRHR_function")
|
||||||
|
@ignore_warnings(category=RemovedInDjango30Warning)
|
||||||
def test_force_rhr(self):
|
def test_force_rhr(self):
|
||||||
rings = (
|
rings = (
|
||||||
((0, 0), (5, 0), (0, 5), (0, 0)),
|
((0, 0), (5, 0), (0, 5), (0, 0)),
|
||||||
|
|
Loading…
Reference in New Issue