Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a patch to plot side colormaps that workaround a GMT bug #1216

Merged
merged 4 commits into from
Jul 13, 2023
Merged
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
18 changes: 15 additions & 3 deletions src/common_options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4565,9 +4565,17 @@ function digests_legend_bag(d::Dict, del::Bool=true)
end

# --------------------------------------------------------------------------------------------------
"""
str = scan_opt(cmd::AbstractString, opt::String, keepX=false)

Scans the CMD string for the OPT option. Note, OPT must be a 2 chars -X GMT option.
'keepX' retains the OPT 2 chars -X GMT option in output.

### Example
scan_opt(" -Baf", "-B", true)
" -Baf"
"""
function scan_opt(cmd::AbstractString, opt::String, keepX::Bool=false)::String
# Scan the CMD string for the OPT option. Note, OPT must be a 2 chars -X GMT option.
# 'keepX' retains the OPT 2 chars -X GMT option in output.
out = ((ind = findfirst(opt, cmd)) !== nothing) ? strtok(cmd[ind[1]+2:end])[1] : ""
(out != "" && cmd[ind[1]+2] == ' ') && (out = "") # Because seeking -R in a " -R -JX" would ret "-JX"
(keepX && out != "") && (out = string(' ', opt, out)) # Keep the option flag in output
Expand Down Expand Up @@ -4598,8 +4606,12 @@ function justify(arg, nowarn::Bool=false)::String
end

# --------------------------------------------------------------------------------------------------
"""
frac = interp_vec(x, val)

Returns the positional fraction that `val` ocupies in the `x` vector
"""
function interp_vec(x, val)
# Returns the positional fraction that `val` ocupies in the `x` vector
(val < x[1] || val > x[end]) && error("Interpolating point ($val) is not inside the vector range [$(x[1]) $(x[end])].")
k = 0
while(val < x[k+=1]) end
Expand Down
32 changes: 24 additions & 8 deletions src/imshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,16 @@ function imshow(arg1::GMTgrid; kw...)

if (!docube && (flat || (opt_p == "" && !have_tilles)))
(flat && opt_p != "") && (d[:p] = opt_p[4:end]) # Restore the meanwhile deleted -p option
if ((nl = size(arg1, 3)) > 1)
nc = 2 # Number of subplot columns
grid = isodd(nl) ? "$((div(nl, nc)+1))x$(nc)" : "$(div(nl, nc))x$(nc)"
if ((n_levels = size(arg1, 3)) > 1)
nc::Int = ((val = find_in_dict(d, [:col :cols :columns])[1]) !== nothing) ? val : 2 # Number of subplot columns
nl = div(n_levels, nc)
(rem(n_levels, nc) != 0) && (nl += 1)
grid = "$(nl)x$(nc)"

(arg1.geog > 0 && all(CTRL.limits .== 0)) && snif_GI_set_CTRLlimits(arg1)
(arg1.geog > 0 && is_in_dict(d, [:J :proj :projection]) === nothing) && (d[:J] = "guess")
opt_J = parse_J(d, "", "", true, false, false)[2]
(startswith(opt_J, " -JX") && !contains(opt_J, '/')) && (opt_J *= "/0") # We always want axis equal
w, h = plot_GI_size(arg1, opt_J) # Compute the plot Width,Height given the arg1 limits and proj
aspect = h / w
_w = 15 / nc; _h = _w * aspect
Expand All @@ -116,10 +120,10 @@ function imshow(arg1::GMTgrid; kw...)
if ((val = find_in_dict(d, [:titles])[1]) === nothing)
if !isempty(arg1.names) tits = arg1.names
elseif !isempty(arg1.v) tits = string.(arg1.v)
else tits = string.(collect(1:nl))
else tits = string.(collect(1:n_levels))
end
elseif (val !== nothing && val != false && val != :no)
(!isa(val, Vector{String}) || (length(val) != nl)) ? @warn("Panel titles must be a string vector with size equal to number of layers in input cube.") : (tits = val)
(!isa(val, Vector{String}) || (length(val) != n_levels)) ? @warn("Panel titles must be a string vector with size equal to number of layers in input cube.") : (tits = val)
end
rt = !isempty(tits) ? tits : nothing

