Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ AlgebraOfGraphics = "0.8, 0.9, 0.10, 0.11"
Aqua = "0.8"
ArrayInterface = "7"
BenchmarkTools = "1"
CairoMakie = "0.10, 0.11, 0.12"
CairoMakie = "0.10, 0.11, 0.12, 0.13"
CategoricalArrays = "0.10"
ColorTypes = "0.11"
Combinatorics = "1"
Expand Down
2 changes: 1 addition & 1 deletion src/Dimensions/Dimensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const LU = Lookups
const LookupArrays = Lookups

import .Lookups: rebuild, order, span, sampling, locus, val, index, set, _set,
metadata, bounds, intervalbounds, units, basetypeof, unwrap, selectindices, hasselection,
metadata, bounds, intervalbounds, units, basetypeof, unwrap, selectindices, hasselection, hasmultipledimensions,
shiftlocus, maybeshiftlocus, ordered_first, ordered_last, ordered_firstindex, ordered_lastindex,
promote_first, _remove
using .Lookups: StandardIndices, SelTuple, CategoricalEltypes,
Expand Down
15 changes: 9 additions & 6 deletions src/Dimensions/dimension.jl
Original file line number Diff line number Diff line change
Expand Up @@ -300,22 +300,25 @@ Base.size(dims::DimTuple) = map(length, dims)
Base.CartesianIndices(dims::DimTuple) = CartesianIndices(map(d -> axes(d, 1), dims))

# Extents.jl
#=
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this commented out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that we still had the old version to refer back to, mainly

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just remove it, but we probably need an explanation for the new complexity

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, now that it works I'll do that

function Extents.extent(ds::DimTuple, args...)
extent_dims = _astuple(dims(ds, args...))
extent_bounds = bounds(extent_dims)
return Extents.Extent{name(extent_dims)}(extent_bounds)
end
=#

function _experimental_extent(ds::DimTuple)
regulardims = dims(ds, x -> !(lookup(x) isa MultiDimensionalLookup))
regular_bounds = bounds.(regulardims)
function Extents.extent(ids::DimTuple, args...)
ds = _astuple(dims(ids, args...))
regulardims = dims(ds, x -> !hasmultipledimensions(lookup(x)))
regular_bounds = map(bounds, regulardims)
regular_bounds_nt = NamedTuple{map(name, regulardims)}(regular_bounds)

multidims = otherdims(ds, regulardims)
multidim_raw_bounds = bounds.(multidims) # we trust that bounds will give us a tuple of bounds one for each enclosed dimension
multidim_raw_bounds = map(bounds, multidims) # we trust that bounds will give us a tuple of bounds one for each enclosed dimension
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be made into a contract, perhaps in the docs of hasmultipledimensions and MultiDimensionalLookup

multidim_dims = combinedims(map(dims, multidims)...; length = false)
multidim_bounds = map(multidim_dims) do outdim
foldl(zip(multidims, multidim_raw_bounds); init = (nothing, nothing)) do (minval, maxval), (dim, bounds)
foldl(map(tuple, multidims, multidim_raw_bounds); init = (nothing, nothing)) do (minval, maxval), (dim, bounds)
if hasdim(dim, outdim)
if isnothing(minval) && isnothing(maxval)
bounds[dimnum(dim, outdim)]
Expand All @@ -329,7 +332,7 @@ function _experimental_extent(ds::DimTuple)
end
end
multidim_bounds_nt = NamedTuple{map(name, multidim_dims)}(multidim_bounds)
return merge(regular_bounds_nt, multidim_bounds_nt)
return Extents.Extent(merge(regular_bounds_nt, multidim_bounds_nt))
end

