Skip to content

Extending DimensionalData

Nearly everything in DimensionalData.jl is designed to be extensible.

  • AbstractDimArray are easily extended to custom array types. Raster or YAXArray are examples from other packages.

  • AbstractDimStack are easily extended to custom mixed array dataset. RasterStack or ArViZ.Dataset are examples.

  • Lookup can have new types added, e.g. to AbstractSampled or AbstractCategorical. Rasters.Projected is a lookup that knows its coordinate reference system, but otherwise behaves as a regular Sampled lookup.

dims, rebuild and format are the key interface methods in most of these cases.

dims

Objects extending DimensionalData.jl that have dimensions must return a Tuple of constructed Dimensions from dims(obj).

Dimension axes

Dimensions return from dims should hold a Lookup or in some cases just an AbstractArray (like wiht DimIndices). When attached to mullti-dimensional objects, lookups must be the same length as the axis of the array it represents, and eachindex(A, i) and eachindex(dim) must return the same values.

This means that if the array has OffsetArrays.jl axes, the array the dimension wraps must also have OffsetArrays.jl axes.

dims keywords

To any dims keyword argument that usually requires the dimension I, objects should accept any Dimension, Type{<:Dimension}, Symbol, Val{:Symbol}, Val{<:Type{<:Dimension}} or also regular Integer.

This is easier than it sounds, calling DD.dims(objs, dims) will return the matching dimension and DD.dimnum(obj, dims) will return the matching Int for any of these inputs as long as dims(obj) is implemented.

rebuild

Rebuild methods are used to rebuild immutable objects with new field values, in a way that is more flexible and extensible than just using ConstructionBase.jl reconstruction. Developers can choose to ignore some of the fields passed by rebuild.

The function signature is always one of:

julia
rebuild(obj, args...)
rebuild(obj; kw...)

rebuild has keyword versions automatically generated for all objects using ConstructionBase.jl.

These will work without further work as long as your object has the fields used by DimensionalData.jl objects. For example, AbstractDimArray will receive these keywords in rebuild: data, dims, refdims, name, metadata.

If your AbstractDimArray does not have all these fields, you must implement rebuild(x::YourDimArray; kw...) manually.

An argument method is also defined with the same arguments as the keyword version. For AbstractDimArray it should only be used for updating data and dims, any more that that is confusing.

For Dimension and Selector the single argument versions are easiest to use, as there is only one argument.

format

When constructing an AbstractDimArray or AbstractDimStack DimensionalData.format must be called on the dims tuple and the parent array:

julia
format(dims, array)

This lets DimensionalData detect the lookup properties, fill in missing fields of a Lookup, pass keywords from Dimension to detected Lookup constructors, and accept a wider range of dimension inputs like tuples of Symbol and Type.

Not calling format in the outer constructors of an AbstractDimArray has undefined behaviour.

Interfaces.jl interterface testing

DimensionalData defines explicit, testable Interfaces.jl interfaces: DimArrayInterface and DimStackInterface.

This is the implementation definition for DimArray:

julia
julia> using DimensionalData, Interfaces

julia> @implements DimensionalData.DimArrayInterface{(:refdims,:name,:metadata)} DimArray [rand(X(10), Y(10)), zeros(Z(10))]

See the DimensionalData.DimArrayInterface docs for options. We can test it with:

julia
julia> Interfaces.test(DimensionalData.DimArrayInterface)

Testing DimArrayInterface is implemented for DimArray
Mandatory components
dims: (defines a `dims` method [true, true],
       dims are updated on getindex [true, true])
refdims_base: `refdims` returns a tuple of Dimension or empty [true, true]
ndims: number of dims matches dimensions of array [true, true]
size: length of dims matches dimensions of array [true, true]
rebuild_parent: rebuild parent from args [true, true]
rebuild_dims: rebuild paaarnet and dims from args [true, true]
rebuild_parent_kw: rebuild parent from args [true, true]
rebuild_dims_kw: rebuild dims from args [true, true]
rebuild: all rebuild arguments and keywords are accepted [true, true]
Optional components
refdims: (refdims are updated in args rebuild [true, true],
          refdims are updated in kw rebuild [true, true],
          dropped dimensions are added to refdims [true, true])
name: (rebuild updates name in arg rebuild [true, true],
       rebuild updates name in kw rebuild [true, true])
metadata: (rebuild updates metadata in arg rebuild [true, true],
           rebuild updates metadata in kw rebuild [true, true])
true