Fixed #27576 -- Made get_srid_info() fallback to GDAL if SpatialRefSys is unavailable.
This commit is contained in:
parent
8ab8a8910c
commit
4884472447
|
@ -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
|
||||
SpatialRefSys = None
|
||||
|
||||
if connection.alias not in _srid_cache:
|
||||
# Initialize SRID dictionary for database if it doesn't exist.
|
||||
_srid_cache[connection.alias] = {}
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
|
|
|
@ -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``
|
||||
=============
|
||||
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue