Skip to content

Commit 6f7fcf3

Browse files
Add support for custom CRS in RectilinearGrid (#1049)
* Add support for custom CRS in 'RectilinearGrid' * Update tests * Format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Fix typo * Rename variables * Apply suggestions * Fix LengthUnit * Fix tests * Fix more tests * Remove unused code --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 59b4745 commit 6f7fcf3

File tree

11 files changed

+123
-55
lines changed

11 files changed

+123
-55
lines changed

src/coarsening/regular.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ function coarsen(grid::CartesianGrid, method::RegularCoarsening)
2525
CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) factors)
2626
end
2727

28-
function coarsen(grid::RectilinearGrid{Datum}, method::RegularCoarsening) where {Datum}
29-
factors = fitdims(method.factors, embeddim(grid))
28+
function coarsen(grid::RectilinearGrid, method::RegularCoarsening)
29+
factors = fitdims(method.factors, paramdim(grid))
3030
dims = vsize(grid)
31-
rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid))
31+
rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid))
3232
xyzₛ = xyz(grid)
33-
xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], embeddim(grid))
34-
RectilinearGrid{Datum}(xyzₜ)
33+
xyzₜ = ntuple(i -> xyzₛ[i][rngs[i]], paramdim(grid))
34+
RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ)
3535
end
3636

3737
function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum}

src/domains.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,4 @@ Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m))
154154

155155
Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{datum(crs(g))}(XYZ(g))
156156

157-
Base.convert(::Type{RectilinearGrid}, g::CartesianGrid) = RectilinearGrid{datum(crs(g))}(xyz(g))
157+
Base.convert(::Type{RectilinearGrid}, g::RegularGrid) = RectilinearGrid{manifold(g),crs(g)}(xyz(g))

src/domains/meshes/rectilineargrid.jl

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
"""
66
RectilinearGrid(x, y, z, ...)
7-
RectilinearGrid{Datum}(x, y, z, ...)
7+
RectilinearGrid{M,C}(x, y, z, ...)
88
99
A rectilinear grid with vertices at sorted coordinates `x`, `y`, `z`, ...,
10-
and a given `Datum` (default to `NoDatum`).
10+
manifold `M` (default to `𝔼`) and CRS type `C` (default to `Cartesian`).
1111
1212
## Examples
1313
@@ -20,49 +20,84 @@ julia> y = [0.0, 0.1, 0.3, 0.7, 0.9, 1.0]
2020
julia> RectilinearGrid(x, y)
2121
```
2222
"""
23-
struct RectilinearGrid{Datum,Dim,ℒ<:Len,V<:AbstractVector{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim}
24-
xyz::NTuple{Dim,V}
25-
topology::GridTopology{Dim}
26-
27-
function RectilinearGrid{Datum}(
28-
xyz::NTuple{Dim,<:AbstractVector{<:Len}},
29-
topology::GridTopology{Dim}
30-
) where {Datum,Dim}
31-
coords = float.(xyz)
32-
V = eltype(coords)
33-
new{Datum,Dim,eltype(V),V}(coords, topology)
34-
end
23+
struct RectilinearGrid{M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractVector}} <: Grid{M,C,N}
24+
xyz::X
25+
topology::GridTopology{N}
26+
RectilinearGrid{M,C,N,X}(xyz, topology) where {M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractVector}} = new(xyz, topology)
3527
end
3628

37-
RectilinearGrid{Datum}(xyz::NTuple{Dim,<:AbstractVector}, topology::GridTopology{Dim}) where {Datum,Dim} =
38-
RectilinearGrid{Datum}(addunit.(xyz, u"m"), topology)
29+
function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}, topology::GridTopology{N}) where {M<:Manifold,C<:CRS,N}
30+
if M <: 🌐 && !(C <: LatLon)
31+
throw(ArgumentError("rectilinear grid on `🌐` requires `LatLon` coordinates"))
32+
end
33+
34+
T = CoordRefSystems.mactype(C)
35+
nc = CoordRefSystems.ncoords(C)
36+
us = CoordRefSystems.units(C)
37+
38+
if N nc
39+
throw(ArgumentError("""
40+
A $N-dimensional rectilinear grid requires a CRS with $N coordinates.
41+
The provided CRS has $nc coordinates.
42+
"""))
43+
end
44+
45+
xyz′ = ntuple(i -> numconvert.(T, _withunit.(xyz[i], us[i])), nc)
46+
RectilinearGrid{M,C,N,typeof(xyz′)}(xyz′, topology)
47+
end
3948

40-
function RectilinearGrid{Datum}(xyz::Tuple) where {Datum}
41-
coords = promote(collect.(xyz)...)
42-
topology = GridTopology(length.(coords) .- 1)
43-
RectilinearGrid{Datum}(coords, topology)
49+
function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}) where {M<:Manifold,C<:CRS,N}
50+
topology = GridTopology(length.(xyz) .- 1)
51+
RectilinearGrid{M,C}(xyz, topology)
4452
end
4553

