Repository: sunpy/sunpy

Issue:
Add a gallery example on aia and hmi contouring
### Provide a general description of the issue or problem.

Example code that contours an hmi map based on intensity values of aia.

```python
import astropy.units as u
import matplotlib.pyplot as plt
import sunpy.map
from astropy.coordinates import SkyCoord
from skimage.measure import label, regionprops
from sunpy.net import Fido
from sunpy.net import attrs as a
from aiapy.calibrate import register

plt.ion()

res = Fido.search(a.Time("2015-02-01 23:00", "2015-02-02", "2015-02-01T23:44:42"),
                  (a.Instrument.aia & a.Wavelength(19.3*u.nm) |
                   a.Instrument.hmi & a.Physobs.los_magnetic_field))
files = sorted(Fido.fetch(res))

aia, hmi = sunpy.map.Map(files[:2])
aia = register(aia)
hmi = register(hmi)

aia = aia.submap(bottom_left=SkyCoord(-250, -900, unit=u.arcsec, frame=aia.coordinate_frame),
                 width=850*u.arcsec,
                 height=600*u.arcsec)
hmi = hmi.submap(bottom_left=SkyCoord(-250, -900, unit=u.arcsec, frame=hmi.coordinate_frame),
                 width=850*u.arcsec,
                 height=600*u.arcsec)

segmented = aia.data < 90
labeled = label(segmented)
regions = regionprops(labeled, hmi.data)

# pick the largest single region
largest_region = sorted(regions, key=lambda r: r.area, reverse=True)[0]
region_map = sunpy.map.Map(largest_region.image_intensity, hmi.meta)
region_map.peek(cmap="hmimag")
```
![Figure_1](https://user-images.githubusercontent.com/1392107/173156955-23ad3e61-5e65-4bc7-9f91-bb634278b06e.png)

We need to decide, do we add aiapy to the gallery build or if this should belong in aiapy's example gallery.



Discussion:
This example has a number of issues:

- This is not contouring, but rather masking.  (We already have [an example that plots HMI contours on an AIA image](https://docs.sunpy.org/en/stable/generated/gallery/map/hmi_contours_wcsaxes.html).)
- Using `aiapy` for this example is silly.  All you need to do is to take the full AIA image, submap as desired, and then reproject the (full) HMI image to your AIA submap.
- We can simply use our sample data.
- The correctness of the region is unclear without a plot of the corresponding AIA image.
- The creation of the map as above happens to work only because the region in that particular case happens to span the full bounding box of the original image.  In general, you need to retrieve the specific bounding box for the region.

Here's how I'd write the example:
```python
import matplotlib.pyplot as plt
import numpy as np
from skimage.measure import label, regionprops

import astropy.units as u
from astropy.coordinates import SkyCoord

import sunpy.map
from sunpy.data.sample import AIA_171_IMAGE, HMI_LOS_IMAGE

aia = sunpy.map.Map(AIA_171_IMAGE)
aia = aia.submap(bottom_left=SkyCoord(-250, 200, unit=u.arcsec, frame=aia.coordinate_frame),
                 width=500*u.arcsec,
                 height=400*u.arcsec)

hmi = sunpy.map.Map(HMI_LOS_IMAGE)
# Reproject the HMI data to exactly the same grid as the AIA data
hmi = hmi.reproject_to(aia.wcs)

# Identify the separate regions below a threshold in the AIA image
segmented = aia.data < 200
labeled = label(segmented)
regions = regionprops(labeled, hmi.data)
regions = sorted(regions, key=lambda r: r.area, reverse=True)  # sort in descending order by size

# Plot the larger regions in the AIA image, with region 0 being the largest
plt.figure()
aia.plot()
aia.draw_contours(levels=200*u.ct, colors='r')
for i in range(7):
    plt.text(*np.flip(regions[i].centroid), str(i), color='w', ha='center', va='center')

# Define a new HMI map that masks out everything but the largest region (region 0)
bbox = regions[0].bbox
mask = np.ones_like(hmi.data, dtype=bool)
mask[bbox[0]:bbox[2], bbox[1]:bbox[3]] = ~regions[0].image
hmi_masked = sunpy.map.Map((hmi.data, hmi.meta), mask=mask)

plt.figure()
hmi_masked.plot(cmap='hmimag')
plt.colorbar()
plt.show()
```
![aia](https://user-images.githubusercontent.com/991759/173169674-8bde4b39-d948-4959-9d95-63e79b71af8d.png)
![hmi](https://user-images.githubusercontent.com/991759/173169680-ed223376-4610-4677-89a1-7bf8739d0f88.png)

To be clear, I wrote this to help someone in the chat and wasn't suggesting we make it into as-is.

My main concern with your re-write is that the pixel values in the HMI image are very likely not physically meaningful any more because current it's not possible to configure reproject to be flux conserving (only photometry iirc).

Using aiapy is probably the best solution at the moment for people who want to do analysis on the extracted HMI region, hence the suggestion for the person in the chat.
> it's not possible to configure reproject to be flux conserving (only photometry iirc).

And `register()` is flux conserving?

If pristine HMI pixels are important, you can instead submap the HMI map and reproject the AIA map to match.
> And register() is flux conserving?

This is equivalent to asking whether our `affine_transform` is flux conserving. I would assume that this depends on the underlying implementation being used (e.g. scipy v. scikit-image), but I don't have a definite answer. 

If you didn't want to use `aiapy` in the example, you could also just call `rotate` with `recenter = True` and the appropriate scaling to make them .6 arcsec per pix, assuming the use of register here is the right route to take. 
Heh, my question was rhetorical.  The answer is no.  None of our affine transformations are flux conserving.
I would like to take this up if it's not solved.
 
> I would like to take this up if it's not solved.

Go ahead. You also do not need to ask if there is no linked pull request. 
> This example has a number of issues:
> 
> * This is not contouring, but rather masking.  (We already have [an example that plots HMI contours on an AIA image](https://docs.sunpy.org/en/stable/generated/gallery/map/hmi_contours_wcsaxes.html).)
> * Using `aiapy` for this example is silly.  All you need to do is to take the full AIA image, submap as desired, and then reproject the (full) HMI image to your AIA submap.
> * We can simply use our sample data.
> * The correctness of the region is unclear without a plot of the corresponding AIA image.
> * The creation of the map as above happens to work only because the region in that particular case happens to span the full bounding box of the original image.  In general, you need to retrieve the specific bounding box for the region.
> 
> Here's how I'd write the example:
> 
> ```python
> import matplotlib.pyplot as plt
> import numpy as np
> from skimage.measure import label, regionprops
> 
> import astropy.units as u
> from astropy.coordinates import SkyCoord
> 
> import sunpy.map
> from sunpy.data.sample import AIA_171_IMAGE, HMI_LOS_IMAGE
> 
> aia = sunpy.map.Map(AIA_171_IMAGE)
> aia = aia.submap(bottom_left=SkyCoord(-250, 200, unit=u.arcsec, frame=aia.coordinate_frame),
>                  width=500*u.arcsec,
>                  height=400*u.arcsec)
> 
> hmi = sunpy.map.Map(HMI_LOS_IMAGE)
> # Reproject the HMI data to exactly the same grid as the AIA data
> hmi = hmi.reproject_to(aia.wcs)
> 
> # Identify the separate regions below a threshold in the AIA image
> segmented = aia.data < 200
> labeled = label(segmented)
> regions = regionprops(labeled, hmi.data)
> regions = sorted(regions, key=lambda r: r.area, reverse=True)  # sort in descending order by size
> 
> # Plot the larger regions in the AIA image, with region 0 being the largest
> plt.figure()
> aia.plot()
> aia.draw_contours(levels=200*u.ct, colors='r')
> for i in range(7):
>     plt.text(*np.flip(regions[i].centroid), str(i), color='w', ha='center', va='center')
> 
> # Define a new HMI map that masks out everything but the largest region (region 0)
> bbox = regions[0].bbox
> mask = np.ones_like(hmi.data, dtype=bool)
> mask[bbox[0]:bbox[2], bbox[1]:bbox[3]] = ~regions[0].image
> hmi_masked = sunpy.map.Map((hmi.data, hmi.meta), mask=mask)
> 
> plt.figure()
> hmi_masked.plot(cmap='hmimag')
> plt.colorbar()
> plt.show()
> ```
> 
> ![aia](https://user-images.githubusercontent.com/991759/173169674-8bde4b39-d948-4959-9d95-63e79b71af8d.png) ![hmi](https://user-images.githubusercontent.com/991759/173169680-ed223376-4610-4677-89a1-7bf8739d0f88.png)

Isn't the code for this is  already written?
> Isn't the code for this is  already written?

Yes but it isn't in the style for an example. There is work needed to do that.

> > Isn't the code for this is  already written?
> 
> Yes but it isn't in the style for an example. There is work needed to do that.

Okay

Resolving patch:
diff --git a/examples/map/masking_hmi.py b/examples/map/masking_hmi.py
new file mode 100644
--- /dev/null
+++ b/examples/map/masking_hmi.py
@@ -0,0 +1,89 @@
+"""
+=========================================
+Masking HMI based on the intensity of AIA
+=========================================
+
+In this example we will demonstrate how to mask out regions within
+a HMI image based on the intensity values of AIA.
+"""
+# sphinx_gallery_thumbnail_number = 3
+
+import matplotlib.pyplot as plt
+import numpy as np
+from matplotlib.colors import Normalize
+from skimage.measure import label, regionprops
+
+import astropy.units as u
+from astropy.coordinates import SkyCoord
+
+import sunpy.map
+from sunpy.data.sample import AIA_171_IMAGE, HMI_LOS_IMAGE
+
+###############################################################################
+# We will use an AIA 171 image from the sample data and crop it to capture a region of interest.
+
+aia = sunpy.map.Map(AIA_171_IMAGE)
+aia = aia.submap(
+    bottom_left=SkyCoord(-250, 200, unit=u.arcsec, frame=aia.coordinate_frame),
+    width=500 * u.arcsec,
+    height=400 * u.arcsec,
+)
+
+###############################################################################
+# Next we call the :meth:`~sunpy.map.GenericMap.reproject_to` to reproject the HMI Map
+# to have exactly the same grid as the AIA Map.
+# :ref:`sphx_glr_generated_gallery_map_transformations_reprojection_align_aia_hmi.py` provides more reference.
+
+hmi = sunpy.map.Map(HMI_LOS_IMAGE)
+hmi = hmi.reproject_to(aia.wcs)
+hmi.nickname = 'HMI magnetogram'
+
+###############################################################################
+# Now we will identify separate regions below a threshold in the AIA Map.
+# In this case, we want the darker patches that have pixel values below 200.
+# Then, using `skimage`, we can :func:`~skimage.measure.label`
+# and calculate the properties of each region using :func:`~skimage.measure.regionprops`.
+
+segmented = aia.data < 200
+labeled = label(segmented)
+regions = regionprops(labeled, hmi.data)
+# We want the largest region, so we will sort by descending order in size.
+regions = sorted(regions, key=lambda r: r.area, reverse=True)
+
+###############################################################################
+# Now to plot and label the first 7 regions seen in AIA with the region "0" being the largest.
+
+fig = plt.figure()
+ax = fig.add_subplot(projection=aia)
+aia.plot(axes=ax)
+aia.draw_contours(axes=ax, levels=200 * u.ct, colors="r")
+for i in range(7):
+    plt.text(*np.flip(regions[i].centroid), str(i), color="w", ha="center", va="center")
+
+###############################################################################
+# Now let's plot those same regions on the reprojected HMI Map.
+
+fig = plt.figure()
+ax = fig.add_subplot(projection=hmi)
+im = hmi.plot(axes=ax, cmap="hmimag", norm=Normalize(-1500, 1500))
+aia.draw_contours(axes=ax, levels=200 * u.ct, colors="r")
+fig.colorbar(im)
+
+###############################################################################
+# Now we have the regions, we need to create a new HMI map that masks out everything but the largest region.
+# To do so, we need to create the mask from the bounding box returned by `skimage`.
+
+bbox = regions[0].bbox
+mask = np.ones_like(hmi.data, dtype=bool)
+mask[bbox[0]: bbox[2], bbox[1]: bbox[3]] = ~regions[0].image
+hmi_masked = sunpy.map.Map((hmi.data, hmi.meta), mask=mask)
+
+###############################################################################
+# Finally, plot the largest HMI region.
+
+fig = plt.figure()
+ax = fig.add_subplot(projection=hmi_masked)
+im = hmi_masked.plot(axes=ax, cmap="hmimag", norm=Normalize(-1500, 1500))
+fig.colorbar(im)
+
+plt.show()


Test patch: