Off-axis Projection & LOS API Reference
Docstrings for the off-axis projection, line-of-sight, and synthetic-observation tools. The narrative guide is in Off-axis Projection; off-axis views are selected through the same projection call documented in the Projections API.
Line-of-sight cubes & spectra
Mera.los_cube — Function
los_cube(dataobject; quantity=:vlos, <view kwargs>, nbins=64, qrange=nothing, q_unit=:standard,
weight=:mass, res=256, pxsize=[size,:unit], xrange=[missing,missing], yrange=[missing,missing], center=[:bc],
range_unit=:standard, binning=:overlap, mask=[false]) -> LosCubeTypeOff-axis line-of-sight distribution cube: cube[i,j,k] is the deposited weight (default mass) at sky pixel (i,j) in bin k of the line-of-sight quantity. The distribution along each sightline, per pixel — i.e. a "spectrum". quantity may be
- a scalar field name (
:T,:rho,:cs, …) → its per-sightline distribution (a PDF), :vlos→ the line-of-sight velocity (a spectral / velocity-channel cube), or- a 3-vector of component names (
(:vx,:vy,:vz),(:ax,:ay,:az),(:bx,:by,:bz)) → its line-of-sight componentvector·ŵ.
The view is chosen with the same keywords as projection. binning accepts the centre-deposit previews :cic/:ngp and the hole-free footprint modes :overlap/:exact (default :overlap, recommended for maps when pixels are finer than cells). qrange (the LOS-axis range) is in the scaled q_unit; samples outside an explicit qrange are dropped. weight should be a cumulative/extensive quantity (:mass, default, or :volume) — it is summed along each sightline, so an intensive field like :rho/:T would give a non-physical "summed density" cube. Returns a LosCubeType; store it with savecube. los_moments gives the column/mean/dispersion maps. cube's sky-pixel edges x/y are in code length units.
Mera.velocity_cube — Function
velocity_cube(dataobject; nv=64, vrange=nothing, v_unit=:km_s, <los_cube kwargs>) -> LosCubeTypeConvenience wrapper of los_cube with quantity=:vlos — a velocity-channel (spectral) cube. The result exposes .velocity (= .bins) and .v_unit aliases.
Mera.getspectrum — Function
getspectrum(c::LosCubeType, i::Integer, j::Integer) -> (centres, values)
getspectrum(c::LosCubeType; x, y, range_unit=c.range_unit) -> (centres, values)The per-pixel line-of-sight spectrum from a LOS / velocity cube: the binned quantity distribution along the sightline through sky pixel (i,j), or — second form — through the pixel nearest the physical sky position (x, y) (offsets from the cube centre, in range_unit).
Returns the bin centres and the per-bin weight (e.g. mass), ready to plot as a line: lines(getspectrum(vc; x=0, y=0)...). For a velocity cube the centres are line-of-sight velocities (a synthetic emission-line profile); for a los_cube(:T) they are temperatures (a PDF). Integrating values over the bins recovers that pixel's column (the moment-0 value).
Mera.integrated_spectrum — Function
integrated_spectrum(c::LosCubeType; mask=nothing) -> (bins, values)The integrated (global) spectrum of a LOS / velocity cube: the per-pixel spectra summed over the whole sky map (or over a boolean mask of size (nx, ny)) — the synthetic global line profile (e.g. an HI/CO single-dish profile). Returns the bin centres and the summed weight per channel. Summing values over the channels equals the cube's total deposited weight (the moment-0 total, e.g. the enclosed mass for a velocity cube).
Mera.los_moments — Function
los_moments(c::LosCubeType) -> (Σ, mean, dispersion)Moment-0/1/2 maps of a LOS cube c: the column Σ (summed weight, e.g. column mass), the weight-weighted mean of the binned quantity, and its dispersion — each a 2D array.
The moments are computed from the cube's bin centres, so the dispersion is biased high by roughly the Sheppard correction σ²true ≈ σ²measured − Δ²/12, where Δ = bin width (step(c.bins)). The bias is negligible once a feature spans several bins (Δ ≪ σ); it only matters for under-resolved, near-delta distributions. To reduce it, build the cube with more nbins or a tighter qrange. For an unbinned (bias-free) dispersion of a vector's LOS component, use los_component(...; dispersion=true), which accumulates the moments from the continuous per-cell samples directly.
Mera.velocity_moments — Function
velocity_moments(vc::LosCubeType) -> (Σ, vlos, σlos)Velocity-cube moments: column mass Σ, mass-weighted mean line-of-sight velocity vlos, and dispersion σlos — i.e. the :sd/:mass, :vlos and :σlos maps recovered from the cube.
Line-of-sight maps
Mera.los_component — Function
los_component(dataobject, vector; <view kwargs>, weight=:mass, unit=:standard,
dispersion=false, res=256, pxsize=[size,:unit], center=[:bc], range_unit=:standard)
-> NamedTuple(map, dispersion, los, up, cam_right, center, pixsize, range_unit, unit, x, y)Mass-weighted 2D map of the line-of-sight component vector·ŵ of an arbitrary vector field, given as a 3-tuple of getvar symbols — e.g. (:vx,:vy,:vz) ⇒ vₗₒₛ, (:ax,:ay,:az) ⇒ LOS acceleration (gravity), (:bx,:by,:bz) ⇒ LOS magnetic field. dispersion=true returns the dispersion instead of the mean.
Returns a NamedTuple whose .map field holds the 2D array (the mean LOS component, or its dispersion when dispersion=true); the .dispersion flag, the camera basis (los/up/ cam_right), numeric center, pixsize, range_unit, unit, and the code-unit bin-edge axes x/y travel with it (so it can be fed to mock_observe and carries provenance).
Mera.moment2 — Function
moment2(dataobject, quantity [, unit]; weight=:mass, <view & range kwargs>) -> NamedTupleOff-axis line-of-sight dispersion (moment 2) of an arbitrary field quantity: the weight-weighted standard deviation σ = √(⟨q²⟩w − ⟨q⟩w²) of q along each sightline. Works for quantity=:vlos (reproduces the σlos map), any scalar getvar field (:T, :rho, …), or a 3-vector (:vx,:vy,:vz) (its line-of-sight component). Returns the same metadata-carrying result as los_component — .map holds the dispersion; the matching mean (moment 1) is los_component(dataobject, quantity; …).
Mera.column_integral — Function
column_integral(dataobject, quantity[, unit]; binning=:exact, <view & range kwargs>)
-> (map, quantity, unit, los, up, cam_right, center, pixsize, extent, boxlen, scale)Line-of-sight column integral ∫ q dl of an arbitrary field q — the path-length-weighted sum along each sightline (not mass-weighted). This is the geometric primitive behind a true column density / optical-depth map: e.g. q=:rho gives the mass column (the same physical quantity as :sd, up to the code↔physical unit conversion of ρ and the path length), a constant opacity κ gives τ = κ·∫ρ dl, and q=:ne (with unit) the dispersion-measure-like ∫n dl.
It is exact when binning=:exact (the analytic chord length through each cube is integrated per pixel) and approximate for :overlap/:cic. Internally this is projection(dataobject, quantity; mode=:sum, weighting=:volume, binning=binning, …) divided by the pixel area, since the volume-weighted :sum deposits Σ q·(cube∩pixel-column volume).
.map holds ∫ q dl with the path length in code units (multiply by the appropriate dataobject.scale factor for a physical length, e.g. .map .* dataobject.scale.cm). The camera basis and extent travel with the result.
Mera.offaxis_slice — Function
offaxis_slice(dataobject, var [, unit]; <view & range kwargs>, res=256, pxsize=nothing)
-> (map, x, y, extent, los, up, cam_right, center, pixsize, range_unit, scale)Off-axis slice (cutting plane): the value of var on the camera plane through the projection center, for an arbitrary line of sight (same view keywords as projection: los/inclination/azimuth/axis/theta/phi/:faceon/:edgeon/position_angle/up). Unlike a projection it does not integrate along the line of sight — each pixel is assigned the value of the cell that the plane passes through there (the cell nearest the plane wins; a coarse cell fills its footprint). This is a nearest-cell sample, hence resolution-dependent and not mass-conserving (unlike projection). Grid data only (hydro/gravity/RT).
Synthetic observations
Mera.emission_map — Function
emission_map(dataobject; kappa, source, <view & range kwargs>, res=256, pxsize=nothing)
-> (map, tau, x, y, extent, los, up, cam_right, center, pixsize, scale)Off-axis emission + absorption map: the front-to-back formal solution of radiative transfer along each sightline, I = Σ_cells S·(1 − e^{−Δτ})·e^{−τ_front} with Δτ = κ·ℓ and the exact box-spline chord length ℓ per cell (the geometry the :exact deposit already computes). This turns the conservative projection into an approximate radiative-transfer mock observation (emission attenuated by intervening optical depth).
kappa— absorption coefficient (per code length): a getvar field name (Symbol), a constant (Real), or a per-cell vector. The emissivity isκ·S, so a small but nonzeroκgives the optically-thin limitI ≈ S·κL, whilekappa=0yields zero emission. For a κ-independent optically-thin column usecolumn_integralormode=:sum.source— source function / emissivityS: a field name, constant, or per-cell vector.
Cells are accumulated nearest→farthest from the observer (the observer is on the −ŵ side; ŵ points away from the observer). Returns .map = observed intensity I and .tau = total optical depth, plus the camera metadata. Validation: a uniform slab of depth L with constant κ,S gives I = S(1 − e^{−κL}) (thin limit S·κL, thick limit S). View/centring keywords are the same as projection.
Mera.mock_observe — Function
mock_observe(map2d; beam_fwhm, pixsize=1.0, noise=0.0, rng=nothing) -> Matrix
mock_observe(m::DataMapsType, var; beam_fwhm, beam_unit=:standard,
distance=0.0, distance_unit=:standard, noise=0.0, rng=nothing) -> MatrixConvolve a 2D map with a Gaussian beam of full-width-half-maximum beam_fwhm and (optionally) add Gaussian noise (standard deviation, in map units), returning the "observed" image.
- Matrix form:
pixsizeis the physical size of a pixel andbeam_fwhmis in the same unit (sobeam_fwhm/pixsizeis the beam in pixels). DataMapsTypeform: pass a projection resultmand a variable key; the pixel size is taken fromm.pixsize.beam_fwhmis interpreted inbeam_unit:- a physical unit (
:kpc,:pc, …) — a beam fixed in physical size; - an angular unit (
:arcsec,:arcmin,:deg,:rad) with a sourcedistance(indistance_unit) — the beam isθ[rad] × distancephysical (small-angle). UseMera's cosmology helpers for the angular-diameter distance of a redshifted source.
- a physical unit (
Beam σ = FWHM / (2√(2 ln 2)). Convolution uses a Gaussian kernel (Images.jl imfilter). Pass a seeded rng for reproducible noise.
Mera.position_velocity — Function
position_velocity(dataobject; <view kwargs>, offset_axis=:right, nbins=256,
offset_unit=:kpc, v_unit=:km_s, center=[:bc], range_unit=:standard,
mask=[false]) -> NamedTupleOff-axis position–velocity (PV) diagram: a 2D mass histogram in (in-plane offset, line-of-sight velocity). Returns a NamedTuple (offset, velocity, pv, offset_unit, v_unit, los) where offset/velocity are the bin edges and pv is the nbins×nbins mass map (M⊙ in code mass units).
The view is set with the same keywords as projection (los, inclination/azimuth/axis, theta/phi, direction=:faceon/:edgeon, position_angle). offset_axis selects which in-plane camera axis is the position coordinate (:right = image x, or :up = image y).
Sequences, storage & export
Mera.rotation_sequence — Function
rotation_sequence(dataobject, var, [unit]; sweep=:azimuth, angles,
axis=:angmom, inclination=0, fov=nothing, fov_unit=:standard,
center=[:bc], range_unit=:standard, res=256, <projection kwargs>)
-> Vector{AMRMapsType}Render var from a sequence of viewing angles for an orbit movie, all sharing ONE fixed field of view so successive frames do not jitter (a plain per-frame projection recomputes the extent for every angle). sweep selects which angle varies (:azimuth, :inclination, or :position_angle) and angles is the list of values (degrees by default).
The shared FOV is the symmetric window ±fov (in fov_unit) about center. If fov is nothing it is set to the maximum cell/particle distance from center (so the object fits at every angle). Returns a Vector of map objects — one per angle — ready to animate.
Mera.savecube — Function
savecube(c::LosCubeType, filename; verbose=true) -> String
loadcube(filename; verbose=true) -> LosCubeTypeSave / load a LOS cube to a JLD2 file (the .jld2 extension is added if missing). The cube, its axes, the binned quantity/units, the camera basis and the simulation info are all stored.
Mera.savemap — Function
savemap(p::DataMapsType, filename; verbose=true) -> String
loadmap(filename; verbose=true) -> DataMapsTypeSave / load a projection result (an AMRMapsType/PartMapsType from projection) to a JLD2 file — the same lightweight, Julia-native way savecube/loadcube persist a LOS cube (the .jld2 extension is added if missing). The whole object round-trips: every map and its unit, the extent/pixsize, the off-axis camera basis, and the simulation info — so a reloaded map still plots, re-projects, and carries provenance.
p = projection(gas, [:sd, :vx])
savemap(p, "maps.jld2")
p2 = loadmap("maps.jld2") # AMRMapsType, identical to pJLD2 is a subset of the HDF5 format, so these files also open in h5py and other HDF5 readers.
(loadcube/loadmap are documented together with savecube/savemap above; savefits and the LosCubeType struct are in the Complete API Reference.)
Save a projection result the same Julia-native, JLD2 way a cube is saved:
p = projection(gas, [:sd, :vx])
savemap(p, "maps.jld2") # all maps + units + geometry + provenance
p2 = loadmap("maps.jld2") # → AMRMapsType, ready to plot/re-projectJLD2 is a subset of the HDF5 format, so these files also open in h5py / other HDF5 readers.
Camera kinematics (internal helpers)
These are not exported but underlie every off-axis call; documented for reference.
Mera.build_camera_basis — Function
build_camera_basis(los, up=nothing; roll=0.0) -> (right, up, w)Construct a right-handed orthonormal camera basis from a line-of-sight vector los (the viewing direction) and an optional up hint.
Returns three unit 3-vectors (right, up, w) where w = los/‖los‖ is the viewing direction, and right, up span the image plane (image x = right, image y = up). The basis is right-handed with right × up = w.
If up is nothing — or (anti)parallel to los — a deterministic auto-up is chosen (the world axis least parallel to los), so the result is fully reproducible.
roll (radians) rotates the image plane about the line of sight — i.e. it sets the orientation of the image on the "sky" (the astronomical position angle / camera roll). It leaves w unchanged and rotates (right, up) together, so it composes with any way of choosing los.
Convention check: los=[0,0,1], up=[0,1,0] ⇒ right=[1,0,0], up=[0,1,0], matching the axis-aligned direction=:z mapping (image x→sim x, image y→sim y).
Mera.resolve_los — Function
resolve_los(; los, theta, phi, inclination, azimuth, axis,
direction=:z, angle_unit=:deg, up=nothing, L=nothing) -> (los_vec, up_hint)Resolve a user-facing view specification into a (los_vec, up_hint) pair. Give exactly one of the alternatives below (a second one raises an error — no silent precedence). All angles are in angle_unit (:deg by default, or :rad):
- explicit
los3-vector, inclination/azimuth— tilt the view away from a referenceaxisbyinclination(0 ⇒ looking straight down the axis, 90° ⇒ perpendicular to it) and rotate around the axis byazimuth.axisdefaults to the box:z; use:x/:y/:z, a 3-vector, or:angmom(the object's angular momentumL, for disks). The reference axis is kept pointing "up".- spherical angles
(theta, phi)about the box axes (los=[sinθcosφ, sinθsinφ, cosθ]), - preset
direction::x/:y/:z,:faceon(look alongL),:edgeon(⟂L, up =L̂).
:faceon/:edgeon and axis=:angmom need the pre-computed L; the projection wiring supplies it via getvar(obj,[:lx,:ly,:lz]). The image roll (position_angle) is applied separately in build_camera_basis, so it is not a line-of-sight specifier here. Pure — touches no data.