Repository: SciTools/cartopy

Issue:
Adopt NEP29: minimum dependency policy
After Cartopy 0.18, we will be dropping support for Python 2. Currently, for Python 3, we have a minimum of 3.5. This is somewhat problematic on conda-forge because they've already dropped support for it.

For the future, we should implement something like [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html) as a minimum dependency policy. For Python, this is a 42-month window and for NumPy, it is a 24-month window.

Assuming that 0.18 is out by the end of the month, and 0.19 takes at least 3 months, that'd be end of July. Extending that policy to other dependencies using 42 months (January 30, 2017), that would mean:
* Python 3.7
* NumPy 1.16
* Cython 0.26 (older than current requirement)
* Proj 5.0.0
* GEOS 3.6.2
* Shapely 1.6b4
* pyshp 1.2.11 (older than current requirement)
* pyepsg 0.3.2 (older than current requirement)
* OWSLib 0.15.0
* Pillow 4.1.0
* Matplotlib 2.0.1
* GDAL 2.2.0
* SciPy 0.19.0
* pytest 3.0.7 (older than current requirement)

Extending that policy to other dependencies using 24 months (July 30, 2018) instead would mean:
* Python 3.7
* NumPy 1.16
* Cython 0.28.5
* Proj 5.2.0
* GEOS 3.6.3
* Shapely 1.7a1
* pyshp 2.0.0
* pyepsg 0.4.0
* OWSLib 0.17.0
* Pillow 5.3.0
* Matplotlib 2.2.3
* GDAL 2.3.2
* SciPy 1.2.0
* pytest 3.7.0

Though note, we probably shouldn't switch minimums to alphas or betas; that's just there for reference. We may also want slightly different policy for system deps like Proj and GEOS.


Discussion:
I'm all for this and would prefer the 24-month version. Python 3.5 is getting pretty old at this point even and we aren't even testing against Python 3.7 yet and this is a minimum version in your findings here.
I’d be good to go with things just like NEP 29: Longer for Python itself (so we’d be going back to 3.7) and 24 months for everything else.

Resolving patch:
diff --git a/lib/cartopy/img_transform.py b/lib/cartopy/img_transform.py
--- a/lib/cartopy/img_transform.py
+++ b/lib/cartopy/img_transform.py
@@ -269,10 +269,7 @@ def regrid(array, source_x_coords, source_y_coords, source_cs, target_proj,
     else:
         # Versions of scipy >= v0.16 added the balanced_tree argument,
         # which caused the KDTree to hang with this input.
-        try:
-            kdtree = scipy.spatial.cKDTree(xyz, balanced_tree=False)
-        except TypeError:
-            kdtree = scipy.spatial.cKDTree(xyz)
+        kdtree = scipy.spatial.cKDTree(xyz, balanced_tree=False)
         _, indices = kdtree.query(target_xyz, k=1)
     mask = indices >= len(xyz)
     indices[mask] = 0
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -47,9 +47,9 @@
 
 if not IS_SDIST or FORCE_CYTHON:
     import Cython
-    if Cython.__version__ < '0.28':
+    if Cython.__version__ < '0.29':
         raise ImportError(
-            "Cython 0.28+ is required to install cartopy from source.")
+            "Cython 0.29+ is required to install cartopy from source.")
 
     from Cython.Distutils import build_ext as cy_build_ext
 
@@ -61,7 +61,7 @@
 
 
 # Please keep in sync with INSTALL file.
-GEOS_MIN_VERSION = (3, 3, 3)
+GEOS_MIN_VERSION = (3, 7, 2)
 PROJ_MIN_VERSION = (4, 9, 0)
 
 


Test patch:
diff --git a/lib/cartopy/tests/io/test_ogc_clients.py b/lib/cartopy/tests/io/test_ogc_clients.py
--- a/lib/cartopy/tests/io/test_ogc_clients.py
+++ b/lib/cartopy/tests/io/test_ogc_clients.py
@@ -35,16 +35,10 @@ class TestWMSRasterSource:
 
     def test_string_service(self):
         source = ogc.WMSRasterSource(self.URI, self.layer)
-        if isinstance(WebMapService, type):
-            # OWSLib < 0.13.0
-            assert isinstance(source.service, WebMapService)
-        else:
-            # OWSLib >= 0.13.0: WebMapService is a function that creates
-            # instances of these two classes.
-            from owslib.map.wms111 import WebMapService_1_1_1
-            from owslib.map.wms130 import WebMapService_1_3_0
-            assert isinstance(source.service,
-                              (WebMapService_1_1_1, WebMapService_1_3_0))
+        from owslib.map.wms111 import WebMapService_1_1_1
+        from owslib.map.wms130 import WebMapService_1_3_0
+        assert isinstance(source.service,
+                          (WebMapService_1_1_1, WebMapService_1_3_0))
         assert isinstance(source.layers, list)
         assert source.layers == [self.layer]