From d7ce36edb5d692cd2454f62fcb1109affd5fef11 Mon Sep 17 00:00:00 2001 From: MARCHAND MANON Date: Tue, 10 Sep 2024 15:48:51 +0200 Subject: [PATCH] feat: add option to turn off optimization --- CHANGELOG.md | 2 ++ python/mocpy/moc/moc.py | 16 +++++++++++----- python/mocpy/moc/plot/fill.py | 8 ++++++-- python/mocpy/moc/plot/utils.py | 29 +++++++++++++++-------------- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0d6cf59..bd3b065d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Add support of `regions.Regions` [#163] +* Add option to turn off optimization in `fill`. The optimization degrades MOCs that are +way more precise than the given WCS [#166] * Creation of a MOC from a zone (defined by min/max ra and dec)`MOC.from_zone` * Creation of a single MOC from a lot of small cones is faster with the new option in `MOC.from_cones`: the keyword 'union_strategy' can now take the value 'small_cones'. diff --git a/python/mocpy/moc/moc.py b/python/mocpy/moc/moc.py index 7898a90c..ce322b2a 100644 --- a/python/mocpy/moc/moc.py +++ b/python/mocpy/moc/moc.py @@ -384,13 +384,15 @@ def contains_lonlat(self, lon, lat, keep_inside=True): # TODO: implement: def contains_including_surrounding(self, lon, lat, distance) - def fill(self, ax, wcs, **kw_mpl_pathpatch): + def fill(self, ax, wcs, optimize=True, **kw_mpl_pathpatch): """ Draw the MOC on a matplotlib axis. - This performs the projection of the cells from the world coordinate system to the pixel image coordinate system. - You are able to specify various styling kwargs for `matplotlib.patches.PathPatch` - (see the `list of valid keywords `__). + This performs the projection of the cells from the world coordinate system to the + pixel image coordinate system. You can provide style keyword arguments as in + `matplotlib.patches.PathPatch` + (see the `list of valid keywords + `__). Parameters ---------- @@ -398,6 +400,10 @@ def fill(self, ax, wcs, **kw_mpl_pathpatch): Matplotlib axis. wcs : `astropy.wcs.WCS` WCS defining the World system <-> Image system projection. + optimize : bool, optional + If this is set to True, the MOC will be degraded so that no HEALPix will be + smaller than one pixel as defined by the WCS. It can be useful to deactivate this + optimization for svg outputs or if you take an insert of a WCS. Default is True. kw_mpl_pathpatch Plotting arguments for `matplotlib.patches.PathPatch`. @@ -418,7 +424,7 @@ def fill(self, ax, wcs, **kw_mpl_pathpatch): >>> ax = fig.add_subplot(projection=wcs) >>> moc.fill(ax, wcs, color='blue') """ - fill.fill(self, ax, wcs, **kw_mpl_pathpatch) + fill.fill(self, ax, wcs, optimize=optimize, **kw_mpl_pathpatch) def border(self, ax, wcs, **kw_mpl_pathpatch): """ diff --git a/python/mocpy/moc/plot/fill.py b/python/mocpy/moc/plot/fill.py index 0e1b420e..550a3c20 100644 --- a/python/mocpy/moc/plot/fill.py +++ b/python/mocpy/moc/plot/fill.py @@ -112,7 +112,7 @@ def add_patches_to_mpl_axe(patches, ax, wcs, **kw_mpl_pathpatch): _set_wcs(ax, wcs) -def fill(moc, ax, wcs, **kw_mpl_pathpatch): +def fill(moc, ax, wcs, *, optimize=True, **kw_mpl_pathpatch): """Fill the figure's ax with the patches. Parameters @@ -122,11 +122,15 @@ def fill(moc, ax, wcs, **kw_mpl_pathpatch): ax : matplotlib.pyplot.axes wcs : astropy.wcs.WCS projection from astropy + optimize : bool, optional + If this is set to True, the MOC will be degraded so that no HEALPix will be + smaller than one pixel as defined by the WCS. It can be useful to deactivate this + optimization for svg outputs or if you take an insert of a WCS. Default to True. """ # Simplify the MOC for plotting purposes: # 1. Degrade the MOC if the FOV is enough big so that we cannot see the smallest HEALPix cells. # 2. For small FOVs, plot the MOC & POLYGONAL_MOC_FROM_FOV. - moc_to_plot = build_plotting_moc(moc=moc, wcs=wcs) + moc_to_plot = build_plotting_moc(moc=moc, wcs=wcs, optimize=optimize) # If the FOV contains no cells, then moc_to_plot (i.e. the intersection between the moc # and the MOC created from the FOV polygon) will be empty. diff --git a/python/mocpy/moc/plot/utils.py b/python/mocpy/moc/plot/utils.py index 29fb79e0..13f69677 100644 --- a/python/mocpy/moc/plot/utils.py +++ b/python/mocpy/moc/plot/utils.py @@ -22,20 +22,21 @@ def _set_wcs(ax, wcs): ax.set_ylim([y_min, y_max]) -def build_plotting_moc(moc, wcs): +def build_plotting_moc(moc, wcs, optimize=True): """Plot a moc.""" - # Get the WCS cdelt giving the deg.px^(-1) resolution. - cdelt = wcs.wcs.cdelt - # Convert in rad.px^(-1) - cdelt = np.abs((2 * np.pi / 360) * cdelt[0]) - # Get the minimum depth such as the resolution of a cell is contained in 1px. - depth_res = int(np.floor(np.log2(np.sqrt(np.pi / 3) / cdelt))) - depth_res = max(depth_res, 0) - # Degrade the moc to that depth for plotting purposes. It is not necessary to plot pixels - # that we will not see because they are contained in 1px. - moc_plot = moc - if moc.max_order > depth_res: - moc_plot = moc.degrade_to_order(depth_res) + moc_plot = moc # creates a copy to keep the original moc untouched + if optimize: + # Get the WCS cdelt giving the deg.px^(-1) resolution. + cdelt = wcs.wcs.cdelt + # Convert in rad.px^(-1) + cdelt = np.abs((2 * np.pi / 360) * cdelt[0]) + # Get the minimum depth such as the resolution of a cell is contained in 1px. + depth_res = int(np.floor(np.log2(np.sqrt(np.pi / 3) / cdelt))) + depth_res = max(depth_res, 0) + # Degrade the moc to that depth for plotting purposes. It is not necessary to plot pixels + # that we will not see because they are contained in 1px. + if moc.max_order > depth_res: + moc_plot = moc.degrade_to_order(depth_res) # Get the MOC delimiting the FOV polygon width_px = int(wcs.wcs.crpix[0] * 2.0) # Supposing the wcs is centered in the axis @@ -56,7 +57,7 @@ def build_plotting_moc(moc, wcs): Y_px = np.append(Y_px, Y[-1, :-1]) Y_px = np.append(Y_px, Y[1:, 0][::-1]) - # Disable the output of warnings when encoutering NaNs. + # Disable the output of warnings when encountering NaNs. warnings.filterwarnings("ignore") # Inverse projection from pixel coordinate space to the world coordinate space viewport = pixel_to_skycoord(X_px, Y_px, wcs)