Kaydet (Commit) 48844724 authored tarafından Sergey Fedoseev's avatar Sergey Fedoseev Kaydeden (comit) Tim Graham

Fixed #27576 -- Made get_srid_info() fallback to GDAL if SpatialRefSys is unavailable.

üst 8ab8a891
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):
......
......@@ -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()
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment