Skip to content

Commit

Permalink
Merge pull request #1216 from GenericMappingTools/viz-mosaic-cpt
Browse files Browse the repository at this point in the history
Implement a patch to plot side colormaps that workaround a GMT bug
  • Loading branch information
joa-quim authored Jul 13, 2023
2 parents 08959ee + 013ebdd commit 97ef65c
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
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

0 comments on commit 97ef65c

Please sign in to comment.