text:
# obsidify-keep.jl
# @author Thomas Nichols <thomas@nexus10.com>
# @desc   Convert Google Keep notes to Obsidian Markdown

# A Google Keep repository can be exported using [Google Takeout](https://takeout.google.com/settings/takeout)
# - this generates a zip file of a Takeout/Keep directory containing:
#
# * json: the primary textual content, processed and output to vault
# * Labels.txt: list of label (tag) names used - added to report
# * audio/video files: copied to vault/media
# * html: currently ignored
# * other files: copied to vault and added to report file

module ObsidifyKeep

using ArgParse, DataFrames, Dates, Debugger, JSON3, Pkg;
Pkg.add("DataFrames");
Pkg.add("JSON3");

include("spinner.jl");

LABEL_FILE = "Labels.txt";
MEDIA_EXTENSIONS = [".3gp", ".png", ".jpg", ".jpeg", ".mpeg", ".mp3", ".mp4", ".awb", ".gif"];
RECOGNIZED_EXTENSIONS = [".json", ".html"]
VAULT_SUBDIR = "vault";
MEDIA_SUBDIR = "media";
REPORT_FILE = "__Obsidify_Log.md"

function main()
    params = read_args();
    make_output_dirs(params);
    status = chomp_all_files(params);
    output_report(params, status);
    thats_all(status);
end

#-- Implementation -------- --#

struct Params
    input_dir::String;
    output_dir::String;
    verbose::Bool;
end

struct Status
    labels::Vector{String};
    warnings::Vector{String};
end

function read_args() :: Params
    s = ArgParseSettings();
    @add_arg_table s begin
        "--input-dir", "-i"
        help = "location of the Takeout/Keep directory"
        arg_type = String
        default = "./Takeout/Keep"
        "--output-dir", "-o"
        help = "location of the output directory"
        arg_type = String
        default = "."
        "--verbose", "-v"
        help = "enable detailed logging"
        arg_type = Bool
        default = false
    end
    parsed_args = parse_args(Base.ARGS, s);
    params = Params(parsed_args["input-dir"],
                    joinpath(parsed_args["output-dir"], VAULT_SUBDIR),
                    parsed_args["verbose"]);
    params.verbose ? println("---- ARGS: ",      Base.ARGS)          : 0 ;
    params.verbose ? println("---- input-dir=",  params.input_dir)   : 0 ;
    params.verbose ? println("---- output-dir=", params.output_dir)  : 0 ;
    params.verbose ? println("---- verbose=",    params.verbose)     : 0 ;
    return params;
end

function make_output_dirs(params::Params)
    vault = params.output_dir;
    # unlike mkdir(), mkpath() creates intermediate directories, does not error if they exist
    mkpath(vault, mode=0o777);
    mkpath(joinpath(vault, MEDIA_SUBDIR), mode=0o777);
end

function chomp_all_files(params::Params) :: Status
    # Log all warnings and store labels in Status object
    status = Status([], []);
    println();
    spinner_pos = UInt16(1);
    # readdir() gives Vector{String} of filenames
    filenames = readdir(params.input_dir);
    # match file extension to relevant chomp method
    for fn in filenames
        spinner_pos = crank_spinner(spinner_pos);
        ext = get_file_extension(fn);
        if (ext==".json")
            chomp_json_file(params, status, joinpath(params.input_dir, fn));
        elseif(ext==".html")
            ;  # no action
        elseif(fn==LABEL_FILE)
            chomp_labels_file(params, status, joinpath(params.input_dir, LABEL_FILE));
        else
            is_media = ext in MEDIA_EXTENSIONS;
            chomp_generic_file(params, status,is_media, fn);
        end
        spinner_pos = crank_spinner(spinner_pos);
    end #for
    return status;
end

function chomp_labels_file(params::Params, status::Status, filename::String)
    #
    # return a Vector of all labels used in the Keep repo.
    # TODO
    append!(status.labels,
            ["LABEL1",
             "LABEL2",
            ]);
end

function chomp_generic_file(params::Params, status::Status, is_media::Bool, filename::String);
    target_dir = params.output_dir;
    if is_media
        target_dir = joinpath(target_dir,MEDIA_SUBDIR);
    end
    raw_fn = splitpath(filename)[end] # discard directories
    try
        # TODO use force=false and change filename in case of collision
        # (though OS-level collision handling - e.g. Windows "Filename (Copy).md" is viable)
        cp(joinpath(params.input_dir, raw_fn),  joinpath(target_dir, raw_fn), force=true, follow_symlinks=true);
    catch err
        push!(status.warnings, filename * ": " * err.msg);
    end

    # TODO
end

function chomp_json_file(params::Params, status::Status, filename::String)
    # TODO handle isArchive and isTrash
    # TODO strip trailing spaces from filenames / titles
    # TODO at two trailing spaces to output lines to force newlines
    #
    #
    # TODO: Not even thinking about UTF-8 and graphemes...

    df = JSON3.read.(eachline(filename)) |> DataFrame;
    for r in eachrow(df)
        check_for_unknown_keys(params, status, keys(r));
        unixdate_seconds = r[:userEditedTimestampUsec] / 10^6;
        datetime = Dates.unix2datetime(unixdate_seconds);
        ymddate = Dates.format(datetime, "yyyy-mm-dd");

        md_filename=splitpath(filename)[end];    # get filename without [path
        md_filename = replace(md_filename, ".json" => ".md");
        md_filename = joinpath(params.output_dir, md_filename);

        try
            open(md_filename, "w") do file
                print_metadata(file, r, ymddate);
                title = getstring(r, :title);
                println(file, "# ", title);
                println(file, "");
               print_attachments(file, r)
                print_text_content(file, r);
                print_list_content(file, r);
                print_annotations(file, r);
            end
        catch err
            msg = "";
            if (typeof(err) == ErrorException)
                msg = filename * ": " * err.msg;
            elseif (typeof(err) == SystemError)
                msg = filename * ": " * err.prefix;
            else
                msg = "Unknown $(typeof(err))"
            end
            push!(status.warnings, filename * ": " * msg);
            params.verbose ? println("ERROR: ", msg) : 0 ;
        end
    end
end

function output_report(params::Params, status::Status)
    println("TODO: output report");
    open(joinpath(params.input_dir, REPORT_FILE), "w") do rf
        for warning in status.warnings
            println(rf, "------- warning: ", warning);
        end
        for label in status.labels
            println(rf, "-------- label: ", label);
        end
    end
end

function thats_all(status::Status)
    println("TODO: That's All Folks!");
end

function getstring(row::DataFrameRow, key::Symbol) ::String
    key in keys(row) ? row[key] : "";
end

function getbool(row::DataFrameRow, key::Symbol) ::Bool
    key in keys(row) ? row[key] : False;
end

function getvector(row::DataFrameRow, key::Symbol) ::Vector{Any}
    key in keys(row) ? row[key] :  [nothing];
end

function get_file_extension(filename)
    return filename[findlast(isequal('.'),filename):end];
end

function check_for_unknown_keys(params::Params, status::Status, keyvec::Vector{Symbol})
    # TODO scream if there's a field name I don't recognize
    # TODO check these are all processed
    known_keys = [
                  :annotations,
                  :attachments,
                  :color,
                  :isArchived,
                  :isTrashed,
                  :isPinned,
                  :labels,
                  :listContent,
                  :sharees,
                  :textContent,
                  :title,
                  :userEditedTimestampUsec,
                 ];

    for key in keyvec;
        if !(key in known_keys)
            warning = "unknown key: " * String(key);
            push!(status.warnings, warning);
        end
    end
end

function print_metadata(file::IO,row::DataFrameRow, ymddate::String)

    title = getstring(row, :title);
    println(file, "---");
    println(file, "title: ", title);
    println(file, "date: ", ymddate);
    if getbool(row, :isPinned)
        println(file, "pinned: true");
    end
    if getbool(row, :isArchived)
        println(file, "archived: true");
    end
    if getbool(row, :isTrashed)
        println(file, "trashed: true");
    end
    # TODO: check for Obsidian-recognized meta-tags
    println(file, "---");
    println(file, "");
end

function print_attachments(file::IO, row::DataFrameRow)
    attachments = getvector(row, :attachments);
    if attachments == [nothing] || attachments == []
        return
    end

    # Each attachment has (filePath, mimetype) - possible multiple attachments - in
    # addition to textContent
    # NOTE: VLC can play the audio/amr-wb ".amr" and audio/3gp ".3gp" formats.
    # FUTURE: support output of <audio.../> and <video.../> tags for inline players
    # BUG: Takeout mangles filenames - the file extension shown in JSON input is the last segment of the mime-type but the media files have 3-char extensions - hence we need to remap:
    # TODO: confirm no other extensions are mis-mapped.

    extension_map = Dict(
                         "amr-wb" => "awb",
                         "3gp"    => "3gp",
                         "jpeg"   => "jpg"
                        );

    for attachment in attachments
        file_path = get(attachment, "filePath", "");
        mimetype = attachment[:mimetype];
        # Get the final segment of the mimetype, which needs to be remapped:
        buggy_extension = rsplit(mimetype, '/', limit=2)[end];
        @bp
        patched_extension = get(extension_map, buggy_extension, "");
        if (patched_extension != "")
            file_path = replace(file_path, "." * buggy_extension => "." * patched_extension);
            alt_text = file_path;
            uri = joinpath(MEDIA_SUBDIR, file_path);
            # TODO: test with "![[assetfile]] instead - maybe with filename as alt text?
            # See ![[file|file]] syntax - will this set alt text?
            println(file, "![$alt_text]($uri)");
        end
    end
end

function print_annotations(file::IO, row::DataFrameRow)
    annotations = getvector(row, :annotations);
    if annotations == [nothing] || annotations == []
        return
    end

    # Output a horizontal break
    println(file);
    println(file, "--------");
    println(file);

    for annotation in annotations
        source = annotation[:source];
        if (source == "WEBLINK")
            title = annotation[:title];
            description = annotation[:description];
            url = annotation[:url];
            println(file, "- [$title]($url \"$description\")");
        end
    end
end

function print_text_content(file::IO, row::DataFrameRow)
    text_content = getstring(row, :textContent);
    if (length(text_content) > 0)
        println(file, text_content);
    end
end

function print_list_content(file::IO, row::DataFrameRow)
    # Render as:
    # - [ ] Monday
    # - [x] Tuesday"

    list_content = getvector(row, :listContent);
    if list_content == [nothing]
        return
    end
    for list_item in list_content
        text = list_item[:text];
        flag = (list_item[:isChecked]) ? "[x]" : "[ ]";
        println(file, "- ", flag, " ", text);
    end
end

end # module

if abspath(PROGRAM_FILE) == @__FILE__
    ObsidifyKeep.main()
end

max_stars_repo_name: thomasn/obsidify-keep

max_issues_repo_name: thomasn/obsidify-keep

max_forks_repo_name: thomasn/obsidify-keep


text:
abstract type BandwidthSelector end

bandwidth(h::Number, kernel, rddata) = h
_string(::Number) = ""

struct ImbensKalyanaraman <: BandwidthSelector end

_string(::ImbensKalyanaraman) = "Imbens Kalyanaraman bandwidth"

function kernel_constant(::ImbensKalyanaraman, kernel)
    kernel = EquivalentKernel(kernel)
    νs = OffsetVector([kernel_moment(kernel, Val(j)) for j = 0:3], 0:3)
    πs = OffsetVector([squared_kernel_moment(kernel, Val(j)) for j = 0:3], 0:3)
    C1 = 1 / 4 * abs2((abs2(νs[2]) - νs[1] * νs[3]) / (νs[2] * νs[0] - abs2(νs[1])))
    C2_num = abs2(νs[2]) * πs[0] - 2 * νs[1] * νs[2] * πs[1] + abs2(νs[1]) * πs[2]
    C2_denom = abs2(νs[2] * νs[0] - abs2(νs[1]))
    C2 = C2_num / C2_denom
    (C2 / (4 * C1))^(1 / 5)
    #(πs[0]/abs2(νs[2]))^(1/5)
end

