|
| 1 | +export choropleth |
| 2 | + |
| 3 | + |
| 4 | +""" |
| 5 | + choropleth(D, ids, vals; attrib="CODE", cmap="turbo", outline=true, kw...) |
| 6 | + choropleth(D, att=""; attrib="", cmap="turbo", outline=true, kw...) |
| 7 | + choropleth(D, data::GMTdataset, att=""; attrib="", cmap="turbo", outline=true, kw...) |
| 8 | + choropleth(D, data::Dict; outline=true, kw...) |
| 9 | + choropleth(D, data::NamedTuple; outline=true, kw...) |
| 10 | + choropleth(D, vals::Vector{<:Real}; attrib="CODE", outline=true, kw...) |
| 11 | +
|
| 12 | +Create a choropleth map visualization from geographic data with color-coded regions. |
| 13 | +
|
| 14 | +### Arguments |
| 15 | +- `D`: Geographic data - either a file path (String) or Vector{GMTdataset} containing polygons. |
| 16 | +- `ids::Vector{<:AbstractString}`: Region identifiers to match with the attribute values. |
| 17 | +- `vals::Vector{<:Real}`: Numeric values to map to colors for each region. |
| 18 | +- `att`: Attribute column name (positional argument) for coloring when values are in the dataset. |
| 19 | +- `data::GMTdataset`: Dataset containing attributes to join with geographic features (must have text column). |
| 20 | +- `data::Dict`: Dictionary mapping region identifiers to values. |
| 21 | +- `data::NamedTuple`: Named tuple with region identifiers as keys and values. |
| 22 | +
|
| 23 | +### Keywords |
| 24 | +- `attrib`: Attribute name to match identifiers (default: "CODE"). Takes precedence over positional `att`. |
| 25 | +- `cmap`: A colormap name (default: "turbo") or a GMTcpt colormap. |
| 26 | +- `outline`: Draw polygon outlines. `true` (default), `false`, or a pen specification string. |
| 27 | +- `kw...`: Additional arguments passed to the plotting function (e.g., `proj`, `region`, `title`). |
| 28 | +
|
| 29 | +### Returns |
| 30 | +Nothing or a GMTps type (when saving to file). |
| 31 | +
|
| 32 | +### Methods |
| 33 | +1. **With IDs and values**: `choropleth(D, ids, vals)` - Explicit region IDs and corresponding values. |
| 34 | +2. **From attribute**: `choropleth(D, att)` - Extract values from a numeric attribute column in D. |
| 35 | +3. **With GMTdataset**: `choropleth(D, data, att)` - Join D with data using text column for matching. |
| 36 | +4. **With Dict**: `choropleth(D, Dict("PT"=>10, "ES"=>20))` - Map from region codes to values. |
| 37 | +5. **With NamedTuple**: `choropleth(D, (PT=10, ES=20))` - Same as Dict but with named tuple syntax. |
| 38 | +6. **Values only**: `choropleth(D, vals)` - Values in order of unique IDs extracted from D. |
| 39 | +
|
| 40 | +### Examples |
| 41 | +```julia |
| 42 | +# Using explicit IDs and values |
| 43 | +D = getdcw("PT,ES,FR") |
| 44 | +choropleth(D, ["PT","ES","FR"], [10.0, 20.0, 30.0]) |
| 45 | +
|
| 46 | +# Using a Dict |
| 47 | +choropleth(D, Dict("PT"=>10, "ES"=>20, "FR"=>30)) |
| 48 | +
|
| 49 | +# From attribute in dataset (values stored in polygon attributes) |
| 50 | +choropleth("countries.shp", "GDP_PER_CAPITA") |
| 51 | +
|
| 52 | +# With custom colormap and no outlines |
| 53 | +choropleth(D, ["PT","ES","FR"], [1.0, 2.0, 3.0], cmap="bamako", outline=false) |
| 54 | +
|
| 55 | +# The example in "Tutorials" |
| 56 | +D = getdcw("US", states=true, file=:ODS); |
| 57 | +Df = filter(D, _region=(-125,-66,24,50), _unique=true); |
| 58 | +pop = gmtread(TESTSDIR * "assets/uspop.csv"); |
| 59 | +choropleth(Df, pop, "NAME", show=true) |
| 60 | +``` |
| 61 | +""" |
| 62 | +choropleth(D::String, ids::Vector{<:AbstractString}, vals::Vector{<:Real}; attrib::StrSymb="CODE", cmap="turbo", outline=true, kw...) = |
| 63 | + choropleth(gmtread(D), ids, vals; attrib=attrib, cmap=cmap, outline=outline, kw...) |
| 64 | +function choropleth(D, ids::Vector{<:AbstractString}, vals::Vector{<:Real}; attrib::StrSymb="CODE", |
| 65 | + cmap="turbo", outline=true, kw...) |
| 66 | + |
| 67 | + helper_plot_choropleth(D, ids, Float64.(vals), string(attrib), cmap, outline; kw...) |
| 68 | +end |
| 69 | + |
| 70 | + |
| 71 | +# ------------------------------------------------------------------------------------------------------ |
| 72 | +choropleth(D::String, att::StrSymb=""; attrib::StrSymb="", cmap="turbo", outline=true, kw...) = |
| 73 | + choropleth(gmtread(D), att; attrib=attrib, cmap=cmap, outline=outline, kw...) |
| 74 | +function choropleth(D::Vector{<:GMTdataset}, att::StrSymb=""; attrib::StrSymb="", cmap="turbo", outline=true, kw...) |
| 75 | + |
| 76 | + _att = (attrib !== "") ? attrib : att # May use either positional or keyword argument. Later takes precedence |
| 77 | + ids, vals = extract_ids_vals(D, string(_att)) |
| 78 | + helper_plot_choropleth(D, ids, vals, string(_att), cmap, outline; kw...) |
| 79 | +end |
| 80 | + |
| 81 | +# ------------------------------------------------------------------------------------------------------ |
| 82 | +choropleth(D::String, data::GMTdataset, att::StrSymb=""; attrib::StrSymb="", cmap="turbo", outline=true, kw...) = |
| 83 | + choropleth(gmtread(D), data, att; attrib=attrib, cmap=cmap, outline=outline, kw...) |
| 84 | +function choropleth(D::Vector{<:GMTdataset}, data::GMTdataset, att::StrSymb=""; attrib::StrSymb="", cmap="turbo", outline=true, kw...) |
| 85 | + isempty(data.text) && error("The 'data' GMTdataset has no text column to guid the joining operation.") |
| 86 | + _att = (attrib !== "") ? attrib : att # May use either positional or keyword argument. Later takes precedence |
| 87 | + (get(D[1].attrib, _att, "") === "") && error("The 'D' GMTdatasets has no attribute $(_att).") |
| 88 | + zvals = polygonlevels(D, data, att=_att) |
| 89 | + helper_plot_choropleth(D, String[], view(data, :,1), string(_att), cmap, outline, zvals; kw...) |
| 90 | +end |
| 91 | + |
| 92 | +# ------------------------------------------------------------------------------------------------------ |
| 93 | +choropleth(D::String, data::Dict; outline=true, kw...) = choropleth(gmtread(D), data; outline=outline, kw...) |
| 94 | +function choropleth(D::Vector{<:GMTdataset}, data::Dict; outline=true, kw...) |
| 95 | + ids = collect(keys(data)) |
| 96 | + vals = [data[k] for k in ids] |
| 97 | + choropleth(D, string.(ids), Float64.(vals); outline=outline, kw...) |
| 98 | +end |
| 99 | + |
| 100 | +# ------------------------------------------------------------------------------------------------------ |
| 101 | +choropleth(D::String, data::NamedTuple; outline=true, kw...) = choropleth(gmtread(D), data; outline=outline, kw...) |
| 102 | +choropleth(D::Vector{<:GMTdataset}, data::NamedTuple; outline=true, kw...) = choropleth(D, Dict(pairs(data)); outline=outline, kw...) |
| 103 | + |
| 104 | +# ------------------------------------------------------------------------------------------------------ |
| 105 | +choropleth(D::String, vals::Vector{<:Real}; attrib::StrSymb="CODE", outline=true, kw...) = |
| 106 | + choropleth(gmtread(D), vals; attrib=attrib, outline=outline, kw...) |
| 107 | +function choropleth(D::Vector{<:GMTdataset}, vals::Vector{<:Real}; attrib::StrSymb="CODE", outline=true, kw...) |
| 108 | + # Extract unique IDs in order of first appearance |
| 109 | + att = string(attrib) |
| 110 | + ids = String[] |
| 111 | + last_id = "" |
| 112 | + for d in D |
| 113 | + id = get(d.attrib, att, "") |
| 114 | + (id == last_id) && continue # skip obvious duplicates |
| 115 | + !(id in ids) && push!(ids, id) |
| 116 | + last_id = id |
| 117 | + end |
| 118 | + |
| 119 | + length(vals) != length(ids) && error("Values length ($(length(vals))) != unique IDs ($(length(ids))): $(join(ids, ", "))") |
| 120 | + choropleth(D, ids, vals; attrib=att, outline=outline, kw...) |
| 121 | +end |
| 122 | + |
| 123 | +# ------------------------------------------------------------------------------------------------------ |
| 124 | +function extract_ids_vals(D, attrib::String) |
| 125 | + ids, vals = String[], String[] |
| 126 | + last_id = "" |
| 127 | + for d in D |
| 128 | + id = get(d.attrib, attrib, "") |
| 129 | + (id == last_id) && continue # skip obvious duplicates |
| 130 | + !(id in ids) && (push!(ids, id); push!(vals, d.attrib[attrib])) |
| 131 | + last_id = id |
| 132 | + end |
| 133 | + ind = findall(x -> x === "", vals) # Shit, can't use tryparse() because it f. returns Nothing's instaed of NaN's for "" |
| 134 | + if (!isempty(ind)) |
| 135 | + vals = deleteat!(vals, ind) |
| 136 | + ids = deleteat!(ids, ind) |
| 137 | + end |
| 138 | + ids, parse.(Float64, vals) |
| 139 | +end |
| 140 | + |
| 141 | +# ------------------------------------------------------------------------------------------------------ |
| 142 | +function helper_plot_choropleth(D, ids, vals, attrib::String, cmap, outline, _zvals=Float64[]; kw...) |
| 143 | + zvals = (isempty(_zvals)) ? polygonlevels(D, ids, vals, att=attrib) : _zvals |
| 144 | + pen_ouline = isa(outline, Bool) ? "0" : outline |
| 145 | + C = isa(cmap, GMTcpt) ? cmap : makecpt(T=(minimum(vals), maximum(vals)), C=string(cmap)) |
| 146 | + plot_outline = (outline != false) |
| 147 | + (!plot_outline) && return plot(D; level=zvals, cmap=C, kw...) |
| 148 | + d = KW(kw) |
| 149 | + do_show, fmt, savefig = get_show_fmt_savefig(d, false) |
| 150 | + plot(D; level=zvals, cmap=C) |
| 151 | + #plot!(D; pen=pen_ouline, fmt=fmt, savefig=savefig, show=do_show, d...) |
| 152 | + d[:fmt]=fmt; d[:savefig]=savefig; d[:show]=do_show; d[:pen]=pen_ouline |
| 153 | + common_plot_xyz("", D, "plot", false, false, d) |
| 154 | +end |
| 155 | + |
| 156 | +# ------------------------------------------------------------------------------------------------------ |
| 157 | +choropleth!(args...; kw...) = choropleth(args...; first=false, kw...) |
0 commit comments