dims(extent::Extents.Extent{K}) where K = map(rebuild, name2dim(K), values(extent))
Expand Down
2 changes: 1 addition & 1 deletion src/Dimensions/indexing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Convert a `Dimension` or `Selector` `I` to indices of `Int`, `AbstractArray` or
@inline function dims2indices(dims::DimTuple, I::DimTuple)
extradims = otherdims(I, dims) # extra dims in the query, I
# Extract "multi dimensional" lookups like MergedLookup or Rasters' GeometryLookup
multidims = Dimensions.dims(otherdims(dims, I), x -> lookup(x) isa MultiDimensionalLookup && !isempty(Dimensions.dims(x, I)))
multidims = Dimensions.dims(otherdims(dims, I), x -> hasmultipledimensions(lookup(x)) && !isempty(Dimensions.dims(x, I)))
# Warn if any dims from I were not picked up by multidims
actuallyextradims = otherdims(extradims, x -> any(y -> hasdim(y, x), multidims)) # one way setdiff(extradims, multidims) essentially
length(actuallyextradims) > 0 && _extradimswarn(actuallyextradims)
Expand Down
2 changes: 2 additions & 0 deletions src/Dimensions/merged.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
abstract type MultiDimensionalLookup{T} <: Lookup{T,1} end
hasmultipledimensions(::MultiDimensionalLookup) = true