function bandwidth(ik::ImbensKalyanaraman, kernel::SupportedKernels, rddata::RDData)
    Z = rddata.ZsR.Zs
    Y = rddata.Ys

    rdd_df = DataFrame(Z = Z, Y = Y)

    N = length(Z)
    left_idx = findall(Z .< 0)
    right_idx = findall(Z .>= 0)
    N_left = length(left_idx)
    N_right = length(right_idx)

    # Step 1: Density and conditional variance at 0
    h₁ = 1.84 * std(Z) * N^(-1 / 5)

    h₁_left_idx = findall(-h₁ .<= Z .< 0)
    h₁_right_idx = findall(+h₁ .>= Z .>= 0)

    N_h₁_left = length(h₁_left_idx)
    N_h₁_right = length(h₁_right_idx)

    Ȳ_h₁_left = mean(Y[h₁_left_idx])
    sd_Y_h₁_left = std(Y[h₁_left_idx])
    Ȳ_h₁_right = mean(Y[h₁_right_idx])
    sd_Y_h₁_right = std(Y[h₁_right_idx])

    f̂₀ = (N_h₁_left + N_h₁_right) / 2 / N / h₁

    # Step 2: Estimation of second derivatives
    global_cubic_lm = lm(@formula(Y ~ 1 + (Z >= 0) + Z + Z^2 + Z^3), rdd_df)

    m̂₀_triple_prime = 6 * coef(global_cubic_lm)[5]

    h₂_left = 3.56 * (sd_Y_h₁_left^2 / f̂₀ / m̂₀_triple_prime^2)^(1 / 7) * N_left^(-1 / 7)
    h₂_right =
        3.56 * (sd_Y_h₁_right^2 / f̂₀ / m̂₀_triple_prime^2)^(1 / 7) * N_right^(-1 / 7)

    h₂_left_idx = findall(-h₂_left .<= Z .< 0)
    h₂_right_idx = findall(+h₂_right .>= Z .>= 0)

    N_h₂_left = length(h₂_left_idx)
    N_h₂_right = length(h₂_right_idx)

    quadratic_fit_left = lm(@formula(Y ~ 1 + Z + Z^2), rdd_df[h₂_left_idx, :])
    m̂₀_double_prime_left = 2 * last(coef(quadratic_fit_left))

    quadratic_fit_right = lm(@formula(Y ~ 1 + Z + Z^2), rdd_df[h₂_right_idx, :])
    m̂₀_double_prime_right = 2 * last(coef(quadratic_fit_right))

    # Step 3: Calculation of regularization terms

    r̂_left = 2160 * sd_Y_h₁_left^2 / N_h₂_left / h₂_left^4
    r̂_right = 2160 * sd_Y_h₁_right^2 / N_h₂_right / h₂_right^4

    Ckernel = kernel_constant(ik, kernel)# 3.4375

    regularized_squared_diff =
        abs2(m̂₀_double_prime_right - m̂₀_double_prime_left) + r̂_left + r̂_right
    ĥ_IK =
        Ckernel *
        ((sd_Y_h₁_left^2 + sd_Y_h₁_right^2) / f̂₀ / regularized_squared_diff)^(1 / 5) *
        N^(-1 / 5)
    ĥ_IK
end

ext: jl


text:
# Should split out editor.julia.methods
handle("editor.julia.doc") do editor, data
  code = data["code"]
  c = cursor(data["cursor"]["line"], data["cursor"]["col"])
  mod = Jewel.getthing(data["module"])

  if get(data, "type", nothing) == "methods"
    meths = Jewel.methodsorwith(code, c, mod)
    meths != nothing &&
      raise(editor, "editor.julia.doc", @d(:doc => stringmime("text/html", meths),
                                           :loc => @d(:line => c.line-1),
                                           :html => true))
  else
    help = Jewel.doc(code, c, mod)
    help != nothing &&
      raise(editor, "editor.julia.doc", @d(:doc => help,
                                           :loc => @d(:line => c.line-1)))
  end
  notify_done("")
end


text:
# Modifications to the RLBase functions to work with different GMPPI algorithms
function (env::MountainCarEnv{<:ClosedInterval})(a::Vector)
    length(a) == 1 || error("Only implented for 1 step")
    env(a[1])
end
function (env::MountainCarEnv{<:Base.OneTo{Int}})(a::Vector)
    length(a) == 1 || error("Only implented for 1 step")
    env(a[1])
end

# Modified MountainCar reward function
function RLBase.reward(env::MountainCarEnv{A,T}) where {A,T}
    rew = 0.0

    if env.state[1] >= env.params.goal_pos &&
        env.state[2] >= env.params.goal_velocity
        rew += 100000
    end

    rew += abs(env.state[2])
    rew += env.done ? 0.0 : -1.0

    return rew
end

"""
simulate_mountaincar(; kwargs...)
    Simulate the mountaincar envrionment
kwargs:
 - num_trials = 1,                                   # Number of trials
 - num_steps = 200,                                  # Max number of steps per scenario
 - policy_type = :cemppi,                            # Type of policy (see `get_policy` for options)
 - num_samples = 20,                                 # Number of initial samples for the policy
 - horizon = 15,                                     # Time step horizon
 - λ = 0.1,                                          # Inverve temperature settings for IT weighting
 - α = 1.0,                                          # Control cost parameter
 - U₀ = [0.0],                                       # Default initial contorl
 - cov_mat = [1.5]                                   # Control covariance matrix
 - ais_its = 5,                                      # Number of AIS algo iterations
 - λ_ais = 0.1,                                      # Inverse temperature for AIS algo (if applicable)
 - ce_elite_threshold = 0.8,                         # CE elite threshold (if applicable)
 - ce_Σ_est = :mle,                                  # CE Σ estimation methods (if applicable)
 - cma_σ = 0.75,                                     # CMA step factor (if applicable)
 - cma_elite_threshold = 0.8,                        # CMA elite threshold (if applicable)
 - seed = Int(rand(1:10e10)),                        # Seed algorithm and envrionment (incremtented by trial number)
 - log_runs = true,                                  # Output results at each of each trial
 - plot_steps = false,                               # Plot each step (NOT RECOMMENDED FOR LARGE RUNS)
 - pol_log = false,                                  # Turn on policy logger (slows down the process)
 - save_gif = false,                                 # Save gif
"""
function simulate_mountaincar(;
    num_trials = 1,
    num_steps = 200,
    policy_type = :cemppi,
    num_samples = 20,
    horizon = 15,
    λ = 0.1,
    α = 1.0,
    U₀ = [0.0],
    cov_mat = [1.5],
    ais_its = 5,
    λ_ais = 0.1,
    ce_elite_threshold = 0.8,
    ce_Σ_est = :mle,
    cma_σ = 0.75,
    cma_elite_threshold = 0.8,
    seed = Int(rand(1:10e10)),
    log_runs = true,
    plot_steps = false,
    pol_log = false,
    save_gif = false,
)

    sim_type = "MountainCar"

    @printf("\n")
    @printf("%-30s%s\n", "Sim Type:", sim_type)
    @printf("%-30s%d\n", "Num Trails:", num_trials)
    @printf("%-30s%d\n", "Num Steps:", num_steps)
    @printf("%-30s%s\n","Policy Type:", policy_type)
    @printf("%-30s%d\n", "Num samples", num_samples)
    @printf("%-30s%d\n", "Horizon", horizon)
    @printf("%-30s%.2f\n", "λ (inverse temp):", λ)
    @printf("%-30s%.2f\n", "α (control cost param):", α)
    if policy_type != :mppi && policy_type != :gmppi
        @printf("%-30s%d\n", "# AIS Iterations:", ais_its)
        if policy_type ∈ [:μΣaismppi, :μaismppi, :pmcmppi]
            @printf("%-30s%.2f\n", "λ_ais (ais inverse temp):", λ_ais)
        elseif policy_type == :cemppi
            @printf("%-30s%.2f\n", "CE Elite Threshold:", ce_elite_threshold)
            @printf("%-30s%s\n", "CE Σ Est Method:", ce_Σ_est)
        elseif policy_type == :cmamppi
            @printf("%-30s%.2f\n", "CMA Step Factor (σ):", cma_σ)
            @printf("%-30s%.2f\n", "CMA Elite Perc Thres:", cma_elite_threshold)
        end
    end
    @printf("%-30s[%.4f, ..., %.4f]\n", "U₀", U₀[1], U₀[end])
    @printf("%-30s[%.4f]\n", "Σ", cov_mat[1,1])
    @printf("%-30s%d\n", "Seed:", seed)
    @printf("\n")

    gif_name = "$sim_type-$policy_type-$num_samples-$horizon-$λ-$α-"
    if policy_type != :mppi && policy_type != :gmppi
        gif_name = gif_name * "$ais_its-"
    end
    if policy_type == :cemppi
        gif_name = gif_name * "$ce_elite_threshold-"
        gif_name = gif_name * "$ce_Σ_est-"
    elseif policy_type ∈ [:μΣaismppi, :μaismppi, :pmcmppi]
        gif_name = gif_name * "$λ_ais-"
    elseif policy_type == :cmamppi
        gif_name = gif_name * "$cma_σ-"
        gif_name = gif_name * "$cma_elite_threshold-"
    end
    gif_name = gif_name * "$num_trials.gif"
    anim = Animation()

    rews = zeros(Float64, num_trials)
    steps = zeros(Float64, num_trials)
    rews_per_step = zeros(Float64, num_trials)
    exec_times = zeros(Float64, num_trials)

    @printf("Trial    #: %12s : %7s: %12s", "Reward", "Steps", "Reward/Step")
    @printf(" : %7s", "Ex Time")
    @printf("\n")

    for k ∈ 1:num_trials
        env = MountainCarEnv(continuous=true, rng=MersenneTwister())
        pol = get_policy(
            policy_type,
            env,num_samples, horizon, λ, α, U₀, cov_mat, pol_log,
            ais_its,
            λ_ais,
            ce_elite_threshold, ce_Σ_est,
            cma_σ, cma_elite_threshold,
        )

        seed!(env, seed + k)
        seed!(pol, seed + k)

        # Start timer
        time_start = Dates.now()

        rew, cnt = 0, 0
        # Main simulation loop
        while !env.done && cnt <= num_steps
            # Get action from policy
            act = pol(env)
            # Apply action to envrionment
            env(act)
            cnt += 1
            # Ger reward at the step
            step_rew = reward(env)
            rew += step_rew

            # Plot or collect the plot for the animation
            if plot_steps || save_gif
                p = plot(env)
                if save_gif frame(anim) end
                if plot_steps display(p) end
            end
        end

        # Stop timer
        time_end = Dates.now()
        seconds_ran = Dates.value(time_end - time_start) / 1000

        rews[k] = rew
        steps[k] = cnt-1
        rews_per_step[k] = rews[k]/steps[k]
        exec_times[k] = seconds_ran

        if log_runs
            @printf("Trial %4d: %12.2f : %7d: %12.2f", k, rew, cnt-1, rew/(cnt-1))
            @printf(" : %7.2f", seconds_ran)
            @printf("\n")
        end
    end

    # Output summary results
    @printf("-----------------------------------\n")
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "AVE", mean(rews), mean(steps), mean(rews_per_step))
    @printf(" : %7.2f\n", mean(exec_times))
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "STD", std(rews), std(steps), std(rews_per_step))
    @printf(" : %7.2f\n", std(exec_times))
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "MED",
        quantile_ci(rews)[2], quantile_ci(steps)[2], quantile_ci(rews_per_step)[2])
    @printf(" : %7.2f\n", quantile_ci(exec_times)[2])
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "L95",
        quantile_ci(rews)[1], quantile_ci(steps)[1], quantile_ci(rews_per_step)[1])
    @printf(" : %7.2f\n", quantile_ci(exec_times)[1])
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "U95",
        quantile_ci(rews)[3], quantile_ci(steps)[3], quantile_ci(rews_per_step)[3])
    @printf(" : %7.2f\n", quantile_ci(exec_times)[3])
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "MIN",
        minimum(rews), minimum(steps), minimum(rews_per_step))
    @printf(" : %7.2f\n", minimum(exec_times))
    @printf("Trials %3s: %12.2f : %7.2f: %12.2f", "MAX", maximum(rews), maximum(steps), maximum(rews_per_step))
    @printf(" : %7.2f\n", maximum(exec_times))

    if save_gif
        println("Saving gif...$gif_name")
        gif(anim, gif_name, fps=10)
    end
end

max_stars_repo_name: sisl/MPOPIS

max_issues_repo_name: sisl/MPOPIS

max_forks_repo_name: sisl/MPOPIS


text:
######### CmdStan batch program example  ###########

using CmdStan, Statistics, Test

