Repository: sunpy/sunpy

Issue:
No way to specify line colors for a CompositeMap plot that includes contours
If I want to specify the line colours of a contour map using `CompositeMap`, I think the correct code is:

```python
import matplotlib.pyplot as plt

import astropy.units as u
from astropy.coordinates import SkyCoord

import sunpy.data.sample
import sunpy.map

aia_map = sunpy.map.Map(sunpy.data.sample.AIA_171_IMAGE)
hmi_map = sunpy.map.Map(sunpy.data.sample.HMI_LOS_IMAGE)
comp_map = sunpy.map.Map(aia_map, hmi_map, composite=True)

comp_map.set_levels(index=1, levels=[-1000, -500, -250, 250, 500, 1000] * u.G)

fig = plt.figure()
ax = plt.subplot(projection=aia_map)
comp_map.plot(colors='black')
plt.show()
```

This fails however, as the keyword arguments given to `comp_map.plot()` are handed to *both* calls to `plot()` and `draw_contours()`, and an error is raised when `.plot(colors='black')` is passed (see below). The fix here is to work out a way to hand both `plot` and `draw_contours` keyword arguments separately to `CompositeMap.plot()`.

```python
Traceback (most recent call last):
  File "/Users/dstansby/github/sunpy/test.py", line 19, in <module>
    comp_map.plot(linewidths=1, colors='black')
  File "/Users/dstansby/github/sunpy/sunpy/map/compositemap.py", line 419, in plot
    ret.append(m.plot(**params))
  File "/Users/dstansby/github/astropy/astropy/units/decorators.py", line 253, in wrapper
    return_ = wrapped_function(*func_args, **func_kwargs)
  File "/Users/dstansby/github/sunpy/sunpy/map/mapbase.py", line 2467, in plot
    ret = axes.pcolormesh(data, **imshow_args)
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/__init__.py", line 1361, in inner
    return func(ax, *map(sanitize_sequence, args), **kwargs)
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/axes/_axes.py", line 6192, in pcolormesh
    collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords,
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/collections.py", line 1996, in __init__
    super().__init__(**kwargs)
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/_api/deprecation.py", line 431, in wrapper
    return func(*inner_args, **inner_kwargs)
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/collections.py", line 217, in __init__
    self.update(kwargs)
  File "/Users/dstansby/miniconda3/envs/dev/lib/python3.9/site-packages/matplotlib/artist.py", line 1062, in update
    raise AttributeError(f"{type(self).__name__!r} object "
AttributeError: 'QuadMesh' object has no property 'colors'
```



Discussion:
There's code in `CompositeMap.plot()` already to handle `linewidths`:
https://github.com/sunpy/sunpy/blob/112dab490ef9a43078872583208210de7940d1c3/sunpy/map/compositemap.py#L406-L410
So this can be fixed by adding `colors` to the same list.

I'm trying to think what else we could add to this list. The most obvious to me would be `linestyles`, although there are many other parameters that someone would conceivably want to apply to images or contours or both, and I don't think it will always be clear which. (For example, matplotlib `pcolormesh` accepts `linewidth`, `linewidths`, `lw`, `color`, `linestyle`, `linestyles` and `ls`, while `imshow` accepts none of those, and `contour` only accepts `linewidths`, `colors` and `linestyles`.)

I propose we use the following list of contour-only parameters, and add to the docstring that linewidth/style/color related parameters are only used when plotting contour maps, so it is clearer.

`['linewidth', 'linewidths', 'lw', 'color', 'colors', 'c', 'linestyle', 'linestyles', 'ls']`

So these kwargs will only be passed to the matplotlib contour function. And matplotlib will raise a `UserWarning: The following kwargs were not used by contour: 'c'` for anything in this list other than `linewidths`, `colors` and `linestyles`.

What do you think?