"""
MergedLookup <: MultiDimensionalLookup <: Lookup
Expand Down Expand Up @@ -63,6 +64,7 @@ struct MergedLookup{T,A<:AbstractVector{T},D,Me} <: MultiDimensionalLookup{T}
end
MergedLookup(data, dims; metadata=NoMetadata()) = MergedLookup(data, dims, metadata)

hasmultipledimensions(::MergedLookup) = true
order(m::MergedLookup) = Unordered()
dims(m::MergedLookup) = m.dims
dims(d::Dimension{<:MergedLookup}) = dims(val(d))
Expand Down
2 changes: 1 addition & 1 deletion src/Lookups/Lookups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export reducelookup, shiftlocus, maybeshiftlocus, promote_first
export index

export issampled, iscategorical, iscyclic, isnolookup, isintervals, ispoints, isregular,
isexplicit, isstart, iscenter, isend, isordered, isforward, isreverse
isexplicit, isstart, iscenter, isend, isordered, isforward, isreverse, hasmultipledimensions

export Selector
export At, Between, Touches, Contains, Near, Where, All
Expand Down
2 changes: 2 additions & 0 deletions src/Lookups/lookup_arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ metadata(lookup::AutoLookup) = hasproperty(lookup.kw, :metadata) ? lookup.kw.met
Base.step(lookup::AutoLookup) = Base.step(parent(lookup))

bounds(lookup::Lookup) = _bounds(order(lookup), lookup)
# Fallback for raw arrays (e.g., when lookup returns a raw range)
bounds(x::AbstractArray) = (first(x), last(x))

_bounds(::ForwardOrdered, l::Lookup) = first(l), last(l)
_bounds(::ReverseOrdered, l::Lookup) = last(l), first(l)
Expand Down
2 changes: 2 additions & 0 deletions src/Lookups/predicates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ isintervals(::Intervals) = true
isintervals(::Points) = false
ispoints(::Points) = true
ispoints(::Intervals) = false
hasmultipledimensions(::Lookup) = false
hasmultipledimensions(::Any) = false # Fallback for non-Lookup types (e.g., raw arrays)

# Forward them from lookups
for f in (:isregular, :isexplicit)
Expand Down
110 changes: 108 additions & 2 deletions test/merged.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DimensionalData, Test, Unitful
using DimensionalData, Test, Unitful, Extents
using DimensionalData.Lookups, DimensionalData.Dimensions
using Statistics: mean
using DimensionalData.Dimensions: SelOrStandard
Expand Down Expand Up @@ -38,7 +38,7 @@ end

@test index(da[Coord(:, Between(1, 2), :)], Coord) == [(1.0,1.0,1.0), (1.0,2.0,2.0)]

@test bounds(da) == (((1.0, 3.0), (1.0, 4.0), (1.0, 4.0)),)
@test DimensionalData.bounds(da) == (((1.0, 3.0), (1.0, 4.0), (1.0, 4.0)),)

@testset "merged named reduction" begin
m = mean(da2; dims=Coord)
Expand Down Expand Up @@ -107,4 +107,110 @@ end
da = ones(X(1:10), Y(1:10), Dim{:random}(1:10))
merged = mergedims(da, (X, Y) => :space)
@test_warn "Z" merged[Z(1)]
end

@testset "hasmultipledimensions trait for PR #991" begin
# Test default behavior for regular lookups
@testset "Regular lookups return false" begin
@test hasmultipledimensions(NoLookup()) == false
@test hasmultipledimensions(Sampled(1:10)) == false
@test hasmultipledimensions(Categorical([:a, :b, :c])) == false
@test hasmultipledimensions(Cyclic(1:12; cycle=12)) == false
@test hasmultipledimensions(Sampled(1:10; sampling=Points())) == false
@test hasmultipledimensions(Sampled(1:10; sampling=Intervals())) == false
end

@testset "MergedLookup returns true" begin
x_dim = X(1:3)
y_dim = Y(10:10:30)
merged_data = vec(DimPoints((x_dim, y_dim)))
merged_lookup = Dimensions.MergedLookup(merged_data, (x_dim, y_dim))
@test hasmultipledimensions(merged_lookup) == true
end

@testset "Extent passthrough for MergedLookup" begin
# Basic extent with MergedLookup
x_vals = 1.0:3.0
y_vals = 10.0:10.0:30.0
x_dim = X(x_vals)
y_dim = Y(y_vals)

merged_dims = mergedims((x_dim, y_dim) => :space)
ext = Extents.extent((merged_dims,))

@test haskey(ext, :X)
@test haskey(ext, :Y)
@test ext.X == (1.0, 3.0)
@test ext.Y == (10.0, 30.0)

# Mixed regular and merged dimensions
t_dim = Ti(1:5)
z_dim = Z(100:100:300)
all_dims = (t_dim, merged_dims, z_dim)
ext2 = Extents.extent(all_dims)

@test ext2.Ti == (1, 5)
@test ext2.Z == (100, 300)
@test ext2.X == (1.0, 3.0)
@test ext2.Y == (10.0, 30.0)
end

@testset "Multiple merged dimensions extent" begin
x1_dim = X(1:3)
y1_dim = Y(10:10:30)
t_dim = Ti(0.0:0.5:1.0)
z_dim = Z(-5:5)

merged_space = mergedims((x1_dim, y1_dim) => :space)
merged_tz = mergedims((t_dim, z_dim) => :timez)
all_dims = (merged_space, merged_tz)

ext = Extents.extent(all_dims)
@test ext.X == (1, 3)
@test ext.Y == (10, 30)
@test ext.Ti == (0.0, 1.0)
@test ext.Z == (-5, 5)
end

@testset "Extent with subset of dimensions" begin
x_dim = X(1:5)
y_dim = Y(10:10:50)
z_dim = Z(100:100:300)

merged = mergedims((x_dim, y_dim) => :space)
all_dims = (merged, z_dim)

# Get extent for just the Z dimension
ext_z = Extents.extent(all_dims, Z)
@test ext_z.Z == (100, 300)
@test !haskey(ext_z, :X)
@test !haskey(ext_z, :Y)

# Get extent for merged dimension by name
ext_space = Extents.extent(all_dims, :space)
@test ext_space.X == (1, 5)
@test ext_space.Y == (10, 50)
@test !haskey(ext_space, :Z)
end

@testset "Operations preserve hasmultipledimensions" begin
x = X(1:3)
y = Y(10:10:30)
data = rand(3, 3)

da = DimArray(data, (x, y))
merged_da = mergedims(da, (X, Y) => :space)

# Broadcasting preserves trait
result = merged_da .+ 1
@test hasmultipledimensions(lookup(dims(result, :space)))

# Slicing with additional dimension preserves trait
z = Z(1:3)
data3d = rand(3, 3, 3)
da3d = DimArray(data3d, (x, y, z))
merged_da3d = mergedims(da3d, (X, Y) => :space)
sliced = merged_da3d[Z(At(2))]
@test hasmultipledimensions(lookup(dims(sliced, :space)))
end
end
Loading