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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user