1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Fixed #26753 -- Made GDAL a required dependency for contrib.gis

Thanks Tim Graham for the review.
This commit is contained in:
Claude Paroz
2016-06-14 16:18:33 +02:00
parent 7def55c3f6
commit f7a363ee1d
29 changed files with 117 additions and 368 deletions

View File

@@ -1,8 +1,7 @@
from django.contrib.admin import ModelAdmin
from django.contrib.gis.admin.widgets import OpenLayersWidget
from django.contrib.gis.db import models
from django.contrib.gis.gdal import HAS_GDAL, OGRGeomType
from django.core.exceptions import ImproperlyConfigured
from django.contrib.gis.gdal import OGRGeomType
spherical_mercator_srid = 3857
@@ -59,12 +58,6 @@ class GeoModelAdmin(ModelAdmin):
3D editing).
"""
if isinstance(db_field, models.GeometryField) and db_field.dim < 3:
if not HAS_GDAL and db_field.srid != self.map_srid:
raise ImproperlyConfigured(
"Map SRID is %s and SRID of `%s` is %s. GDAL must be "
"installed to perform the transformation."
% (self.map_srid, db_field, db_field.srid)
)
# Setting the widget with the newly defined widget.
kwargs['widget'] = self.get_map_widget(db_field)
return db_field.formfield(**kwargs)

View File

@@ -1,5 +1,3 @@
import re
from django.contrib.gis import gdal
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
@@ -11,47 +9,32 @@ class SpatialRefSysMixin(object):
The SpatialRefSysMixin is a class used by the database-dependent
SpatialRefSys objects to reduce redundant code.
"""
# For pulling out the spheroid from the spatial reference string. This
# regular expression is used only if the user does not have GDAL installed.
# TODO: Flattening not used in all ellipsoids, could also be a minor axis,
# or 'b' parameter.
spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
# For pulling out the units on platforms w/o GDAL installed.
# TODO: Figure out how to pull out angular units of projected coordinate system and
# fix for LOCAL_CS types. GDAL should be highly recommended for performing
# distance queries.
units_regex = re.compile(r'.+UNIT ?\["(?P<unit_name>[\w \.\'\(\)]+)", ?(?P<unit>[^ ,\]]+)', re.DOTALL)
@property
def srs(self):
"""
Returns a GDAL SpatialReference object, if GDAL is installed.
Returns a GDAL SpatialReference object.
"""
if gdal.HAS_GDAL:
# TODO: Is caching really necessary here? Is complexity worth it?
if hasattr(self, '_srs'):
# Returning a clone of the cached SpatialReference object.
return self._srs.clone()
else:
# Attempting to cache a SpatialReference object.
# Trying to get from WKT first.
try:
self._srs = gdal.SpatialReference(self.wkt)
return self.srs
except Exception as e:
msg = e
try:
self._srs = gdal.SpatialReference(self.proj4text)
return self.srs
except Exception as e:
msg = e
raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
# TODO: Is caching really necessary here? Is complexity worth it?
if hasattr(self, '_srs'):
# Returning a clone of the cached SpatialReference object.
return self._srs.clone()
else:
raise Exception('GDAL is not installed.')
# Attempting to cache a SpatialReference object.
# Trying to get from WKT first.
try:
self._srs = gdal.SpatialReference(self.wkt)
return self.srs
except Exception as e:
msg = e
try:
self._srs = gdal.SpatialReference(self.proj4text)
return self.srs
except Exception as e:
msg = e
raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
@property
def ellipsoid(self):
@@ -59,14 +42,7 @@ class SpatialRefSysMixin(object):
Returns a tuple of the ellipsoid parameters:
(semimajor axis, semiminor axis, and inverse flattening).
"""
if gdal.HAS_GDAL:
return self.srs.ellipsoid
else:
m = self.spheroid_regex.match(self.wkt)
if m:
return (float(m.group('major')), float(m.group('flattening')))
else:
return None
return self.srs.ellipsoid
@property
def name(self):
@@ -86,70 +62,37 @@ class SpatialRefSysMixin(object):
@property
def projected(self):
"Is this Spatial Reference projected?"
if gdal.HAS_GDAL:
return self.srs.projected
else:
return self.wkt.startswith('PROJCS')
return self.srs.projected
@property
def local(self):
"Is this Spatial Reference local?"
if gdal.HAS_GDAL:
return self.srs.local
else:
return self.wkt.startswith('LOCAL_CS')
return self.srs.local
@property
def geographic(self):
"Is this Spatial Reference geographic?"
if gdal.HAS_GDAL:
return self.srs.geographic
else:
return self.wkt.startswith('GEOGCS')
return self.srs.geographic
@property
def linear_name(self):
"Returns the linear units name."
if gdal.HAS_GDAL:
return self.srs.linear_name
elif self.geographic:
return None
else:
m = self.units_regex.match(self.wkt)
return m.group('unit_name')
return self.srs.linear_name
@property
def linear_units(self):
"Returns the linear units."
if gdal.HAS_GDAL:
return self.srs.linear_units
elif self.geographic:
return None
else:
m = self.units_regex.match(self.wkt)
return m.group('unit')
return self.srs.linear_units
@property
def angular_name(self):
"Returns the name of the angular units."
if gdal.HAS_GDAL:
return self.srs.angular_name
elif self.projected:
return None
else:
m = self.units_regex.match(self.wkt)
return m.group('unit_name')
return self.srs.angular_name
@property
def angular_units(self):
"Returns the angular units."
if gdal.HAS_GDAL:
return self.srs.angular_units
elif self.projected:
return None
else:
m = self.units_regex.match(self.wkt)
return m.group('unit')
return self.srs.angular_units
@property
def units(self):
@@ -167,11 +110,7 @@ class SpatialRefSysMixin(object):
Return a tuple of (unit_value, unit_name) for the given WKT without
using any of the database fields.
"""
if gdal.HAS_GDAL:
return gdal.SpatialReference(wkt).units
else:
m = cls.units_regex.match(wkt)
return float(m.group('unit')), m.group('unit_name')
return gdal.SpatialReference(wkt).units
@classmethod
def get_spheroid(cls, wkt, string=True):
@@ -179,17 +118,9 @@ class SpatialRefSysMixin(object):
Class method used by GeometryField on initialization to
retrieve the `SPHEROID[..]` parameters from the given WKT.
"""
if gdal.HAS_GDAL:
srs = gdal.SpatialReference(wkt)
sphere_params = srs.ellipsoid
sphere_name = srs['spheroid']
else:
m = cls.spheroid_regex.match(wkt)
if m:
sphere_params = (float(m.group('major')), float(m.group('flattening')))
sphere_name = m.group('name')
else:
return None
srs = gdal.SpatialReference(wkt)
sphere_params = srs.ellipsoid
sphere_name = srs['spheroid']
if not string:
return sphere_name, sphere_params
@@ -203,10 +134,6 @@ class SpatialRefSysMixin(object):
def __str__(self):
"""
Returns the string representation. If GDAL is installed,
it will be 'pretty' OGC WKT.
Returns the string representation, a 'pretty' OGC WKT.
"""
try:
return six.text_type(self.srs)
except Exception:
return six.text_type(self.wkt)
return six.text_type(self.srs)

View File

@@ -1,9 +1,8 @@
from django.contrib.gis import forms
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 HAS_GDAL
from django.contrib.gis.gdal.error import GDALException
from django.contrib.gis.geometry.backend import Geometry, GeometryException
from django.core.exceptions import ImproperlyConfigured
@@ -186,18 +185,16 @@ class BaseSpatialField(Field):
"""
Return a GDALRaster if conversion is successful, otherwise return None.
"""
from django.contrib.gis.gdal import GDALRaster
if isinstance(value, GDALRaster):
if isinstance(value, gdal.GDALRaster):
return value
elif is_candidate:
try:
return GDALRaster(value)
return gdal.GDALRaster(value)
except GDALException:
pass
elif isinstance(value, dict):
try:
return GDALRaster(value)
return gdal.GDALRaster(value)
except GDALException:
raise ValueError("Couldn't create spatial object from lookup value '%s'." % value)
@@ -228,10 +225,8 @@ class BaseSpatialField(Field):
else:
# Check if input is a candidate for conversion to raster or geometry.
is_candidate = isinstance(obj, (bytes, six.string_types)) or hasattr(obj, '__geo_interface__')
# With GDAL installed, try to convert the input to raster.
raster = False
if HAS_GDAL:
raster = self.get_raster_prep_value(obj, is_candidate)
# Try to convert the input to raster.
raster = self.get_raster_prep_value(obj, is_candidate)
if raster:
obj = raster
@@ -425,11 +420,6 @@ class RasterField(BaseSpatialField):
geom_type = 'RASTER'
geography = False
def __init__(self, *args, **kwargs):
if not HAS_GDAL:
raise ImproperlyConfigured('RasterField requires GDAL.')
super(RasterField, self).__init__(*args, **kwargs)
def _check_connection(self, connection):
# Make sure raster fields are used only on backends with raster support.
if not connection.features.gis_enabled or not connection.features.supports_raster:
@@ -451,13 +441,11 @@ class RasterField(BaseSpatialField):
def contribute_to_class(self, cls, name, **kwargs):
super(RasterField, self).contribute_to_class(cls, name, **kwargs)
# Importing GDALRaster raises an exception on systems without gdal.
from django.contrib.gis.gdal import GDALRaster
# Setup for lazy-instantiated Raster object. For large querysets, the
# instantiation of all GDALRasters can potentially be expensive. This
# delays the instantiation of the objects to the moment of evaluation
# of the raster attribute.
setattr(cls, self.attname, SpatialProxy(GDALRaster, self))
setattr(cls, self.attname, SpatialProxy(gdal.GDALRaster, self))
def get_transform(self, name):
try:

View File

@@ -91,6 +91,7 @@ class OSMWidget(BaseGeometryWidget):
template_name = 'gis/openlayers-osm.html'
default_lon = 5
default_lat = 47
map_srid = 3857
class Media:
js = (
@@ -104,12 +105,3 @@ class OSMWidget(BaseGeometryWidget):
self.attrs[key] = getattr(self, key)
if attrs:
self.attrs.update(attrs)
@property
def map_srid(self):
# Use the official spherical mercator projection SRID when GDAL is
# available; otherwise, fallback to 900913.
if gdal.HAS_GDAL:
return 3857
else:
return 900913

View File

@@ -24,12 +24,6 @@
library name for the current OS. The default library path may be overridden
by setting `GDAL_LIBRARY_PATH` in your settings with the path to the GDAL C
library on your system.
GDAL links to a large number of external libraries that consume RAM when
loaded. Thus, it may desirable to disable GDAL on systems with limited
RAM resources -- this may be accomplished by setting `GDAL_LIBRARY_PATH`
to a non-existent file location (e.g., `GDAL_LIBRARY_PATH='/null/path'`;
setting to None/False/'' will not work as a string must be given).
"""
from django.contrib.gis.gdal.envelope import Envelope
from django.contrib.gis.gdal.error import ( # NOQA

View File

@@ -64,8 +64,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
g = wkb_r().read(force_bytes(geo_input))
elif json_regex.match(geo_input):
# Handling GeoJSON input.
if not gdal.HAS_GDAL:
raise ValueError('Initializing geometry from JSON input requires GDAL.')
g = wkb_r().read(gdal.OGRGeometry(geo_input).wkb)
else:
raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.')
@@ -476,8 +474,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
@property
def ogr(self):
"Returns the OGR Geometry for this Geometry."
if not gdal.HAS_GDAL:
raise GEOSException('GDAL required to convert to an OGRGeometry.')
if self.srid:
try:
return gdal.OGRGeometry(self.wkb, self.srid)
@@ -488,8 +484,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
@property
def srs(self):
"Returns the OSR SpatialReference for SRID of this Geometry."
if not gdal.HAS_GDAL:
raise GEOSException('GDAL required to return a SpatialReference object.')
if self.srid:
try:
return gdal.SpatialReference(self.srid)
@@ -520,9 +514,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
else:
return
if not gdal.HAS_GDAL:
raise GEOSException("GDAL library is not available to transform() geometry.")
if isinstance(ct, gdal.CoordTransform):
# We don't care about SRID because CoordTransform presupposes
# source SRS.

View File

@@ -98,8 +98,6 @@ class Command(BaseCommand):
def handle(self, *args, **options):
data_source, model_name = options.pop('data_source'), options.pop('model_name')
if not gdal.HAS_GDAL:
raise CommandError('GDAL is required to inspect geospatial data sources.')
# Getting the OGR DataSource from the string parameter.
try:

View File

@@ -1,9 +1,7 @@
from __future__ import unicode_literals
from django.contrib.gis.gdal import HAS_GDAL
from django.core.serializers.base import (
SerializationError, SerializerDoesNotExist,
)
from django.core.serializers.base import SerializerDoesNotExist
from django.core.serializers.json import Serializer as JSONSerializer
if HAS_GDAL:
@@ -53,10 +51,6 @@ class Serializer(JSONSerializer):
if self._geometry:
if self._geometry.srid != self.srid:
# If needed, transform the geometry in the srid of the global geojson srid
if not HAS_GDAL:
raise SerializationError(
'Unable to convert geometry to SRID %s when GDAL is not installed.' % self.srid
)
if self._geometry.srid not in self._cts:
srs = SpatialReference(self.srid)
self._cts[self._geometry.srid] = CoordTransform(self._geometry.srs, srs)