ProjDir = dirname(@__FILE__)
cd(ProjDir) do

  dyes ="
  data {
    int BATCHES;
    int SAMPLES;
    real y[BATCHES, SAMPLES];
    // vector[SAMPLES] y[BATCHES];
  }

  parameters {
    real<lower=0> tau_between;
    real<lower=0> tau_within;
    real theta;
    real mu[BATCHES];
  }

  transformed parameters {
    real sigma_between;
    real sigma_within;
    sigma_between <- 1/sqrt(tau_between);
    sigma_within <- 1/sqrt(tau_within);
  }

  model {
    theta ~ normal(0.0, 1E5);
    tau_between ~ gamma(.001, .001);
    tau_within ~ gamma(.001, .001);

    mu ~ normal(theta, sigma_between);
    for (n in 1:BATCHES)
      y[n] ~ normal(mu[n], sigma_within);
  }

  generated quantities {
    real sigmasq_between;
    real sigmasq_within;

    sigmasq_between <- 1 / tau_between;
    sigmasq_within <- 1 / tau_within;
  }
  "

  dyesdata = Dict("BATCHES" => 6,
      "SAMPLES" => 5,
      "y" => reshape([
        [1545, 1540, 1595, 1445, 1595];
        [1520, 1440, 1555, 1550, 1440];
        [1630, 1455, 1440, 1490, 1605];
        [1595, 1515, 1450, 1520, 1560];
        [1510, 1465, 1635, 1480, 1580];
        [1495, 1560, 1545, 1625, 1445]
      ], 6, 5)
    )

  global stanmodel, rc, sim, cnames

  stanmodel = Stanmodel(name="dyes", model=dyes);

  @time rc, sim, cnames = stan(stanmodel, dyesdata, ProjDir,
    CmdStanDir=CMDSTAN_HOME)

  if rc == 0
    pi = filter(p -> length(p) > 2 && p[end-1:end] == "__", cnames)
    p = filter(p -> !(p in  pi), cnames)

    chns = set_section(sim,
      Dict(
        :parameters => ["theta",
          "tau_between", "tau_within",
          "sigma_between", "sigma_within",
          "sigmasq_between", "sigmasq_within"],
        :mu => ["mu.$i" for i in 1:6],
        :internals => pi
      )
    )
    describe(chns)
    describe(chns, sections=[:mu])
    #=
    println()
    println("Test round.(mean(theta)/100.0, digits=0) ≈ 15.0")
    @test round.(mean(sim[:,10,:])/100.0, digits=0) ≈ 15.0
    =#
  end
end # cd


text:
export paramscan

"""
    data_collecter_aggregate(model::ABM, field_aggregator::Dict; step=1)

`field_aggregator` is a dictionary whose keys are field names of agents (they should be symbols) and whose values are aggregator functions to be applied to those fields. For example, if your agents have a field called `wealth`, and you want to calculate mean and median population wealth, your `field_aggregator` dict will be `Dict(:wealth => [mean, median])`.

If an agent field returns an array instead of a single number, the mean of that array will be calculated before the aggregator functions are applied to them.

To apply a function to the list of agents, use `:agent` as a dictionary key.

To apply a function to the model object, use `:model` as a dictionary key.

Returns two arrays: the first one is the values of applying aggregator functions to the fields, and the second one is a header column for the first array.
"""
function data_collecter_aggregate(model::ABM, field_aggregator::Dict; step=1)
  ncols = 1
  colnames = ["step"]
  for (k,v) in field_aggregator
    ncols += length(v)
    for vv in v
      push!(colnames, join([vv,"(", string(k), ")"], ""))
    end
  end
  output = Array{Any}(undef, ncols)
  output[1] = step
  agent_ids = keys(model.agents)
  counter = 2
  rand_agent_id = 0
  for aa in agent_ids
    rand_agent_id = aa
    break
  end
  for (fn, aggs) in field_aggregator
    if fn == :agent
      temparray = values(model.agents)
    elseif fn == :model
      temparray = model
    elseif typeof(getproperty(model[rand_agent_id], fn)) <: AbstractArray
      temparray = [mean(getproperty(model[i], fn)) for i in agent_ids]
    else
      temparray = [getproperty(model[i], fn) for i in agent_ids]
    end
    for agg in aggs
      output[counter] = agg(temparray)
      counter += 1
    end
  end
  return output, colnames
end

"""
    data_collecter_raw( model::ABM, properties::Array{Symbol})

Collects agent properties (fields of the agent object) into a dataframe.

If  an agent field returns an array, the mean of those arrays will be recorded.

"""
function data_collecter_raw(model::ABM, properties::Array{Symbol}; step=1)
  dd = DataFrame()
  agent_ids = sort(collect(keys(model.agents)))
  rand_agent_id = 0
  for aa in agent_ids
    rand_agent_id = aa
    break
  end

  agentslen = nagents(model)
  dd[!, :id] = agent_ids
  for fn in properties
    if typeof(getproperty(model.agents[rand_agent_id], fn)) <: AbstractArray
      temparray = [mean(getproperty(model.agents[i], fn)) for i in agent_ids]
    else
      temparray = [getproperty(model.agents[i], fn) for i in agent_ids]
    end
    dd[!, fn] = temparray
  end
  dd[!, :step] = [step for i in 1:size(dd, 1)]
  return dd
end

"""
    data_collector(model::ABM, field_aggregator::Dict, step::Integer [, df::DataFrame])

Used in the `step!` function.

Returns a DataFrame of collected data. If `df` is supplied, appends to collected data to it.
"""
function data_collector(model::ABM, field_aggregator::Dict, step::Integer)
  d, colnames = data_collecter_aggregate(model, field_aggregator, step=step)
  dict = Dict(Symbol(colnames[i]) => d[i] for i in 1:length(d))
  df = DataFrame(dict)
  return df
end

function data_collector(model::ABM, field_aggregator::Dict, step::Integer, df::DataFrame)
  d, colnames = data_collecter_aggregate(model, field_aggregator, step=step)
  dict = Dict(Symbol(colnames[i]) => d[i] for i in 1:length(d))
  push!(df, dict)
  return df
end

"""
    data_collector(model::ABM, properties::Array{Symbol}, step::Integer [, df::DataFrame])

Used in the `step!` function.

Returns a DataFrame of collected data. If `df` is supplied, appends to collected data to it.
"""
function data_collector(model::ABM, properties::Array{Symbol}, step::Integer)
  df = data_collecter_raw(model, properties, step=step)
  return df
end

function data_collector(model::ABM, properties::Array{Symbol}, step::Integer, df::DataFrame)
  d = data_collecter_raw(model, properties, step=step)
  df = vcat(df, d) #join(df, d, on=:id, kind=:outer)
  return df
end

function _step!(model, agent_step!, model_step!, properties, when, n::F, step0, df) where F<:Function
  ss = 1
  while !n(model)
    step!(model, agent_step!, model_step!, 1)
    if ss in when
      df = data_collector(model, properties, ss, df)
    end
    ss += 1
  end
  df
end

function _step!(model, agent_step!, model_step!, properties, when, n::Int, step0, df)
  for ss in 1:n
    step!(model, agent_step!, model_step!, 1)
    if ss in when
      df = data_collector(model, properties, ss, df)
    end
  end
  df
end

function _step!(model, agent_step!, model_step!, properties, when, n, step0)
  if step0
    df = data_collector(model, properties, 0)
  else
    df = data_collector(model, properties, 0)
    colnames = names(df)
    coltypes = [eltype(df[!, i]) for i in colnames]
    df = DataFrame(coltypes, colnames)
  end
  df = _step!(model, agent_step!, model_step!, properties, when, n, step0, df)
  return df
end

function series_replicates(model, agent_step!, model_step!, properties, when, n, replicates, step0)

  dataall = _step!(deepcopy(model), agent_step!, model_step!, properties, when, n, step0)
  dataall[!, :replicate] = [1 for i in 1:size(dataall, 1)]

  for rep in 2:replicates
    data = _step!(deepcopy(model), agent_step!, model_step!, properties, when, n, step0)
    data[!, :replicate] = [rep for i in 1:size(data, 1)]

    dataall = vcat(dataall, data)
  end
  return dataall
end

"""
    paramscan(parameters, initialize; kwargs...)

Run the model with all the parameter value combinations given in `parameters`,
while initializing the model with `initialize`.
This function uses `DrWatson`'s [`dict_list`](https://juliadynamics.github.io/DrWatson.jl/dev/run&list/#DrWatson.dict_list)
internally. This means that every entry of `parameters` that is a `Vector`,
contains many parameters and thus is scanned. All other entries of
`parameters` that are not `Vector`s are not expanded in the scan.

`initialize` is a function that creates an ABM. It should accept keyword arguments.

### Keywords
All the following keywords are propagated into [`step!`](@ref):
`agent_step!, properties, n, when = 1:n, model_step! = dummystep`,
`step0::Bool = true`, `parallel::Bool = false`, `replicates::Int = 0`.

The following keywords modify the `paramscan` function:

`include_constants::Bool=false` determines whether constant parameters should be
included in the output `DataFrame`.

`progress::Bool = true` whether to show the progress of simulations.
"""
function paramscan(parameters::Dict, initialize;
  agent_step!, properties, n,
  when = 1:n,
  model_step! = dummystep,
  include_constants::Bool = false,
  replicates::Int = 0,
  step0::Bool = true,
  progress::Bool = true,
  parallel::Bool = false
  )

  params = dict_list(parameters)
  if include_constants
    changing_params = keys(parameters)
  else
    changing_params = [k for (k, v) in parameters if typeof(v)<:Vector]
  end

  alldata = DataFrame()
  combs = dict_list(parameters)
  ncombs = length(combs)
  counter = 0
  for d in combs
    model = initialize(; d...)
    data = step!(model, agent_step!, model_step!, n, properties, when=when, replicates=replicates, step0=step0, parallel=parallel)
    addparams!(data, d, changing_params)
    alldata = vcat(data, alldata)
    if progress
      # show progress
      counter += 1
      print("\u1b[1G")
      percent = round(counter*100/ncombs, digits=2)
      print("Progress: $percent%")
      print("\u1b[K")
    end
  end
  println()
  return alldata
end

"""
Adds new columns for each parameter in `changing_params`.
"""
function addparams!(df::AbstractDataFrame, params::Dict, changing_params)
  nrows = size(df, 1)
  for c in changing_params
    df[!, c] = [params[c] for i in 1:nrows]
  end
end

# This function is taken from DrWatson:
function dict_list(c::Dict)
    iterable_fields = filter(k -> typeof(c[k]) <: Vector, keys(c))
    non_iterables = setdiff(keys(c), iterable_fields)

    iterable_dict = Dict(iterable_fields .=> getindex.(Ref(c), iterable_fields))
    non_iterable_dict = Dict(non_iterables .=> getindex.(Ref(c), non_iterables))

    vec(
        map(Iterators.product(values(iterable_dict)...)) do vals
            dd = Dict(keys(iterable_dict) .=> vals)
            if isempty(non_iterable_dict)
                dd
            elseif isempty(iterable_dict)
                non_iterable_dict
            else
                merge(non_iterable_dict, dd)
            end
        end
    )
end


