Conversation
…id.jl Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Requires Makie 0.25 (compute-graph recipes; unreleased). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Open
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ngle Requires AstroAngles 0.3 (JuliaAstro/AstroAngles.jl#33). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ticks on exact component multiples (e.g. 3°20' steps) are decomposed by integer arithmetic, fixing truncation (-46.67° shown as -46°) and deg2dmsmu float fuzz ((-46, 39, 59, 999, 999.99...)). Labels now display down to the deepest nonzero component. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tick registration compared gridline/edge intersections with exact float ==, so separable frames (e.g. longitude/velocity slices) registered no ticks; now tolerance-based. - world_to_pixel(parent=true) fills frozen world axes with a constant from pixel (1,1,...), drifting off the slice plane on curved transforms; the grid layout now refines the inverse onto the exact frozen-pixel plane by fixed-point iteration. - WCSGrid(img) had x/y extents transposed (invisible on square images). - wcsticks now maps plotted axis -> WCS axis for sliced cubes. - DataAspect is only applied when both plotted axes are angular. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CUNIT is optional and the WCS standard makes `deg` the default unit of a celestial axis, so an absent CUNIT means degrees, not "unknown". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Constrain the layout cell instead of the axis box, so the colorbar sits flush against the image, and size the owned figure onto the result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v0.6 #119 +/- ##
===========================================
+ Coverage 61.95% 80.00% +18.04%
===========================================
Files 9 10 +1
Lines 1062 1200 +138
===========================================
+ Hits 658 960 +302
+ Misses 404 240 -164 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Member
|
@icweaver this is amazing! I'm so excited to use this! |
Member
Author
|
Thanks @sefffal! This draft is still super messy, but we have some tentative usage examples here (repo: https://github.com/JuliaAstro/learn-JuliaAstro) that we'd love to get your input on. Happy to make any changes! |
icweaver
marked this pull request as ready for review
July 24, 2026 23:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Doc preview: https://juliaastro.org/AstroImages.jl/previews/PR119/
Replaces the RecipesBase/Plots.jl recipes with Makie, targeting the
ff/breaking-0.25branch (MakieOrg/Makie.jl#5484).What changed
WCSGrid,wcsgridspec,wcslabels,ctype_label, …) moved unchanged fromsrc/plot-recipes.jltosrc/wcs-grid.jl; the recipes themselves were removed.implot/implot!/implotview/polquiver/polquiver!are now exported function stubs (src/plot-interface.jl).ext/AstroImagesMakieExt.jlimplements the stubs as Makie 0.25 compute-graph recipes. Loading any Makie backend activates it.Design notes
Native colorbars with non-linear stretches. Instead of pre-rendering RGBA via
imview, raw data flows through Makie's colormapping pipeline:clims(incl.Percent/Zscale) resolve tocolorrange.stretchbecomes acolorscale: aReversibleScaleclosure that applies the stretch to the clims-normalized value (the DS9/imviewconvention. This matters because astro data is routinely negative, where raw-datalogscaling fails). All exported stretches have analytic inverses, so Colorbar tick placement works.contrast/biasfold into a resampled colormap (Makie renormalizes colorscale output affinely, so they cannot live in the scale).Makie.Colorbar(fig[1, 2], plt)therefore shows real data units with correctly warped tick spacing, something the Plots recipe could only fake with a second image subplot. AMakie._extract_colormap(::ImPlot)method points Colorbar at the right child (the recipe also draws grid lines).WCS axes. The recipe configures auto-created axes through the new
preferred_axis_attributeshook: sexagesimal ticks and labels fromwcsgridspec, title fromrefdims, tight limits,DataAspect. The (curved) WCS grid is overplotted by the recipe. Makie'simageconvention (first dim = x, y-up) matches FITS, so all the flip/transpose gymnastics from the Plots recipe are gone.Dynamic zoom-aware WCS ticks (
WCSTicks). Tick positions and sexagesimal labels recompute from the current axis limits through an overload of Makie'sget_ticks(locator, scale, formatter, vmin, vmax)protocol. Zooming from the full frame into a 100-pixel window re-ticks from arcminute to arcsecond steps automatically, something the static Plots recipe (and astropy's WCSAxes) doesn't do. Because WCS tick positions on one axis depend on the extent of both axes, the x/y locators share a mutable extent state that each call refreshes for its own dimension (at most one interaction stale in the other, exact for unrotated WCS). Labels are generated in screen order so the full-length anchor label sits at the axis start even where world coordinates run right-to-left (RA). The context-bearing x-axis labels (the anchor tick and roll-overs) are stacked onto two lines, with the finest component over full context, e.g.48.00ˢover23ʰ23ᵐ, following the pattern of Makie'sDateTimeTicks, so that dense tick labels don't crowd each other.ImPlotViewblock recipe. Makie 0.25's new@Blockcomplex recipes bundle an Axis + WCS ticks + labeled Colorbar in one call (ImPlotView(fig[1, 1], img)), replacing the old@layouthack. Colorbar label defaults to theUNIT/BUNITheader. The block also syncs the tick state and the recipe'sviewextentattribute from the axisfinallimits, so the curved grid overlay refines together with the ticks on zoom/pan. (A plainimplotinto a user-created Axis keeps the static full-extent grid, since a plot cannot see its axis's limits.)Direct world-coordinate overplotting (
world_transform) (7ee4d8a). The analogue of matplotlib WCSAxes'ax.scatter(..., transform=ax.get_transform("world")): pass the new exportedworld_transformto any plot'stransformationkeyword to plot world-coordinate data (in the WCS's world units, typically degrees) directly over an image, with no manualworld_to_pixelround-trip —scatter!(iv.ax, ra, dec; transformation = world_transform(iv)). It returns aMakie.Transformationwhose transform func maps plotted-dim world coordinates into the (platescale-scaled, parent-frame) pixel space the recipe draws in, reusing the grid code's_world_to_plotted_pixelso frozen slice axes get the same fixed-point refinement. Following GeoMakie's precedent for nonlinear projections, points are NaN/error-guarded and Rect bounding boxes are densified by grid sampling instead of corner-mapping — those Rects feed axis autolimits, which is what makes world overlays co-register with the pixel-space image automatically. The transform is invertible (Makie.inverse_transformwrapspixel_to_world(...; parent = true)), so picking and inspection tooling keep working.world_transform(plt)/world_transform(iv)derive the image,wcsn, andplatescalefrom an existing plot or view so they cannot drift out of sync with what is displayed;world_transform(img; wcsn, platescale)covers transform-first use. Angular wrap-around (RA crossing 0°/360°) is not special-cased, matching the grid code's standing limitation.Dynamic updates work through the compute graph:
update!(plt, stretch = asinhstretch, cmap = :viridis)re-renders live.WCS grid correctness for sliced and non-square images (
a18bec2). The position–velocity docs figure (HIcube[Y = 45]) exposed three latent bugs, now fixed with regression tests: tick registration compared gridline/edge intersections with exact float==(separable frames registered no ticks at all). The grid layout inherited the constant frozen-axis fill ofworld_to_pixel(...; parent = true), which drifts off the slice plane on curved transforms (now refined onto the exact frozen-pixel plane by fixed-point iteration); and theWCSGrid(img)convenience constructor had its x/y extents transposed since the Plots era, invisible on square images.wcsticksalso now maps plotted axis --> WCS axis for sliced cubes, andDataAspectis only applied when both plotted axes are angular, so mixed longitude/velocity panels fill their layout cell.Panel layout and aspect ratio (
bf79dc3,11dcd9e). AnAxisaspectshrinks the axis inside its layout cell, so the cell keeps whatever shape the layout hands it and the leftover space opens up as a gap between the image and the colorbar.ImPlotViewnow shapes the cell instead (the approach in Makie's aspect tutorial), so the axis fills it and the colorbar sits flush. Two wrinkles that the obviouscolsize!(layout, 1, Aspect(1, r))+resize_to_layout!pair does not survive:Aspectderives one cell dimension from the other and the derived one is unbounded, so deriving the width from the height overflows the layout for wide images (and vice versa for tall ones) — a nested view would grow straight over its neighbours. The block now picks the direction that fits the space it was actually given, and revisits it whenever that space changes.resize_to_layout!is a no-op here: anAspectrow or column leaves the enclosing layout's size undetermined, so a block containing one reports no preferred size and the figure has nothing to shrink onto. Whenimplotviewcreates its own figure it now sizes it from the block's solved content box instead (an explicitfigure = (; size = ...)still wins, and a view placed into someone else's figure never resizes it).Sized panels: figures that shrink-wrap around their content. The
Aspect-based cell shaping above is the fill-the-window mode: the panel takes whatever space the layout gives it, which is right for interactive use. The complementary mode is now supported too:implot(fig[1, 1], img; width = 400)(new recipe attributeswidth/height, injected as axis layout sizes) andimplotview(fig[1, 2], img; axis = (; height = 300))fix the panel's image-box size, deriving a missing dimension from the image extent so the panel keeps the data aspect. Fixed sizes make the layout fully determined, which flips the sizing direction:resize_to_layout!(fig)shrink-wraps the figure around any arrangement of panels instead of the panels depending on the figure size, eliminating hand-tunedFigure(size = ...)from every multi-panel docs figure. Makie 0.25 blocks report a determinable inner layout as their own autosize, so this works throughImPlotViewpanels in grids with no extra machinery, and a standalone sized view shrink-wraps its own figure automatically; unsized views keep the aspect-locked fill behavior above.Blank
CUNITmeans degrees (bf79dc3).CUNITis optional, and the WCS standard makesdegthe default unit of a celestial axis. Testingcunit == "deg"therefore misread any image that omits it, including the HST Eagle Nebula frame that headlines the docs, which was drawn with a distorted sky aspect and decimal-degree RA ticks (two of which rounded to the same label).isangularnow falls back to the ctype (RA/DEC, thexLON/xLATfamily, and the genericyzLN/yzLTform).Smaller additions
imviewgained anan_colorkeyword (default transparent, unchanged): NaN/missingpixels can be rendered opaque, e.g.imview(clipped; nan_color = :black)— without it, transparent holes read as whatever backdrop the image lands on (white axis panel vs. page background), which made sigma-clipped stars look like bright sources.ImPlotViewforwards the matchingnan_color,lowclip,highclip, andalphaattributes to its internal plot, completing its claim of accepting allimplotrendering kwargs (with the clip colors set, the Colorbar grows Makie's triangular out-of-range indicators automatically).ImPlotViewexposes its Axis asiv.axfor overplotting (e.g.lines!(iv.ax, apertures)) and the image plot asiv.plt.implotviewpanels with colorbars throughout, aperture overlays via Photometry.jl's new Makie extension,extract_sourcesoutput feeding apertures directly (paired with the Photometry 0.10 x/y convention fix below), and animviewmosaic viaImages.mosaic. A consistency pass applies the sized-panel pattern across the manual and guide pages, and the custom docs stylesheet is gone: the 450px image cap it existed for is superseded by letting figures fill the content column under Documenter's stockmax-width: 100%styling. The contours guide's overlays go throughimplotviewas well, and its Contour.jl section is reframed around what still requires that package in the Makie era: Access to the contour geometry itself (Makie vendors the same marching-squares algorithm internally but never exposes the computed polylines) demonstrated by re-projecting contour lines into world coordinates, with a colorbar of its own.Upstream requirements
sorta kwarg Photometry.jl#126)format_angleAstroAngles.jl#33, merged): tick label assembly delegates to the variable-lengthformat_angleadded there. The[sources]entry now tracks AstroAnglesmainand can be dropped once 0.3 is tagged (compat here is already0.3; Add AstroAngles.jl v0.3 compat to Project.toml #124 makes the same bump onmain). Only visible output change: final tick components are zero-padded like the others (00.00ˢinstead of0.00ˢ).docs/[sources]devs Makie, CairoMakie, and ComputePipeline (unregistered at 0.2) from the monorepo branch viasubdir.extract_colormap_recursiveundefinedTsidestepped by ourMakie._extract_colormap(::ImPlot)override, and Add plot-local dim convert for color MakieOrg/Makie.jl#5673 (still open) deletes that code path entirely — the override remains the documented pattern before and after.cunitas"deg "including the quotes), which brokecunit == "deg"detection for tick labels. Worked around withstripfitsstr. FITSWCS should probably dequote header string values.Deferred / follow-ups
optimize_ticks(6v)/6heuristic rather than true sexagesimal steps (30°, 10°, 1°, 30′, …). A genericSexagesimalTickslocator would fix this and could be upstreamed to Makie next toDateTimeTicks/AngularTicksonce 0.25 settles. Tick density is part of the same gap: the step comes from the world extent with no notion of how many pixels the axis has, so an extreme aspect ratio (now that panels honour it) crowds the short axis's labels into each other.Angle.to_string(precision=...), e.g.format_angle(deg; precision=:mas)snapping to the target component by integer arithmetic before decomposing). This would subsume AstroImages' privatedeg2dmsmμhelper, which we deliberately did not upstream as-is: itsfloorchain amplifies float fuzz (-46°40′decomposes to(-46, 39, 59, 999, 999.99…)), it isn't a standard notation, and after thesnappedpartsfix it is only the fallback path. Pairs naturally with theSexagesimalTicksitem above, since nice sexagesimal steps guarantee component-exact tick values.ImPlotViewvariant could restore this).maxpixels/restrictdownsampling not ported (Makie backends handle large images well).polquivercalledimresizewithout any package providing it (broken onmain). The Makie port uses its own block-averaging.PLT*/PPO*/AMDX/Y*) into a standard FK5 solution, which FITSWCS does not implement, so the photometry guide's M67 DSS cutout shows pixel axes where the pre-migration docs showed RA/Dec. A visible figure difference vs.stable, not a plotting change in this PR.