Skip to content

Commit

Permalink
Merge pull request #1239 from GenericMappingTools/filter-findall-attribs
Browse files Browse the repository at this point in the history
Rework the getbyattrib function to select more elements. Make filter and findall aliases of it.
  • Loading branch information
joa-quim authored Aug 29, 2023
2 parents c3d7994 + 5b05e38 commit f7d5eae
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/show_pretty_datasets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ function batch_compacttype(types::Vector{Any}, maxwidths::Vector{Int})
end
end

#=
function batch_compacttype(types::Vector{Any}, maxwidth::Int)
cache = Dict{Type, String}()
return map(types) do T
Expand All @@ -216,6 +217,7 @@ function batch_compacttype(types::Vector{Any}, maxwidth::Int)
end
end
end
=#

"""
compacttype(T::Type, maxwidth::Int=8, initial::Bool=true)
Expand Down
72 changes: 54 additions & 18 deletions src/utils_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1892,17 +1892,28 @@ end

# ---------------------------------------------------------------------------------------------------
"""
getbyattrib(D::Vector{<:GMTdataset}[, index::Bool]; kw...)
getbyattrib(D::Vector{<:GMTdataset}[, index::Bool=false]; kw...)
Take a GMTdataset vector and return only its elememts that match the condition(s) set by the `attrib` keywords.
or
filter(D::Vector{<:GMTdataset}; kw...)
or
findall(D::Vector{<:GMTdataset}; kw...)
Take a GMTdataset vector and return only its elements that match the condition(s) set by the `kw` keywords.
Note, this assumes that `D` has its `attrib` fields set with usable information.
NOTE: Instead of ``getbyattrib`` one case use instead ``filter`` (==> `index=false`) or ``findall`` (==> `index=true`)
### Parameters
- `attrib` or `att`: keyword with the attribute `name` used in selection. It can be a single name as in `att="NAME_2"`
or a NamedTuple with the attribname, attribvalue as in `att=(NAME_2="value")`. Use more elements if
wishing to do a composite match. E.g. `att=(NAME_1="val1", NAME_2="val2")` in which case oly segments
matching the two conditions are returned.
- `val` or `value`: keyword with the attribute ``value`` used in selection. Use this only when `att` is not a NamedTuple.
- `attrib name(s)=value(s)`: Easier to explain by examples: `NAME="Antioquia"`, select all elements that have
that attribute/value combination. `NAME=("Antioquia", "Caldas"), pick elements that have those `NAME` attributes.
Add as many as wished. If using two `kwargs` the second works as a condition. ``(..., NAME=("Antioquia", "Caldas"), feature_id=0)``
means select all elements from ``Antioquia`` and ``Caldas`` that have the attribute `feature_id` = 0.
- `attrib` or `att`: (OLD SYNTAX) A NamedTuple with the attribname, attribvalue as in `att=(NAME_2="value",)`.
Use more elements if wishing to do a composite match. E.g. `att=(NAME_1="val1", NAME_2="val2")` in which
case only segments matching the two conditions are returned.
- `index`: Use this `positional` argument = `true` to return only the segment indices that match the `att` condition(s).
### Returns
Expand All @@ -1911,31 +1922,52 @@ Or `nothing` if the query results in an empty GMTdataset
## Example:
D = getbyattrib(D, attrib="NAME_2", val="Porto");
D = filter(D, NAME_2="Porto");
"""
function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
# This method does the work but it's not the one normally used by the public.
# It returns the indices of the selected segments.
(isempty(D[1].attrib)) && (@warn("This datset does not have an `attrib` field and is hence unusable here."); return Int[])
((_att = find_in_kwargs(kw, [:att :attrib])[1]) === nothing) && error("Must provide the `attribute` NAME.")
if isa(_att, NamedTuple)
atts, vals = string.(keys(_att)), string.(values(_att))
dounion = Int(1e9) # Just a big number
if ((_att = find_in_kwargs(kw, [:att :attrib])[1]) !== nothing) # For backward compat.
if !isa(_att, NamedTuple)
((val = find_in_kwargs(kw, [:val :value])[1]) === nothing) && error("Must provide the `attribute` VALUE.")
end
atts, vals = isa(_att, NamedTuple) ? (string.(keys(_att)), string.(values(_att))) : ((string(_att),), (string(val),))
else
atts = (string(_att),)
((val = find_in_kwargs(kw, [:val :value])[1]) === nothing) && error("Must provide the `attribute` VALUE.")
vals = (string(val),)
# FCK unbelievable case. Julia can be f. desparing. It took me an whole afternoon to make this work.
#d = KW(kw); Keys = keys(d);
#Keys[1] => Internal Error: MethodError: no method matching getindex(::Base.KeySet{Symbol, Dict{Symbol, Any}}, ::Int64)
count, kk = 0, 1
v = values(kw)
for k = 1:numel(kw) count += (isa(values(v[k]), Tuple)) ? length(values(v[k])) : 1 end
atts, vals = Vector{String}(undef, count), Vector{String}(undef, count)

_keys = string.(keys(kw))
for k = 1:numel(kw)
vv = values(v[k])
if (isa(vv, Tuple))
atts[kk:kk+length(vv)-1] .= _keys[k]
vals[kk:kk+length(vv)-1] .= string.(vv)
kk += length(vv)
else
atts[kk], vals[kk] = _keys[k], string(vv)
kk += 1
end
(k == 1) && (dounion = kk) # Index that separates the unions from the interceptions
end
end

indices::Vector{Int} = Int[]
ky = keys(D[1].attrib)
for n = 1:numel(atts)
ky = keys(D[1].attrib)
((ind = findfirst(ky .== atts[n])) === nothing) && return Int[]
((ind = findfirst(ky .== atts[n])) === nothing) && continue#return Int[]
tf = fill(false, length(D))
for k = 1:length(D)
(!isempty(D[k].attrib) && (D[k].attrib[atts[n]] == vals[n])) && (tf[k] = true)
end
if (n == 1) indices = findall(tf)
else indices = intersect(indices, findall(tf))
else indices = (dounion > n) ? union(indices, findall(tf)) : intersect(indices, findall(tf))
end
end
return indices
Expand All @@ -1945,4 +1977,8 @@ function getbyattrib(D::Vector{<:GMTdataset}; kw...)::Union{Nothing, Vector{GMTd
# This is the intended public method. It returns a subset of the selected segments
ind = getbyattrib(D, true; kw...)
return isempty(ind) ? nothing : D[ind]
end
end

# ---------------------------------------------------------------------------------------------------
Base.:filter(D::Vector{<:GMTdataset}; kw...) = getbyattrib(D; kw...)
Base.:findall(D::Vector{<:GMTdataset}; kw...) = getbyattrib(D, true; kw...)
4 changes: 3 additions & 1 deletion test/test_common_opts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@
GMT.polygonlevels(D, ["a", "b", "c"], [1,2,missing], att="nome");
GMT.edit_segment_headers!(D, [1], "0");
GMT.getbyattrib(D, att="nome", val="a");
GMT.getbyattrib(D, att=(nome="a", nome2="b"));
filter(D, nome="a", nome2="b");
filter(D, nome=("a","b"));
findall(D, nome="a");
D = mat2ds([0 0; 1 1],["a", "b"]); D.header = "-Wred";
@test GMT.edit_segment_headers!(D, 'W', :get) == "red"
@test GMT.edit_segment_headers!(D, 'W', :set, "blue") == "-Wblue"
Expand Down
2 changes: 1 addition & 1 deletion test/test_misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
grdcontour(G, axis="a", color=cpt, pen="+c", fmt=:png, savefig="lixo")
D = grdcontour(G, cont=[-2,0,5], dump=true);

show(makecpt(C=:rainbow))
info(makecpt(C=:rainbow))

add2PSfile("Bla")
add2PSfile(["Bla", "Bla"])
Expand Down

0 comments on commit f7d5eae

Please sign in to comment.