sdss / marvin

Data access and visualization for MaNGA. http://sdss-marvin.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
55 stars 32 forks source link

Enhanced map plot results in bad pixel scaling #772

Open prajwel opened 2 years ago

prajwel commented 2 years ago

Hi,

Thank you for maintaining this excellent software. I would like to report the following problem.

The enhanced map plot results in bad pixel scaling.

To reproduce the problem, please run the following script

import marvin

target = "8485-1901"
maps = marvin.tools.Maps(target)
ha = maps.emline_gflux_ha_6564
hb = maps.emline_gflux_hb_4862
ha_hb = ha / hb
log_ha_hb = np.log10(ha_hb)

# Good scaling
ha_hb.plot()

Figure 1

# Bad scaling
log_ha_hb.plot()

Figure 2

Expected behaviour: the pixel colour scaling should be comparable to that produced by ha_hb.plot().

Could you please let me know if there is a way to correct this?

bretthandrews commented 2 years ago

Hopefully the log_cb keyword should do the trick:

log_ha_hb.plot(log_cb=True)

prajwel commented 2 years ago
log_ha_hb.plot(log_cb=True)

gives the following error.

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_2243/3305198056.py in <module>
----> 1 log_ha_hb.plot(log_cb=True)

~/miniconda3/envs/py38/lib/python3.8/site-packages/marvin/tools/quantities/map.py in plot(self, *args, **kwargs)
    633     @add_doc(marvin.utils.plot.map.plot.__doc__)
    634     def plot(self, *args, **kwargs):
--> 635         return marvin.utils.plot.map.plot(dapmap=self, *args, **kwargs)
    636 
    637 

~/miniconda3/envs/py38/lib/python3.8/site-packages/marvin/utils/plot/map.py in plot(*args, **kwargs)
    509 
    510         # plot unmasked spaxels
--> 511         p = ax.imshow(good_spax, cmap=cb_kws['cmap'], zorder=10, **imshow_kws)
    512 
    513         fig, cb = colorbar._draw_colorbar(fig, mappable=p, ax=ax, **cb_kws)

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*args, **kwargs)
    454                 "parameter will become keyword-only %(removal)s.",
    455                 name=name, obj_type=f"parameter of {func.__name__}()")
--> 456         return func(*args, **kwargs)
    457 
    458     # Don't modify *func*'s signature, as boilerplate.py needs it.

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/__init__.py in inner(ax, data, *args, **kwargs)
   1410     def inner(ax, *args, data=None, **kwargs):
   1411         if data is None:
-> 1412             return func(ax, *map(sanitize_sequence, args), **kwargs)
   1413 
   1414         bound = new_sig.bind(ax, *args, **kwargs)

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/axes/_axes.py in imshow(self, X, cmap, norm, aspect, interpolation, alpha, vmin, vmax, origin, extent, interpolation_stage, filternorm, filterrad, resample, url, **kwargs)
   5445             # image does not already have clipping set, clip to axes patch
   5446             im.set_clip_path(self.patch)
-> 5447         im._scale_norm(norm, vmin, vmax)
   5448         im.set_url(url)
   5449 

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/cm.py in _scale_norm(self, norm, vmin, vmax)
    378             self.set_clim(vmin, vmax)
    379             if norm is not None:
--> 380                 raise ValueError(
    381                     "Passing parameters norm and vmin/vmax simultaneously is "
    382                     "not supported. Please pass vmin/vmax directly to the "

ValueError: Passing parameters norm and vmin/vmax simultaneously is not supported. Please pass vmin/vmax directly to the norm when creating it.

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/miniconda3/envs/py38/lib/python3.8/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

~/miniconda3/envs/py38/lib/python3.8/site-packages/ipympl/backend_nbagg.py in _repr_mimebundle_(self, **kwargs)
    304 
    305         buf = io.BytesIO()
--> 306         self.figure.savefig(buf, format='png', dpi='figure')
    307         self._data_url = b64encode(buf.getvalue()).decode('utf-8')
    308         # Figure width in pixels

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/figure.py in savefig(self, fname, transparent, **kwargs)
   3010                         ax.patch._cm_set(facecolor='none', edgecolor='none'))
   3011 
