@@ -178,9 +178,14 @@ NOTE: Instead of ``getbyattrib`` one case use instead ``filter`` (...,`index=fal
178178 means select all elements from ``Antioquia`` and ``Caldas`` that have the attribute `feature_id` = 0.
179179
180180 A second set of attributes can be used to select elements by region, number of polygon vertices and area.
181- For that, name the keyword with a leading underscore, e.g. `_region`, `_nps`, `_area`. Their values are
181+ For that, name the keyword with a leading underscore, e.g. `_region`, `_nps`, `_area`, `_unique` . Their values are
182182 passed respectively a 4 elements tuple and numbers. E.g. `_region=(-8.0, -7.0, 37.0, 38.0)`, `_nps=100`, `_area=10`.
183- Areas are in square km when input is in geographic coordinates, otherwise squre data unites.
183+ Areas are in square km when input is in geographic coordinates, otherwise square data units. The `_unique` case is
184+ a bit special and is meant to be used when more than one polygon share the same attribute value (e.g. countries with islands).
185+ In that case, set the value of `_unique` to the name of the attribute that is shared by the polygons (e.g. `_unique="NAME"`).
186+ By default (e.g. `_unique=true`), the attribute name is `Feature_ID` which is the one used by GMT when creating unique
187+ IDs for polygons read from OGR formats (.shp, .geojson, etc). The uniqueness is determined by selecting the polygon
188+ with the largest area.
184189
185190- `invert, or reverse, or not`: If `true` return all segments that do NOT match the query condition(s).
186191
@@ -208,7 +213,7 @@ function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
208213 invert = (find_in_kwargs (kw, [:invert :not :revert :reverse ])[1 ] != = nothing )
209214 if ((_att = find_in_kwargs (kw, [:att :attrib ])[1 ]) != = nothing ) # For backward compat.
210215 if ! isa (_att, NamedTuple)
211- ((val = find_in_kwargs (kw, [:val :value ])[1 ]) === nothing ) && error (" Must provide the `attribute` VALUE." )
216+ ((val = find_in_kwargs (kw, [:val :value ])[1 ]) === nothing ) && error (" Must provide the `attribute` VALUE." )
212217 end
213218 atts, vals = isa (_att, NamedTuple) ? (string .(keys (_att)), string .(values (_att))) : ((string (_att),), (string (val),))
214219 _keys = repeat (" " , length (kw))
@@ -224,6 +229,7 @@ function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
224229 end
225230 atts, vals = Vector {String} (undef, count), Vector {String} (undef, count)
226231
232+ attrib_name = " Feature_ID" # The default name for the _unique attribute name
227233 for k = 1 : numel (kw)
228234 vv = values (v[k])
229235 if (isa (vv, Tuple) && (_keys[k][1 ] != ' _' ))
@@ -236,6 +242,11 @@ function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
236242 kk += length (vv)
237243 else
238244 atts[kk], vals[kk] = _keys[k], string (vv)
245+ if (atts[kk] == " _unique" )
246+ # If we can't parse the arg as a number then it must be an attribute name. If not, an error will be raised later.
247+ (vals[kk] != " true" && (tryparse (Int, vals[kk]) === nothing ) && (tryparse (Float64, vals[kk]) === nothing )) && (attrib_name = vals[kk])
248+ vals[kk] = " 0" # If _unique the value doesn't matter but must be parseable to float
249+ end
239250 kk += 1
240251 end
241252 (k == 1 ) && (dounion = kk) # Index that separates the unions from the interceptions
@@ -256,14 +267,30 @@ function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
256267 for k = 1 : numel (D) _tf[k] = size (D[k]. data,1 ) >= nps_ end
257268 _tf
258269 end
259- function clip_area (D, areas_, area_, _tf) # Clip by number of points
270+ function clip_area (D, areas_, area_, _tf) # Clip by number of points
260271 for k = 1 : numel (D) _tf[k] = areas_[k] >= area_ end
261272 _tf
262273 end
274+ function clip_unique (D, areas_, _tf, name) # Clip by uniqueness by using the areas to select the largest by group.
275+ att_tbl, att_names = make_attrtbl (D, true )
276+ ((ind_name = findfirst (att_names .== name)) === nothing ) && error (" Attribute name $name not found in dataset." )
277+ _, ind = gunique (att_tbl[:,ind_name])
278+ for k = 1 : numel (ind) _tf[ind[k]] = true end # Set the unique values to true.
279+ k = 1
280+ while (k <= numel (ind)- 1 )
281+ ((ind[k+= 1 ] - ind[k- 1 ]) == 1 ) && continue
282+ n_start, n_end = ind[k - 1 ], ind[k] - 1
283+ # Here we have a group (from n_start to n_end) of the same type of attribute
284+ this_ind = argmax (view (areas_,n_start: n_end,1 )) + n_start - 1 # Get the index of the largest area
285+ _tf[n_start], _tf[this_ind] = false , true # Turn off the first and turn on the largest of this group
286+ end
287+ _tf
288+ end
263289
264290 (ind = findfirst (atts .== " _region" )) != = nothing && (lims = parse .(Float64, split (vals[ind][2 : end - 1 ], " , " )))
265291 (ind = findfirst (atts .== " _nps" )) != = nothing && (nps = parse .(Float64, vals[ind]))
266- (ind = findfirst (atts .== " _area" )) != = nothing && (area = parse .(Float64, vals[ind]); areas = gmt_centroid_area (G_API[1 ], D, Int (isgeog (D)))[:,3 ])
292+ ((ind = findfirst (atts .== " _area" )) != = nothing || (ind = findfirst (atts .== " _unique" )) != = nothing ) &&
293+ (area = parse .(Float64, vals[ind]); areas = gmt_centroid_area (G_API[1 ], D, Int (isgeog (D)), ca= 1 ); isgeog (D) && (areas .*= 1e-6 )) # areas in km^2 if geographic coords
267294
268295 indices:: Vector{Int} = Int[]
269296 ky = keys (D[1 ]. attrib)
@@ -274,6 +301,7 @@ function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
274301 if (special)
275302 if (atts[n] == " _region" ) tf = clip_region (D, lims, tf)
276303 elseif (atts[n] == " _area" ) tf = clip_area (D, areas, area, tf)
304+ elseif (atts[n] == " _unique" ) tf = clip_unique (D, areas, tf, attrib_name)
277305 else tf = clip_np (D, nps, tf)
278306 end
279307 else
@@ -301,7 +329,18 @@ function getbyattrib(D::Vector{<:GMTdataset}; indices=false, kw...)::Union{Nothi
301329end
302330
303331# ---------------------------------------------------------------------------------------------------
332+ """
333+ filter(D::Vector{<:GMTdataset}; kw...)
334+
335+ See `? getbyattrib`
336+ """
304337Base.:filter (D:: Vector{<:GMTdataset} ; kw... ) = getbyattrib (D; kw... )
338+ # ---------------------------------------------------------------------------------------------------
339+ """
340+ findall(D::Vector{<:GMTdataset}; kw...)
341+
342+ See `? getbyattrib`
343+ """
305344Base.:findall (D:: Vector{<:GMTdataset} ; kw... ) = getbyattrib (D, true ; kw... )
306345
307346# ---------------------------------------------------------------------------------------------------
0 commit comments