Browse Source

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

Sergey Fedoseev 5 years ago
parent
commit
a5855c8f0f

+ 2 - 0
django/contrib/gis/db/backends/oracle/operations.py

@@ -65,6 +65,8 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
     function_names = {
         'Area': 'SDO_GEOM.SDO_AREA',
         'AsGeoJSON': 'SDO_UTIL.TO_GEOJSON',
+        'AsWKB': 'SDO_UTIL.TO_WKBGEOMETRY',
+        'AsWKT': 'SDO_UTIL.TO_WKTGEOMETRY',
         'BoundingCircle': 'SDO_GEOM.SDO_MBC',
         'Centroid': 'SDO_GEOM.SDO_CENTROID',
         'Difference': 'SDO_GEOM.SDO_DIFFERENCE',

+ 2 - 0
django/contrib/gis/db/backends/postgis/operations.py

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

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

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

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

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

+ 2 - 0
docs/ref/contrib/gis/db-api.txt

@@ -364,6 +364,8 @@ Function                              PostGIS  Oracle         MariaDB      MySQL
 :class:`AsGML`                        X        X                                       X
 :class:`AsKML`                        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:`BoundingCircle`               X        X
 :class:`Centroid`                     X        X              X            X           X

+ 45 - 3
docs/ref/contrib/gis/functions.txt

@@ -27,9 +27,9 @@ Measurement                Relationships             Operations              Edi
 :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:`Length`            :class:`Envelope`         :class:`Union`          :class:`Scale`           :class:`AsSVG`      :class:`NumPoints`
-:class:`Perimeter`         :class:`LineLocatePoint`                          :class:`SnapToGrid`      :class:`GeoHash`
-..                         :class:`PointOnSurface`                           :class:`Transform`
-..                                                                           :class:`Translate`
+:class:`Perimeter`         :class:`LineLocatePoint`                          :class:`SnapToGrid`      :class:`AsWKB`
+..                         :class:`PointOnSurface`                           :class:`Transform`       :class:`AsWKT`
+..                                                                           :class:`Translate`       :class:`GeoHash`
 =========================  ========================  ======================  =======================  ==================  =====================
 
 ``Area``
@@ -168,6 +168,48 @@ Keyword Argument       Description
 
 __ 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``
 ===========
 

+ 3 - 0
docs/releases/3.1.txt

@@ -66,6 +66,9 @@ Minor features
 * :class:`~django.contrib.gis.db.models.functions.AsGeoJSON` is now supported
   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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 23 - 0
tests/gis_tests/geoapp/test_functions.py

@@ -147,6 +147,29 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
         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)
 
+    @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")
     def test_azimuth(self):
         # Returns the azimuth in radians.