-> 3012             self.canvas.print_figure(fname, **kwargs)
   3013 
   3014     def ginput(self, n=1, timeout=30, show_clicks=True,

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2312                 # force the figure dpi to 72), so we need to set it again here.
   2313                 with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2314                     result = print_method(
   2315                         filename,
   2316                         facecolor=facecolor,

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs)
   1641             kwargs.pop(arg)
   1642 
-> 1643         return func(*args, **kwargs)
   1644 
   1645     return wrapper

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    410                          else deprecation_addendum,
    411                 **kwargs)
--> 412         return func(*inner_args, **inner_kwargs)
    413 
    414     DECORATORS[wrapper] = decorator

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs, *args)
    538             *metadata*, including the default 'Software' key.
    539         """
--> 540         FigureCanvasAgg.draw(self)
    541         mpl.image.imsave(
    542             filename_or_obj, self.buffer_rgba(), format="png", origin="upper",

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in draw(self)
    434              (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
    435               else nullcontext()):
--> 436             self.figure.draw(self.renderer)
    437             # A GUI class may be need to update a window using this draw, so
    438             # don't forget to call the superclass.

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     71     @wraps(draw)
     72     def draw_wrapper(artist, renderer, *args, **kwargs):
---> 73         result = draw(artist, renderer, *args, **kwargs)
     74         if renderer._rasterizing:
     75             renderer.stop_rasterizing()

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
   2801 
   2802             self.patch.draw(renderer)
-> 2803             mimage._draw_list_compositing_images(
   2804                 renderer, self, artists, self.suppressComposite)
   2805 

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw(self, renderer)
   3080             renderer.stop_rasterizing()
   3081 
-> 3082         mimage._draw_list_compositing_images(
   3083             renderer, self, artists, self.figure.suppressComposite)
   3084 

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/image.py in draw(self, renderer, *args, **kwargs)
    644                 renderer.draw_image(gc, l, b, im, trans)
    645         else:
--> 646             im, l, b, trans = self.make_image(
    647                 renderer, renderer.get_image_magnification())
    648             if im is not None:

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/image.py in make_image(self, renderer, magnification, unsampled)
    954         clip = ((self.get_clip_box() or self.axes.bbox) if self.get_clip_on()
    955                 else self.figure.bbox)
--> 956         return self._make_image(self._A, bbox, transformed_bbox, clip,
    957                                 magnification, unsampled=unsampled)
    958 

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/image.py in _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification, unsampled, round_to_pixel_border)
    545                                            vmax=s_vmax,
    546                                            ):
--> 547                         output = self.norm(resampled_masked)
    548             else:
    549                 if A.ndim == 2:  # _interpolation_stage == 'rgba'

~/miniconda3/envs/py38/lib/python3.8/site-packages/matplotlib/colors.py in __call__(self, value, clip)
   1529             self.autoscale_None(value)
   1530             if self.vmin > self.vmax:
-> 1531                 raise ValueError("vmin must be less or equal to vmax")
   1532             if self.vmin == self.vmax:
   1533                 return np.full_like(value, 0)

ValueError: vmin must be less or equal to vmax

Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous view', 'arrow-left', 'back'), ('Forward', 'Forward to next view', 'arrow-right', 'forward'), ('Pan', 'Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect', 'arrows', 'pan'), ('Zoom', 'Zoom to rectangle\nx/y fixes axis', 'square-o', 'zoom'), ('Download', 'Download plot', 'floppy-o', 'save_figure')]))
havok2063 commented 2 years ago

Which version of sdss-marvin, and matplotlib do you have installed?

prajwel commented 2 years ago

I am using the Marvin image on SciServer. The versions are as follows.

marvin: 2.7.1
matplotlib: 3.5.0
bretthandrews commented 2 years ago

Right! I believe that the issue is that the signal-to-noise mask doesn't play well with log quantities. One way around this is to set snr_min=-1000 or something low enough to not cut any good data. The other way is to use the linear ratio and use a log-scaled colorbar: ha_hb.plot(log_cb=True).