diff --git a/Project.toml b/Project.toml
index 4c12dca..b3f3df8 100644
--- a/Project.toml
+++ b/Project.toml
@@ -10,10 +10,20 @@ FITSIO = "525bcba6-941b-5504-bd06-fd0dc1a4d2eb"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
UnitfulAstro = "6112ee07-acf9-5e0f-b108-d242c714bf9f"
+[weakdeps]
+Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
+
+[extensions]
+MakieExt = "Makie"
+
[compat]
BSplineKit = "0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19"
DataDeps = "0.7"
FITSIO = "0.13.0, 0.14, 0.15, 0.16.1, 0.17"
+Makie = "0.21.18, 0.22"
Unitful = "0.17.0, 1"
UnitfulAstro = "0.3.0, 0.4, 1"
julia = "1.6"
+
+[extras]
+Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
diff --git a/docs/Project.toml b/docs/Project.toml
index 6700a49..920993b 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -1,9 +1,9 @@
[deps]
+CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DustExtinction = "fb44c06c-c62f-5397-83f5-69249e0a3c8e"
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
-Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
UnitfulAstro = "6112ee07-acf9-5e0f-b108-d242c714bf9f"
@@ -11,7 +11,6 @@ UnitfulAstro = "6112ee07-acf9-5e0f-b108-d242c714bf9f"
Documenter = "1"
LaTeXStrings = "1"
Measurements = "2"
-Plots = "1"
Unitful = "1"
UnitfulAstro = "1"
diff --git a/docs/make.jl b/docs/make.jl
index aff14ef..55563b0 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -1,11 +1,15 @@
using Documenter
using DustExtinction
+using CairoMakie
+
+CairoMakie.activate!(type="png", px_per_unit=3)
DocMeta.setdocmeta!(DustExtinction, :DocTestSetup, :(using DustExtinction); recursive = true)
+MakieExt = Base.get_extension(DustExtinction, :MakieExt)
include("pages.jl")
makedocs(;
- modules = [DustExtinction],
+ modules = [DustExtinction, MakieExt],
sitename = "DustExtinction.jl",
format = Documenter.HTML(;
prettyurls = get(ENV, "CI", nothing) == "true",
diff --git a/docs/pages.jl b/docs/pages.jl
index dc1aaf7..03b8fae 100644
--- a/docs/pages.jl
+++ b/docs/pages.jl
@@ -1,5 +1,6 @@
pages = [
"Home" => "index.md",
"Color Laws" => "color_laws.md",
- "Dust Maps" => "dust_maps.md"
-]
\ No newline at end of file
+ "Dust Maps" => "dust_maps.md",
+ "Plotting" => "plotting.md",
+]
diff --git a/docs/plots.jl b/docs/plots.jl
deleted file mode 100644
index 3957e79..0000000
--- a/docs/plots.jl
+++ /dev/null
@@ -1,187 +0,0 @@
-using Plots, LaTeXStrings
-import DustExtinction: ccm89_ca, ccm89_cb, od94_ca, od94_cb,
- # TODO: replace deprecated invum functions
- ccm89_invum, vcg04_invum, gcc09_invum, m14_invum,
- f99_invum, f04_invum, f19_invum,
- CAL00, FM90, G16, SFD98Map,
- # Kludge until Makie PR merged
- aa_to_invum
-
-dir = joinpath(@__DIR__, "src", "assets")
-
-#--------------------------------------------------------------------------------
-# ccm89
-
-w = range(0.3, 10.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = ccm89_invum.(w, rv, Ref(ccm89_ca), Ref(ccm89_cb))
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "ccm89_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# od94
-
-w = range(0.3, 10.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = ccm89_invum.(w, rv, Ref(od94_ca), Ref(od94_cb))
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "od94_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# cal00
-
-w = range(0.46, 8.3, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.0, 4.05, 5.0, 6.0]
- m = CAL00(Rv=rv).(aa_to_invum.(w)) # kludge until Makie
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "cal00_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# sfd98
-
-l = range(-pi, pi, length=400)
-b = range(-pi/64, pi/64, length=300)
-dustmap = SFD98Map()
-m = [dustmap(li, bj) for li in l, bj in b]
-heatmap(l, b, m, label="", transpose=true, colorbar_title="E(B-V)")
-xlabel!("l (rad)")
-ylabel!("b (rad)")
-savefig(joinpath(dir, "sfd98_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# gcc09
-
-w = range(3.3, 11.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = gcc09_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "gcc09_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# vcg04
-
-w = range(3.3, 8.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = vcg04_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "vcg04_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# F99
-
-w = range(0.3, 10.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = f99_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "F99_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# F04
-
-w = range(0.3, 10.0, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = f04_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "F04_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# F19
-
-w = range(0.3, 8.7, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = f19_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "F19_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# M14
-
-w = range(0.3, 3.3, length=1000)
-plot(legend=:topleft)
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = m14_invum.(w, rv)
- plot!(w, m, label="Rv=$rv")
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "M14_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# FM90
-
-w = range(3.8, 8.6, step = 0.001)
-x = 1e4 ./ w
-plot(legend=:top)
-
-m1 = FM90().(x)
-plot!(w, m1, label = "total")
-
-m2 = FM90(c3=0.0, c4=0.0).(x)
-plot!(w, m2, label = "linear term")
-
-m3 = FM90(c1=0.0, c2=0.0, c4=0.0).(x)
-plot!(w, m3, label = "bump term")
-
-m4 = FM90(c1=0.0, c2=0.0, c3=0.0).(x)
-plot!(w, m4, label = "FUV rise term")
-
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"E(x - V)/E(B - V)")
-savefig(joinpath(dir, "FM90_plot.svg"))
-
-#--------------------------------------------------------------------------------
-# G16
-
-# Fixed f_A = 1.0, variable Rv
-w = range(0.3, 10.0, length=1000)
-x = 1e4 ./ w
-plot()
-for rv in [2.0, 3.1, 4.0, 5.0, 6.0]
- m = G16(Rv=rv, f_A=1.0).(x)
- plot!(w, m, label="Rv=$rv", legendtitle="f_A = 1.0", legend=:topleft)
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "G16_fixed_f_A_plot.svg"))
-
-# Fixed Rv = 3.1, variable f_A
-plot()
-for f_A in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
- m = G16(Rv=3.1, f_A=f_A).(x)
- plot!(w, m, label="f_A=$f_A", legendtitle="Rv = 3.1", legend=:topleft)
-end
-xlabel!(L"x\ \left[\mu m ^{-1}\right]")
-ylabel!(L"A(x)/A(V)")
-savefig(joinpath(dir, "G16_fixed_Rv_plot.svg"))
diff --git a/docs/src/assets/F04_plot.svg b/docs/src/assets/F04_plot.svg
deleted file mode 100644
index d976f6c..0000000
--- a/docs/src/assets/F04_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/F19_plot.svg b/docs/src/assets/F19_plot.svg
deleted file mode 100644
index ad47d9d..0000000
--- a/docs/src/assets/F19_plot.svg
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
diff --git a/docs/src/assets/F99_plot.svg b/docs/src/assets/F99_plot.svg
deleted file mode 100644
index 79ed36c..0000000
--- a/docs/src/assets/F99_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/FM90_plot.svg b/docs/src/assets/FM90_plot.svg
deleted file mode 100644
index 8d42d0f..0000000
--- a/docs/src/assets/FM90_plot.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
diff --git a/docs/src/assets/G16_fixed_Rv_plot.svg b/docs/src/assets/G16_fixed_Rv_plot.svg
deleted file mode 100644
index 07f34e6..0000000
--- a/docs/src/assets/G16_fixed_Rv_plot.svg
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
diff --git a/docs/src/assets/G16_fixed_f_A_plot.svg b/docs/src/assets/G16_fixed_f_A_plot.svg
deleted file mode 100644
index 9c6e3e2..0000000
--- a/docs/src/assets/G16_fixed_f_A_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/M14_plot.svg b/docs/src/assets/M14_plot.svg
deleted file mode 100644
index a4cfe64..0000000
--- a/docs/src/assets/M14_plot.svg
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
diff --git a/docs/src/assets/cal00_plot.svg b/docs/src/assets/cal00_plot.svg
deleted file mode 100644
index cca66da..0000000
--- a/docs/src/assets/cal00_plot.svg
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
diff --git a/docs/src/assets/ccm89_plot.svg b/docs/src/assets/ccm89_plot.svg
deleted file mode 100644
index 6931812..0000000
--- a/docs/src/assets/ccm89_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/gcc09_plot.svg b/docs/src/assets/gcc09_plot.svg
deleted file mode 100644
index 4dc35e5..0000000
--- a/docs/src/assets/gcc09_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg
deleted file mode 100644
index a6002a1..0000000
--- a/docs/src/assets/logo.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/docs/src/assets/od94_plot.svg b/docs/src/assets/od94_plot.svg
deleted file mode 100644
index 5fc5d16..0000000
--- a/docs/src/assets/od94_plot.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/docs/src/assets/sfd98_plot.svg b/docs/src/assets/sfd98_plot.svg
deleted file mode 100644
index c2abce4..0000000
--- a/docs/src/assets/sfd98_plot.svg
+++ /dev/null
@@ -1,1593 +0,0 @@
-
-
diff --git a/docs/src/assets/vcg04_plot.svg b/docs/src/assets/vcg04_plot.svg
deleted file mode 100644
index 239c465..0000000
--- a/docs/src/assets/vcg04_plot.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
diff --git a/docs/src/color_laws.md b/docs/src/color_laws.md
index 096fa5a..91c9376 100644
--- a/docs/src/color_laws.md
+++ b/docs/src/color_laws.md
@@ -1,3 +1,8 @@
+```@setup plotting
+using DustExtinction, CairoMakie
+using DustExtinction: ExtinctionLaw
+include("./plotting.jl")
+```
# [Color laws](@id laws)
@@ -56,7 +61,7 @@ true
```
-## Advanced Usage
+## [Advanced Usage](@id color_laws_advanced_usage)
The color laws also have built-in support for uncertainties using [Measurements.jl](https://github.com/juliaphysics/measurements.jl).
@@ -138,7 +143,9 @@ and is loosely associated with the size of the dust grains in the interstellar m
### Clayton, Cardelli and Mathis (1989)
-
+```@example plotting
+lplot(CCM89) # hide
+```
```@docs
CCM89
@@ -146,7 +153,9 @@ CCM89
### O'Donnell 1994
-
+```@example plotting
+lplot(OD94) # hide
+```
```@docs
OD94
@@ -154,7 +163,9 @@ OD94
### Calzetti et al. (2000)
-
+```@example plotting
+lplot(CAL00) # hide
+```
```@docs
CAL00
@@ -162,7 +173,9 @@ CAL00
### Valencic, Clayton, & Gordon (2004)
-
+```@example plotting
+lplot(VCG04) # hide
+```
```@docs
VCG04
@@ -170,7 +183,9 @@ VCG04
### Gordon, Cartledge, & Clayton (2009)
-
+```@example plotting
+lplot(GCC09) # hide
+```
```@docs
GCC09
@@ -178,7 +193,9 @@ GCC09
### Fitzpatrick (1999)
-
+```@example plotting
+lplot(F99) # hide
+```
```@docs
F99
@@ -186,7 +203,9 @@ F99
### Fitzpatrick (2004)
-
+```@example plotting
+lplot(F04) # hide
+```
```@docs
F04
@@ -194,7 +213,9 @@ F04
### Fitzpatrick (2019)
-
+```@example plotting
+lplot(F19) # hide
+```
```@docs
F19
@@ -202,46 +223,55 @@ F19
### Maiz Apellaniz et al. (2014)
-
-
-```@docs
-M14
+```@example plotting
+lplot(M14) # hide
```
-## API/Reference
-
```@docs
-redden
-deredden
-DustExtinction.ExtinctionLaw
-DustExtinction.bounds
-DustExtinction.checkbounds
+M14
```
-### Fittable Extinction Laws
+## Fittable Extinction Laws
-#### Fitzpatrick & Massa (1990)
+### Fitzpatrick & Massa (1990)
-
+```@example plotting
+lplot(FM90) # hide
+```
```@docs
FM90
```
-### Mixture Extinction Laws
+## Mixture Extinction Laws
-#### Gordon et al. (2003)
+### Gordon et al. (2003)
```@docs
G03_SMCBar
G03_LMCAve
```
-#### Gordon et al. (2016)
+### Gordon et al. (2016)
-
-
+```@example plotting
+mplot(G16, (2.0, 3.1, 4.0, 5.0, 6.0), 1.0) # hide
+```
+
+```@example plotting
+mplot(G16, 3.1, 0.0:0.2:1.0) # hide
+```
```@docs
G16
```
+
+## API/Reference
+
+```@docs
+redden
+deredden
+DustExtinction.ExtinctionLaw
+DustExtinction.bounds
+DustExtinction.checkbounds
+```
diff --git a/docs/src/dust_maps.md b/docs/src/dust_maps.md
index 2326eab..3b322d1 100644
--- a/docs/src/dust_maps.md
+++ b/docs/src/dust_maps.md
@@ -1,3 +1,9 @@
+```@setup plotting
+using DustExtinction, CairoMakie
+using DustExtinction: ExtinctionLaw
+include("./plotting.jl")
+```
+
# [Dust Maps](@id maps)
## Usage
@@ -31,6 +37,10 @@ julia> [dustmap(l[i], b[j]) for i in 1:length(l), j in 1:length(b)]
```
+```@example plotting
+dplot() # hide
+```
+
## Advanced Usage
Our dust maps also have native support for `Unitful.jl` and `Measurements.jl`.
@@ -56,10 +66,6 @@ julia> dustmap(l, b)
## API/Reference
-### Schlegel, Finkbeiner and Davis (1998)
-
-
-
```@docs
SFD98Map
```
diff --git a/docs/src/plotting.jl b/docs/src/plotting.jl
new file mode 100644
index 0000000..3129123
--- /dev/null
+++ b/docs/src/plotting.jl
@@ -0,0 +1,124 @@
+"""
+ lplot(law::ExtinctionLaw; args...)
+
+Rv law plot with automatic axis labels.
+"""
+lplot(law::ExtinctionLaw; args...) = Makie.lines(
+ law;
+ axis = (;
+ xlabel = Makie.rich("x [μm", Makie.superscript("-1"), "]"),
+ ylabel = Makie.rich("A(x) / A(V)"),
+ ),
+ args...,
+)
+
+"""
+ lplot(law::Type{<:ExtinctionLaw}; args...)
+
+Rv law series plot with automatic axis labels.
+"""
+function lplot(law::Type{<:ExtinctionLaw}; args...)
+ # Dummy plot
+ fig, ax, p = lplot(law())
+
+ for Rv in (2.0, 3.1, 4.0, 5.0, 6.0)
+ Makie.lines!(ax, law(Rv); label=Makie.rich("Rv = $(Rv)"), args...)
+ end
+
+ Makie.axislegend(ax; position=:lt)
+
+ fig
+end
+
+"""
+ lplot(law::FM90; args...)
+
+Fittable law plot with automatic axis labels.
+"""
+lplot(law::FM90; args...) = Makie.lines(law;
+ axis = (;
+ xlabel = Makie.rich("x [μm", Makie.superscript("-1"), "]"),
+ ylabel = Makie.rich("(E(λ) - V) / E(B - V)"),
+ ),
+ args...,
+)
+
+"""
+ lplot(law::FM90; args...)
+
+Fittable law series plot with automatic axis labels.
+"""
+function lplot(law::Type{<:FM90}; args...)
+ # m1
+ fig, ax, p = lplot(law(); label="total")
+
+ # m2
+ Makie.lines!(ax, law(c3=0.0, c4=0.0); label="linear term")
+
+ # m3
+ Makie.lines!(ax, law(c1=0.0, c2=0.0, c4=0.0); label="bump term")
+
+ # m4
+ Makie.lines!(ax, law(c1=0.0, c2=0.0, c3=0.0); label="FUV rise term")
+
+ # for Rv in (2.0, 3.1, 4.0, 5.0, 6.0)
+ # Makie.lines!(ax, law(Rv); label=Makie.rich("Rv = $(Rv)"), args...)
+ # end
+
+ Makie.axislegend(ax; position=:lt)
+
+ fig
+end
+
+"""
+ mplot(law::Type{G16}, Rvs, f_A::Real; args...)
+
+Mixture law plot with automatic axis labels.
+"""
+function mplot(law::Type{G16}, Rvs, f_A::Real; args...)
+ # Dummy plot
+ fig, ax, p = lplot(law())
+
+ for Rv in Rvs
+ Makie.lines!(ax, law(; Rv, f_A); label=Makie.rich("Rv = $(Rv)"), args...)
+ end
+
+ Makie.axislegend(ax, "f_A = $(f_A)"; position=:lt)
+
+ fig
+end
+
+"""
+ mplot(law::Type{G16}, Rvs, f_A::Real; args...)
+
+Mixture law series plot with automatic axis labels.
+"""
+function mplot(law::Type{G16}, Rv::Real, f_As; args...)
+ # Dummy plot
+ fig, ax, p = lplot(law())
+
+ for f_A in f_As
+ Makie.lines!(ax, law(; Rv, f_A); label=Makie.rich("f_A = $(f_A)"), args...)
+ end
+
+ Makie.axislegend(ax, "Rv = $(Rv)"; position=:lt)
+
+ fig
+end
+
+"""
+ dplot(dustmap=SFD98Map(); lrange=(-3, 3), brange=(-1, 1))
+
+Heatmap of the given `dustmap`, with galactic longitude ``(l)`` on the x-axis and galactic latitude ``(b)`` on the y-axis. Angles are displayed in degrees by default. The plot axis ranges for both are set by `lrange` and `brange`, respectively.
+"""
+function dplot(dustmap=SFD98Map(); lrange=(-3, 3), brange=(-1, 1))
+ l = range(lrange..., length=400)
+ b = range(brange..., length=300)
+
+ fig, ax, p = heatmap(l, b, dustmap; colorrange=(0, 3), colormap=:cividis)
+ ax.xlabel = "l [°]"
+ ax.ylabel = "b [°]"
+ Colorbar(fig[1, 2], p; label="E(B - V) [mag]")
+
+ fig
+end
diff --git a/docs/src/plotting.md b/docs/src/plotting.md
new file mode 100644
index 0000000..ac99369
--- /dev/null
+++ b/docs/src/plotting.md
@@ -0,0 +1,105 @@
+# [Plotting](@id plotting)
+
+DustExtinction.jl is designed to work automatically with many of Makie.jl's core plotting functions, which can then be composed together to form more complex figures. Below we show a few practical applications.
+
+!!! note
+ By default, all plots adopt the wavelength limits (in units of 1/μm) defined by [`DustExtinction.bounds`](@ref).
+
+## Model plot example
+For a given [`DustExtinction.ExtinctionLaw`](@ref), Makie's usual PointBased plotting functions (e.g., [lines](https://docs.makie.org/stable/reference/plots/lines), [scatter](https://docs.makie.org/stable/reference/plots/scatter), [stairs](https://docs.makie.org/stable/reference/plots/stairs), etc.) should work right out-of-the-box:
+
+```@example a
+using DustExtinction, CairoMakie
+```
+
+```@example a
+model = CCM89()
+```
+
+```@example a
+# Automatic limits defined by model
+lines(model; linewidth=3, color=:cornflowerblue) # Or plot(model; [plot options])
+```
+
+```@example a
+# Custom limits
+fig = Figure()
+ax1 = Axis(fig[1, 1])
+ax2 = Axis(fig[1, 2]; limits=(3, 6, 0, 5))
+
+lines!(ax1, model)
+stairs!(ax2, model; color=:orange)
+
+linkyaxes!(ax1, ax2)
+
+fig
+```
+
+A predefined vector of wavelengths can also be passed to these plotting functions directly. Let's combine this with Makie's integration with Measurements.jl to visualize the underlying uncertainty in our data:
+
+```@example a
+using Measurements, Unitful, UnitfulAstro
+
+wavs = let
+ x = range(2_000, 3_000; length=1_000)
+ x = x .± 1e5 * inv.(ustrip.(x)) # "Å"
+end
+
+extinction = model.(wavs) # mag
+
+wavs_sampled, extinction_sampled = let
+ N_samples = 7
+ wavs[range(begin, step=end ÷ N_samples; length=N_samples)],
+ extinction[range(begin, step=end ÷ N_samples; length=N_samples)]
+end
+
+fig, ax, p = band(wavs, extinction; alpha=0.5, label="model uncertainty")
+
+lines!(ax, wavs, extinction; label="model: CCM89")
+
+scatter!(ax, wavs_sampled, extinction_sampled; color=:orange, label="observations")
+
+# Currently ambiguous for both x and y being Measurements
+# so we focus on the y-uncertainty instead
+errorbars!(ax, Measurements.value.(wavs_sampled), extinction_sampled;
+ whiskerwidth = 10,
+ color = :orange,
+ label = "obs. uncertainty",
+)
+
+axislegend()
+ax.xlabel = "Wavelength [Å]"
+ax.ylabel = "A(x) / A(V) [mag]"
+
+fig
+```
+
+## Dust map example
+A [`heatmap`](https://docs.makie.org/stable/reference/plots/heatmap#heatmap) plot also works automatically for [`DustExtinction.SFD98Map`](@ref):
+
+```@example a
+dustmap = SFD98Map()
+
+heatmap(dustmap; colorrange=(0, 3), colormap=:cividis) # Or plot(dustmap; [plot kwargs])
+```
+
+Similarly to the extinction law plots, we can create our own custom dust map plots. Here is an example using [Unitful.jl](https://painterqubits.github.io/Unitful.jl/stable/)'s integration:
+
+```@example a
+using Unitful
+
+lrange = range(-1, 1; length=400)u"°"
+brange = range(-0.25, 0.25; length=300)u"°"
+
+fig, ax, p = heatmap(lrange, brange, dustmap; colorrange=(0, 3), colormap=:cividis)
+
+ax.xlabel = "l [°]"
+ax.ylabel = "b [°]"
+
+Colorbar(fig[1, 2], p; label="E(B - V) [mag]")
+
+fig
+```
+
+!!! tip
+ See [plotting.jl](https://github.com/JuliaAstro/DustExtinction.jl/blob/docs-makie/docs/src/plotting.jl) for more plotting examples. The convenience functions defined there are used to generate the other figures shown in this documentation.
diff --git a/ext/MakieExt.jl b/ext/MakieExt.jl
new file mode 100644
index 0000000..61d81f1
--- /dev/null
+++ b/ext/MakieExt.jl
@@ -0,0 +1,37 @@
+module MakieExt
+ using DustExtinction
+ import DustExtinction: bounds, aa_to_invum, ExtinctionLaw
+ import Makie as M
+
+ # Ex: plot(CCM89())
+ function M.convert_arguments(P::M.PointBased, law::ExtinctionLaw)
+ aa = range(bounds(law)...; length=1_000)
+ m = map(law, aa)
+ invum = map(aa_to_invum, aa)
+ return M.convert_arguments(P, invum, m)
+ end
+ M.plottype(::ExtinctionLaw) = M.Lines
+
+ # Ex: plot(wavs, CCM89())
+ function M.convert_arguments(P::M.PointBased, x, law::ExtinctionLaw)
+ m = map(law, x)
+ M.convert_arguments(P, x, m)
+ end
+ M.plottype(x, ::ExtinctionLaw) = M.Lines
+
+ # Ex: heatmap(SFD98Map())
+ function M.convert_arguments(P::M.CellGrid, dustmap::SFD98Map)
+ l = range(-3, 3; length=400)
+ b = range(-1, 1; length=300)
+ m = [dustmap(li, bj) for li in l, bj in b]
+ return M.convert_arguments(P, l, b, m)
+ end
+ M.plottype(::SFD98Map) = M.Heatmap
+
+ # Ex: heatmap(lrange, brange, SFD98Map())
+ function M.convert_arguments(P::M.CellGrid, lrange, brange, dustmap::SFD98Map)
+ m = [dustmap(li, bj) for li in lrange, bj in brange]
+ return M.convert_arguments(P, lrange, brange, m)
+ end
+ M.plottype(x, y, ::SFD98Map) = M.Heatmap
+end
diff --git a/test/Project.toml b/test/Project.toml
index 0bd039d..18d1eff 100644
--- a/test/Project.toml
+++ b/test/Project.toml
@@ -1,5 +1,6 @@
[deps]
DataDeps = "124859b0-ceae-595e-8997-d05f6a7a8dfe"
+Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/test/makie_recipes.jl b/test/makie_recipes.jl
new file mode 100644
index 0000000..edbc223
--- /dev/null
+++ b/test/makie_recipes.jl
@@ -0,0 +1,33 @@
+import Makie: Makie as M
+
+law = CCM89()
+aa = range(bounds(law)...; length=1_000)
+xs = invum = map(DustExtinction.aa_to_invum, aa)
+ys = map(law, aa)
+law_plot_args(wavs, vals) = (M.Point{2, Float64}[[x, y] for (x, y) in zip(wavs, vals)], )
+
+@testset "PointBased" begin
+ @test all(M.convert_arguments(M.PointBased(), law) .≈ law_plot_args(xs, ys))
+ @test all(M.convert_arguments(M.PointBased(), aa, law) .≈ law_plot_args(aa, ys))
+ @test M.plottype(law) == M.Lines
+ @test M.plottype(aa, law) == M.Lines
+end
+
+lrange = range(-3, 3; length=400)
+(lmin, lmax), llength, lstep = extrema(lrange), length(lrange) + 1, step(lrange)
+lrange_plot = range(lmin - lstep/2, lmax + lstep/2; length=llength) |> collect
+
+brange = range(-1, 1; length=300)
+(bmin, bmax), blength, bstep = extrema(brange), length(brange) + 1, step(brange)
+brange_plot = range(bmin - bstep/2, bmax + bstep/2; length=blength) |> collect
+
+dustmap = SFD98Map()
+m_plot = Float32[dustmap(li, bj) for li in lrange, bj in brange]
+dustmap_plot_args = (lrange_plot, brange_plot, m_plot, )
+
+@testset "CellGrid" begin
+ @test all(M.convert_arguments(M.CellGrid(), dustmap) .≈ dustmap_plot_args)
+ @test all(M.convert_arguments(M.CellGrid(), lrange, brange, dustmap) .≈ dustmap_plot_args)
+ @test M.plottype(dustmap) == M.Heatmap
+ @test M.plottype(lrange, brange, dustmap) == M.Heatmap
+end
diff --git a/test/runtests.jl b/test/runtests.jl
index 3557455..2efdcc7 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -10,6 +10,7 @@ using Test, Measurements, Unitful, UnitfulAstro, Random
include("dust_maps.jl")
include("fittable_laws.jl")
include("mixture_laws.jl")
+ VERSION ≥ v"1.9" && include("makie_recipes.jl")
@testset "interfaces" begin
for LAW in [CCM89, OD94, CAL00, GCC09, VCG04, FM90, G16, F99, F04, F19, M14]