Refs #31014 -- Added FromWKB and FromWKT GIS database functions.

Co-authored-by: Ondřej Böhm <ondrej.bohm@firma.seznam.cz>
Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Co-authored-by: Sergey Fedoseev <fedoseev.sergey@gmail.com>
This commit is contained in:
Mariusz Felisiak 2023-01-10 11:51:09 +01:00 committed by GitHub
parent 72efd840a8
commit 552384fa97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 17 deletions

View File

@ -45,6 +45,8 @@ class BaseSpatialOperations:
"Difference",
"Distance",
"Envelope",
"FromWKB",
"FromWKT",
"GeoHash",
"GeometryDistance",
"Intersection",

View File

@ -62,6 +62,11 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
models.Union,
)
function_names = {
"FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText",
}
@cached_property
def unsupported_functions(self):
unsupported = {

View File

@ -76,6 +76,8 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
"Difference": "SDO_GEOM.SDO_DIFFERENCE",
"Distance": "SDO_GEOM.SDO_DISTANCE",
"Envelope": "SDO_GEOM_MBR",
"FromWKB": "SDO_UTIL.FROM_WKBGEOMETRY",
"FromWKT": "SDO_UTIL.FROM_WKTGEOMETRY",
"Intersection": "SDO_GEOM.SDO_INTERSECTION",
"IsValid": "SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT",
"Length": "SDO_GEOM.SDO_LENGTH",

View File

@ -172,6 +172,8 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
"AsWKB": "ST_AsBinary",
"AsWKT": "ST_AsText",
"BoundingCircle": "ST_MinimumBoundingCircle",
"FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText",
"NumPoints": "ST_NPoints",
}
return function_names

View File

