Repository: sunpy/sunpy

Issue:
Map HTML repr crashes when data array is a Dask array
If your map is backed by a Dask array (which `Map` does *nominally* support), the nifty HTML repr freaks out.

```python
import sunpy.data.sample
import sunpy.map
import dask.array
m = sunpy.map.Map(sunpy.data.sample.AIA_171_IMAGE)
m_dask = sunpy.map.Map(dask.array.from_array(m.data),m.meta)
m_dask._repr_html_()
```

gives

```python traceback
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-89689641455e> in <module>
----> 1 m_dask._repr_html_()

~/Documents/codes/sunpy/sunpy/map/mapbase.py in _repr_html_(self)
    356             FigureCanvasBase(fig)
    357         ax = fig.subplots()
--> 358         values, bins, patches = ax.hist(finite_data.ravel(), bins=100)
    359         norm_centers = norm(0.5 * (bins[:-1] + bins[1:])).data
    360         for c, p in zip(norm_centers, patches):

~/miniconda3/envs/sunpy-dev/lib/python3.8/site-packages/matplotlib/__init__.py in inner(ax, data, *args, **kwargs)
   1445     def inner(ax, *args, data=None, **kwargs):
   1446         if data is None:
-> 1447             return func(ax, *map(sanitize_sequence, args), **kwargs)
   1448
   1449         bound = new_sig.bind(ax, *args, **kwargs)

~/miniconda3/envs/sunpy-dev/lib/python3.8/site-packages/matplotlib/axes/_axes.py in hist(self, x, bins, range, density, weights, cumulative, bottom, histtype, align, orientation, rwidth, log, color, label, stacked, **kwargs)
   6569
   6570         # Massage 'x' for processing.
-> 6571         x = cbook._reshape_2D(x, 'x')
   6572         nx = len(x)  # number of datasets
   6573

~/miniconda3/envs/sunpy-dev/lib/python3.8/site-packages/matplotlib/cbook/__init__.py in _reshape_2D(X, name)
   1373
   1374     # Iterate over list of iterables.
-> 1375     if len(X) == 0:
   1376         return [[]]
   1377

TypeError: 'float' object cannot be interpreted as an integer
```


Discussion:
It seems that `Axes.hist()` behaves really badly on `dask` arrays
Potential bandaid:
```python
if isinstance(finite_data, dask.array.Array):
    finite_data = finite_data.compute()
```
The data has to be pulled into memory anyway to be plotted.
Yup, that'd be the easiest short-term fix.  The question to ponder for long term is whether we'd want `_repr_html_()` to do something different (i.e., less) for `dask` arrays since triggering a full computation for just a representation somewhat defeats the purpose of using `dask`.
Hello Guys @ayshih @wtbarnes .  I am new to this repository and looking for a good first issue to contribute. If no one is working on this issue can I go ahead a submit a PR for this one. 
Why don't we use the really nice dask html repr as part of our html repr?
> Why don't we use the really nice dask html repr as part of our html repr?

Yes, I think that makes the most sense. Unfortunately, by not bringing the array into memory, we lose a lot of the nice quicklook stuff that is included in the repr. 

Going this route, I think it would make the most sense to just have the metadata table and then the dask repr on the right and suppress the imshow plot and the histogram.
If we go this route, I'm inclined to break the equivalence of the HTML repr and the output of `.quicklook()`.  I think a user explicitly calling `.quicklook()` should always get the plot and histogram.
Hey folks @wtbarnes @ayshih is this issue still in need of a fix? If so, is anybody currently working on a PR or could I submit one? This looks like a decent bug for me to tackle but I see that it has not been addressed in a long time. 
I would suggest starting off in checking it is still an issue. If it is, you can work on it if you would like.
Sure go ahead. I do not believe anyone is working on this at the moment.

I think the most sensible route is to follow @ayshih's suggestion of splitting up the HTML repr and the quicklook method and have the HTML repr show the Dask array html summary and the quicklook method pull the array into memory and plot it. 
After some initial testing, looks like the error does still occur in my dev environment. 

For splitting up the html_repr() and quicklook() behavior, the most straightforward solution I can think of would be to pass a flag in to the html_repr() method. If set, pull the array into memory, otherwise use the dask html repr. Would this be good practice or is there another solution that I should consider? 

Something like 
`html_repr(self, compute_dask=false)`

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -18,6 +18,12 @@
 from matplotlib.backend_bases import FigureCanvasBase
 from matplotlib.figure import Figure
 
+try:
+    from dask.array import Array as DaskArray
+    DASK_INSTALLED = True
+except ImportError:
+    DASK_INSTALLED = False
+
 import astropy.units as u
 import astropy.wcs
 from astropy.coordinates import Longitude, SkyCoord, UnitSphericalRepresentation
@@ -300,7 +306,7 @@ def __str__(self):
     def __repr__(self):
         return f"{object.__repr__(self)}\n{self}"
 
-    def _repr_html_(self):
+    def _repr_html_(self, compute_dask=False):
         """
         Produce an HTML summary with plots for use in Jupyter notebooks.
         """
@@ -317,6 +323,25 @@ def _repr_html_(self):
         count_nan = np.isnan(self.data).sum()
         count_inf = np.isinf(self.data).sum()
 
+        if DASK_INSTALLED and isinstance(finite_data, DaskArray):
+            # This will fetch the entire data array into memory and only happens for the quicklook method
+            if compute_dask:
+                finite_data = finite_data.compute()
+            else:
+                dask_html = self.data._repr_html_()
+                return textwrap.dedent(f"""\
+                    <pre>{html.escape(object.__repr__(self))}</pre>
+                    <table>
+                        <tr>
+                            <td>{text_to_table}</td>
+                            <td>
+                                {dask_html}
+                            </td>
+                        </tr>
+                        <tr>
+                        </tr>
+                    </table>""")
+
         # Assemble an informational string with the counts of bad pixels
         bad_pixel_text = ""
         if count_nan + count_inf > 0:
@@ -471,7 +496,7 @@ def quicklook(self):
             f.write(textwrap.dedent(f"""\
                 <html>
                     <title>Quicklook summary for {html.escape(object.__repr__(self))}</title>
-                    <body>{self._repr_html_()}</body>
+                    <body>{self._repr_html_(compute_dask=True)}</body>
                 </html>"""))
         webbrowser.open_new_tab(url)
 


Test patch:
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
@@ -1181,6 +1181,17 @@ def test_quicklook(aia171_test_map):
         assert aia171_test_map._repr_html_() in html_string
 
 
+def test_dask_array(aia171_test_map):
+    dask_array = pytest.importorskip('dask.array')
+    da = dask_array.from_array(aia171_test_map.data, chunks=(1, 1))
+    pair_map = sunpy.map.Map(da, aia171_test_map.meta)
+
+    # Check that _repr_html_ functions for a dask array
+    html_dask_repr = pair_map._repr_html_(compute_dask=False)
+    html_computed_repr = pair_map._repr_html_(compute_dask=True)
+    assert html_dask_repr != html_computed_repr
+
+
 @pytest.fixture
 def generic_map2(generic_map):
     generic_map.meta["CTYPE1"] = "HPLN-TAN"