Browse Source

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

Thanks Tim Graham for the review.
Claude Paroz 8 years ago
parent
commit
f7a363ee1d

+ 1 - 8
django/contrib/gis/admin/options.py

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

+ 35 - 108
django/contrib/gis/db/backends/base/models.py

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

+ 7 - 19
django/contrib/gis/db/models/fields.py

@@ -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:

+ 1 - 9
django/contrib/gis/forms/widgets.py

@@ -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
django/contrib/gis/gdal/__init__.py

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

+ 0 - 9
django/contrib/gis/geos/geometry.py

@@ -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.

+ 0 - 2
django/contrib/gis/management/commands/ogrinspect.py

@@ -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 - 7
django/contrib/gis/serializers/geojson.py

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

+ 3 - 3
docs/ref/contrib/gis/db-api.txt

@@ -91,9 +91,9 @@ transform procedure::
 
 Thus, geometry parameters may be passed in using the ``GEOSGeometry`` object, WKT
 (Well Known Text [#fnwkt]_), HEXEWKB (PostGIS specific -- a WKB geometry in
-hexadecimal [#fnewkb]_), and GeoJSON [#fngeojson]_ (requires GDAL). Essentially,
-if the input is not a ``GEOSGeometry`` object, the geometry field will attempt to
-create a ``GEOSGeometry`` instance from the input.
+hexadecimal [#fnewkb]_), and GeoJSON [#fngeojson]_. Essentially, if the input is
+not a ``GEOSGeometry`` object, the geometry field will attempt to create a
+``GEOSGeometry`` instance from the input.
 
 For more information creating :class:`~django.contrib.gis.geos.GEOSGeometry`
 objects, refer to the :ref:`GEOS tutorial <geos-tutorial>`.

+ 5 - 13
docs/ref/contrib/gis/geos.txt

@@ -195,7 +195,7 @@ Format                   Input Type
 WKT / EWKT               ``str`` or ``unicode``
 HEX / HEXEWKB            ``str`` or ``unicode``
 WKB / EWKB               ``buffer``
-GeoJSON (requires GDAL)  ``str`` or ``unicode``
+GeoJSON                  ``str`` or ``unicode``
 =======================  ======================
 
 Properties
@@ -345,10 +345,6 @@ another object.
     Returns an :class:`~django.contrib.gis.gdal.OGRGeometry` object
     corresponding to the GEOS geometry.
 
-    .. note::
-
-        Requires GDAL.
-
 .. _wkb:
 
 .. attribute:: GEOSGeometry.wkb
@@ -618,10 +614,6 @@ Other Properties & Methods
     Returns a :class:`~django.contrib.gis.gdal.SpatialReference` object
     corresponding to the SRID of the geometry or ``None``.
 
-    .. note::
-
-        Requires GDAL.
-
 .. method:: GEOSGeometry.transform(ct, clone=False)
 
     Transforms the geometry according to the given coordinate transformation
@@ -635,10 +627,10 @@ Other Properties & Methods
 
     .. note::
 
-        Requires GDAL. Raises :class:`~django.contrib.gis.geos.GEOSException` if
-        GDAL is not available or if the geometry's SRID is ``None`` or less than
-        0. It doesn't impose any constraints on the geometry's SRID if called
-        with a :class:`~django.contrib.gis.gdal.CoordTransform` object.
+        Raises :class:`~django.contrib.gis.geos.GEOSException` if GDAL is not
+        available or if the geometry's SRID is ``None`` or less than 0. It
+        doesn't impose any constraints on the geometry's SRID if called with a
+        :class:`~django.contrib.gis.gdal.CoordTransform` object.
 
         .. versionchanged:: 1.10
 

+ 6 - 8
docs/ref/contrib/gis/install/geolibs.txt

@@ -10,7 +10,7 @@ Program                   Description                           Required
 ========================  ====================================  ================================  ===================================
 :doc:`GEOS <../geos>`     Geometry Engine Open Source           Yes                               3.4, 3.3
 `PROJ.4`_                 Cartographic Projections library      Yes (PostgreSQL and SQLite only)  4.9, 4.8, 4.7, 4.6, 4.5, 4.4
-:doc:`GDAL <../gdal>`     Geospatial Data Abstraction Library   Yes (SQLite only)                 2.1, 2.0, 1.11, 1.10, 1.9, 1.8, 1.7
+:doc:`GDAL <../gdal>`     Geospatial Data Abstraction Library   Yes                               2.1, 2.0, 1.11, 1.10, 1.9, 1.8, 1.7
 :doc:`GeoIP <../geoip>`   IP-based geolocation library          No                                1.4
 `PostGIS`__               Spatial extensions for PostgreSQL     Yes (PostgreSQL only)             2.2, 2.1
 `SpatiaLite`__            Spatial extensions for SQLite         Yes (SQLite only)                 4.3, 4.2, 4.1, 4.0
@@ -19,6 +19,11 @@ Program                   Description                           Required
 Note that older or more recent versions of these libraries *may* also work
 totally fine with GeoDjango. Your mileage may vary.
 
+.. versionchanged:: 1.11
+
+    In older versions, GDAL is required only for SQLite. Now it's required for
+    all databases.
+
 ..
     Libs release dates:
     GEOS 3.3.0 2011-05-30
@@ -37,13 +42,6 @@ totally fine with GeoDjango. Your mileage may vary.
     Spatialite 4.2.0 2014-07-25
     Spatialite 4.3.0 2015-09-07
 
-.. admonition::  Install GDAL
-
-    While :ref:`gdalbuild` is technically not required, it is *recommended*.
-    Important features of GeoDjango (including the :doc:`../layermapping`,
-    geometry reprojection, and the geographic admin) depend on its
-    functionality.
-
 .. note::
 
     The GeoDjango interfaces to GEOS, GDAL, and GeoIP may be used

+ 3 - 3
docs/ref/contrib/gis/install/index.txt

@@ -58,9 +58,9 @@ supported versions, and any notes for each of the supported database backends:
 ==================  ==============================  ==================  =========================================
 Database            Library Requirements            Supported Versions  Notes
 ==================  ==============================  ==================  =========================================
-PostgreSQL          GEOS, PROJ.4, PostGIS           9.3+                Requires PostGIS.
-MySQL               GEOS                            5.5+                Not OGC-compliant; :ref:`limited functionality <mysql-spatial-limitations>`.
-Oracle              GEOS                            11.2+               XE not supported.
+PostgreSQL          GEOS, GDAL, PROJ.4, PostGIS     9.3+                Requires PostGIS.
+MySQL               GEOS, GDAL                      5.5+                Not OGC-compliant; :ref:`limited functionality <mysql-spatial-limitations>`.
+Oracle              GEOS, GDAL                      11.2+               XE not supported.
 SQLite              GEOS, GDAL, PROJ.4, SpatiaLite  3.6.+               Requires SpatiaLite 4.0+, pysqlite2 2.5+
 ==================  ==============================  ==================  =========================================
 

+ 0 - 4
docs/ref/contrib/gis/serializers.txt

@@ -8,10 +8,6 @@
 GeoDjango provides a specific serializer for the `GeoJSON`__ format. See
 :doc:`/topics/serialization` for more information on serialization.
 
-The GDAL library is required if any of the serialized geometries need
-coordinate transformations (that is if the geometry's spatial reference system
-differs from the ``srid`` serializer option).
-
 __ http://geojson.org/
 
 The ``geojson`` serializer is not meant for round-tripping data, as it has no

+ 3 - 8
docs/ref/contrib/gis/tutorial.txt

@@ -668,7 +668,7 @@ for popular geospatial formats::
     MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
     >>> sm.mpoly.wkb # WKB (as Python binary buffer)
     <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
-    >>> sm.mpoly.geojson # GeoJSON (requires GDAL)
+    >>> sm.mpoly.geojson # GeoJSON
     '{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
 
 This includes access to all of the advanced geometric operations provided by
@@ -753,13 +753,8 @@ This provides more context (including street and thoroughfare details) than
 available with the :class:`~django.contrib.gis.admin.GeoModelAdmin`
 (which uses the `Vector Map Level 0`_ WMS dataset hosted at `OSGeo`_).
 
-First, there are some important requirements:
-
-* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that
-  :doc:`GDAL <gdal>` is installed.
-
-* The PROJ.4 datum shifting files must be installed (see the
-  :ref:`PROJ.4 installation instructions <proj4>` for more details).
+The PROJ.4 datum shifting files must be installed (see the :ref:`PROJ.4
+installation instructions <proj4>` for more details).
 
 If you meet this requirement, then just substitute the ``OSMGeoAdmin``
 option class in your ``admin.py`` file::

+ 7 - 0
docs/releases/1.11.txt

@@ -227,6 +227,13 @@ Validators
 Backwards incompatible changes in 1.11
 ======================================
 
+:mod:`django.contrib.gis`
+-------------------------
+
+* To simplify the codebase and because it's easier to install than when
+  ``contrib.gis`` was first released, :ref:`gdalbuild` is now a required
+  dependency for GeoDjango. In older versions, it's only required for SQLite.
+
 Database backend API
 --------------------
 

+ 19 - 25
tests/gis_tests/distapp/tests.py

@@ -1,16 +1,13 @@
 from __future__ import unicode_literals
 
-from unittest import skipUnless
-
 from django.contrib.gis.db.models.functions import (
     Area, Distance, Length, Perimeter, Transform,
 )
-from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.geos import GEOSGeometry, LineString, Point
 from django.contrib.gis.measure import D  # alias for Distance
 from django.db import connection
 from django.db.models import F, Q
-from django.test import TestCase, ignore_warnings, mock, skipUnlessDBFeature
+from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
 from django.utils.deprecation import RemovedInDjango20Warning
 
 from ..utils import no_oracle, oracle, postgis
@@ -497,7 +494,6 @@ class DistanceFunctionsTests(TestCase):
             tol
         )
 
-    @skipUnless(HAS_GDAL, "GDAL is required.")
     @skipUnlessDBFeature("has_Distance_function", "has_Transform_function")
     def test_distance_projected(self):
         """
@@ -520,26 +516,24 @@ class DistanceFunctionsTests(TestCase):
                         455411.438904354, 519386.252102563, 696139.009211594,
                         232513.278304279, 542445.630586414, 456679.155883207]
 
-        for has_gdal in [False, True]:
-            with mock.patch('django.contrib.gis.gdal.HAS_GDAL', has_gdal):
-                # Testing using different variations of parameters and using models
-                # with different projected coordinate systems.
-                dist1 = SouthTexasCity.objects.annotate(distance=Distance('point', lagrange)).order_by('id')
-                if oracle:
-                    dist_qs = [dist1]
-                else:
-                    dist2 = SouthTexasCityFt.objects.annotate(distance=Distance('point', lagrange)).order_by('id')
-                    dist_qs = [dist1, dist2]
-
-                # Original query done on PostGIS, have to adjust AlmostEqual tolerance
-                # for Oracle.
-                tol = 2 if oracle else 5
-
-                # Ensuring expected distances are returned for each distance queryset.
-                for qs in dist_qs:
-                    for i, c in enumerate(qs):
-                        self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
-                        self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
+        # Testing using different variations of parameters and using models
+        # with different projected coordinate systems.
+        dist1 = SouthTexasCity.objects.annotate(distance=Distance('point', lagrange)).order_by('id')
+        if oracle:
+            dist_qs = [dist1]
+        else:
+            dist2 = SouthTexasCityFt.objects.annotate(distance=Distance('point', lagrange)).order_by('id')
+            dist_qs = [dist1, dist2]
+
+        # Original query done on PostGIS, have to adjust AlmostEqual tolerance
+        # for Oracle.
+        tol = 2 if oracle else 5
+
+        # Ensuring expected distances are returned for each distance queryset.
+        for qs in dist_qs:
+            for i, c in enumerate(qs):
+                self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
+                self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
 
     @skipUnlessDBFeature("has_Distance_function", "supports_distance_geodetic")
     def test_distance_geodetic(self):

+ 3 - 8
tests/gis_tests/geo3d/tests.py

@@ -2,13 +2,11 @@ from __future__ import unicode_literals
 
 import os
 import re
-from unittest import skipUnless
 
 from django.contrib.gis.db.models import Extent3D, Union
 from django.contrib.gis.db.models.functions import (
     AsGeoJSON, AsKML, Length, Perimeter, Scale, Translate,
 )
-from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
 from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
 from django.utils._os import upath
@@ -19,10 +17,6 @@ from .models import (
     MultiPoint3D, Point2D, Point3D, Polygon2D, Polygon3D,
 )
 
-if HAS_GDAL:
-    from django.contrib.gis.utils import LayerMapping, LayerMapError
-
-
 data_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), '..', 'data'))
 city_file = os.path.join(data_path, 'cities', 'cities.shp')
 vrt_file = os.path.join(data_path, 'test_vrt', 'test_vrt.vrt')
@@ -101,7 +95,6 @@ class Geo3DLoadingHelper(object):
         Polygon3D.objects.create(name='3D BBox', poly=bbox_3d)
 
 
-@skipUnless(HAS_GDAL, "GDAL is required for Geo3DTest.")
 @skipUnlessDBFeature("gis_enabled", "supports_3d_storage")
 class Geo3DTest(Geo3DLoadingHelper, TestCase):
     """
@@ -147,6 +140,9 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
         """
         Testing LayerMapping on 3D models.
         """
+        # Import here as GDAL is required for those imports
+        from django.contrib.gis.utils import LayerMapping, LayerMapError
+
         point_mapping = {'point': 'POINT'}
         mpoint_mapping = {'mpoint': 'MULTIPOINT'}
 
@@ -310,7 +306,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
                 self.assertEqual(city_dict[city.name][2] + ztrans, city.translate.z)
 
 
-@skipUnless(HAS_GDAL, "GDAL is required for Geo3DTest.")
 @skipUnlessDBFeature("gis_enabled", "supports_3d_functions")
 class Geo3DFunctionsTests(Geo3DLoadingHelper, TestCase):
     def test_kml(self):

+ 0 - 12
tests/gis_tests/geoadmin/models.py

@@ -17,17 +17,5 @@ class City(models.Model):
         return self.name
 
 
-@python_2_unicode_compatible
-class CityMercator(models.Model):
-    name = models.CharField(max_length=30)
-    point = models.PointField(srid=3857)
-
-    class Meta:
-        required_db_features = ['gis_enabled']
-
-    def __str__(self):
-        return self.name
-
 site = admin.AdminSite(name='admin_gis')
 site.register(City, admin.OSMGeoAdmin)
-site.register(CityMercator, admin.OSMGeoAdmin)

+ 2 - 22
tests/gis_tests/geoadmin/tests.py

@@ -1,15 +1,11 @@
 from __future__ import unicode_literals
 
-from unittest import skipUnless
-
 from django.contrib.gis import admin
-from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.geos import Point
-from django.core.exceptions import ImproperlyConfigured
-from django.test import TestCase, mock, override_settings, skipUnlessDBFeature
+from django.test import TestCase, override_settings, skipUnlessDBFeature
 
 from .admin import UnmodifiableAdmin
-from .models import City, CityMercator, site
+from .models import City, site
 
 
 @skipUnlessDBFeature("gis_enabled")
@@ -56,22 +52,6 @@ class GeoAdminTest(TestCase):
             """"http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic', format: 'image/jpeg'});""",
             result)
 
-    @mock.patch('django.contrib.gis.admin.options.HAS_GDAL', False)
-    def test_no_gdal_admin_model_diffent_srid(self):
-        msg = (
-            'Map SRID is 3857 and SRID of `geoadmin.City.point` is 4326. '
-            'GDAL must be installed to perform the transformation.'
-        )
-        with self.assertRaisesMessage(ImproperlyConfigured, msg):
-            geoadmin = site._registry[City]
-            geoadmin.get_changelist_form(None)()
-
-    @mock.patch('django.contrib.gis.admin.options.HAS_GDAL', False)
-    def test_no_gdal_admin_model_same_srid(self):
-        geoadmin = site._registry[CityMercator]
-        geoadmin.get_changelist_form(None)()
-
-    @skipUnless(HAS_GDAL, "GDAL is required.")
     def test_olwidget_has_changed(self):
         """
         Check that changes are accurately noticed by OpenLayersWidget.

+ 1 - 10
tests/gis_tests/geoapp/test_serializers.py

@@ -4,8 +4,7 @@ import json
 
 from django.contrib.gis.geos import LinearRing, Point, Polygon
 from django.core import serializers
-from django.test import TestCase, mock, skipUnlessDBFeature
-from django.utils import six
+from django.test import TestCase, skipUnlessDBFeature
 
 from .models import City, MultiFields, PennsylvaniaCity
 
@@ -89,14 +88,6 @@ class GeoJSONSerializerTests(TestCase):
             [1564802, 5613214]
         )
 
-    @mock.patch('django.contrib.gis.serializers.geojson.HAS_GDAL', False)
-    def test_without_gdal(self):
-        # Without coordinate transformation, the serialization should succeed:
-        serializers.serialize('geojson', City.objects.all())
-        with six.assertRaisesRegex(self, serializers.base.SerializationError, '.*GDAL is not installed'):
-            # Coordinate transformations need GDAL
-            serializers.serialize('geojson', City.objects.all(), srid=2847)
-
     def test_deserialization_exception(self):
         """
         GeoJSON cannot be deserialized.

+ 0 - 2
tests/gis_tests/geogapp/tests.py

@@ -8,7 +8,6 @@ from unittest import skipUnless
 
 from django.contrib.gis.db import models
 from django.contrib.gis.db.models.functions import Area, Distance
-from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.measure import D
 from django.db import connection
 from django.db.models.functions import Cast
@@ -70,7 +69,6 @@ class GeographyTest(TestCase):
         with self.assertRaises(ValueError):
             City.objects.get(point__exact=htown.point)
 
-    @skipUnless(HAS_GDAL, "GDAL is required.")
     def test05_geography_layermapping(self):
         "Testing LayerMapping support on models with geography fields."
         # There is a similar test in `layermap` that uses the same data set,

+ 0 - 24
tests/gis_tests/geos_tests/test_geos.py

@@ -1092,19 +1092,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
         self.assertEqual(g1.srid, 4326)
         self.assertIsNot(g1, g, "Clone didn't happen")
 
-        with mock.patch('django.contrib.gis.gdal.HAS_GDAL', False):
-            g = GEOSGeometry('POINT (-104.609 38.255)', 4326)
-            gt = g.tuple
-            g.transform(4326)
-            self.assertEqual(g.tuple, gt)
-            self.assertEqual(g.srid, 4326)
-
-            g = GEOSGeometry('POINT (-104.609 38.255)', 4326)
-            g1 = g.transform(4326, clone=True)
-            self.assertEqual(g1.tuple, g.tuple)
-            self.assertEqual(g1.srid, 4326)
-            self.assertIsNot(g1, g, "Clone didn't happen")
-
     @skipUnless(HAS_GDAL, "GDAL is required.")
     def test_transform_nosrid(self):
         """ Testing `transform` method (no SRID or negative SRID) """
@@ -1125,17 +1112,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
         with self.assertRaises(GEOSException):
             g.transform(2774, clone=True)
 
-    @mock.patch('django.contrib.gis.gdal.HAS_GDAL', False)
-    def test_transform_nogdal(self):
-        """ Testing `transform` method (GDAL not available) """
-        g = GEOSGeometry('POINT (-104.609 38.255)', 4326)
-        with self.assertRaises(GEOSException):
-            g.transform(2774)
-
-        g = GEOSGeometry('POINT (-104.609 38.255)', 4326)
-        with self.assertRaises(GEOSException):
-            g.transform(2774, clone=True)
-
     def test_extent(self):
         "Testing `extent` method."
         # The xmin, ymin, xmax, ymax of the MultiPoint should be returned.

+ 0 - 5
tests/gis_tests/gis_migrations/test_operations.py

@@ -1,9 +1,6 @@
 from __future__ import unicode_literals
 
-from unittest import skipUnless
-
 from django.contrib.gis.db.models import fields
-from django.contrib.gis.gdal import HAS_GDAL
 from django.core.exceptions import ImproperlyConfigured
 from django.db import connection, migrations, models
 from django.db.migrations.migration import Migration
@@ -117,7 +114,6 @@ class OperationTests(TransactionTestCase):
             self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
 
     @skipIfDBFeature('supports_raster')
-    @skipUnless(HAS_GDAL, 'A different error is raised if GDAL is not installed.')
     def test_create_raster_model_on_db_without_raster_support(self):
         """
         Test creating a model with a raster field on a db without raster support.
@@ -127,7 +123,6 @@ class OperationTests(TransactionTestCase):
             self.set_up_test_model(True)
 
     @skipIfDBFeature('supports_raster')
-    @skipUnless(HAS_GDAL, 'A different error is raised if GDAL is not installed.')
     def test_add_raster_field_on_db_without_raster_support(self):
         """
         Test adding a raster field on a db without raster support.

+ 2 - 4
tests/gis_tests/inspectapp/tests.py

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
 
 import os
 import re
-from unittest import skipUnless
 
 from django.contrib.gis.gdal import HAS_GDAL
 from django.core.management import call_command
@@ -20,9 +19,8 @@ if HAS_GDAL:
     from .models import AllOGRFields
 
 
-@skipUnless(HAS_GDAL, "InspectDbTests needs GDAL support")
+@skipUnlessDBFeature("gis_enabled")
 class InspectDbTests(TestCase):
-    @skipUnlessDBFeature("gis_enabled")
     def test_geom_columns(self):
         """
         Test the geo-enabled inspectdb command.
@@ -60,7 +58,7 @@ class InspectDbTests(TestCase):
             self.assertIn('poly = models.GeometryField(', output)
 
 
-@skipUnless(HAS_GDAL, "OGRInspectTest needs GDAL support")
+@skipUnlessDBFeature("gis_enabled")
 @modify_settings(
     INSTALLED_APPS={'append': 'django.contrib.gis'},
 )

+ 0 - 3
tests/gis_tests/layermap/tests.py

@@ -5,7 +5,6 @@ import os
 import unittest
 from copy import copy
 from decimal import Decimal
-from unittest import skipUnless
 
 from django.conf import settings
 from django.contrib.gis.gdal import HAS_GDAL
@@ -39,7 +38,6 @@ NUMS = [1, 2, 1, 19, 1]  # Number of polygons for each.
 STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
 
 
-@skipUnless(HAS_GDAL, "LayerMapTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 class LayerMapTest(TestCase):
 
@@ -337,7 +335,6 @@ class OtherRouter(object):
         return True
 
 
-@skipUnless(HAS_GDAL, "LayerMapRouterTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 @override_settings(DATABASE_ROUTERS=[OtherRouter()])
 class LayerMapRouterTest(TestCase):

+ 17 - 16
tests/gis_tests/rasterapp/models.py

@@ -1,23 +1,24 @@
-from ..models import models
-
+from django.contrib.gis.gdal import HAS_GDAL
 
-class RasterModel(models.Model):
-    rast = models.RasterField('A Verbose Raster Name', null=True, srid=4326, spatial_index=True, blank=True)
-    rastprojected = models.RasterField('A Projected Raster Table', srid=3086, null=True)
-    geom = models.PointField(null=True)
+from ..models import models
 
-    class Meta:
-        required_db_features = ['supports_raster']
+if HAS_GDAL:
+    class RasterModel(models.Model):
+        rast = models.RasterField('A Verbose Raster Name', null=True, srid=4326, spatial_index=True, blank=True)
+        rastprojected = models.RasterField('A Projected Raster Table', srid=3086, null=True)
+        geom = models.PointField(null=True)
 
-    def __str__(self):
-        return str(self.id)
+        class Meta:
+            required_db_features = ['supports_raster']
 
+        def __str__(self):
+            return str(self.id)
 
-class RasterRelatedModel(models.Model):
-    rastermodel = models.ForeignKey(RasterModel, models.CASCADE)
+    class RasterRelatedModel(models.Model):
+        rastermodel = models.ForeignKey(RasterModel, models.CASCADE)
 
-    class Meta:
-        required_db_features = ['supports_raster']
+        class Meta:
+            required_db_features = ['supports_raster']
 
-    def __str__(self):
-        return str(self.id)
+        def __str__(self):
+            return str(self.id)

+ 2 - 15
tests/gis_tests/rasterapp/test_rasterfield.py

@@ -7,18 +7,14 @@ from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.geos import GEOSGeometry
 from django.contrib.gis.measure import D
 from django.contrib.gis.shortcuts import numpy
-from django.core.exceptions import ImproperlyConfigured
 from django.db.models import Q
-from django.test import (
-    TestCase, TransactionTestCase, mock, skipUnlessDBFeature,
-)
+from django.test import TransactionTestCase, skipUnlessDBFeature
 
 from ..data.rasters.textrasters import JSON_RASTER
-from ..models import models
-from .models import RasterModel, RasterRelatedModel
 
 if HAS_GDAL:
     from django.contrib.gis.gdal import GDALRaster
+    from .models import RasterModel, RasterRelatedModel
 
 
 @skipUnlessDBFeature('supports_raster')
@@ -330,12 +326,3 @@ class RasterFieldTest(TransactionTestCase):
         msg = "Couldn't create spatial object from lookup value '%s'." % obj
         with self.assertRaisesMessage(ValueError, msg):
             RasterModel.objects.filter(geom__intersects=obj)
-
-
-@mock.patch('django.contrib.gis.db.models.fields.HAS_GDAL', False)
-class RasterFieldWithoutGDALTest(TestCase):
-
-    def test_raster_field_without_gdal_exception(self):
-        msg = 'RasterField requires GDAL.'
-        with self.assertRaisesMessage(ImproperlyConfigured, msg):
-            models.OriginalRasterField()

+ 0 - 7
tests/gis_tests/test_geoforms.py

@@ -1,14 +1,10 @@
-from unittest import skipUnless
-
 from django.contrib.gis import forms
-from django.contrib.gis.gdal import HAS_GDAL
 from django.contrib.gis.geos import GEOSGeometry
 from django.forms import ValidationError
 from django.test import SimpleTestCase, override_settings, skipUnlessDBFeature
 from django.utils.html import escape
 
 
-@skipUnless(HAS_GDAL, "GeometryFieldTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 class GeometryFieldTest(SimpleTestCase):
 
@@ -89,7 +85,6 @@ class GeometryFieldTest(SimpleTestCase):
         self.assertFalse(form.has_changed())
 
 
-@skipUnless(HAS_GDAL, "SpecializedFieldTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 class SpecializedFieldTest(SimpleTestCase):
     def setUp(self):
@@ -260,7 +255,6 @@ class SpecializedFieldTest(SimpleTestCase):
             self.assertFalse(GeometryForm(data={'g': invalid.wkt}).is_valid())
 
 
-@skipUnless(HAS_GDAL, "OSMWidgetTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 class OSMWidgetTest(SimpleTestCase):
     def setUp(self):
@@ -302,7 +296,6 @@ class OSMWidgetTest(SimpleTestCase):
                 rendered)
 
 
-@skipUnless(HAS_GDAL, "CustomGeometryWidgetTest needs GDAL support")
 @skipUnlessDBFeature("gis_enabled")
 class CustomGeometryWidgetTest(SimpleTestCase):
 

+ 1 - 10
tests/gis_tests/test_spatialrefsys.py

@@ -1,8 +1,7 @@
 import re
 import unittest
 
-from django.contrib.gis.gdal import HAS_GDAL
-from django.test import mock, skipUnlessDBFeature
+from django.test import skipUnlessDBFeature
 from django.utils import six
 
 from .utils import SpatialRefSys, oracle, postgis, spatialite
@@ -51,7 +50,6 @@ test_srs = ({
 })
 
 
-@unittest.skipUnless(HAS_GDAL, "SpatialRefSysTest needs gdal support")
 @skipUnlessDBFeature("has_spatialrefsys_table")
 class SpatialRefSysTest(unittest.TestCase):
 
@@ -61,13 +59,6 @@ class SpatialRefSysTest(unittest.TestCase):
         self.assertEqual(unit_name, 'degree')
         self.assertAlmostEqual(unit, 0.01745329251994328)
 
-    @mock.patch('django.contrib.gis.gdal.HAS_GDAL', False)
-    def test_get_units_without_gdal(self):
-        epsg_4326 = next(f for f in test_srs if f['srid'] == 4326)
-        unit, unit_name = SpatialRefSys().get_units(epsg_4326['wkt'])
-        self.assertEqual(unit_name, 'degree')
-        self.assertAlmostEqual(unit, 0.01745329251994328)
-
     def test_retrieve(self):
         """
         Test retrieval of SpatialRefSys model objects.