Resolving patch:
diff --git a/sunpy/map/compositemap.py b/sunpy/map/compositemap.py
--- a/sunpy/map/compositemap.py
+++ b/sunpy/map/compositemap.py
@@ -371,8 +371,11 @@ def plot(self, axes=None, annotate=True,
 
         Notes
         -----
-        If a line-width Matplotlib argument (``linewidth``, ``linewidths``, or ``lw``)
-        is provided, it will apply only to those maps that are plotted as contours.
+        Matplotlib arguments for setting the line width (``linewidths``), line style
+        (``linestyles``) or line color (``colors``) will apply only to those maps
+        that are plotted as contours. Similar arguments (i.e., ``linewidth``,
+        ``lw``, ``color``, ``c``, ``linestyle`` and ``ls``) will also be ignored
+        for maps plotted as images.
 
         If a transformation is required to overlay the maps with the correct
         alignment, the plot limits may need to be manually set because
@@ -404,8 +407,11 @@ def plot(self, axes=None, annotate=True,
             # The request to show a map layer rendered as a contour is indicated by a
             # non False levels property.
             if m.levels is False:
-                # Check if any linewidth argument is provided, if so, then delete it from params.
-                for item in ['linewidth', 'linewidths', 'lw']:
+                # Check if any linewidth, color, or linestyle arguments are provided,
+                # if so, then delete them from params.
+                for item in ['linewidth', 'linewidths', 'lw',
+                             'color', 'colors', 'c',
+                             'linestyle', 'linestyles', 'ls']:
                     if item in matplot_args:
                         del params[item]
 


Test patch:
diff --git a/sunpy/map/tests/test_compositemap.py b/sunpy/map/tests/test_compositemap.py
--- a/sunpy/map/tests/test_compositemap.py
+++ b/sunpy/map/tests/test_compositemap.py
@@ -51,6 +51,27 @@ def test_plot_composite_map_linewidths(composite_test_map):
     composite_test_map.plot(linewidths=0.5)
 
 
+@figure_test
+def test_plot_composite_map_linestyles(composite_test_map):
+    composite_test_map.set_levels(1, np.arange(-75, 76, 25) << u.percent)
+    composite_test_map.plot(linestyles='--')
+
+
+@figure_test
+def test_plot_composite_map_colors(composite_test_map):
+    composite_test_map.set_levels(1, np.arange(-75, 76, 25) << u.percent)
+    composite_test_map.plot(colors='red')
+
+
+def test_plot_composite_map_mplkwargs(composite_test_map):
+    composite_test_map.set_levels(1, np.arange(-75, 76, 25) << u.percent)
+    for k, v in [('linewidth', 0.5), ('lw', 0.5), ('color', 'red'),
+                 ('c', 'red'), ('linestyle', '--'), ('ls', '--')]:
+        with pytest.warns(UserWarning, match=f"The following kwargs were not "
+                                             f"used by contour: '{k}'"):
+            composite_test_map.plot(**{k: v})
+
+
 def test_remove_composite_map(composite_test_map):
     composite_test_map.remove_map(0)
     with pytest.raises(IndexError):
diff --git a/sunpy/tests/figure_hashes_mpl_332_ft_261_astropy_42.json b/sunpy/tests/figure_hashes_mpl_332_ft_261_astropy_42.json
--- a/sunpy/tests/figure_hashes_mpl_332_ft_261_astropy_42.json
+++ b/sunpy/tests/figure_hashes_mpl_332_ft_261_astropy_42.json
@@ -2,6 +2,8 @@
   "sunpy.map.tests.test_compositemap.test_plot_composite_map": "8160a5896607f62b7e7fa316236f565f54bd7a08f511808e98c9ad329678d21a",
   "sunpy.map.tests.test_compositemap.test_plot_composite_map_contours": "909ac12088dde3c15d0c7908f08b4b3c0da23e2a4966ed1ef8e705adb8727d2a",
   "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "27aa1ac78d4e49f731bf1e43f4d2b398fe8a84d53ed9234794c7954fda54c45a",
+  "sunpy.map.tests.test_compositemap.test_plot_composite_map_linestyles": "aa77398390f40d90f791a3fd45ff8d92b7fe98cc8332d9828acc31e7eddc5e88",
+  "sunpy.map.tests.test_compositemap.test_plot_composite_map_colors": "ca7af0e1ee21df8b76bac76f27ac934c452f7906d4bc9000f1faa1dde742aa62",
   "sunpy.map.tests.test_compositemap.test_set_alpha_composite_map": "b9d729ff67b0307c037ac62f97c69bb80d22ad6679a0e56352075845d4ef732b",
   "sunpy.map.tests.test_compositemap.test_peek_composite_map": "bd1df27446d420f3e8cb6fa07daff88b77dd0f0441e4acf83ee48341a9e3218f",
   "sunpy.map.tests.test_mapsequence.test_norm_animator": "a400eef26fdd915ccf12769031bd8dadedbf8f6a93e31cd5982bffe2484702ee",
diff --git a/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev.json b/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev.json
--- a/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev.json
+++ b/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev.json
@@ -3,6 +3,8 @@
   "sunpy.map.tests.test_compositemap.test_plot_composite_map_contours": "c5d25db0fbd1fad526cab34de9ce740edea0cf63a3537748cf7523991322ec51",
   "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "ce0985940b73bbe1d0142a81e2be16397df06d389bfd18ba8e977de6ab22b23e",
   "sunpy.map.tests.test_compositemap.test_set_alpha_composite_map": "f811cf6be8abfcd1b3a7f4bae7781806b37b1b2b0cb77ac34c4ed4491a1d633a",
+  "sunpy.map.tests.test_compositemap.test_plot_composite_map_linestyles": "9cef7e320ddb45e6fc8022acfe2d9c304e07e094474b0bd9e486de1ad2d77f4a",
+  "sunpy.map.tests.test_compositemap.test_plot_composite_map_colors": "2e40f5b5db4ac54c5128511b4cf9afe69df5e4f566a7e65c3832644c4ca88136",
   "sunpy.map.tests.test_compositemap.test_peek_composite_map": "d459182443472330df9b0dde3bde647479c100f56f45b6321619fbf67810c174",
   "sunpy.map.tests.test_mapsequence.test_norm_animator": "17d50a2992e9b66ede4984376a70cd0aa67dd1434a9a3bc04e23e4e4213cf7a4",
   "sunpy.map.tests.test_mapsequence.test_map_sequence_plot": "e4622b1aea9e0aaf076eddf93dbdc06daf5b3472d78aae31ed89a77499d58e0f",