Commit f7a363ee authored by Claude Paroz's avatar Claude Paroz
Browse files

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

Thanks Tim Graham for the review.
parent 7def55c3
Loading
Loading
Loading
Loading
+1 −8
Original line number Diff line number Diff line
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)
+35 −108
Original line number Diff line number Diff line
import re

from django.contrib.gis import gdal
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
@@ -11,24 +9,11 @@ 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.
@@ -50,8 +35,6 @@ class SpatialRefSysMixin(object):
                msg = e

            raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
        else:
            raise Exception('GDAL is not installed.')

    @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

    @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')

    @property
    def local(self):
        "Is this Spatial Reference local?"
        if gdal.HAS_GDAL:
        return self.srs.local
        else:
            return self.wkt.startswith('LOCAL_CS')

    @property
    def geographic(self):
        "Is this Spatial Reference geographic?"
        if gdal.HAS_GDAL:
        return self.srs.geographic
        else:
            return self.wkt.startswith('GEOGCS')

    @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')

    @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')

    @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')

    @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')

    @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')

    @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

        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)
+7 −19
Original line number Diff line number Diff line
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,9 +225,7 @@ 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:
            # Try to convert the input to raster.
            raster = self.get_raster_prep_value(obj, is_candidate)

            if 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:
+1 −9
Original line number Diff line number Diff line
@@ -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
+0 −6
Original line number Diff line number Diff line
@@ -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
Loading