Skip to content

Commit

Permalink
Fix a regresion in colorbar where a default frame was added even if L…
Browse files Browse the repository at this point in the history
… option was used. (#1582)

* Add psconvert options as globals to allow setting the white margin.

* Add a 'layout' option to gdaltranslate.

* Fix reversion. Cannot add a def -B if -L

* Planar FV's do not need sorting nor hiding.

* Check if a plotting FV has embedded colors and use them if yes.

* In round_wesn() be tolerant to the case when Zmin == Zmax

* Add a 'color' member to the GMTfv type.

* Add a draft function to create FaceVertices objects and paint each face from an image.

* Fix failing case of accessing more elements of the FV.color than existed.
  • Loading branch information
joa-quim authored Nov 6, 2024
1 parent ca1dc64 commit be01370
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 24 deletions.
2 changes: 2 additions & 0 deletions src/GMT.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ const DEF_FIG_AXES_BAK = " -Baf -BWSen" # Default fig axes for plot l
const DEF_FIG_AXES3_BAK = " -Baf -Bza" # "" but for 3D views
const global DEF_FIG_AXES = [DEF_FIG_AXES_BAK] # This one may be be changed by theme()
const global DEF_FIG_AXES3 = [DEF_FIG_AXES3_BAK] # ""
const global FIG_MARGIN = [1] # Figure margin in points after convertion by 'psconvert'
const global PSCONV_PAR = [" -Qg4 -Qt4 "] # In showfig() both are combined to get (default) " -A1p -Qg4 -Qt4 "
const global CTRL = CTRLstruct(zeros(13), zeros(6), [true], [false],
[:arrows, :bubblechart, :basemap, :band, :clip, :coast, :colorbar, :hband, :hlines, :inset, :logo, :lines, :grdvector, :plot, :plot3, :quiver, :scatter, :scatter3, :stairs, :text, :vlines, :vband], fill(nothing, 6), ["","",""], ["","", "", " "], ["",""], ["",""], [false,true], [C_NULL], [Dict()])
const global CTRLshapes = CTRLstruct2([true], [true], [""]) # Used in sub-module Drawing
Expand Down
30 changes: 23 additions & 7 deletions src/common_options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -916,11 +916,20 @@ function guess_proj(lonlim::VecOrMat{Float64}, latlim::VecOrMat{Float64})::Strin
end

# ---------------------------------------------------------------------------------------------------
"""
parse_grid(d::Dict, args, opt_B::String="", stalone::Bool=true) -> String
Parse the contents of the "grid" option (internal use).
This option can be used as grid=(pen=:red, x=10), case on which the parsed result will be appended
to DEF_FIG_AXES, or as a member of the "frame" option. In this case DEF_FIG_AXES is dropped and
only the contents of "frame" will be used. The argument can be a NamedTuple, which allows setting
grid pen and individual axes, or as a string.
### Examples
grid=:on => -Bg; grid=:x => -Bxg; grid="x10" => -Bxg10; grid=:y ...; grid=:xyz => " -Bg -Bzg"
"""
function parse_grid(d::Dict, args, opt_B::String="", stalone::Bool=true)::String
# Parse the contents of the "grid" option. This option can be used as grid=(pen=:red, x=10), case on
# which the parsed result will be appended to DEF_FIG_AXES, or as a member of the "frame" option.
# In this case DEF_FIG_AXES is dropped and only the contents of "frame" will be used. The argument can
# be a NamedTuple, which allows setting grid pen and individual axes, or as a string (see ex bellow).
pre::String = (stalone) ? " -B" : ""
get_int(oo) = return (tryparse(Float64, oo) !== nothing) ? oo : "" # Micro nested-function
if (isa(args, NamedTuple)) # grid=(pen=?, x=?, y=?, xyz=?)
Expand Down Expand Up @@ -3875,7 +3884,13 @@ function round_wesn(_wesn::Vector{Float64}, geo::Bool=false, pad=zeros(2))::Vect
end
range[1] = wesn[2] - wesn[1]
range[2] = wesn[4] - wesn[3]
(n_axe == 3) && (range[3] = wesn[6] - wesn[5])
if (n_axe == 3)
if (wesn[5] == wesn[6])
wesn[5] -= abs(wesn[5]) * 0.05; wesn[6] += abs(wesn[6]) * 0.05
(wesn[5] == wesn[6]) && (wesn[5] = -0.1; wesn[6] = 0.1) # z was = 0
end
range[3] = wesn[6] - wesn[5]
end
if (geo) # Special checks due to periodicity
if (range[1] > 306.0) # If within 15% of a full 360 we promote to 360
if ((wesn[1] + wesn[2]) / 2 < 100) wesn[1] = -180.; wesn[2] = 180.
Expand Down Expand Up @@ -4173,9 +4188,10 @@ function showfig(d::Dict, fname_ps::String, fname_ext::String, opt_T::String, K:
opt_T = " -Tg"; fname_ext = "png" # In Jupyter or Pluto, png only
end

pscvt_cmd = "psconvert -A$(FIG_MARGIN[1])p" * PSCONV_PAR[1] # The default is "psconvert -A1p -Qg4 -Qt4 "
if (fname_ps != "" && isPluto) # A patch attempt to an undebugable Pluto thing
(K) && close_PS_file(fname_ps) # Close the PS file first
gmt("psconvert -A2p -Qg4 -Qt4 " * fname_ps * " -Tg *")
gmt(pscvt_cmd * fname_ps * " -Tg *")
out = TMPDIR_USR[1] * "/" * "GMTjl_" * TMPDIR_USR[2] * TMPDIR_USR[3] * ".png"
opt_T, fname_ps = "", ""
end
Expand All @@ -4186,7 +4202,7 @@ function showfig(d::Dict, fname_ps::String, fname_ext::String, opt_T::String, K:
if (opt_T != "")
(K) && close_PS_file(fname_ps) # Close the PS file first
((val = find_in_dict(d, [:dpi :DPI])[1]) !== nothing) && (opt_T *= string(" -E", val))
gmt("psconvert -A2p -Qg4 -Qt4 " * fname_ps * opt_T * " *")
gmt(pscvt_cmd * fname_ps * opt_T * " *")
reset_theme()
out::String = fname_ps[1:end-2] * fname_ext
(fname != "") && (out = mv(out, fname, force=true))
Expand Down
4 changes: 2 additions & 2 deletions src/gdal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1550,7 +1550,7 @@ end
gdalwarp(ds::Dataset, opts=String[]; dest="/vsimem/tmp", gdataset=false) = gdalwarp([ds], opts; dest=dest, gdataset=gdataset)
gdalwarp(ds::IDataset, opts=String[]; dest="/vsimem/tmp", gdataset=false) = gdalwarp([Dataset(ds.ptr)], opts; dest=dest, gdataset=gdataset)

function gdaltranslate(dataset::Dataset, options=String[]; dest="/vsimem/tmp", gdataset=false, save::AbstractString="")
function gdaltranslate(dataset::Dataset, options=String[]; dest="/vsimem/tmp", gdataset=false, save::AbstractString="", layout::String="")
(save != "") && (dest = save)
_options = GDALTranslateOptionsNew(options, C_NULL)
usage_error = Ref{Cint}()
Expand All @@ -1560,7 +1560,7 @@ end
if (dest != "/vsimem/tmp")
GDALClose(result); return nothing
end
return (gdataset) ? IDataset(result) : gd2gmt(IDataset(result))
return (gdataset) ? IDataset(result) : gd2gmt(IDataset(result), layout=layout)
end
function gdaltranslate(ds::IDataset, opts=String[]; dest="/vsimem/tmp", gdataset=false, save::AbstractString="")
(save != "") && (dest = save)
Expand Down
4 changes: 2 additions & 2 deletions src/gdal_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ function gd2gmt(_dataset; band::Int=0, bands=Vector{Int}(), sds::Int=0, pad::Int
(!isa(mat, Matrix) && size(mat,3) == 1) && (mat = reshape(mat, size(mat,1), size(mat,2))) # Fck pain
if (layout != "" && !startswith(layout, "TRB")) # From GDAL it always come as a TR but not sure about the interleave
if (startswith(layout, "BR")) mat = reverse(mat, dims=1) # Just flipUD
elseif (startswith(layout, "TC")) mat = collect(mat')
elseif (startswith(layout, "BC")) mat = reverse(mat', dims=1)
elseif (startswith(layout, "TC")) mat = (size(mat,3) == 1) ? collect(mat') : permutedims(mat, [2,1,3])
elseif (startswith(layout, "BC")) mat = (size(mat,3) == 1) ? reverse(mat', dims=1) : reverse(permutedims(mat, [2,1,3]), dims=1)
else @warn("Unsuported layout ($(layout)) change")
end
layout = layout[1:2] * "B" # Till further knowledge, assume it's always Band interleaved
Expand Down
5 changes: 4 additions & 1 deletion src/gmt_main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1109,13 +1109,16 @@ function dataset_init_FV(API::Ptr{Nothing}, FV::GMTfv)::Ptr{GMT_MATRIX}
n_records, _seg = 0, 0
tmp = zeros(maximum(size.(what_faces,2)), 3) # The maximum number of vertices in any polygon of all geometries

have_colors = !isempty(FV.color[1]) # Does this FV have a color for each polygon?
hdr = ""
for geom = 1:numel(what_faces) # If we have more than one type of geometries (e.g. triangles and quadrangles)
n_segs = size(what_faces[geom], 1) # Number of segments or faces (polygons) in this geometry (what_faces[geom])
n_rows = size(what_faces[geom], 2) # Number of rows (vertices of the polygon) in this geometry
for seg = 1:n_segs # Each row in a face is a new data segment (a polygon)
_seg += 1 # Counter that keeps track on the current number of polygon.
DSv = convert(Ptr{Nothing}, unsafe_load(DT.segment, _seg)) # DT.segment = Ptr{Ptr{GMT_DATASEGMENT}}
S = GMT_Alloc_Segment(API, GMT_NO_STRINGS, n_rows, 3, "", DSv) # Ptr{GMT_DATASEGMENT}
have_colors && (hdr = FV.color[geom][seg])
S = GMT_Alloc_Segment(API, GMT_NO_STRINGS, n_rows, 3, hdr, DSv) # Ptr{GMT_DATASEGMENT}
Sb = unsafe_load(S) # GMT_DATASEGMENT; Sb.data -> Ptr{Ptr{Float64}}

for c = 1:3, r = 1:n_rows
Expand Down
2 changes: 2 additions & 0 deletions src/gmt_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ The fields of this struct are:
- `verts::AbstractMatrix{T}`: Mx3 Matrix with the data vertices
- `faces`::Vector{<:AbstractMatrix{<:Integer}} A vector of matrices with the faces. Each row is a face
- `faces_view`::Vector{Matrix{Int}} A subset of `faces` with only the visible faces from a certain perspective
- `color`::Vector{Vector{String}} A vector with G option colors (in hexadecimal) for each face
- `bbox`::Vector{Float64} The vertices BoundingBox
- `zscale`::Float64 A multiplicative factor to scale the z values
- `bfculling`::Bool If culling of invisible faces is wished
Expand All @@ -312,6 +313,7 @@ Base.@kwdef mutable struct GMTfv{T<:AbstractFloat} <: AbstractMatrix{T}
verts::AbstractMatrix{T}=Matrix{Float64}(undef,0,0)
faces::Vector{<:AbstractMatrix{<:Integer}}=Vector{Matrix{Int}}(undef,0)
faces_view::Vector{Matrix{Int}}=Vector{Matrix{Int}}(undef,0)
color::Vector{Vector{String}}=[String[]]
bbox::Vector{Float64}=zeros(6)
zscale::Float64=1.0
bfculling::Bool=true
Expand Down
5 changes: 2 additions & 3 deletions src/psscale.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ function colorbar(arg1::Union{Nothing, GMTcpt}=nothing; first=true, kwargs...)
parse_paper(d) # See if user asked to temporarily pass into paper mode coordinates

cmd = parse_BJR(d, "", "", O, "")[1]
if (!contains(cmd, " -B") && !IamModern[1])
cmd *= DEF_FIG_AXES[1]
end
opt_B = (!contains(cmd, " -B") && !IamModern[1]) ? DEF_FIG_AXES[1] : ""
cmd = parse_JZ(d, cmd; O=O, is3D=(CTRL.pocket_J[3] != ""))[1] # We can't use parse_J(d)[1]
cmd = parse_common_opts(d, cmd, [:F :UVXY :params :c :p :t]; first=first)[1]
cmd = parse_these_opts(cmd, d, [[:G :truncate], [:I :shade], [:M :monochrome], [:N :dpi],
Expand All @@ -77,6 +75,7 @@ function colorbar(arg1::Union{Nothing, GMTcpt}=nothing; first=true, kwargs...)
end

cmd = add_opt(d, cmd, "L", [:L :equal :equal_size], (range="i", gap="")) # Aditive
(opt_B != "" && !contains(cmd, " -L")) && (cmd *= opt_B) # If no -B & no -L, add default -B
isempty(opt_D) && (cmd *= " -DJMR") # So that we can call it with just a CPT

r = finish_PS_module(d, gmt_proggy * cmd, "", K, O, true, arg1)
Expand Down
37 changes: 28 additions & 9 deletions src/psxy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ kwargs, we use the dotprod and a gray CPT to set the fill color that will be mod
function deal_faceverts(arg1::GMTfv, d; del::Bool=true)::GMTfv
azim, elev = get_numeric_view()
arg1, dotprod = sort_visible_faces(arg1, azim, elev; del=del) # Sort & kill (or not) invisible
if (is_in_dict(d, [:G :fill]) === nothing) # If fill not set we use the dotprod and a gray CPT to set the fill
if (is_in_dict(d, [:G :fill]) === nothing && isempty(arg1.color[1]))# If fill not set we use the dotprod and a gray CPT to set the fill
is_in_dict(d, [:Z :level :levels]) === nothing && (d[:Z] = abs.(dotprod))
(is_in_dict(d, CPTaliases) === nothing) && (d[:C] = gmt("makecpt -T0/1 -C140,220")) # Users may still set a CPT
end
Expand Down Expand Up @@ -1448,6 +1448,8 @@ end
"""
FV, projs = sort_visible_faces(FV, azim, elev; del=true) -> Tuple{Vector{GMTdataset}, Vector{Float64}}

Sort faces by distance and optionally delete the invisible ones.

Take a Faces-Vertices dataset and delete the invisible faces from view vector. Next sort them by distance so
that the furthest faces are drawn first and hence do not hide the others. NOTE: to not change the original
FV we store the resultant matix(ces) of faces in a FV0s fiels called "faces_view", which is a vector
Expand All @@ -1467,36 +1469,53 @@ function sort_visible_faces(FV::GMTfv, azim, elev; del::Bool=true)::Tuple{GMTfv,
view_vec = [sin_az * cos_el, cos_az * cos_el, sin_el]
projs = Float64[]

!FV.bfculling && (del = false) # Do not delete if bfculling is set to false (for example if FV is not closed)
!FV.bfculling && (del = false) # Do not delete if bfculling is set to false (for example if FV is not closed)
isPlane = (FV.bbox[1] == FV.bbox[2]) || (FV.bbox[3] == FV.bbox[4]) || (FV.bbox[5] == FV.bbox[6]) # Is this FV a plane?
isPlane && (del = false) # Planes have no invisibles
needNormals = isempty(FV.color[1]) # If we have no color, we need to compute the normals

isPlane && !needNormals && return FV, projs # Nothing to do here in this case.

have_colors = !isempty(FV.color[1]) # Does this FV have a color for each polygon?
first_face_vis = true
for k = 1:numel(FV.faces) # Loop over number of face groups (we can have triangles, quads, etc)
for k = 1:numel(FV.faces) # Loop over number of face groups (we can have triangles, quads, etc)
n_faces::Int = size(FV.faces[k], 1) # Number of faces (polygons)
this_face_nverts::Int = size(FV.faces[k], 2)
tmp = zeros(this_face_nverts, 3)
del && (isVisible = fill(false, n_faces))
dists = NTuple{2,Float64}[]
_projs = Float64[]
for face = 1:n_faces
for c = 1:3, v = 1:this_face_nverts # Build the polygon from the FV collection
for face = 1:n_faces # Loop over the faces of this group
for c = 1:3, v = 1:this_face_nverts # Build the polygon from the FV collection
tmp[v,c] = FV.verts[FV.faces[k][face,v], c]
end
this_proj = dot(facenorm(tmp, zfact=FV.zscale), view_vec)
if (!del || (isVisible[face] = this_proj > 0))
cx, cy, cz = sum(tmp[:,1]), sum(tmp[:,2]), sum(tmp[:,3]) # Pseudo-centroids. Good enough for the sorting purpose
push!(dists, (cx * sin_az + cy * cos_az, cz * sin_el))
push!(_projs, this_proj)
if (!isPlane) # Plades do not need sorting
cx, cy, cz = sum(tmp[:,1]), sum(tmp[:,2]), sum(tmp[:,3]) # Pseudo-centroids. Good enough for sorting
push!(dists, (cx * sin_az + cy * cos_az, cz * sin_el))
end
push!(_projs, this_proj) # But need the normals as stated at the begining of this function
end
end

if (isPlane) # Here, FV being a plane we only care about storing the normals
projs = (first_face_vis) ? _projs[ind] : append!(projs, _projs[ind])
first_face_vis = false
continue
end

data::Matrix{Integer} = del ? FV.faces[k][isVisible, :] : FV.faces[k]
isempty(data) && continue
ind = sortperm(dists)
data = data[ind, :]
(first_face_vis) ? (FV.faces_view = [data]) : append!(FV.faces_view, [data])
projs = (first_face_vis) ? _projs[ind] : append!(projs, _projs[ind])
have_colors && (FV.color[k] = FV.color[k][ind])
first_face_vis = false
end
sum(size.(FV.faces_view, 1)) < sum(size.(FV.faces, 1) / 3) &&
@warn("More than 2/3 of the faces found invisible. This often indicates that the Z and X,Y units are not the same. Consider using the `zscale` field of the `FV` input.")
@warn("More than 2/3 of the faces found invisible (actually: $(100 - sum(size.(FV.faces_view, 1)) / sum(size.(FV.faces, 1))*100)%). This often indicates that the Z and X,Y units are not the same. Consider setting `bfculling` to false or using the `zscale` field of the `FV` input.")

return FV, projs
end
Expand Down
32 changes: 32 additions & 0 deletions src/triangulate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,35 @@ function tri_z(D::Vector{<:GMTdataset})::Vector{Float64}
end
return Zs
end


# ---------------------------------------------------------------------------------------------------
"""
"""
function imgfv(I::Union{GMTimage, AbstractString})::GMTfv
_I = isa(I, GMTimage) ? I : gmtread(I)
n_rows, n_cols, n_bands = size(_I)
nxy, nxy2 = n_rows * n_cols, n_rows * n_cols * 2
X,Y = meshgrid(linspace(0, 1, n_cols+1), linspace(1, 0, n_rows+1))
#X,Y = meshgrid(linspace(0, n_cols-1, n_cols), linspace(n_rows-1, 0, n_rows))
FV = surf2fv(X, Y, zeros(n_rows+1, n_cols+1), type=:quad)
cor = Vector{String}(undef, nxy)

kk = 0
#=
if (_I.layout[3] == 'B')
for n = 1:n_cols, m = 1:n_rows
k = (m-1) * n_cols + n
cor[kk+=1] = @sprintf("-G#%.2x%.2x%.2x", _I[k], _I[k+nxy], _I[k+nxy2])
end
else
=#
for n = 1:n_cols, m = 1:n_rows
k = (m-1) * n_cols * 3 + (n-1) * 3
cor[kk+=1] = @sprintf("-G#%.2x%.2x%.2x", _I[k+=1], _I[k+=1], _I[k+=1])
end
#end

FV.color = [cor]
return FV
end

0 comments on commit be01370

Please sign in to comment.