diff --git a/src/common_options.jl b/src/common_options.jl index ad0125568..b2ee292dc 100644 --- a/src/common_options.jl +++ b/src/common_options.jl @@ -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 @@ -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 diff --git a/src/imshow.jl b/src/imshow.jl index fd76a468c..7fa659213 100644 --- a/src/imshow.jl +++ b/src/imshow.jl @@ -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 @@ -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 @@ -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) diff --git a/src/streamlines.jl b/src/streamlines.jl index b8c26f17f..8409d2c1f 100644 --- a/src/streamlines.jl +++ b/src/streamlines.jl @@ -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` @@ -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. @@ -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) diff --git a/src/utils_types.jl b/src/utils_types.jl index 7cc807303..8b8e005ea 100644 --- a/src/utils_types.jl +++ b/src/utils_types.jl @@ -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 @@ -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 @@ -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 @@ -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") @@ -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="))