@ -67,6 +67,8 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
function_names = {
"AsWKB": "St_AsBinary",
"ForcePolygonCW": "ST_ForceLHR",
"FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText",
"Length": "ST_Length",
"LineLocatePoint": "ST_Line_Locate_Point",
"NumPoints": "ST_NPoints",

View File

@ -69,6 +69,8 @@ class GeoFuncMixin:
def resolve_expression(self, *args, **kwargs):
res = super().resolve_expression(*args, **kwargs)
if not self.geom_param_pos:
return res
# Ensure that expressions are geometric.
source_fields = res.get_source_fields()
@ -351,6 +353,18 @@ class ForcePolygonCW(GeomOutputGeoFunc):
arity = 1
class FromWKB(GeoFunc):
output_field = GeometryField(srid=0)
arity = 1
geom_param_pos = ()
class FromWKT(GeoFunc):
output_field = GeometryField(srid=0)
arity = 1
geom_param_pos = ()
class GeoHash(GeoFunc):
output_field = TextField()

View File

@ -360,6 +360,8 @@ Function PostGIS Oracle MariaDB MySQL
:class:`Distance` X X X X X
:class:`Envelope` X X X X X
:class:`ForcePolygonCW` X X
:class:`FromWKB` X X X X X
:class:`FromWKT` X X X X X
:class:`GeoHash` X X X (LWGEOM/RTTOPO)
:class:`Intersection` X X X X X
:class:`IsEmpty` X

View File

@ -20,17 +20,17 @@ get a ``NotImplementedError`` exception.
Function's summary:
========================= ======================== ====================== ======================= ================== =====================
Measurement Relationships Operations Editors Output format Miscellaneous
========================= ======================== ====================== ======================= ================== =====================
:class:`Area` :class:`Azimuth` :class:`Difference` :class:`ForcePolygonCW` :class:`AsGeoJSON` :class:`IsEmpty`
:class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`MakeValid` :class:`AsGML` :class:`IsValid`
:class:`GeometryDistance` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`MemSize`
:class:`Length` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumGeometries`
:class:`Perimeter` :class:`LineLocatePoint` :class:`SnapToGrid` :class:`AsWKB` :class:`NumPoints`
.. :class:`PointOnSurface` :class:`Transform` :class:`AsWKT`
.. :class:`Translate` :class:`GeoHash`
========================= ======================== ====================== ======================= ================== =====================
========================= ======================== ====================== ======================= ================== ================== ======================
Measurement Relationships Operations Editors Input format Output format Miscellaneous
========================= ======================== ====================== ======================= ================== ================== ======================
:class:`Area` :class:`Azimuth` :class:`Difference` :class:`ForcePolygonCW` :class:`AsGeoJSON` :class:`IsEmpty`
:class:`Distance` :class:`BoundingCircle` :class:`Intersection` :class:`MakeValid` :class:`AsGML` :class:`IsValid`
:class:`GeometryDistance` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`MemSize`
:class:`Length` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumGeometries`
:class:`Perimeter` :class:`LineLocatePoint` :class:`SnapToGrid` :class:`FromWKB` :class:`AsWKB` :class:`NumPoints`
:class:`PointOnSurface` :class:`Transform` :class:`FromWKT` :class:`AsWKT`
:class:`Translate` :class:`GeoHash`
========================= ======================== ====================== ======================= ================== ================== ======================
``Area``
========
@ -174,15 +174,13 @@ __ https://www.w3.org/Graphics/SVG/
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.
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``
=========
@ -193,15 +191,13 @@ __ https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well
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.
(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``
===========
@ -327,6 +323,32 @@ 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.
``FromWKB``
===========
.. versionadded:: 4.2
.. class:: FromWKB(expression, **extra)
*Availability*: MariaDB, `MySQL
<https://dev.mysql.com/doc/refman/en/gis-wkb-functions.html#function_st-geomfromwkb>`__,
Oracle, `PostGIS <https://postgis.net/docs/ST_GeomFromWKB.html>`__, SpatiaLite
Creates geometry from `Well-known binary (WKB)`_ representation.
``FromWKT``
===========
.. versionadded:: 4.2
.. class:: FromWKT(expression, **extra)
*Availability*: MariaDB, `MySQL
<https://dev.mysql.com/doc/refman/en/gis-wkt-functions.html#function_st-geomfromtext>`__,
Oracle, `PostGIS <https://postgis.net/docs/ST_GeomFromText.html>`__, SpatiaLite
Creates geometry from `Well-known text (WKT)`_ representation.
``GeoHash``
===========
@ -596,3 +618,6 @@ parameters.
Accepts two geographic fields or expressions and returns the union of both
geometries.
.. _`Well-known binary (WKB)`: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary
.. _`Well-known text (WKT)`: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry

View File

@ -167,6 +167,11 @@ Minor features
:class:`IsEmpty() <django.contrib.gis.db.models.functions.IsEmpty>`
expression allow filtering empty geometries on PostGIS.
* The new :class:`FromWKB() <django.contrib.gis.db.models.functions.FromWKB>`
and :class:`FromWKT() <django.contrib.gis.db.models.functions.FromWKT>`
functions allow creating geometries from Well-known binary (WKB) and
Well-known text (WKT) representations.
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -326,6 +326,24 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
).get(name="Foo")
self.assertEqual(rhr_rings, st.force_polygon_cw.coords)
@skipUnlessDBFeature("has_FromWKB_function")
def test_fromwkb(self):
g = Point(56.811078, 60.608647)
g2 = City.objects.values_list(
functions.FromWKB(Value(g.wkb.tobytes())),
flat=True,
)[0]
self.assertIs(g.equals_exact(g2, 0.00001), True)
@skipUnlessDBFeature("has_FromWKT_function")
def test_fromwkt(self):
g = Point(56.811078, 60.608647)
g2 = City.objects.values_list(
functions.FromWKT(Value(g.wkt)),
flat=True,
)[0]
self.assertIs(g.equals_exact(g2, 0.00001), True)
@skipUnlessDBFeature("has_GeoHash_function")
def test_geohash(self):
# Reference query: