Repository: SciTools/cartopy

Issue:
Add support for latitude of true scale to Mercator
While most Mercator projections use 0 deg as the latitude of true scale (default), it is possible to modify this latitude using the "+lat_ts" parameter in Proj4.  This parameter currently isn't supported in the Mercator projection class.  



Discussion:
Sure. If you're interested in submitting a PR (and following https://github.com/SciTools/cartopy/blob/master/CONTRIBUTING.md) I can't see a reason why it shouldn't be accepted. There may be some complexity in the domain calculation - https://github.com/SciTools/cartopy/blob/master/lib/cartopy/crs.py#L963 - but shouldn't be anything insurmountable.

Cheers!


Resolving patch:
diff --git a/lib/cartopy/crs.py b/lib/cartopy/crs.py
--- a/lib/cartopy/crs.py
+++ b/lib/cartopy/crs.py
@@ -940,7 +940,7 @@ class Mercator(Projection):
 
     def __init__(self, central_longitude=0.0,
                  min_latitude=-80.0, max_latitude=84.0,
-                 globe=None):
+                 globe=None, latitude_true_scale=0.0):
         """
         Kwargs:
 
@@ -951,11 +951,13 @@ def __init__(self, central_longitude=0.0,
                              Defaults to 84 degrees.
             * globe - A :class:`cartopy.crs.Globe`.
                       If omitted, a default globe is created.
+            * latitude_true_scale - the latitude where the scale is 1.
+                                    Defaults to 0 degrees.
 
         """
         proj4_params = [('proj', 'merc'),
                         ('lon_0', central_longitude),
-                        ('k', 1),
+                        ('lat_ts', latitude_true_scale),
                         ('units', 'm')]
         super(Mercator, self).__init__(proj4_params, globe=globe)
 


Test patch:
diff --git a/lib/cartopy/tests/crs/test_mercator.py b/lib/cartopy/tests/crs/test_mercator.py
--- a/lib/cartopy/tests/crs/test_mercator.py
+++ b/lib/cartopy/tests/crs/test_mercator.py
@@ -28,8 +28,8 @@
 def test_default():
     crs = ccrs.Mercator()
 
-    assert_equal(crs.proj4_init, ('+ellps=WGS84 +proj=merc +lon_0=0.0 +k=1 '
-                                  '+units=m +no_defs'))
+    assert_equal(crs.proj4_init, ('+ellps=WGS84 +proj=merc +lon_0=0.0 '
+                                  '+lat_ts=0.0 +units=m +no_defs'))
     assert_almost_equal(crs.boundary.bounds,
                         [-20037508, -15496571, 20037508, 18764656], decimal=0)
 
@@ -39,7 +39,7 @@ def test_eccentric_globe():
                        ellipse=None)
     crs = ccrs.Mercator(globe=globe, min_latitude=-40, max_latitude=40)
     assert_equal(crs.proj4_init, ('+a=10000 +b=5000 +proj=merc +lon_0=0.0 '
-                                  '+k=1 +units=m +no_defs'))
+                                  '+lat_ts=0.0 +units=m +no_defs'))
 
     assert_almost_equal(crs.boundary.bounds,
                         [-31415.93, -2190.5, 31415.93, 2190.5], decimal=2)
@@ -64,7 +64,7 @@ def test_equality():
 def test_central_longitude():
     cl = 10.0
     crs = ccrs.Mercator(central_longitude=cl)
-    proj4_str = ('+ellps=WGS84 +proj=merc +lon_0={} +k=1 '
+    proj4_str = ('+ellps=WGS84 +proj=merc +lon_0={} +lat_ts=0.0 '
                  '+units=m +no_defs'.format(cl))
     assert_equal(crs.proj4_init, proj4_str)
 
@@ -72,6 +72,17 @@ def test_central_longitude():
                         [-20037508, -15496570, 20037508, 18764656], decimal=0)
 
 
+def test_latitude_true_scale():
+    lat_ts = 20.0
+    crs = ccrs.Mercator(latitude_true_scale=lat_ts)
+    proj4_str = ('+ellps=WGS84 +proj=merc +lon_0=0.0 +lat_ts={} '
+                 '+units=m +no_defs'.format(lat_ts))
+    assert_equal(crs.proj4_init, proj4_str)
+
+    assert_almost_equal(crs.boundary.bounds,
+                        [-18836475, -14567718, 18836475, 17639917], decimal=0)
+
+
 if __name__ == '__main__':
     import nose
     nose.runmodule(argv=['-s', '--with-doctest'], exit=False)