Fixed #30996 -- Added AsWKB and AsWKT GIS functions.

This commit is contained in:
Sergey Fedoseev 2019-11-15 23:37:43 +05:00 committed by Mariusz Felisiak
parent 8929afb8ec
commit a5855c8f0f
8 changed files with 89 additions and 4 deletions

View File

@ -65,6 +65,8 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
function_names = { function_names = {
'Area': 'SDO_GEOM.SDO_AREA', 'Area': 'SDO_GEOM.SDO_AREA',
'AsGeoJSON': 'SDO_UTIL.TO_GEOJSON', 'AsGeoJSON': 'SDO_UTIL.TO_GEOJSON',
'AsWKB': 'SDO_UTIL.TO_WKBGEOMETRY',
'AsWKT': 'SDO_UTIL.TO_WKTGEOMETRY',
'BoundingCircle': 'SDO_GEOM.SDO_MBC', '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',

View File

@ -148,6 +148,8 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
@cached_property @cached_property
def function_names(self): def function_names(self):
function_names = { function_names = {
'AsWKB': 'ST_AsBinary',
'AsWKT': 'ST_AsText',
'BoundingCircle': 'ST_MinimumBoundingCircle', 'BoundingCircle': 'ST_MinimumBoundingCircle',
'NumPoints': 'ST_NPoints', 'NumPoints': 'ST_NPoints',
} }

View File

@ -67,6 +67,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
select = 'CAST (AsEWKB(%s) AS BLOB)' select = 'CAST (AsEWKB(%s) AS BLOB)'
function_names = { function_names = {
'AsWKB': 'St_AsBinary',
'ForcePolygonCW': 'ST_ForceLHR', 'ForcePolygonCW': 'ST_ForceLHR',
'Length': 'ST_Length', 'Length': 'ST_Length',
'LineLocatePoint': 'ST_Line_Locate_Point', 'LineLocatePoint': 'ST_Line_Locate_Point',

View File

@ -5,7 +5,7 @@ from django.contrib.gis.db.models.sql import AreaField, DistanceField
from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.geos import GEOSGeometry
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db.models import ( from django.db.models import (
BooleanField, FloatField, IntegerField, TextField, Transform, BinaryField, BooleanField, FloatField, IntegerField, TextField, Transform,
) )
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
@ -209,6 +209,16 @@ class AsSVG(GeoFunc):
super().__init__(*expressions, **extra) super().__init__(*expressions, **extra)
class AsWKB(GeoFunc):
output_field = BinaryField()
arity = 1
class AsWKT(GeoFunc):
output_field = TextField()
arity = 1
class BoundingCircle(OracleToleranceMixin, GeoFunc): class BoundingCircle(OracleToleranceMixin, GeoFunc):
def __init__(self, expression, num_seg=48, **extra): def __init__(self, expression, num_seg=48, **extra):
super().__init__(expression, num_seg, **extra) super().__init__(expression, num_seg, **extra)

View File

@ -364,6 +364,8 @@ Function PostGIS Oracle MariaDB MySQL
: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:`AsWKB` X X X X X
:class:`AsWKT` X X X X X
:class:`Azimuth` X X (LWGEOM) :class:`Azimuth` X X (LWGEOM)
:class:`BoundingCircle` X X :class:`BoundingCircle` X X
:class:`Centroid` X X X X X :class:`Centroid` X X X X X

View File

@ -27,9 +27,9 @@ Measurement Relationships Operations Edi
:class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`MakeValid` :class:`AsGML` :class:`MemSize` :class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`MakeValid` :class:`AsGML` :class:`MemSize`
:class:`GeometryDistance` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`NumGeometries` :class:`GeometryDistance` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`NumGeometries`
:class:`Length` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumPoints` :class:`Length` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumPoints`
:class:`Perimeter` :class:`LineLocatePoint` :class:`SnapToGrid` :class:`GeoHash` :class:`Perimeter` :class:`LineLocatePoint` :class:`SnapToGrid` :class:`AsWKB`
.. :class:`PointOnSurface` :class:`Transform` .. :class:`PointOnSurface` :class:`Transform` :class:`AsWKT`
.. :class:`Translate` .. :class:`Translate` :class:`GeoHash`
========================= ======================== ====================== ======================= ================== ===================== ========================= ======================== ====================== ======================= ================== =====================
``Area`` ``Area``
@ -168,6 +168,48 @@ Keyword Argument Description
__ https://www.w3.org/Graphics/SVG/ __ https://www.w3.org/Graphics/SVG/
``AsWKB``
=========
.. class:: AsWKB(expression, **extra)
.. versionadded:: 3.1
*Availability*: MariaDB, `MySQL
<https://dev.mysql.com/doc/refman/en/gis-format-conversion-functions.html#function_st-asbinary>`__,
Oracle, `PostGIS <https://postgis.net/docs/ST_AsBinary.html>`__, SpatiaLite
Accepts a single geographic field or expression and returns a `Well-known
binary (WKB)`__ representation of the geometry.
Example::
>>> bytes(City.objects.annotate(wkb=AsWKB('point')).get(name='Chelyabinsk').wkb)
b'\x01\x01\x00\x00\x00]3\xf9f\x9b\x91K@\x00X\x1d9\xd2\xb9N@'
__ https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary
``AsWKT``
=========
.. class:: AsWKT(expression, **extra)
.. versionadded:: 3.1
*Availability*: MariaDB, `MySQL
<https://dev.mysql.com/doc/refman/en/gis-format-conversion-functions.html#function_st-astext>`__,
Oracle, `PostGIS <https://postgis.net/docs/ST_AsText.html>`__, SpatiaLite
Accepts a single geographic field or expression and returns a `Well-known text
(WKT)`__ representation of the geometry.
Example::
>>> City.objects.annotate(wkt=AsWKT('point')).get(name='Chelyabinsk').wkt
'POINT (55.137555 61.451728)'
__ https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry
``Azimuth`` ``Azimuth``
=========== ===========

View File

@ -66,6 +66,9 @@ Minor features
* :class:`~django.contrib.gis.db.models.functions.AsGeoJSON` is now supported * :class:`~django.contrib.gis.db.models.functions.AsGeoJSON` is now supported
on Oracle. on Oracle.
* Added the :class:`~django.contrib.gis.db.models.functions.AsWKB` and
:class:`~django.contrib.gis.db.models.functions.AsWKT` functions.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -147,6 +147,29 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
self.assertEqual(svg1, City.objects.annotate(svg=functions.AsSVG('point')).get(name='Pueblo').svg) self.assertEqual(svg1, City.objects.annotate(svg=functions.AsSVG('point')).get(name='Pueblo').svg)
self.assertEqual(svg2, City.objects.annotate(svg=functions.AsSVG('point', relative=5)).get(name='Pueblo').svg) self.assertEqual(svg2, City.objects.annotate(svg=functions.AsSVG('point', relative=5)).get(name='Pueblo').svg)
@skipUnlessDBFeature('has_AsWKB_function')
def test_aswkb(self):
wkb = City.objects.annotate(
wkb=functions.AsWKB(Point(1, 2, srid=4326)),
).first().wkb
# WKB is either XDR or NDR encoded.
self.assertIn(
bytes(wkb),
(
b'\x00\x00\x00\x00\x01?\xf0\x00\x00\x00\x00\x00\x00@\x00\x00'
b'\x00\x00\x00\x00\x00',
b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00'
b'\x00\x00\x00\x00\x00@',
),
)
@skipUnlessDBFeature('has_AsWKT_function')
def test_aswkt(self):
wkt = City.objects.annotate(
wkt=functions.AsWKT(Point(1, 2, srid=4326)),
).first().wkt
self.assertEqual(wkt, 'POINT (1.0 2.0)' if oracle else 'POINT(1 2)')
@skipUnlessDBFeature("has_Azimuth_function") @skipUnlessDBFeature("has_Azimuth_function")
def test_azimuth(self): def test_azimuth(self):
# Returns the azimuth in radians. # Returns the azimuth in radians.