text:
"""

### List of Continents

* Africa
* Asia
* Australia
* Europe
* North America
* South America

### List of SREX regions

| Short Name | Long Name |
| --- | --- |
|ALA|Alaska/N.W. Canada
|AMZ|Amazon
|CAM|Central America/Mexico
|CAR|small islands regions Caribbean
|CAS|Central Asia
|CEU|Central Europe
|CGI|Canada/Greenland/Iceland
|CNA|Central North America
|EAF|East Africa
|EAS|East Asia
|ENA|East North America
|MED|South Europe/Mediterranean
|NAS|North Asia
|NAU|North Australia
|NEB|North-East Brazil
|NEU|North Europe
|SAF|Southern Africa
|SAH|Sahara
|SAS|South Asia
|SAU|South Australia/New Zealand
|SEA|Southeast Asia
|SSA|Southeastern South America
|TIB|Tibetan Plateau
|WAF|West Africa
|WAS|West Asia
|WNA|West North America
|WSA|West Coast South America
|ANT|Antarctica
|ARC|Arctic
|NTP|Pacific Islands region
|STP|Southern Topical Pacific
|ETP|Pacific Islands region
|WIO|West Indian Ocean

### List of countries

| Short Name | Long Name |
| --- | --- |
|ABW|Aruba
|AFG|Afghanistan
|AGO|Angola
|AIA|Anguilla
|ALB|Albania
|ALA|Aland
|AND|Andorra
|ARE|United Arab Emirates
|ARG|Argentina
|ARM|Armenia
|ASM|American Samoa
|ATA|Antarctica
|Ashm|Ashmore and Cartier Is.
|ATF|Fr. S. and Antarctic Lands
|ATG|Antigua and Barb.
|AUS|Australia
|AUT|Austria
|AZE|Azerbaijan
|BDI|Burundi
|BEL|Belgium
|BEN|Benin
|BFA|Burkina Faso
|BGD|Bangladesh
|BGR|Bulgaria
|BHR|Bahrain
|BHS|Bahamas
|BIH|Bosnia and Herz.
|BLM|St. Barthelemy
|BLR|Belarus
|BLZ|Belize
|BMU|Bermuda
|BOL|Bolivia
|BRA|Brazil
|BRB|Barbados
|BRN|Brunei
|BTN|Bhutan
|BWA|Botswana
|CAF|Central African Rep.
|CAN|Canada
|CHE|Switzerland
|CHL|Chile
|CHN|China
|CIV|Ivory Coast
|CLP|Clipperton I.
|CMR|Cameroon
|CNM|
|COD|Congo (Kinshasa)
|COG|Congo (Brazzaville)
|COK|Cook Is.
|COL|Colombia
|COM|Comoros
|CPV|Cape Verde
|CRI|Costa Rica
|CSI|Coral Sea Is.
|CUB|Cuba
|CUW|Curacao
|CYM|Cayman Is.
|CYN|N. Cyprus
|CYP|Cyprus
|CZE|Czech Rep.
|DEU|Germany
|DJI|Djibouti
|DMA|Dominica
|DNK|Denmark
|DOM|Dominican Rep.
|DZA|Algeria
|ECU|Ecuador
|EGY|Egypt
|ERI|Eritrea
|ESB|Dhekelia
|ESP|Spain
|EST|Estonia
|ETH|Ethiopia
|FIN|Finland
|FJI|Fiji
|FLK|Falkland Is.
|FRA|France
|FRO|Faroe Is.
|FSM|Micronesia
|GAB|Gabon
|Gaza|Gaza
|GBR|United Kingdom
|GEO|Georgia
|GGY|Guernsey
|GHA|Ghana
|GIB|Gibraltar
|GIN|Guinea
|GMB|Gambia
|GNB|Guinea Bissau
|GNQ|Eq. Guinea
|GRC|Greece
|GRD|Grenada
|GRL|Greenland
|GTM|Guatemala
|GUM|Guam
|GUY|Guyana
|HKG|Hong Kong
|HMD|Heard I. and McDonald Is.
|HND|Honduras
|HRV|Croatia
|HTI|Haiti
|HUN|Hungary
|IDN|Indonesia
|IMN|Isle of Man
|IND|India
|IOA|Indian Ocean Ter.
|IOT|Br. Indian Ocean Ter.
|IRL|Ireland
|IRN|Iran
|IRQ|Iraq
|ISL|Iceland
|ISR|Israel
|ITA|Italy
|JAM|Jamaica
|JEY|Jersey
|JOR|Jordan
|JPN|Japan
|KAB|Baykonur
|KAS|
|KAZ|Kazakhstan
|KEN|Kenya
|KGZ|Kyrgyzstan
|KHM|Cambodia
|KIR|Kiribati
|KNA|St. Kitts and Nevis
|KNM|
|KOR|S. Korea
|KOS|Kosovo
|KWT|Kuwait
|LAO|Laos
|LBN|Lebanon
|LBR|Liberia
|LBY|Libya
|LCA|Saint Lucia
|LIE|Liechtenstein
|LKA|Sri Lanka
|LSO|Lesotho
|LTU|Lithuania
|LUX|Luxembourg
|LVA|Latvia
|MAC|Macau
|MAF|St. Martin
|MAR|Morocco
|MCO|Monaco
|MDA|Moldova
|MDG|Madagascar
|MDV|Maldives
|MEX|Mexico
|MHL|Marshall Is.
|MKD|Macedonia
|MLI|Mali
|MLT|Malta
|MMR|Myanmar
|MNE|Montenegro
|MNG|Mongolia
|MNP|N. Mariana Is.
|MOZ|Mozambique
|MRT|Mauritania
|MSR|Montserrat
|MUS|Mauritius
|MWI|Malawi
|MYS|Malaysia
|NAM|Namibia
|NCL|New Caledonia
|NER|Niger
|NFK|Norfolk Island
|NGA|Nigeria
|NIC|Nicaragua
|NIU|Niue
|NLD|Netherlands
|NOR|Norway
|NPL|Nepal
|NRU|Nauru
|NZL|New Zealand
|OMN|Oman
|PAK|Pakistan
|PAN|Panama
|PCN|Pitcairn Is.
|PER|Peru
|PHL|Philippines
|PLW|Palau
|PNG|Papua New Guinea
|POL|Poland
|PRI|Puerto Rico
|PRK|N. Korea
|PRT|Portugal
|PRY|Paraguay
|PYF|Fr. Polynesia
|QAT|Qatar
|ROU|Romania
|RUS|Russia
|RWA|Rwanda
|SAH|W. Sahara
|SAU|Saudi Arabia
|SDN|Sudan
|SDS|S. Sudan
|SEN|Senegal
|SGP|Singapore
|SGS|S. Geo. and S. Sandw. Is.
|SHN|Saint Helena
|SLB|Solomon Is.
|SLE|Sierra Leone
|SLV|El Salvador
|SMR|San Marino
|SOL|Somaliland
|SOM|Somalia
|SPM|St. Pierre and Miquelon
|SRB|Serbia
|STP|Sao Tome and Principe
|SUR|Suriname
|SVK|Slovakia
|SVN|Slovenia
|SWE|Sweden
|SWZ|Swaziland
|SXM|Sint Maarten
|SYC|Seychelles
|SYR|Syria
|TCA|Turks and Caicos Is.
|TCD|Chad
|TGO|Togo
|THA|Thailand
|TJK|Tajikistan
|TKM|Turkmenistan
|TLS|East Timor
|TON|Tonga
|TTO|Trinidad and Tobago
|TUN|Tunisia
|TUR|Turkey
|TUV|Tuvalu
|TWN|Taiwan
|TZA|Tanzania
|UGA|Uganda
|UKR|Ukraine
|UMI|U.S. Minor Outlying Is.
|URY|Uruguay
|USA|United States
|USG|Guantanamo Bay USNB
|UZB|Uzbekistan
|VAT|Vatican
|VCT|St. Vin. and Gren.
|VEN|Venezuela
|VGB|British Virgin Is.
|VIR|U.S. Virgin Is.
|VNM|Vietnam
|VUT|Vanuatu
|PSE|West Bank
|WLF|Wallis and Futuna
|WSB|Akrotiri
|WSM|Samoa
|YEM|Yemen
|ZAF|South Africa
|ZMB|Zambia
|ZWE|Zimbabwe
"""
known_regions = Dict{String,NTuple{4,Float64}}(
"World"=>(-180.0,-90.0,179.999999999999,90.0),
"Africa"=>(-17.0,-40.0,51.0,40.0),
"Asia"=>(30.0,5.0,179.5,80.0),
"Australia"=>(110.0,-40.0,155.0,-8.0),
"Europe"=>(-10.0,35.0,33.0,70.0),
"North America"=>(-168.0,10.0,-55.0,80.0),
"South America"=>(-85.0,-60.0,-33.0,15.0),

  "Alaska/N.W. Canada" => (-168.0,60.0,-105.0,72.6),
  "ALA" => (-168.0,60.0,-105.0,72.6),
  "Amazon" => (-79.7,-20.0,-50.0,11.4),
  "AMZ" => (-79.7,-20.0,-50.0,11.4),
  "Central America/Mexico" => (-118.3,-1.2,-68.8,28.6),
  "CAM" => (-118.3,-1.2,-68.8,28.6),
  "small islands regions Caribbean" => (-85.8,11.4,-60.0,25.0),
  "CAR" => (-85.8,11.4,-60.0,25.0),
  "Central Asia" => (60.0,30.0,75.0,50.0),
  "CAS" => (60.0,30.0,75.0,50.0),
  "Central Europe" => (-10.0,45.0,40.0,61.3),
  "CEU" => (-10.0,45.0,40.0,61.3),
  "Canada/Greenland/Iceland" => (-105.0,50.0,-10.0,85.0),
  "CGI" => (-105.0,50.0,-10.0,85.0),
  "Central North America" => (-105.0,28.6,-85.0,50.0),
  "CNA" => (-105.0,28.6,-85.0,50.0),
  "East Africa" => (25.0,-11.4,52.0,15.0),
  "EAF" => (25.0,-11.4,52.0,15.0),
  "East Asia" => (100.0,20.0,145.0,50.0),
  "EAS" => (100.0,20.0,145.0,50.0),
  "East North America" => (-85.0,25.0,-60.0,50.0),
  "ENA" => (-85.0,25.0,-60.0,50.0),
  "South Europe/Mediterranean" => (-10.0,30.0,40.0,45.0),
  "MED" => (-10.0,30.0,40.0,45.0),
  "North Asia" => (40.0,50.0,180.0,70.0),
  "NAS" => (40.0,50.0,180.0,70.0),
  "North Australia" => (110.0,-30.0,155.0,-10.0),
  "NAU" => (110.0,-30.0,155.0,-10.0),
  "North-East Brazil" => (-50.0,-20.0,-34.0,0.0),
  "NEB" => (-50.0,-20.0,-34.0,0.0),
  "North Europe" => (-10.0,48.0,40.0,75.0),
  "NEU" => (-10.0,48.0,40.0,75.0),
  "Southern Africa" => (-10.0,-35.0,52.0,-11.4),
  "SAF" => (-10.0,-35.0,52.0,-11.4),
  "Sahara" => (-20.0,15.0,40.0,30.0),
  "SAH" => (-20.0,15.0,40.0,30.0),
  "South Asia" => (60.0,5.0,100.0,30.0),
  "SAS" => (60.0,5.0,100.0,30.0),
  "South Australia/New Zealand" => (110.0,-50.0,180.0,-30.0),
  "SAU" => (110.0,-50.0,180.0,-30.0),
  "Southeast Asia" => (95.0,-10.0,155.0,20.0),
  "SEA" => (95.0,-10.0,155.0,20.0),
  "Southeastern South America" => (-72.1,-56.7,-39.4,-20.0),
  "SSA" => (-72.1,-56.7,-39.4,-20.0),
  "Tibetan Plateau" => (75.0,30.0,100.0,50.0),
  "TIB" => (75.0,30.0,100.0,50.0),
  "West Africa" => (-20.0,-11.4,25.0,15.0),
  "WAF" => (-20.0,-11.4,25.0,15.0),
  "West Asia" => (40.0,15.0,60.0,50.0),
  "WAS" => (40.0,15.0,60.0,50.0),
  "West North America" => (-130.0,28.6,-105.0,60.0),
  "WNA" => (-130.0,28.6,-105.0,60.0),
  "West Coast South America" => (-82.0,-56.7,-66.4,0.5),
  "WSA" => (-82.0,-56.7,-66.4,0.5),
  "Antarctica" => (-180.0,-90.0,180.0,-50.0),
  "ANT" => (-180.0,-90.0,180.0,-50.0),
  "Arctic" => (-180.0,67.5,180.0,90.0),
  "ARC" => (-180.0,67.5,180.0,90.0),
  "Pacific Islands region" => (155.0,5.0,210.0,25.0),
  "NTP" => (155.0,5.0,210.0,25.0),
  "Southern Topical Pacific" => (155.0,-25.0,230.0,-5.0),
  "STP" => (155.0,-25.0,230.0,-5.0),
  "Pacific Islands region" => (155.0,-5.0,210.0,5.0),
  "ETP" => (155.0,-5.0,210.0,5.0),
  "West Indian Ocean" => (52.0,-25.0,75.0,5.0),
  "WIO" => (52.0,-25.0,75.0,5.0),
  "Aruba" => (-70.06163610918688,12.417537740615657,-69.87609187856879,12.631994934356726),
  "ABW" => (-70.06163610918688,12.417537740615657,-69.87609187856879,12.631994934356726),
  "Afghanistan" => (60.48677779166434,29.386605327043327,74.89230676263242,38.47367340094172),
  "AFG" => (60.48677779166434,29.386605327043327,74.89230676263242,38.47367340094172),
  "Angola" => (11.663659708643195,-18.031404723897893,24.06171431433637,-4.391203714658943),
  "AGO" => (11.663659708643195,-18.031404723897893,24.06171431433637,-4.391203714658943),
  "Anguilla" => (-63.25977738516059,18.168866272138487,-62.97188798016279,18.29009918224598),
  "AIA" => (-63.25977738516059,18.168866272138487,-62.97188798016279,18.29009918224598),
  "Albania" => (19.27203251137547,39.63701324478362,21.03667932123608,42.65481353749786),
  "ALB" => (19.27203251137547,39.63701324478362,21.03667932123608,42.65481353749786),
  "Aland" => (19.513774861192303,59.90445831327351,21.096623982733973,60.480753886297734),
  "ALA" => (19.513774861192303,59.90445831327351,21.096623982733973,60.480753886297734),
  "Andorra" => (1.4064563391808633,42.42867746999369,1.7650907794132706,42.64936167468633),
  "AND" => (1.4064563391808633,42.42867746999369,1.7650907794132706,42.64936167468633),
  "United Arab Emirates" => (51.56954431148705,22.620945943368483,56.384082478713594,26.07479197206466),
  "ARE" => (51.56954431148705,22.620945943368483,56.384082478713594,26.07479197206466),
  "Argentina" => (-73.58803584821413,-55.05209075954144,-53.661551880133686,-21.7869377648081),
  "ARG" => (-73.58803584821413,-55.05209075954144,-53.661551880133686,-21.7869377648081),
  "Armenia" => (43.436293985673615,38.86370127384886,46.60261234547039,41.290452372831155),
  "ARM" => (43.436293985673615,38.86370127384886,46.60261234547039,41.290452372831155),
  "American Samoa" => (-171.0865290944145,-14.532910257659893,-168.15947261668148,-11.051313984903288),
  "ASM" => (-171.0865290944145,-14.532910257659893,-168.15947261668148,-11.051313984903288),
  "Antarctica" => (-179.99900834195893,-89.99982838943765,180.0000000000001,-60.51625335736412),
  "ATA" => (-179.99900834195893,-89.99982838943765,180.0000000000001,-60.51625335736412),
  "Ashmore and Cartier Is." => (123.57434818542748,-12.438516133167951,123.59708581534068,-12.426527201048259),
  "Ashm" => (123.57434818542748,-12.438516133167951,123.59708581534068,-12.426527201048259),
  "Fr. S. and Antarctic Lands" => (39.69373173059432,-49.7216150919308,77.5856824141186,-11.550559997376993),
  "ATF" => (39.69373173059432,-49.7216150919308,77.5856824141186,-11.550559997376993),
  "Antigua and Barb." => (-61.893400838926965,16.98909332279986,-61.6668513593813,17.72749786365263),
  "ATG" => (-61.893400838926965,16.98909332279986,-61.6668513593813,17.72749786365263),
  "Australia" => (112.90724735541767,-54.75040374732733,159.10664961838825,-9.2401067096187),
  "AUS" => (112.90724735541767,-54.75040374732733,159.10664961838825,-9.2401067096187),
  "Austria" => (9.52115482604438,46.37864308737653,17.14833785014025,49.00977447586234),
  "AUT" => (9.52115482604438,46.37864308737653,17.14833785014025,49.00977447586234),
  "Azerbaijan" => (44.77455855264759,38.39264475499907,50.62608768757502,41.89044158925999),
  "AZE" => (44.77455855264759,38.39264475499907,50.62608768757502,41.89044158925999),
  "Burundi" => (28.98689172350504,-4.463344014747037,30.833962443262465,-2.303062438640481),
  "BDI" => (28.98689172350504,-4.463344014747037,30.833962443262465,-2.303062438640481),
  "Belgium" => (2.5221610093376228,49.49510418608672,6.374484007399985,51.49642905588651),
  "BEL" => (2.5221610093376228,49.49510418608672,6.374484007399985,51.49642905588651),
  "Benin" => (0.7598808189068791,6.2136270139292265,3.837419067547785,12.399244283180025),
  "BEN" => (0.7598808189068791,6.2136270139292265,3.837419067547785,12.399244283180025),
  "Burkina Faso" => (-5.5225780841703624,9.391882629902625,2.3901689041002214,15.079907532148098),
  "BFA" => (-5.5225780841703624,9.391882629902625,2.3901689041002214,15.079907532148098),
  "Bangladesh" => (88.02178959210516,20.738476868022204,92.64285119635053,26.6235440065996),
  "BGD" => (88.02178959210516,20.738476868022204,92.64285119635053,26.6235440065996),
  "Bulgaria" => (22.34502323385388,41.23810414691846,28.6040210366196,44.22841257798578),
  "BGR" => (22.34502323385388,41.23810414691846,28.6040210366196,44.22841257798578),
  "Bahrain" => (50.44930261250397,25.789279683832973,50.62102339658526,26.242378642025017),
  "BHR" => (50.44930261250397,25.789279683832973,50.62102339658526,26.242378642025017),
  "Bahamas" => (-79.59433589689905,20.91753570617449,-72.74539994352085,26.928279928236336),
  "BHS" => (-79.59433589689905,20.91753570617449,-72.74539994352085,26.928279928236336),
  "Bosnia and Herz." => (15.715877643504996,42.55932598781679,19.618998574510243,45.284534321161104),
  "BIH" => (15.715877643504996,42.55932598781679,19.618998574510243,45.284534321161104),
  "St. Barthelemy" => (-62.86662308756709,17.881803691223865,-62.790917115027725,17.92888092057575),
  "BLM" => (-62.86662308756709,17.881803691223865,-62.790917115027725,17.92888092057575),
  "Belarus" => (23.16564497903221,51.23516835544345,32.71953210487396,56.1568059287834),
  "BLR" => (23.16564497903221,51.23516835544345,32.71953210487396,56.1568059287834),
  "Belize" => (-89.2365122074907,15.879651998452815,-87.78225988854877,18.490758768771016),
  "BLZ" => (-89.2365122074907,15.879651998452815,-87.78225988854877,18.490758768771016),
  "Bermuda" => (-64.85973140485552,32.264491679493204,-64.67534989419923,32.388463440666754),
  "BMU" => (-64.85973140485552,32.264491679493204,-64.67534989419923,32.388463440666754),
  "Bolivia" => (-69.66649226934697,-22.897257588282088,-57.465660767135546,-9.679821471736773),
  "BOL" => (-69.66649226934697,-22.897257588282088,-57.465660767135546,-9.679821471736773),
  "Brazil" => (-74.01847469085729,-33.742280374903544,-28.876346395269564,5.267224833380098),
  "BRA" => (-74.01847469085729,-33.742280374903544,-28.876346395269564,5.267224833380098),
  "Barbados" => (-59.65348588083208,13.050935771286134,-59.42618709336611,13.344380391880975),
  "BRB" => (-59.65348588083208,13.050935771286134,-59.42618709336611,13.344380391880975),
  "Brunei" => (113.99917036956987,4.0166810103857955,115.36074100812664,5.057056783387992),
  "BRN" => (113.99917036956987,4.0166810103857955,115.36074100812664,5.057056783387992),
  "Bhutan" => (88.73006677303033,26.696149393773027,92.08877648931161,28.320623880955708),
  "BTN" => (88.73006677303033,26.696149393773027,92.08877648931161,28.320623880955708),
  "Botswana" => (19.97834598757629,-26.8917941279432,29.350073689899887,-17.781807556082242),
  "BWA" => (19.97834598757629,-26.8917941279432,29.350073689899887,-17.781807556082242),
  "Central African Rep." => (14.38726607284218,2.2364537557343453,27.44130131039043,11.000828349136981),
  "CAF" => (14.38726607284218,2.2364537557343453,27.44130131039043,11.000828349136981),
  "Canada" => (-141.0055486664508,41.669085591910815,-52.615930948991945,83.11611643539153),
  "CAN" => (-141.0055486664508,41.669085591910815,-52.615930948991945,83.11611643539153),
  "Switzerland" => (5.954809204366512,45.820718486152344,10.466626830623994,47.801166076865826),
  "CHE" => (5.954809204366512,45.820718486152344,10.466626830623994,47.801166076865826),
  "Chile" => (-109.45292679875851,-55.91488047270987,-66.42010127799821,-17.506578877659337),
  "CHL" => (-109.45292679875851,-55.91488047270987,-66.42010127799821,-17.506578877659337),
  "China" => (73.60225630745038,18.16915049297326,134.77257938699165,53.56944447871258),
  "CHN" => (73.60225630745038,18.16915049297326,134.77257938699165,53.56944447871258),
  "Ivory Coast" => (-8.618719848603604,4.343922017943413,-2.50632808413917,10.726478169841158),
  "CIV" => (-8.618719848603604,4.343922017943413,-2.50632808413917,10.726478169841158),
  "Clipperton I." => (-109.23363785891937,10.281544088004622,-109.20978918498741,10.310999654376062),
  "CLP" => (-109.23363785891937,10.281544088004622,-109.20978918498741,10.310999654376062),
  "Cameroon" => (8.505609577987514,1.6545512902707031,16.207723429180533,13.081140645577818),
  "CMR" => (8.505609577987514,1.6545512902707031,16.207723429180533,13.081140645577818),
  "CNM" => (32.585225057049286,34.977762757041035,34.02151981623291,35.19940297432572),
  "Congo (Kinshasa)" => (12.210913126438186,-13.45835052497381,31.280446817782035,5.375280252984908),
  "COD" => (12.210913126438186,-13.45835052497381,31.280446817782035,5.375280252984908),
  "Congo (Brazzaville)" => (11.127568399783854,-5.013439636393684,18.64240686052605,3.708276062612839),
  "COG" => (11.127568399783854,-5.013439636393684,18.64240686052605,3.708276062612839),
  "Cook Is." => (-165.82354284704456,-21.939073181184455,-157.31186600862208,-8.94684295617374),
  "COK" => (-165.82354284704456,-21.939073181184455,-157.31186600862208,-8.94684295617374),
  "Colombia" => (-81.72288814355286,-4.2364844768446375,-66.87506058812926,13.373758442988759),
  "COL" => (-81.72288814355286,-4.2364844768446375,-66.87506058812926,13.373758442988759),
  "Comoros" => (43.21377526836261,-12.380535176394673,44.5295605878369,-11.361372579044016),
  "COM" => (43.21377526836261,-12.380535176394673,44.5295605878369,-11.361372579044016),
  "Cape Verde" => (-25.359713501223922,14.8037745161771,-22.665846924231005,17.19636749299731),
  "CPV" => (-25.359713501223922,14.8037745161771,-22.665846924231005,17.19636749299731),
  "Costa Rica" => (-87.11684830759069,5.514884134892483,-82.56283687402257,11.209937031952862),
  "CRI" => (-87.11684830759069,5.514884134892483,-82.56283687402257,11.209937031952862),
  "Coral Sea Is." => (147.87906620323815,-22.202622985844386,155.80023645027796,-16.132915954197188),
  "CSI" => (147.87906620323815,-22.202622985844386,155.80023645027796,-16.132915954197188),
  "Cuba" => (-84.94884110559153,19.827628072689933,-74.13213700380089,23.265557759817412),
  "CUB" => (-84.94884110559153,19.827628072689933,-74.13213700380089,23.265557759817412),
  "Curacao" => (-69.17094112143059,12.041126607774842,-68.73897782452565,12.391337789238065),
  "CUW" => (-69.17094112143059,12.041126607774842,-68.73897782452565,12.391337789238065),
  "Cayman Is." => (-81.42957271326546,19.26463918047601,-79.73073584065511,19.760526225170324),
  "CYM" => (-81.42957271326546,19.26463918047601,-79.73073584065511,19.760526225170324),
  "N. Cyprus" => (32.60186486811699,35.002722473193174,34.59295780473775,35.69177602161268),
  "CYN" => (32.60186486811699,35.002722473193174,34.59295780473775,35.69177602161268),
  "Cyprus" => (32.272324259056916,34.625019430083015,34.09955122869576,35.18684560143565),
  "CYP" => (32.272324259056916,34.625019430083015,34.09955122869576,35.18684560143565),
  "Czech Rep." => (12.076140991528007,48.55791575109711,18.83743371619022,51.04001230947176),
  "CZE" => (12.076140991528007,48.55791575109711,18.83743371619022,51.04001230947176),
  "Germany" => (5.852489868407986,47.27112091090886,15.022059366567618,55.065063781781205),
  "DEU" => (5.852489868407986,47.27112091090886,15.022059366567618,55.065063781781205),
  "Djibouti" => (41.74911014840501,10.929824930589518,43.41924076346345,12.70775258373854),
  "DJI" => (41.74911014840501,10.929824930589518,43.41924076346345,12.70775258373854),
  "Dominica" => (-61.48825760921375,15.201657206183597,-61.24855647848577,15.633672180830189),
  "DMA" => (-61.48825760921375,15.201657206183597,-61.24855647848577,15.633672180830189),
  "Denmark" => (8.094575235899299,54.568298238859214,15.151870565072414,57.7508946736253),
  "DNK" => (8.094575235899299,54.568298238859214,15.151870565072414,57.7508946736253),
  "Dominican Rep." => (-72.00983760651982,17.545338440134472,-68.32789180559504,19.937492167391397),
  "DOM" => (-72.00983760651982,17.545338440134472,-68.32789180559504,19.937492167391397),
  "Algeria" => (-8.682385213080181,18.9755612188938,11.968860717365317,37.093757629905554),
  "DZA" => (-8.682385213080181,18.9755612188938,11.968860717365317,37.093757629905554),
  "Ecuador" => (-92.01161413599723,-5.011372578883567,-75.2272639570038,1.664369812094705),
  "ECU" => (-92.01161413599723,-5.011372578883567,-75.2272639570038,1.664369812094705),
  "Egypt" => (24.68834273260552,21.99436920194175,36.89958703583261,31.656311747388273),
  "EGY" => (24.68834273260552,21.99436920194175,36.89958703583261,31.656311747388273),
  "Eritrea" => (36.42364709494177,12.360021870927625,43.124323364550094,18.00469024679991),
  "ERI" => (36.42364709494177,12.360021870927625,43.124323364550094,18.00469024679991),
  "Dhekelia" => (33.674099161778656,34.93761016881919,33.92147424335553,35.11899444599584),
  "ESB" => (33.674099161778656,34.93761016881919,33.92147424335553,35.11899444599584),
  "Spain" => (-18.166612508385555,27.641983134451223,4.3519096271352,43.80518259730745),
  "ESP" => (-18.166612508385555,27.641983134451223,4.3519096271352,43.80518259730745),
  "Estonia" => (21.832909790133442,57.515818583424235,28.186475463644484,59.670622457398636),
  "EST" => (21.832909790133442,57.515818583424235,28.186475463644484,59.670622457398636),
  "Ethiopia" => (32.98979984509276,3.40333343540496,47.97916914872718,14.879532165558743),
  "ETH" => (32.98979984509276,3.40333343540496,47.97916914872718,14.879532165558743),
  "Finland" => (20.623164509596165,59.81097564722644,31.569524780263635,70.07531036428914),
  "FIN" => (20.623164509596165,59.81097564722644,31.569524780263635,70.07531036428914),
  "Fiji" => (-179.99978348919961,-21.71128346731254,179.99937951713645,-12.475413106395905),
  "FJI" => (-179.99978348919961,-21.71128346731254,179.99937951713645,-12.475413106395905),
  "Falkland Is." => (-61.31744116268078,-52.34176523863069,-57.7336030683299,-51.02773691830977),
  "FLK" => (-61.31744116268078,-52.34176523863069,-57.7336030683299,-51.02773691830977),
  "France" => (-61.79707596902833,-21.370839131730165,55.85501916556859,51.08716705318797),
  "FRA" => (-61.79707596902833,-21.370839131730165,55.85501916556859,51.08716705318797),
  "Faroe Is." => (-7.644102336009297,61.39382476876811,-6.275762085051156,62.398595479711005),
  "FRO" => (-7.644102336009297,61.39382476876811,-6.275762085051156,62.398595479711005),
  "Micronesia" => (138.0640580654458,5.269705302032534,163.0467708746266,9.588692322242053),
  "FSM" => (138.0640580654458,5.269705302032534,163.0467708746266,9.588692322242053),
  "Gabon" => (8.696088907489099,-3.913403421829102,14.498990519482504,2.3224950158242734),
  "GAB" => (8.696088907489099,-3.913403421829102,14.498990519482504,2.3224950158242734),
  "Gaza" => (34.20042362475698,31.21137766555171,34.5304293209025,31.582879537030834),
  "Gaza" => (34.20042362475698,31.21137766555171,34.5304293209025,31.582879537030834),
  "United Kingdom" => (-13.691355956779432,49.90961619098756,1.7717053630859425,60.84755320288569),
  "GBR" => (-13.691355956779432,49.90961619098756,1.7717053630859425,60.84755320288569),
  "Georgia" => (39.985910278441736,41.04411082000942,46.69480310034879,43.575842590752615),
  "GEO" => (39.985910278441736,41.04411082000942,46.69480310034879,43.575842590752615),
  "Guernsey" => (-2.672932902186858,49.41124867764081,-2.1702245691482176,49.73112579360556),
  "GGY" => (-2.672932902186858,49.41124867764081,-2.1702245691482176,49.73112579360556),
  "Ghana" => (-3.2625093184995535,4.736921285009586,1.1879683832051455,11.162937316965426),
  "GHA" => (-3.2625093184995535,4.736921285009586,1.1879683832051455,11.162937316965426),
  "Gibraltar" => (-5.385118774137283,36.111181946526756,-5.358763794031006,36.14110259998358),
  "GIB" => (-5.385118774137283,36.111181946526756,-5.358763794031006,36.14110259998358),
  "Guinea" => (-15.080444295404362,7.190208232147398,-7.662447469387985,12.673387756904773),
  "GIN" => (-15.080444295404362,7.190208232147398,-7.662447469387985,12.673387756904773),
  "Gambia" => (-16.829071410910615,13.064836731287983,-13.818712525052973,13.819984435993973),
  "GMB" => (-16.829071410910615,13.064836731287983,-13.818712525052973,13.819984435993973),
  "Guinea Bissau" => (-16.727785603707133,10.927447814722683,-13.66071183292425,12.679433898907718),
  "GNB" => (-16.727785603707133,10.927447814722683,-13.66071183292425,12.679433898907718),
  "Eq. Guinea" => (5.6125045168452345,-1.475826110688942,11.336341186721228,3.7722773238672715),
  "GNQ" => (5.6125045168452345,-1.475826110688942,11.336341186721228,3.7722773238672715),
  "Greece" => (19.62694624862931,34.814723612343826,28.240322306296804,41.75047597305259),
  "GRC" => (19.62694624862931,34.814723612343826,28.240322306296804,41.75047597305259),
  "Grenada" => (-61.78973791585665,12.002627665021492,-61.42089738017992,12.529623921555725),
  "GRD" => (-61.78973791585665,12.002627665021492,-61.42089738017992,12.529623921555725),
  "Greenland" => (-73.05644038957425,59.792372131434135,-11.376174275708593,83.63381093402974),
  "GRL" => (-73.05644038957425,59.792372131434135,-11.376174275708593,83.63381093402974),
  "Guatemala" => (-92.24544999277148,13.749239400759393,-88.22073441634066,17.816019592394753),
  "GTM" => (-92.24544999277148,13.749239400759393,-88.22073441634066,17.816019592394753),
  "Guam" => (144.6245361739551,13.240794983175007,144.95257816627543,13.653922221194506),
  "GUM" => (144.6245361739551,13.240794983175007,144.95257816627543,13.653922221194506),
  "Guyana" => (-61.396712809470046,1.1858202113446197,-56.48181901100932,8.557799174286018),
  "GUY" => (-61.396712809470046,1.1858202113446197,-56.48181901100932,8.557799174286018),
  "Hong Kong" => (113.83731978415494,22.17869903575557,114.40131636580344,22.56053619388591),
  "HKG" => (113.83731978415494,22.17869903575557,114.40131636580344,22.56053619388591),
  "Heard I. and McDonald Is." => (73.23638716683178,-53.192669372465026,73.84523889159328,-52.96157236765608),
  "HMD" => (73.23638716683178,-53.192669372465026,73.84523889159328,-52.96157236765608),
  "Honduras" => (-89.36379126050053,12.979777324009945,-83.12959814515818,17.418421122323664),
  "HND" => (-89.36379126050053,12.979777324009945,-83.12959814515818,17.418421122323664),
  "Croatia" => (13.50189375848916,42.41604258273934,19.407838175040865,46.54697906525716),
  "HRV" => (13.50189375848916,42.41604258273934,19.407838175040865,46.54697906525716),
  "Haiti" => (-74.48842016658764,18.02577423124471,-71.63911088138221,20.089575907824482),
  "HTI" => (-74.48842016658764,18.02577423124471,-71.63911088138221,20.089575907824482),
  "Hungary" => (16.09403527781575,45.741343485678186,22.87760054530571,48.56923289056027),
  "HUN" => (16.09403527781575,45.741343485678186,22.87760054530571,48.56923289056027),
  "Indonesia" => (95.01293908725006,-10.922743021624015,140.97762699431934,5.9099504603680515),
  "IDN" => (95.01293908725006,-10.922743021624015,140.97762699431934,5.9099504603680515),
  "Isle of Man" => (-4.830578980013087,54.04011342394189,-4.311230835007194,54.418746643021436),
  "IMN" => (-4.830578980013087,54.04011342394189,-4.311230835007194,54.418746643021436),
  "India" => (68.14382979327394,6.7454033406183385,97.36225305206142,35.49530621320055),
  "IND" => (68.14382979327394,6.7454033406183385,97.36225305206142,35.49530621320055),
  "Indian Ocean Ter." => (96.82192427629582,-12.200184427973056,105.71497318576326,-10.430990091949965),
  "IOA" => (96.82192427629582,-12.200184427973056,105.71497318576326,-10.430990091949965),
  "Br. Indian Ocean Ter." => (71.26146895329418,-7.432310078956036,72.49493371565711,-5.227070006950811),
  "IOT" => (71.26146895329418,-7.432310078956036,72.49493371565711,-5.227070006950811),
  "Ireland" => (-10.477572794909634,51.44541392069924,-5.9928852950999385,55.386077779286595),
  "IRL" => (-10.477572794909634,51.44541392069924,-5.9928852950999385,55.386077779286595),
  "Iran" => (44.014863321779046,25.076765035101815,63.319628132952005,39.771526998179596),
  "IRN" => (44.014863321779046,25.076765035101815,63.319628132952005,39.771526998179596),
  "Iraq" => (38.77451135243609,29.06313669840739,48.55970218955724,37.3754975383161),
  "IRQ" => (38.77451135243609,29.06313669840739,48.55970218955724,37.3754975383161),
  "Iceland" => (-24.53924678567367,63.39644154862451,-13.502323568074416,66.5637934371765),
  "ISL" => (-24.53924678567367,63.39644154862451,-13.502323568074416,66.5637934371765),
  "Israel" => (34.24827600134955,29.48944142692966,35.888072550010236,33.40672170046909),
  "ISR" => (34.24827600134955,29.48944142692966,35.888072550010236,33.40672170046909),
  "Italy" => (6.602728311752372,35.48925608314876,18.51797001136765,47.08521494599093),
  "ITA" => (6.602728311752372,35.48925608314876,18.51797001136765,47.08521494599093),
  "Jamaica" => (-78.37391943383196,17.70300323548517,-76.18725704009725,18.524916890932843),
  "JAM" => (-78.37391943383196,17.70300323548517,-76.18725704009725,18.524916890932843),
  "Jersey" => (-2.2414346923674486,49.17105662050693,-2.007650512435731,49.26681304963529),
  "JEY" => (-2.2414346923674486,49.17105662050693,-2.007650512435731,49.26681304963529),
  "Jordan" => (34.9498352458167,29.189950663432484,39.291999145502984,33.37168507918017),
  "JOR" => (34.9498352458167,29.189950663432484,39.291999145502984,33.37168507918017),
  "Japan" => (122.93841801016129,24.212089131573137,153.98561853037154,45.520116679535775),
  "JPN" => (122.93841801016129,24.212089131573137,153.98561853037154,45.520116679535775),
  "Baykonur" => (62.80064172374108,45.5658244835667,63.96713382986559,46.39083872510645),
  "KAB" => (62.80064172374108,45.5658244835667,63.96713382986559,46.39083872510645),
  "KAS" => (76.77735966031611,35.110441996919235,77.80039616702697,35.64816030535394),
  "Kazakhstan" => (46.47827884909765,40.58465566055834,87.3237960209886,55.434550272592716),
  "KAZ" => (46.47827884909765,40.58465566055834,87.3237960209886,55.434550272592716),
  "Kenya" => (33.89046838340187,-4.67780120848812,41.88501916485586,5.49173309383697),
  "KEN" => (33.89046838340187,-4.67780120848812,41.88501916485586,5.49173309383697),
  "Kyrgyzstan" => (69.2262960208011,39.18923695909558,80.25756066911839,43.26170155843408),
  "KGZ" => (69.2262960208011,39.18923695909558,80.25756066911839,43.26170155843408),
  "Cambodia" => (102.31342370613726,10.415773620565716,107.61051639882669,14.70458160400554),
  "KHM" => (102.31342370613726,10.415773620565716,107.61051639882669,14.70458160400554),
  "Kiribati" => (-172.22862992418368,-11.461211446350106,176.85104455641857,4.722891133800999),
  "KIR" => (-172.22862992418368,-11.461211446350106,176.85104455641857,4.722891133800999),
  "St. Kitts and Nevis" => (-62.86031856315054,17.100301005512293,-62.53610062659436,17.41563059531454),
  "KNA" => (-62.86031856315054,17.100301005512293,-62.53610062659436,17.41563059531454),
  "KNM" => (126.6070829674859,37.78306570985609,128.39507770229898,38.6784154256444),
  "S. Korea" => (124.61397465050942,33.197302958397145,130.9209281826533,38.57834350799338),
  "KOR" => (124.61397465050942,33.197302958397145,130.9209281826533,38.57834350799338),
  "Kosovo" => (20.024751424271557,41.84401031504285,21.772758423064317,43.263070983967054),
  "KOS" => (20.024751424271557,41.84401031504285,21.772758423064317,43.263070983967054),
  "Kuwait" => (46.5324357501068,28.53350494449201,48.43299157731758,30.09821563732676),
  "KWT" => (46.5324357501068,28.53350494449201,48.43299157731758,30.09821563732676),
  "Laos" => (100.09707320203876,13.915456645186836,107.6643632405798,22.496044006225475),
  "LAO" => (100.09707320203876,13.915456645186836,107.6643632405798,22.496044006225475),
  "Lebanon" => (35.1001103111534,33.05558034303647,36.60410119614886,34.68754791301882),
  "LBN" => (35.1001103111534,33.05558034303647,36.60410119614886,34.68754791301882),
  "Liberia" => (-11.475548055030004,4.347074280151759,-7.384118204699973,8.565395609871217),
  "LBR" => (-11.475548055030004,4.347074280151759,-7.384118204699973,8.565395609871217),
  "Libya" => (9.286543822928707,19.4961237589053,25.156841267539278,33.18107655847166),
  "LBY" => (9.286543822928707,19.4961237589053,25.156841267539278,33.18107655847166),
  "Saint Lucia" => (-61.077843383839024,13.714461161884046,-60.88224808830387,14.111697896755913),
  "LCA" => (-61.077843383839024,13.714461161884046,-60.88224808830387,14.111697896755913),
  "Liechtenstein" => (9.475886270889731,47.05240041094075,9.615722696789646,47.26280100582463),
  "LIE" => (9.475886270889731,47.05240041094075,9.615722696789646,47.26280100582463),
  "Sri Lanka" => (79.65615035031414,5.923567199535029,81.89069095906274,9.82942698172505),
  "LKA" => (79.65615035031414,5.923567199535029,81.89069095906274,9.82942698172505),
  "Lesotho" => (27.00215498814333,-30.65879933704518,29.43590824351935,-28.570761413812363),
  "LSO" => (27.00215498814333,-30.65879933704518,29.43590824351935,-28.570761413812363),
  "Lithuania" => (20.925368285738102,53.886841126024805,26.800720255276957,56.44260243784994),
  "LTU" => (20.925368285738102,53.886841126024805,26.800720255276957,56.44260243784994),
  "Luxembourg" => (5.714927205589305,49.44132436162499,6.5025793860894225,50.17497467074395),
  "LUX" => (5.714927205589305,49.44132436162499,6.5025793860894225,50.17497467074395),
  "Latvia" => (20.969189900995445,55.666990865412686,28.217274617127657,58.07513844860267),
  "LVA" => (20.969189900995445,55.666990865412686,28.217274617127657,58.07513844860267),
  "Macau" => (113.52106001838325,22.105370178183662,113.58751590986878,22.21766306559438),
  "MAC" => (113.52106001838325,22.105370178183662,113.58751590986878,22.21766306559438),
  "St. Martin" => (-63.146089233795806,18.032595520488428,-63.00997351087443,18.121944071515315),
  "MAF" => (-63.146089233795806,18.032595520488428,-63.00997351087443,18.121944071515315),
  "Morocco" => (-17.013091186367518,21.420063172063394,-1.0319994710863512,35.924164937590135),
  "MAR" => (-17.013091186367518,21.420063172063394,-1.0319994710863512,35.924164937590135),
  "Monaco" => (7.370071655048491,43.730613505409394,7.429499552618466,43.76350555482395),
  "MCO" => (7.370071655048491,43.730613505409394,7.429499552618466,43.76350555482395),
  "Moldova" => (26.617889038203003,45.46177398756319,30.131576375611473,48.48603383432197),
  "MDA" => (26.617889038203003,45.46177398756319,30.131576375611473,48.48603383432197),
  "Madagascar" => (43.22328373182995,-25.59853973371027,50.50428633669708,-11.943765970913574),
  "MDG" => (43.22328373182995,-25.59853973371027,50.50428633669708,-11.943765970913574),
  "Maldives" => (72.68520633958775,-0.6886906941166302,73.7535649006428,7.107060851852253),
  "MDV" => (72.68520633958775,-0.6886906941166302,73.7535649006428,7.107060851852253),
  "Mexico" => (-118.4063596201867,14.546064147948812,-86.69984533696461,32.71283640595168),
  "MEX" => (-118.4063596201867,14.546064147948812,-86.69984533696461,32.71283640595168),
  "Marshall Is." => (165.28244836491575,4.5735979212762174,172.02994348236172,14.610220438831334),
  "MHL" => (165.28244836491575,4.5735979212762174,172.02994348236172,14.610220438831334),
  "Macedonia" => (20.44415734828644,40.849394022701915,23.00958215320682,42.370334778021174),
  "MKD" => (20.44415734828644,40.849394022701915,23.00958215320682,42.370334778021174),
  "Mali" => (-12.264130412398913,10.140054016636157,4.235637654332322,24.99506459560331),
  "MLI" => (-12.264130412398913,10.140054016636157,4.235637654332322,24.99506459560331),
  "Malta" => (14.183970988036918,35.8010199996004,14.567720174049327,36.07531850205383),
  "MLT" => (14.183970988036918,35.8010199996004,14.567720174049327,36.07531850205383),
  "Myanmar" => (92.17497277813015,9.790540466250704,101.17385502186295,28.53846588179688),
  "MMR" => (92.17497277813015,9.790540466250704,101.17385502186295,28.53846588179688),
  "Montenegro" => (18.43353072080319,41.85207183861277,20.355170532458686,43.547885641120956),
  "MNE" => (18.43353072080319,41.85207183861277,20.355170532458686,43.547885641120956),
  "Mongolia" => (87.73570886220375,41.586144918086035,119.90702681490279,52.12958404617834),
  "MNG" => (87.73570886220375,41.586144918086035,119.90702681490279,52.12958404617834),
  "N. Mariana Is." => (144.9024520266015,14.11045766242971,145.86926639893892,20.555232238906555),
  "MNP" => (144.9024520266015,14.11045766242971,145.86926639893892,20.555232238906555),
  "Mozambique" => (30.213845255880358,-26.86027150496102,40.84859663882469,-10.469230651390305),
  "MOZ" => (30.213845255880358,-26.86027150496102,40.84859663882469,-10.469230651390305),
  "Mauritania" => (-17.081174886699245,14.734398905576114,-4.821613118894817,27.285415751728877),
  "MRT" => (-17.081174886699245,14.734398905576114,-4.821613118894817,27.285415751728877),
  "Montserrat" => (-62.22940100113237,16.675158996951836,-62.13984574453434,16.81912953697264),
  "MSR" => (-62.22940100113237,16.675158996951836,-62.13984574453434,16.81912953697264),
  "Mauritius" => (56.510069620554816,-20.51755788202908,63.4942944745697,-10.316681823871662),
  "MUS" => (56.510069620554816,-20.51755788202908,63.4942944745697,-10.316681823871662),
  "Malawi" => (32.663308146354666,-17.13533538769444,35.90429894993582,-9.381235046687209),
  "MWI" => (32.663308146354666,-17.13533538769444,35.90429894993582,-9.381235046687209),
  "Malaysia" => (99.64542118374413,0.8513703409228413,119.27838301596591,7.3556244918121365),
  "MYS" => (99.64542118374413,0.8513703409228413,119.27838301596591,7.3556244918121365),
  "Namibia" => (11.718230020794664,-28.959368184344015,25.25978072111053,-16.951057230723137),
  "NAM" => (11.718230020794664,-28.959368184344015,25.25978072111053,-16.951057230723137),
  "New Caledonia" => (159.92634118669787,-22.67081146242127,168.14439253827493,-19.1092717484202),
  "NCL" => (159.92634118669787,-22.67081146242127,168.14439253827493,-19.1092717484202),
  "Niger" => (0.15294112112820812,11.695773010830848,15.970321899055733,23.517351176349905),
  "NER" => (0.15294112112820812,11.695773010830848,15.970321899055733,23.517351176349905),
  "Norfolk Island" => (167.9123653565972,-29.07998097773816,167.99659794159055,-28.99760874468373),
  "NFK" => (167.9123653565972,-29.07998097773816,167.99659794159055,-28.99760874468373),
  "Nigeria" => (2.671081990226355,4.2719367474833945,14.669936157222338,13.880290832691003),
  "NGA" => (2.671081990226355,4.2719367474833945,14.669936157222338,13.880290832691003),
  "Nicaragua" => (-87.68503068110181,10.713481547387545,-82.72492000342973,15.030969950857383),
  "NIC" => (-87.68503068110181,10.713481547387545,-82.72492000342973,15.030969950857383),
  "Niue" => (-169.94944087789324,-19.142861429811887,-169.7818800463541,-18.964164326858835),
  "NIU" => (-169.94944087789324,-19.142861429811887,-169.7818800463541,-18.964164326858835),
  "Netherlands" => (-68.4166202399085,12.021851298426526,7.198505899695846,53.557868957735096),
  "NLD" => (-68.4166202399085,12.021851298426526,7.198505899695846,53.557868957735096),
  "Norway" => (-9.116828979536734,-54.462359313600835,33.64102624521422,80.76974802721715),
  "NOR" => (-9.116828979536734,-54.462359313600835,33.64102624521422,80.76974802721715),
  "Nepal" => (80.03028772007377,26.343767802014128,88.16906742396219,30.416904195807803),
  "NPL" => (80.03028772007377,26.343767802014128,88.16906742396219,30.416904195807803),
  "Nauru" => (166.9071553960908,-0.5520582072674927,166.95852177009084,-0.4905632521872718),
  "NRU" => (166.9071553960908,-0.5520582072674927,166.95852177009084,-0.4905632521872718),
  "New Zealand" => (-177.956962444279,-52.5773064168165,178.84404952002205,-8.543353372828378),
  "NZL" => (-177.956962444279,-52.5773064168165,178.84404952002205,-8.543353372828378),
  "Oman" => (51.9786149497499,16.642034403545054,59.84505985501022,26.38578074127561),
  "OMN" => (51.9786149497499,16.642034403545054,59.84505985501022,26.38578074127561),
  "Pakistan" => (60.84437870314167,23.69431711857085,77.04897098854588,37.05448354081069),
  "PAK" => (60.84437870314167,23.69431711857085,77.04897098854588,37.05448354081069),
  "Panama" => (-83.05324621658491,7.205556132046553,-77.1632698166458,9.624168193094562),
  "PAN" => (-83.05324621658491,7.205556132046553,-77.1632698166458,9.624168193094562),
  "Pitcairn Is." => (-130.753073690301,-25.07712453209362,-124.77715938395701,-23.92432668047401),
  "PCN" => (-130.753073690301,-25.07712453209362,-124.77715938395701,-23.92432668047401),
  "Peru" => (-81.35443518149634,-18.344563903404477,-68.68425248274613,-0.02909271206854669),
  "PER" => (-81.35443518149634,-18.344563903404477,-68.68425248274613,-0.02909271206854669),
  "Philippines" => (116.95537235531765,4.655556742288979,126.61809004800494,21.12238108366266),
  "PHL" => (116.95537235531765,4.655556742288979,126.61809004800494,21.12238108366266),
  "Palau" => (131.13114790858867,2.9490200804085447,134.66354211457497,7.733895975692562),
  "PLW" => (131.13114790858867,2.9490200804085447,134.66354211457497,7.733895975692562),
  "Papua New Guinea" => (140.84921105976878,-11.63639455189579,155.96787479708075,-1.3465316770113276),
  "PNG" => (140.84921105976878,-11.63639455189579,155.96787479708075,-1.3465316770113276),
  "Poland" => (14.123922973753508,48.99401316392161,24.14315637232059,54.83810089109318),
  "POL" => (14.123922973753508,48.99401316392161,24.14315637232059,54.83810089109318),
  "Puerto Rico" => (-67.93706294792528,17.922731424887857,-65.24389400290951,18.522539775065994),
  "PRI" => (-67.93706294792528,17.922731424887857,-65.24389400290951,18.522539775065994),
  "N. Korea" => (124.32210616101861,37.67542755137693,130.70047652195308,43.0102698771007),
  "PRK" => (124.32210616101861,37.67542755137693,130.70047652195308,43.0102698771007),
  "Portugal" => (-31.284287271989058,30.02922760034626,-6.205947224886785,42.153629659620165),
  "PRT" => (-31.284287271989058,30.02922760034626,-6.205947224886785,42.153629659620165),
  "Paraguay" => (-62.65035721872948,-27.586842142422498,-54.24528885911502,-19.286728617047075),
  "PRY" => (-62.65035721872948,-27.586842142422498,-54.24528885911502,-19.286728617047075),
  "Fr. Polynesia" => (-151.783547119917,-27.641309102687828,-134.94206864485585,-7.950314635950747),
  "PYF" => (-151.783547119917,-27.641309102687828,-134.94206864485585,-7.950314635950747),
  "Qatar" => (50.75140303586039,24.559871521226512,51.61708662972279,26.159851379342598),
  "QAT" => (50.75140303586039,24.559871521226512,51.61708662972279,26.159851379342598),
  "Romania" => (20.242825968205636,43.65004994745149,29.700181518577608,48.27483225557478),
  "ROU" => (20.242825968205636,43.65004994745149,29.700181518577608,48.27483225557478),
  "Russia" => (-179.99900834195893,41.19268056303521,180.0000000000001,81.85836374953307),
  "RUS" => (-179.99900834195893,41.19268056303521,180.0000000000001,81.85836374953307),
  "Rwanda" => (28.85723555462829,-2.826854756124021,30.887809285015635,-1.0586939488559608),
  "RWA" => (28.85723555462829,-2.826854756124021,30.887809285015635,-1.0586939488559608),
  "W. Sahara" => (-17.104015869397983,20.766743876010565,-8.680809081975951,27.661490987791666),
  "SAH" => (-17.104015869397983,20.766743876010565,-8.680809081975951,27.661490987791666),
  "Saudi Arabia" => (34.48991499838087,16.371249898499045,55.637564738347805,32.12134796175691),
  "SAU" => (34.48991499838087,16.371249898499045,55.637564738347805,32.12134796175691),
  "Sudan" => (21.809448689821778,8.681641744252843,38.61565799960175,22.226964823490377),
  "SDN" => (21.809448689821778,8.681641744252843,38.61565799960175,22.226964823490377),
  "S. Sudan" => (24.12155562304875,3.4902015177794112,35.26929895063921,12.216154683692551),
  "SDS" => (24.12155562304875,3.4902015177794112,35.26929895063921,12.216154683692551),
  "Senegal" => (-17.53538488801064,12.305606587504883,-11.37777624523406,16.691385396877337),
  "SEN" => (-17.53538488801064,12.305606587504883,-11.37777624523406,16.691385396877337),
  "Singapore" => (103.64078454658903,1.2641616821644135,104.00386315929893,1.4484656784562446),
  "SGP" => (103.64078454658903,1.2641616821644135,104.00386315929893,1.4484656784562446),
  "S. Geo. and S. Sandw. Is." => (-42.1187680663881,-59.47280283651794,-26.23867793837468,-53.456787617895245),
  "SGS" => (-42.1187680663881,-59.47280283651794,-26.23867793837468,-53.456787617895245),
  "Saint Helena" => (-14.417099771956345,-40.39789479582775,-5.649753784394619,-7.877967631190856),
  "SHN" => (-14.417099771956345,-40.39789479582775,-5.649753784394619,-7.877967631190856),
  "Solomon Is." => (155.5082646080017,-12.290566507754974,168.82579797426672,-6.6001128128002335),
  "SLB" => (155.5082646080017,-12.290566507754974,168.82579797426672,-6.6001128128002335),
  "Sierra Leone" => (-13.300423747223306,6.919268697473413,-10.282235880888777,9.996005961351628),
  "SLE" => (-13.300423747223306,6.919268697473413,-10.282235880888777,9.996005961351628),
  "El Salvador" => (-90.11477901266443,13.158448588541859,-87.7036083584729,14.445372619936975),
  "SLV" => (-90.11477901266443,13.158448588541859,-87.7036083584729,14.445372619936975),
  "San Marino" => (12.385552338091092,43.89236211111275,12.492315847426543,43.98287338210147),
  "SMR" => (12.385552338091092,43.89236211111275,12.492315847426543,43.98287338210147),
  "Somaliland" => (42.64724654121926,7.996515605282383,48.93911055497824,11.49870493607773),
  "SOL" => (42.64724654121926,7.996515605282383,48.93911055497824,11.49870493607773),
  "Somalia" => (40.96538537555534,-1.697001240888227,51.4167887774978,11.988778381862119),
  "SOM" => (40.96538537555534,-1.697001240888227,51.4167887774978,11.988778381862119),
  "St. Pierre and Miquelon" => (-56.395855265283814,46.752496236301326,-56.14401017280818,47.14099965404746),
  "SPM" => (-56.395855265283814,46.752496236301326,-56.14401017280818,47.14099965404746),
  "Serbia" => (18.844833942345616,42.23495302232074,22.984477903950875,46.17406658841726),
  "SRB" => (18.844833942345616,42.23495302232074,22.984477903950875,46.17406658841726),
  "Sao Tome and Principe" => (6.462271769138937,0.023927307400043674,7.463399291467567,1.6996389773760683),
  "STP" => (6.462271769138937,0.023927307400043674,7.463399291467567,1.6996389773760683),
  "Suriname" => (-58.06769120355253,1.833506774737998,-53.988818726112555,6.0060169478531975),
  "SUR" => (-58.06769120355253,1.833506774737998,-53.988818726112555,6.0060169478531975),
  "Slovakia" => (16.844480427630486,47.75000640933621,22.539636678375757,49.60177968398489),
  "SVK" => (16.844480427630486,47.75000640933621,22.539636678375757,49.60177968398489),
  "Slovenia" => (13.365261270740689,45.423636780009105,16.515301554668895,46.86396230052793),
  "SVN" => (13.365261270740689,45.423636780009105,16.515301554668895,46.86396230052793),
  "Sweden" => (11.108758178420459,55.34241119455665,24.163723591938123,69.03635569276317),
  "SWE" => (11.108758178420459,55.34241119455665,24.163723591938123,69.03635569276317),
  "Swaziland" => (30.782906127619185,-27.31626434294766,32.11739831567138,-25.73599904374332),
  "SWZ" => (30.782906127619185,-27.31626434294766,32.11739831567138,-25.73599904374332),
  "Sint Maarten" => (-63.1180806073217,18.01895294200088,-63.01736324088847,18.061921895653057),
  "SXM" => (-63.1180806073217,18.01895294200088,-63.01736324088847,18.061921895653057),
  "Seychelles" => (46.20790775519396,-9.755682474803507,56.287964315285365,-3.791136982966492),
  "SYC" => (46.20790775519396,-9.755682474803507,56.287964315285365,-3.791136982966492),
  "Syria" => (35.72394820151419,32.313041687163675,42.37718550657178,37.32490631155683),
  "SYR" => (35.72394820151419,32.313041687163675,42.37718550657178,37.32490631155683),
  "Turks and Caicos Is." => (-72.48050655174913,21.290096944829898,-71.12808264235989,21.959074198239122),
  "TCA" => (-72.48050655174913,21.290096944829898,-71.12808264235989,21.959074198239122),
  "Chad" => (13.449183797377316,7.45556671190387,23.984406372271707,23.444719950755143),
  "TCD" => (13.449183797377316,7.45556671190387,23.984406372271707,23.444719950755143),
  "Togo" => (-0.16610917165266414,6.100145569034893,1.7823507080938725,11.134980367333768),
  "TGO" => (-0.16610917165266414,6.100145569034893,1.7823507080938725,11.134980367333768),
  "Thailand" => (97.35140100148237,5.629890034947849,105.65099776203061,20.445006409006183),
  "THA" => (97.35140100148237,5.629890034947849,105.65099776203061,20.445006409006183),
  "Tajikistan" => (67.34269006391423,36.67864084874016,75.1641247964335,41.039976704989115),
  "TJK" => (67.34269006391423,36.67864084874016,75.1641247964335,41.039976704989115),
  "Turkmenistan" => (52.43812178514412,35.140646871210905,66.64578169749586,42.791187642832725),
  "TKM" => (52.43812178514412,35.140646871210905,66.64578169749586,42.791187642832725),
  "East Timor" => (124.03023767152797,-9.501227722468641,127.31355147362655,-8.135316263320462),
  "TLS" => (124.03023767152797,-9.501227722468641,127.31355147362655,-8.135316263320462),
  "Tonga" => (-176.21929073123812,-22.33884206155129,-173.71427567669966,-15.55961761465258),
  "TON" => (-176.21929073123812,-22.33884206155129,-173.71427567669966,-15.55961761465258),
  "Trinidad and Tobago" => (-61.92797237223124,10.041868794798518,-60.52133988499028,11.350936177814674),
  "TTO" => (-61.92797237223124,10.041868794798518,-60.52133988499028,11.350936177814674),
  "Tunisia" => (7.479832396964213,30.228905334958526,11.564130899693794,37.34495676724664),
  "TUN" => (7.479832396964213,30.228905334958526,11.564130899693794,37.34495676724664),
  "Turkey" => (25.663838745226315,35.819778544121405,44.8068563228706,42.09849090579904),
  "TUR" => (25.663838745226315,35.819778544121405,44.8068563228706,42.09849090579904),
  "Tuvalu" => (176.1254557699703,-9.420715840453823,179.9066719974308,-5.677688496490504),
  "TUV" => (176.1254557699703,-9.420715840453823,179.9066719974308,-5.677688496490504),
  "Taiwan" => (118.27983931507532,21.904400533302052,122.00560673035795,25.287191467507697),
  "TWN" => (118.27983931507532,21.904400533302052,122.00560673035795,25.287191467507697),
  "Tanzania" => (29.32103153467085,-11.731272480997745,40.44975793442734,-0.9858301792688877),
  "TZA" => (29.32103153467085,-11.731272480997745,40.44975793442734,-0.9858301792688877),
  "Uganda" => (29.548459513343573,-1.4752059930761021,35.00647261547829,4.2196918743563),
  "UGA" => (29.548459513343573,-1.4752059930761021,35.00647261547829,4.2196918743563),
  "Ukraine" => (22.132839803194003,44.38075469993326,40.15954309040518,52.36894928012835),
  "UKR" => (22.132839803194003,44.38075469993326,40.15954309040518,52.36894928012835),
  "U.S. Minor Outlying Is." => (-177.38854752677554,-0.3889673866271153,166.65259729097033,28.215100606845837),
  "UMI" => (-177.38854752677554,-0.3889673866271153,166.65259729097033,28.215100606845837),
  "Uruguay" => (-58.43867631020444,-34.97341969824218,-53.110836140874284,-30.09686981200754),
  "URY" => (-58.43867631020444,-34.97341969824218,-53.110836140874284,-30.09686981200754),
  "United States" => (-179.14247147726383,18.930137634111063,179.78114994357423,71.41217966730896),
  "USA" => (-179.14247147726383,18.930137634111063,179.78114994357423,71.41217966730896),
  "Guantanamo Bay USNB" => (-75.23289668786447,19.88672007258269,-75.09502396668907,19.961056620488378),
  "USG" => (-75.23289668786447,19.88672007258269,-75.09502396668907,19.961056620488378),
  "Uzbekistan" => (55.97583866363459,37.18514740002128,73.148640584431,45.558718974175335),
  "UZB" => (55.97583866363459,37.18514740002128,73.148640584431,45.558718974175335),
  "Vatican" => (12.444336378621074,41.90018178985781,12.458392368513046,41.90845001915318),
  "VAT" => (12.444336378621074,41.90018178985781,12.458392368513046,41.90845001915318),
  "St. Vin. and Gren." => (-61.45980973317617,12.585176086519127,-61.12321529177919,13.380528056289364),
  "VCT" => (-61.45980973317617,12.585176086519127,-61.12321529177919,13.380528056289364),
  "Venezuela" => (-73.39114864151034,0.6493154904436693,-59.81559484866045,12.198739726283137),
  "VEN" => (-73.39114864151034,0.6493154904436693,-59.81559484866045,12.198739726283137),
  "British Virgin Is." => (-64.77400020402163,18.334695943844807,-64.2699999598143,18.746040344289653),
  "VGB" => (-64.77400020402163,18.334695943844807,-64.2699999598143,18.746040344289653),
  "U.S. Virgin Is." => (-65.02031591842223,17.682539367753876,-64.55866451025415,18.38885284485407),
  "VIR" => (-65.02031591842223,17.682539367753876,-64.55866451025415,18.38885284485407),
  "Vietnam" => (102.11865523288668,8.565395609871203,109.47283166569775,23.366275126250443),
  "VNM" => (102.11865523288668,8.565395609871203,109.47283166569775,23.366275126250443),
  "Vanuatu" => (166.52071903495556,-20.253284606970823,169.89922082541216,-13.065041198651613),
  "VUT" => (166.52071903495556,-20.253284606970823,169.89922082541216,-13.065041198651613),
  "West Bank" => (34.86715295440558,31.344909566135755,35.572536254636816,32.54264007613196),
  "PSE" => (34.86715295440558,31.344909566135755,35.572536254636816,32.54264007613196),
  "Wallis and Futuna" => (-178.18470048130845,-14.378811136558937,-176.12456783086478,-13.209063415514862),
  "WLF" => (-178.18470048130845,-14.378811136558937,-176.12456783086478,-13.209063415514862),
  "Akrotiri" => (32.760459838538,34.56833038357891,33.030727579656,34.70077707946476),
  "WSB" => (32.760459838538,34.56833038357891,33.030727579656,34.70077707946476),
  "Samoa" => (-172.78159358810314,-14.052836201748775,-171.4366627615134,-13.462898051136378),
  "WSM" => (-172.78159358810314,-14.052836201748775,-171.4366627615134,-13.462898051136378),
  "Yemen" => (42.54575402844466,12.111199849453413,54.540680785991526,18.99563751210529),
  "YEM" => (42.54575402844466,12.111199849453413,54.540680785991526,18.99563751210529),
  "South Africa" => (16.47054976434137,-46.96581431119364,37.9782808835389,-22.12645192442099),
  "ZAF" => (16.47054976434137,-46.96581431119364,37.9782808835389,-22.12645192442099),
  "Zambia" => (21.979877563633778,-18.069231872196013,33.67420251456417,-8.194124043277697),
  "ZMB" => (21.979877563633778,-18.069231872196013,33.67420251456417,-8.194124043277697),
  "Zimbabwe" => (25.219369751374444,-22.397339782252487,33.0427681886182,-15.614808044416833),
  "ZWE" => (25.219369751374444,-22.397339782252487,33.0427681886182,-15.614808044416833),
)
function replaceregion(kwargs)
  if haskey(kwargs,:region)
    reg = kwargs[:region]
    delete!(kwargs, :region)
    haskey(known_regions,reg) || error("Region $(reg[2]) not known.")
    lon1,lat1,lon2,lat2 = known_regions[reg]
    kwargs[:lon] = lon1..lon2
    kwargs[:lat] = lat1..lat2
  end
end