Expand All @@ -130,13 +134,25 @@ function imshow(arg1::GMTgrid; kw...)
tit = string(val)::String
end

# This is a (poor) pach for a GMT bug that screws when plotting CPTs on the sides.
margin = "0"
if ((val = find_in_dict(d, [:colorbar], false)[1]) !== nothing)
tv = add_opt_module(Dict(:colorbar => val))
t2 = scan_opt(tv[1], "-D")
t2 = replace(t2, "JMR" => "JMC")
!contains(t2, "+o") && (t2 *= "+o$(_w/2 + 0.3)/0")
t3 = scan_opt(tv[1], "-B")
d[:colorbar] = (D = t2, B = t3)
margin = "0.6c/0"
end

row_axes = (rt !== nothing) ? (left=true, row_title=true) : (left=true, )
subplot(grid=grid, dims=(panels=(_w, _h), divlines=(1,:dashed)), row_axes=row_axes, col_axes=(bott=true,), T=tit)
subplot(grid=grid, dims=(panels=(_w, _h),), row_axes=row_axes, col_axes=(bott=true,), T=tit, margins = margin)
(rt !== nothing) && (d[:par] = (MAP_TITLE_OFFSET="0p",); d[:title] = rt[1])
#d[:Vd] = 1
grdimage("", mat2grid(arg1[:,:,1], arg1); d...)
for k = 2:nl
for k = 2:n_levels
!isempty(tits) && (d[:title] = rt[k])
CURRENT_CPT[1] = GMTcpt() # Force creating a new CPT for next layer
grdimage("", mat2grid(arg1[:,:,k], arg1); panel=:next, d...)
end
subplot(see ? :show : :end)
Expand Down
12 changes: 11 additions & 1 deletion src/streamlines.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ This last 2D method let users pass the `x` and `y` vector data coordinates, U an
velocity data and the remaining arguments have the same meaning as in the other methods. Returns a
Vector{GMTdataset} with the streamlines.

S = streamlines(x::Matrix, y::Matrix, U::Matrix, V::Matrix; step=0.1, max_vert::Int=10000)

`x` and `y` are assumed to be meshgrids with the *x* and *y* starting coordinates.

S = streamlines(U::GMTgrid, V::GMTgrid, W::GMTgrid, startX, startY, startZ; step=0.1, max_vert::Int=10000)