46-
RectilinearGrid{Datum}(xyz...) where {Datum} = RectilinearGrid{Datum}(xyz)
54+
RectilinearGrid{M,C}(xyz::AbstractVector...) where {M<:Manifold,C<:CRS} = RectilinearGrid{M,C}(xyz)
55+
56+
function RectilinearGrid(xyz::NTuple{N,AbstractVector}) where {N}
57+
try
58+
L = promote_type(ntuple(i -> _lentype(eltype(xyz[i])), N)...)
59+
M = 𝔼{N}
60+
C = Cartesian{NoDatum,N,L}
61+
RectilinearGrid{M,C}(xyz)
62+
catch
63+
throw(ArgumentError("invalid units for cartesian coordinates"))
64+
end
65+
end
4766

48-
RectilinearGrid(args...) = RectilinearGrid{NoDatum}(args...)
67+
RectilinearGrid(xyz::AbstractVector...) = RectilinearGrid(xyz)
4968

50-
vertex(g::RectilinearGrid{Datum}, ijk::Dims) where {Datum} = Point(Cartesian{Datum}(getindex.(g.xyz, ijk)))
69+
function vertex(g::RectilinearGrid, ijk::Dims)
70+
ctor = CoordRefSystems.constructor(crs(g))
71+
Point(ctor(getindex.(g.xyz, ijk)...))
72+
end
5173

5274
xyz(g::RectilinearGrid) = g.xyz
5375

5476
XYZ(g::RectilinearGrid) = XYZ(xyz(g))
5577

56-
function Base.getindex(g::RectilinearGrid{Datum}, I::CartesianIndices) where {Datum}
57-
@boundscheck _checkbounds(g, I)
58-
dims = size(I)
59-
start = Tuple(first(I))
60-
stop = Tuple(last(I)) .+ 1
61-
xyz = ntuple(i -> g.xyz[i][start[i]:stop[i]], embeddim(g))
62-
RectilinearGrid{Datum}(xyz, GridTopology(dims))
78+
@generated function Base.getindex(g::RectilinearGrid{M,C,N}, I::CartesianIndices) where {M,C,N}
79+
exprs = ntuple(N) do i
80+
:(g.xyz[$i][start[$i]:stop[$i]])
81+
end
82+
83+
quote
84+
@boundscheck _checkbounds(g, I)
85+
dims = size(I)
86+
start = Tuple(first(I))
87+
stop = Tuple(last(I)) .+ 1
88+
xyz = ($(exprs...),)
89+
RectilinearGrid{M,C}(xyz, GridTopology(dims))
90+
end
6391
end
6492

6593
function Base.summary(io::IO, g::RectilinearGrid)
6694
join(io, size(g), "×")
6795
print(io, " RectilinearGrid")
6896
end
97+
98+
# -----------------
99+
# HELPER FUNCTIONS
100+
# -----------------
101+
102+
_lentype(::Type{T}) where {T<:Len} = T
103+
_lentype(::Type{T}) where {T<:Number} = Met{T}

src/domains/meshes/regulargrid.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ A regular grid with dimensions `dims`, with lower left corner of element
1717
1818
```
1919
RegularGrid((10, 20), Point(LatLon(30.0°, 60.0°)), (1.0, 1.0)) # add coordinate units to spacing
20-
RegularGrid((10, 20), Point(Polar(0.0mm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units
20+
RegularGrid((10, 20), Point(Polar(0.0cm, 0.0rad)), (10.0mm, 1.0rad)) # convert spacing units to coordinate units
2121
RegularGrid((10, 20), Point(Marcator(0.0, 0.0)), (1.5, 1.5))
2222
RegularGrid((10, 20, 30), Point(Cylindrical(0.0, 0.0, 0.0)), (3.0, 2.0, 1.0))
2323
```

src/refinement/regular.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ function refine(grid::CartesianGrid, method::RegularRefinement)
2525
CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors)
2626
end
2727

28-
function refine(grid::RectilinearGrid{Datum}, method::RegularRefinement) where {Datum}
29-
factors = fitdims(method.factors, embeddim(grid))
28+
function refine(grid::RectilinearGrid, method::RegularRefinement)
29+
factors = fitdims(method.factors, paramdim(grid))
3030
xyzₛ = xyz(grid)
31-
xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), embeddim(grid))
32-
RectilinearGrid{Datum}(xyzₜ)
31+
xyzₜ = ntuple(i -> _refinedims(xyzₛ[i], factors[i]), paramdim(grid))
32+
RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ)
3333
end
3434

3535
function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {Datum}

src/transforms/lengthunit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ applycoord(t::LengthUnit, len::Len) = uconvert(t.unit, len)
3232

3333
applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit, lens)
3434

35-
applycoord(t::LengthUnit, g::RectilinearGrid) = RectilinearGrid{datum(crs(g))}(map(x -> uconvert.(t.unit, x), xyz(g)))
35+
applycoord(t::LengthUnit, g::RectilinearGrid) = TransformedGrid(g, t)
3636

