Extending DimensionalData
Nearly everything in DimensionalData.jl is designed to be extensible.
AbstractDimArrayis easily extended to custom array types.RasterorYAXArrayare examples from other packages.AbstractDimStackis easily extended to custom mixed array datasets.RasterStackorArViZ.Datasetare examples.Lookupcan have new types added, e.g. toAbstractSampledorAbstractCategorical. For example,Rasters.Projectedis a lookup that knows its coordinate reference system, but otherwise behaves as a regularSampledlookup.
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), like (X(), Y()).
Dimension axes
Dimensions returned from dims should hold a Lookup or in some cases just an AbstractArray (like with DimIndices). When attached to multi-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 more flexible and extensible way 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:
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:
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. The way you indicate that something needs to be filled is by using the Auto types, like AutoOrder](@ref) or AutoSampling.
Not calling format in the outer constructors of an AbstractDimArray has undefined behaviour.
When creating lookup types, you need to define DimensionalData.format on your lookup type.
Interfaces.jl interface testing
DimensionalData defines explicit, testable Interfaces.jl interfaces: DimArrayInterface and DimStackInterface.
This is the implementation definition for DimArray:
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> 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])
Implementation summary:
DimArray correctly implements DimensionalData.DimArrayInterface: true
true