diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py index f41ce0ed64..e2966f5c6b 100644 --- a/django/contrib/gis/db/backends/oracle/operations.py +++ b/django/contrib/gis/db/backends/oracle/operations.py @@ -64,6 +64,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): function_names = { 'Area': 'SDO_GEOM.SDO_AREA', + 'AsGeoJSON': 'SDO_UTIL.TO_GEOJSON', 'BoundingCircle': 'SDO_GEOM.SDO_MBC', 'Centroid': 'SDO_GEOM.SDO_CENTROID', 'Difference': 'SDO_GEOM.SDO_DIFFERENCE', @@ -106,7 +107,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): } unsupported_functions = { - 'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash', + 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash', 'GeometryDistance', 'LineLocatePoint', 'MakeValid', 'MemSize', 'Scale', 'SnapToGrid', 'Translate', } diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py index a79067a45a..4cedbb6504 100644 --- a/django/contrib/gis/db/models/functions.py +++ b/django/contrib/gis/db/models/functions.py @@ -162,6 +162,12 @@ class AsGeoJSON(GeoFunc): expressions.append(options) super().__init__(*expressions, **extra) + def as_oracle(self, compiler, connection, **extra_context): + source_expressions = self.get_source_expressions() + clone = self.copy() + clone.set_source_expressions(source_expressions[:1]) + return super(AsGeoJSON, clone).as_sql(compiler, connection, **extra_context) + class AsGML(GeoFunc): geom_param_pos = (1,) diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt index e7bd672914..8010f6dcba 100644 --- a/docs/ref/contrib/gis/db-api.txt +++ b/docs/ref/contrib/gis/db-api.txt @@ -360,7 +360,7 @@ functions are available on each spatial backend. Function PostGIS Oracle MariaDB MySQL SpatiaLite ==================================== ======= ============== ============ =========== ========== :class:`Area` X X X X X -:class:`AsGeoJSON` X X (≥ 10.2.4) X (≥ 5.7.5) X +:class:`AsGeoJSON` X X X (≥ 10.2.4) X (≥ 5.7.5) X :class:`AsGML` X X X :class:`AsKML` X X :class:`AsSVG` X X diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt index 7736323433..a0403faf9b 100644 --- a/docs/ref/contrib/gis/functions.txt +++ b/docs/ref/contrib/gis/functions.txt @@ -54,7 +54,7 @@ geographic SRSes. *Availability*: MariaDB (≥ 10.2.4), `MySQL `__ (≥ 5.7.5), -`PostGIS `__, SpatiaLite +Oracle, `PostGIS `__, SpatiaLite Accepts a single geographic field or expression and returns a `GeoJSON `_ representation of the geometry. Note that the result is @@ -70,17 +70,23 @@ Example:: Keyword Argument Description ===================== ===================================================== ``bbox`` Set this to ``True`` if you want the bounding box - to be included in the returned GeoJSON. + to be included in the returned GeoJSON. Ignored on + Oracle. ``crs`` Set this to ``True`` if you want the coordinate reference system to be included in the returned - GeoJSON. Ignored on MySQL. + GeoJSON. Ignored on MySQL and Oracle. ``precision`` It may be used to specify the number of significant digits for the coordinates in the GeoJSON - representation -- the default value is 8. + representation -- the default value is 8. Ignored on + Oracle. ===================== ===================================================== +.. versionchanged:: 3.1 + + Oracle support was added. + ``AsGML`` ========= diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 00d620b045..27255d16ae 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -63,6 +63,9 @@ Minor features * Added the :attr:`.LinearRing.is_counterclockwise` property. +* :class:`~django.contrib.gis.db.models.functions.AsGeoJSON` is now supported + on Oracle. + :mod:`django.contrib.messages` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index a386896d24..a504587ab2 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -32,24 +32,27 @@ class GISFunctionsTests(FuncTestMixin, TestCase): return pueblo_json = '{"type":"Point","coordinates":[-104.609252,38.255001]}' - houston_json = ( + houston_json = json.loads( '{"type":"Point","crs":{"type":"name","properties":' '{"name":"EPSG:4326"}},"coordinates":[-95.363151,29.763374]}' ) - victoria_json = ( + victoria_json = json.loads( '{"type":"Point","bbox":[-123.30519600,48.46261100,-123.30519600,48.46261100],' '"coordinates":[-123.305196,48.462611]}' ) - chicago_json = ( + chicago_json = json.loads( '{"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4326"}},' '"bbox":[-87.65018,41.85039,-87.65018,41.85039],"coordinates":[-87.65018,41.85039]}' ) - # MySQL ignores the crs option. - if mysql: - houston_json = json.loads(houston_json) + # MySQL and Oracle ignore the crs option. + if mysql or oracle: del houston_json['crs'] - chicago_json = json.loads(chicago_json) del chicago_json['crs'] + # Oracle ignores also the bbox and precision options. + if oracle: + del chicago_json['bbox'] + del victoria_json['bbox'] + chicago_json['coordinates'] = [-87.650175, 41.850385] # Precision argument should only be an integer with self.assertRaises(TypeError): @@ -75,10 +78,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase): # WHERE "geoapp_city"."name" = 'Houston'; # This time we include the bounding box by using the `bbox` keyword. self.assertJSONEqual( - victoria_json, City.objects.annotate( geojson=functions.AsGeoJSON('point', bbox=True) - ).get(name='Victoria').geojson + ).get(name='Victoria').geojson, + victoria_json, ) # SELECT ST_AsGeoJson("geoapp_city"."point", 5, 3) FROM "geoapp_city"