summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Fedoseev <fedoseev.sergey@gmail.com>2016-12-16 03:36:18 +0600
committerTim Graham <timograham@gmail.com>2016-12-15 16:36:18 -0500
commit4884472447db11c57405edd5bdff36a171decd28 (patch)
tree03c635b03172c09247c57a9c512edc2eb0b7d451
parent8ab8a8910c6f9382610cba5b06ac5c110d52b4b2 (diff)
Fixed #27576 -- Made get_srid_info() fallback to GDAL if SpatialRefSys is unavailable.
-rw-r--r--django/contrib/gis/db/models/fields.py33
-rw-r--r--docs/ref/contrib/gis/functions.txt25
-rw-r--r--tests/gis_tests/distapp/tests.py7
3 files changed, 39 insertions, 26 deletions
diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py
index 4ded5b1b4e..8dc272aaa4 100644
--- a/django/contrib/gis/db/models/fields.py
+++ b/django/contrib/gis/db/models/fields.py
@@ -1,8 +1,11 @@
+from collections import defaultdict
+
from django.contrib.gis import forms, gdal
from django.contrib.gis.db.models.lookups import (
RasterBandTransform, gis_lookups,
)
from django.contrib.gis.db.models.proxy import SpatialProxy
+from django.contrib.gis.gdal import SpatialReference
from django.contrib.gis.gdal.error import GDALException
from django.contrib.gis.geometry.backend import Geometry, GeometryException
from django.core.exceptions import ImproperlyConfigured
@@ -14,7 +17,7 @@ from django.utils.translation import ugettext_lazy as _
# Local cache of the spatial_ref_sys table, which holds SRID data for each
# spatial database alias. This cache exists so that the database isn't queried
# for SRID info each time a distance query is constructed.
-_srid_cache = {}
+_srid_cache = defaultdict(dict)
def get_srid_info(srid, connection):
@@ -29,21 +32,21 @@ def get_srid_info(srid, connection):
# The SpatialRefSys model for the spatial backend.
SpatialRefSys = connection.ops.spatial_ref_sys()
except NotImplementedError:
- # No `spatial_ref_sys` table in spatial backend (e.g., MySQL).
- return None, None, None
-
- if connection.alias not in _srid_cache:
- # Initialize SRID dictionary for database if it doesn't exist.
- _srid_cache[connection.alias] = {}
+ SpatialRefSys = None
- if srid not in _srid_cache[connection.alias]:
- # Use `SpatialRefSys` model to query for spatial reference info.
- sr = SpatialRefSys.objects.using(connection.alias).get(srid=srid)
- units, units_name = sr.units
- spheroid = SpatialRefSys.get_spheroid(sr.wkt)
- _srid_cache[connection.alias][srid] = (units, units_name, spheroid)
+ alias, get_srs = (
+ (connection.alias, lambda srid: SpatialRefSys.objects.using(connection.alias).get(srid=srid).srs)
+ if SpatialRefSys else
+ (None, SpatialReference)
+ )
+ if srid not in _srid_cache[alias]:
+ srs = get_srs(srid)
+ units, units_name = srs.units
+ sphere_name = srs['spheroid']
+ spheroid = 'SPHEROID["%s",%s,%s]' % (sphere_name, srs.semi_major, srs.inverse_flattening)
+ _srid_cache[alias][srid] = (units, units_name, spheroid)
- return _srid_cache[connection.alias][srid]
+ return _srid_cache[alias][srid]
class GeoSelectFormatMixin(object):
@@ -149,8 +152,6 @@ class BaseSpatialField(Field):
system that uses non-projected units (e.g., latitude/longitude).
"""
units_name = self.units_name(connection)
- # Some backends like MySQL cannot determine units name. In that case,
- # test if srid is 4326 (WGS84), even if this is over-simplification.
return units_name.lower() in self.geodetic_units if units_name else self.srid == 4326
def get_placeholder(self, value, compiler, connection):
diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt
index 2917a658af..1fbe1fa212 100644
--- a/docs/ref/contrib/gis/functions.txt
+++ b/docs/ref/contrib/gis/functions.txt
@@ -41,8 +41,12 @@ Measurement Relationships Operations Editors
Accepts a single geographic field or expression and returns the area of the
field as an :class:`~django.contrib.gis.measure.Area` measure. On MySQL, a raw
-float value is returned, as it's not possible to automatically determine the
-unit of the field.
+float value is returned when the coordinates are geodetic.
+
+.. versionchanged:: 1.11
+
+ In older versions, a raw value was returned on MySQL when used on
+ projected SRS.
``AsGeoJSON``
=============
@@ -211,8 +215,7 @@ geometry B.
Accepts two geographic fields or expressions and returns the distance between
them, as a :class:`~django.contrib.gis.measure.Distance` object. On MySQL, a raw
-float value is returned, as it's not possible to automatically determine the
-unit of the field.
+float value is returned when the coordinates are geodetic.
On backends that support distance calculation on geodetic coordinates, the
proper backend function is automatically chosen depending on the SRID value of
@@ -246,6 +249,11 @@ queryset is calculated::
in kilometers. See :doc:`measure` for usage details and the list of
:ref:`supported_units`.
+.. versionchanged:: 1.11
+
+ In older versions, a raw value was returned on MySQL when used on
+ projected SRS.
+
``Envelope``
============
@@ -325,14 +333,19 @@ Returns ``True`` if its value is a valid geometry and ``False`` otherwise.
Accepts a single geographic linestring or multilinestring field or expression
and returns its length as an :class:`~django.contrib.gis.measure.Distance`
-measure. On MySQL, a raw float value is returned, as it's not possible to
-automatically determine the unit of the field.
+measure. On MySQL, a raw float value is returned when the coordinates
+are geodetic.
On PostGIS and SpatiaLite, when the coordinates are geodetic (angular), you can
specify if the calculation should be based on a simple sphere (less
accurate, less resource-intensive) or on a spheroid (more accurate, more
resource-intensive) with the ``spheroid`` keyword argument.
+.. versionchanged:: 1.11
+
+ In older versions, a raw value was returned on MySQL when used on
+ projected SRS.
+
``MakeValid``
=============
diff --git a/tests/gis_tests/distapp/tests.py b/tests/gis_tests/distapp/tests.py
index a62ea46280..6060f00461 100644
--- a/tests/gis_tests/distapp/tests.py
+++ b/tests/gis_tests/distapp/tests.py
@@ -475,8 +475,7 @@ class DistanceFunctionsTests(TestCase):
# Tolerance has to be lower for Oracle
tol = 2
for i, z in enumerate(SouthTexasZipcode.objects.annotate(area=Area('poly')).order_by('name')):
- # MySQL is returning a raw float value
- self.assertAlmostEqual(area_sq_m[i], z.area.sq_m if hasattr(z.area, 'sq_m') else z.area, tol)
+ self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol)
@skipUnlessDBFeature("has_Distance_function")
def test_distance_simple(self):
@@ -488,7 +487,7 @@ class DistanceFunctionsTests(TestCase):
houston = SouthTexasCity.objects.annotate(dist=Distance('point', lagrange)).order_by('id').first()
tol = 2 if oracle else 5
self.assertAlmostEqual(
- houston.dist.m if hasattr(houston.dist, 'm') else houston.dist,
+ houston.dist.m,
147075.069813,
tol
)
@@ -656,7 +655,7 @@ class DistanceFunctionsTests(TestCase):
# Now doing length on a projected coordinate system.
i10 = SouthTexasInterstate.objects.annotate(length=Length('path')).get(name='I-10')
- self.assertAlmostEqual(len_m2, i10.length.m if isinstance(i10.length, D) else i10.length, 2)
+ self.assertAlmostEqual(len_m2, i10.length.m, 2)
self.assertTrue(
SouthTexasInterstate.objects.annotate(length=Length('path')).filter(length__gt=4000).exists()
)