Conpute 3D volume of vector fields with streamline. Here `U`,`V` and `W` are 3D cubes with `x,y,z`
Expand All @@ -50,6 +54,13 @@ function streamlines(x, y, U::Matrix, V::Matrix, sx, sy; step=0.1, max_vert::Int
V = mat2grid(V, x, y)
streamlines(U, V, sx, sy, step=step, max_vert=max_vert)
end
function streamlines(x::Matrix, y::Matrix, U::Matrix, V::Matrix; step=0.1, max_vert::Int=10000)
# S... Matlab way.
xx, yy = x[:,1], y[1,:] # Assume x,y are meshgrid matrices
_U = mat2grid(U, xx, yy)
_V = mat2grid(V, xx, yy)
streamlines(_U, _V, xx, yy, step=step, max_vert=max_vert)
end

function streamlines(U::GMTgrid, V::GMTgrid; side::Union{String, Symbol}="", step=0.1, max_vert::Int=10000, density=1, max_density=4)
# This method auto-generates starting positions along one of the 4 sides of the grid.
Expand Down Expand Up @@ -198,7 +209,6 @@ function streamlines(U::GMTgrid, V::GMTgrid, W::GMTgrid; axis::Bool=false, start

return s,a
end
##

# -----------------------------------------------------------------------------------------------------------
function streamlines(U::GMTgrid, V::GMTgrid, W::GMTgrid, startx, starty, startz; step=0.1, max_vert::Int=10000)
Expand Down
13 changes: 8 additions & 5 deletions src/utils_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ end

# ---------------------------------------------------------------------------------------------------
"""
G = rasters2grid(arg)
G = rasters2grid(arg; scale=1, offset=0)

Deals with Rasters.jl arrays (grids and cubes). The input argument was previously detected (by israsters)
to be a Rasters.jl type. The input array is not copied when it has no 'missings' but is often modified
Expand All @@ -654,7 +654,7 @@ is to call ``G = mat2grid(arg)`` once and use `G`

Returns a GMTgrid type.
"""
function rasters2grid(arg)::GMTgrid
function rasters2grid(arg; scale::Real=1f0, offset::Real=0f0)::GMTgrid
_y = collect(arg.dims[2]); (_y[2] < _y[1]) ? (_y = _y[end:-1:1]; Yorder = 'T') : (Yorder = 'B')
_v = (size(arg,3) > 1) ? collect(arg.dims[3]) : Float64[]
#_v = (size(arg,3) > 1) && (eltype(arg.dims[3]) <: TimeType ? [arg.dims[3][i].instant.periods.value for i=1:length(arg.dims[3])] : Float64[]) # Store in milisecs just to have something numeric
Expand Down Expand Up @@ -684,6 +684,8 @@ function rasters2grid(arg)::GMTgrid
end

(data === nothing) && (data = collect(arg.data))
(scale != 1 || offset != 0) && (data = muladd.(data, scale, offset))

(is_transp && Yorder == 'B') && (reverse!(data, dims=2); layout = "TRB") # GMT expects grids to be scanline and Top->Bot
mat2grid(data, x=collect(arg.dims[1]), y=_y, v=_v, names=names, tit=string(arg.name), rem="Converted from a Rasters object.", is_transposed=is_transp, layout=layout, proj4=proj, wkt=wkt, epsg=epsg)
end
Expand Down Expand Up @@ -1071,6 +1073,7 @@ function slicecube(G::GMTgrid, slice::Int; axis="z")
this_size = size(G,dim)
(slice > this_size) && error("Slice number ($slice) is larger than grid size ($this_size)")

isempty(G.v) && (G.v = collect(1:size(G,3)))
if (_axis == "z")
G_ = mat2grid(G[:,:,slice], G.x, G.y, [G.v[slice]], reg=G.registration, is_transposed=(G.layout[2] == 'R'))
elseif (_axis == "y")
Expand Down Expand Up @@ -1499,11 +1502,11 @@ istransposed(mat) = !isempty(fields(mat)) && (fields(mat)[1] == :parent)
function mat2grid(mat, xx=Vector{Float64}(), yy=Vector{Float64}(), zz=Vector{Float64}(); reg=nothing,
x=Vector{Float64}(), y=Vector{Float64}(), v=Vector{Float64}(), hdr=nothing, proj4::String="",
proj::String="", wkt::String="", epsg::Int=0, geog::Int=-1, title::String="", tit::String="",
rem::String="", cmd::String="", names::Vector{String}=String[], scale::Float32=1f0,
offset::Float32=0f0, layout::String="", is_transposed::Bool=false)
rem::String="", cmd::String="", names::Vector{String}=String[], scale::Real=1f0,
offset::Real=0f0, layout::String="", is_transposed::Bool=false)
# Take a 2/3D array and turn it into a GMTgrid

israsters(mat) && return rasters2grid(mat)
israsters(mat) && return rasters2grid(mat, scale=scale, offset=offset)
!isa(mat[2], Real) && error("input matrix must be of Real numbers")
(isempty(proj4) && !isempty(proj)) && (proj4 = proj) # Allow both proj4 or proj keywords
if (!isempty(proj4) && !startswith(proj4, "+proj=") && !startswith(proj4, "proj="))
Expand Down