Repository: sunpy/sunpy

Issue:
`GenericMap.draw_contours()`: don't run internal transform code if `transform` keyword is provided
#5398 fixed some issues in `GenericMap.draw_contours()` by internally transforming the map to the axes WCS if the map WCS was different.  However, we should run this code only if the `transform` keyword argument was not provided, because we shouldn't do the internal transformation if the user is providing a specific transformation to use.


Discussion:
We could still do the internal transformation but use the provided one rather than getting our own for the `self.wcs`?
Sure, that would work.
This would then be the line to change:
https://github.com/sunpy/sunpy/blob/3189938ac16df5aefc713a424240f4dc54914711/sunpy/map/mapbase.py#L2353

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -2360,7 +2360,11 @@ def draw_contours(self, levels, axes=None, *, fill=False, **contour_args):
         # We do this instead of using the `transform` keyword argument so that Matplotlib does not
         # get confused about the bounds of the contours
         if self.wcs is not axes.wcs:
-            transform = axes.get_transform(self.wcs) - axes.transData  # pixel->pixel transform
+            if "transform" in contour_args:
+                transform_orig = contour_args.pop("transform")
+            else:
+                transform_orig = axes.get_transform(self.wcs)
+            transform = transform_orig - axes.transData  # pixel->pixel transform
             x_1d, y_1d = transform.transform(np.stack([x.ravel(), y.ravel()]).T).T
             x, y = np.reshape(x_1d, x.shape), np.reshape(y_1d, y.shape)
 


Test patch:
diff --git a/sunpy/map/tests/conftest.py b/sunpy/map/tests/conftest.py
--- a/sunpy/map/tests/conftest.py
+++ b/sunpy/map/tests/conftest.py
@@ -140,3 +140,15 @@ def eit_test_map():
     Load SunPy's test EIT image.
     """
     return get_dummy_map_from_header(get_test_filepath("EIT_header/efz20040301.020010_s.header"))
+
+@pytest.fixture
+def sample_171():
+    from sunpy.data.sample import AIA_171_IMAGE
+
+    return sunpy.map.Map(AIA_171_IMAGE)
+
+@pytest.fixture
+def sample_hmi():
+    from sunpy.data.sample import HMI_LOS_IMAGE
+
+    return sunpy.map.Map(HMI_LOS_IMAGE)
diff --git a/sunpy/map/tests/test_mapbase.py b/sunpy/map/tests/test_mapbase.py
--- a/sunpy/map/tests/test_mapbase.py
+++ b/sunpy/map/tests/test_mapbase.py
@@ -10,6 +10,7 @@
 import pytest
 from hypothesis import HealthCheck, given, settings
 from matplotlib.figure import Figure
+from matplotlib.transforms import Affine2D
 from packaging.version import Version
 
 import astropy.units as u
@@ -1653,6 +1654,34 @@ def test_rotation_rect_pixelated_data(aia171_test_map):
     rect_rot_map = rect_map.rotate(30 * u.deg)
     rect_rot_map.peek()
 
+@pytest.mark.remote_data
+@figure_test
+def test_draw_contours_with_transform(sample_171, sample_hmi):
+    aia_map = sunpy.map.Map(sample_171)
+    hmi_map = sunpy.map.Map(sample_hmi)
+    fig = plt.figure(figsize=(16, 4))
+
+    # Panel 1: Implicit transform
+    ax1 = fig.add_subplot(1, 3, 1, projection=aia_map)
+    aia_map.plot(axes=ax1, clip_interval=(1, 99.99)*u.percent)
+    hmi_map.draw_contours([-10, 10]*u.percent)
+    ax1.set_title('Default, correct behavior')
+
+    # Panel 2: Explicit transform
+    ax2 = fig.add_subplot(1, 3, 2, projection=aia_map)
+    aia_map.plot(axes=ax2, clip_interval=(1, 99.99)*u.percent)
+    hmi_map.draw_contours([-10, 10]*u.percent, transform=ax2.get_transform(hmi_map.wcs))
+    ax2.set_title('Explicitly specifying the correct transform')
+
+    # Panel 3: Explicit transform with wacky rotation
+    ax3 = fig.add_subplot(1, 3, 3, projection=aia_map)
+    rotate_transform = Affine2D().rotate_deg_around(512, 512, 90)
+    composite_transform = rotate_transform + ax3.get_transform(hmi_map.wcs)
+    aia_map.plot(axes=ax3, clip_interval=(1, 99.99)*u.percent)
+    hmi_map.draw_contours([-10, 10]*u.percent, transform=composite_transform)
+    ax3.set_title('Contours rotated by 90 deg CCW')
+
+    return fig
 
 @pytest.mark.parametrize('method', _rotation_registry.keys())
 @figure_test
diff --git a/sunpy/tests/figure_hashes_mpl_382_ft_261_astropy_600_animators_111.json b/sunpy/tests/figure_hashes_mpl_382_ft_261_astropy_600_animators_111.json
--- a/sunpy/tests/figure_hashes_mpl_382_ft_261_astropy_600_animators_111.json
+++ b/sunpy/tests/figure_hashes_mpl_382_ft_261_astropy_600_animators_111.json
@@ -10,6 +10,7 @@
   "sunpy.map.tests.test_compositemap.test_peek_composite_map": "a21143a11c499d95b3618d2f82a6d6e8a3cbad6291ab52b20b218b3f4a03c45b",
   "sunpy.map.tests.test_map_factory.test_map_jp2_HMI": "3b731f25ab5777f187def8296fbed88d27809867405e0e97d5e808ca1bb4b42c",
   "sunpy.map.tests.test_mapbase.test_rotation_rect_pixelated_data": "5b37819c36a50a2a7a26944a2f1481027dd17390e73158f6b5510034102d35bb",
+  "sunpy.map.tests.test_mapbase.test_draw_contours_with_transform": "c0c7a13980dd5302e6a365080a9933caa70fab9e313d166c53384490218a2eea",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[scipy]": "9168fca9a4e04def3da12624196b29ebe954004ad269faa4bab11276a85366af",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[scikit-image]": "17748c119a97c112e77fcb368af595f2b5faada067a7188abe846c7b160f9ea8",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[opencv]": "cd86fb9c0ed0e26eaae00d816d0b5d3f381b2be22b8e99d45afe5a511ccd55e5",
diff --git a/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json b/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
--- a/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
+++ b/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
@@ -10,6 +10,7 @@
   "sunpy.map.tests.test_compositemap.test_peek_composite_map": "601847a982ee4a372def1eede3a992921a33986735e6ec5b7ca1a92a7a056846",
   "sunpy.map.tests.test_map_factory.test_map_jp2_HMI": "d54c99b975dc2e5805bfd39e00627ad92fba9fe2d38e510fc72b143cfbc28e4c",
   "sunpy.map.tests.test_mapbase.test_rotation_rect_pixelated_data": "356822787795278a4dab61bb8bbf5d62cc29d979ec1280653286a85b6188e944",
+  "sunpy.map.tests.test_mapbase.test_draw_contours_with_transform": "cb1bfc0b30651b48df1430782ec43ebde5f2a785b892f99e4b9763abb4758221",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[scipy]": "c15c6c707b3b0e707936988054aeb818eee7b39e2249db5309ca43585d325a51",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[scikit-image]": "911a0442055922f0d31dd48d8f1ede3d1b8b4d11a55f00a3403e8124761c2346",
   "sunpy.map.tests.test_mapbase.test_derotating_nonpurerotation_pcij[opencv]": "072505693731f65824dcaa7f0843087990f15f34b519285a2bc8965a1fa0ed37",