3737
applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g)))
3838

src/transforms/scale.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ function applycoord(t::Scale, g::CartesianGrid)
7676
CartesianGrid(dims, orig, spac, offs)
7777
end
7878

79-
applycoord(t::Scale{Dim}, g::RectilinearGrid{Datum,Dim}) where {Datum,Dim} =
80-
RectilinearGrid{Datum}(ntuple(i -> t.factors[i] * xyz(g)[i], Dim))
79+
applycoord(t::Scale, g::RectilinearGrid) =
80+
RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g)))
8181

8282
applycoord(t::Scale{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} =
8383
StructuredGrid{Datum}(ntuple(i -> t.factors[i] * XYZ(g)[i], Dim))

src/transforms/shadow.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ apply(t::Shadow, tr::Torus) = TransformedGeometry(tr, t), nothing
6363

6464
apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct))), nothing
6565

66+
apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing
67+
6668
# -----------------
6769
# HELPER FUNCTIONS
6870
# -----------------
@@ -97,8 +99,6 @@ function _shadow(g::CartesianGrid, dims)
9799
CartesianGrid(sz, or, sp, of)
98100
end
99101

100-
_shadow(g::RectilinearGrid, dims) = RectilinearGrid{datum(crs(g))}(xyz(g)[dims])
101-
102102
function _shadow(g::StructuredGrid, dims)
103103
ndims = length(size(g))
104104
inds = ntuple(i -> ifelse(i dims, :, 1), ndims)

src/transforms/translate.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ applycoord(::Translate, v::Vec) = v
3737
# SPECIAL CASES
3838
# --------------
3939

40-
apply(t::Translate, g::RectilinearGrid{Datum}) where {Datum} =
41-
RectilinearGrid{Datum}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], embeddim(g))), nothing
40+
apply(t::Translate, g::RectilinearGrid) =
41+
RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> xyz(g)[i] .+ t.offsets[i], paramdim(g))), nothing
4242

4343
revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g))
4444

test/meshes.jl

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -431,10 +431,31 @@ end
431431
@test vertex(grid, 1) == cart(0, 0)
432432
@test vertex(grid, 121) == cart(10, 10)
433433

434-
# constructor with datum & datum propagation
435-
grid = RectilinearGrid{WGS84Latest}(x, y)
436-
@test datum(crs(grid)) === WGS84Latest
437-
@test datum(crs(centroid(grid))) === WGS84Latest
434+
# constructor with manifold and CRS
435+
x = range(zero(T), stop=one(T), length=6)
436+
y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]
437+
C = typeof(Mercator(T(0), T(0)))
438+
grid = RectilinearGrid{𝔼{2},C}(x, y)
439+
@test manifold(grid) === 𝔼{2}
440+
@test crs(grid) === C
441+
@test crs(grid[1, 1]) === C
442+
@test crs(centroid(grid)) === C
443+
C = typeof(LatLon(T(0), T(0)))
444+
grid = RectilinearGrid{🌐,C}(x, y)
445+
@test manifold(grid) === 🌐
446+
@test crs(grid) === C
447+
@test crs(grid[1, 1]) === C
448+
@test crs(centroid(grid)) === C
449+
450+
# units
451+
x = range(zero(T), stop=one(T), length=6) * u"mm"
452+
y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"cm"
453+
grid = RectilinearGrid(x, y)
454+
@test unit(Meshes.lentype(grid)) == u"m"
455+
# error: invalid units for cartesian coordinates
456+
x = range(zero(T), stop=one(T), length=6) * u"m"
457+
y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"°"
458+
@test_throws ArgumentError RectilinearGrid(x, y)
438459

439460
# conversion
440461
cg = cartgrid(10, 10)
@@ -453,6 +474,20 @@ end
453474
@test topology(rg) == topology(cg)
454475
@test vertices(rg) == vertices(cg)
455476

477+
# type stability
478+
x = range(zero(T), stop=one(T), length=6) * u"mm"
479+
y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0] * u"cm"
480+
ρ = range(zero(T), stop=one(T), length=6)
481+
ϕ = range(zero(T), stop=T(2π), length=6)
482+
C = typeof(Polar(T(0), T(0)))
483+
grid = RectilinearGrid{𝔼{2},C}(ρ, ϕ)
484+
@inferred RectilinearGrid(x, y)
485+
@inferred RectilinearGrid{𝔼{2},C}(ρ, ϕ)
486+
@inferred vertex(grid, (1, 1))
487+
@inferred grid[1, 1]
488+
@inferred grid[1:2, 1:2]
489+
@inferred Meshes.XYZ(grid)
490+
456491
x = range(zero(T), stop=one(T), length=6)
457492
y = T[0.0, 0.1, 0.3, 0.7, 0.9, 1.0]
458493
grid = RectilinearGrid(x, y)

0 commit comments

Comments
 (0)