Core Functions
This page documents the main functions and types for creating geofaceted visualizations with GeoFacetMakie.jl.
Main Plotting Function
GeoFacetMakie.geofacet Function
geofacet(data, region_col, plot_func;
grid = us_state_grid1,
link_axes = :none,
hide_inner_decorations = true,
title = "",
titlekwargs = NamedTuple[],
figure_kwargs = NamedTuple(),
common_axis_kwargs = NamedTuple(),
axis_kwargs_list = NamedTuple[],
legend_kwargs = NamedTuple(),
func_kwargs = (missing_regions = :skip,))Create a geographically faceted plot using the specified grid layout.
Arguments
data: DataFrame or similar tabular data structureregion_col: Symbol or string specifying the column containing region identifiersplot_func: Function that takes(gridlayout, data_subset; kwargs...)for single-axis plots or(gridlayout, data_subset; processed_axis_kwargs_list)for multi-axis plots
Keyword Arguments
grid: GeoGrid object defining the spatial layout (default:us_state_grid1)link_axes: Symbol controlling axis linking (:none,:x,:y,:both)hide_inner_decorations: Bool controlling whether to hide axis decorations on inner facets when axes are linked (default:true). Only affects linked axes - e.g., iflink_axes = :x, only x-axis decorations are hidden for facets with neighbors below.title: String for the overall plot title (default:""for no title)titlekwargs: NamedTuple of keyword arguments passed to the titleLabelconstructorfigure_kwargs: NamedTuple passed toFigure()constructorcommon_axis_kwargs: NamedTuple applied to all axes in each facet. Important: To ensure proper hiding of axis decorations whenhide_inner_decorations = true, axis labels (xlabel,ylabel) should be specified here rather than within the plot function.axis_kwargs_list: Vector of NamedTuples for per-axis specific kwargs. Each element corresponds to an axis in the order they are created in the plot function. These are merged withcommon_axis_kwargs(per-axis takes precedence). If multiple axes are plotted on the same facet, you should set the position within each NamedTuple using the kwargyaxisposition(defaults to:left)legend_kwargs: NamedTuple of keyword arguments for legend creation. Special keys::title: Legend title (extracted and passed separately toLegend):legend_position: Tuple(row, col)specifying legend position in grid layout. Usenothingfor a dimension to use default (e.g.,(nothing, ncols+1)for rightmost column)All other keys are passed directly to the
Legendconstructor
func_kwargs: NamedTuple of additional keyword arguments passed to the plot function. Required key::missing_regions: How to handle regions in grid but not in data (:skip,:empty,:error). Defaults to:skipif not specified.
Returns
A Makie Figure object containing the geofaceted plot.
Examples
using DataFrames, GeoFacetMakie
# Sample data
data = DataFrame(
state = ["CA", "TX", "NY"],
population = [39_500_000, 29_000_000, 19_500_000],
gdp = [3_200_000, 2_400_000, 1_900_000]
)
# Single-axis plot with title and legend
result = geofacet(data, :state, (gl, data; kwargs...) -> begin
ax = Axis(gl[1, 1]; kwargs...)
barplot!(ax, [1], data.population, color = :blue, label = "Population")
ax.title = data.state[1] # Set title to state name
ax.xticksvisible = false # Clean up x-axis
ax.xticklabelsvisible = false
end;
title = "US State Population",
titlekwargs = (fontsize = 16, color = :darkblue),
common_axis_kwargs = (ylabel = "Population (M)",),
legend_kwargs = (title = "Metrics",)
)
# Multi-axis plot with common and per-axis kwargs
result = geofacet(data, :state, (gl, data; processed_axis_kwargs_list) -> begin
ax1 = Axis(gl[1, 1]; processed_axis_kwargs_list[1]...)
ax2 = Axis(gl[2, 1]; processed_axis_kwargs_list[2]...)
barplot!(ax1, [1], data.population)
barplot!(ax2, [1], data.gdp)
end;
common_axis_kwargs = (titlesize = 12,),
axis_kwargs_list = [
(xlabel = "Index", ylabel = "Population (M)"),
(xlabel = "Index", ylabel = "GDP (B)", yscale = log10)
],
figure_kwargs = (size = (1200, 800),)
)
# Time series example with linked y-axes
time_data = DataFrame(
state = repeat(["CA", "TX", "NY"], inner = 5),
year = repeat(2019:2023, 3),
value = rand(15) .* 100
)
result = geofacet(time_data, :state, (gl, data; kwargs...) -> begin
ax = Axis(gl[1, 1]; kwargs...)
lines!(ax, data.year, data.value, color = :darkgreen, linewidth = 2)
ax.title = data.state[1]
end;
link_axes = :y, # Link y-axes for comparison
common_axis_kwargs = (xlabel = "Year", ylabel = "Value"),
func_kwargs = (missing_regions = :skip,) # Skip regions not in data
)
# Error handling example
error_data = DataFrame(
state = ["CA", "TX", "INVALID"], # INVALID state will be skipped
value = [1, 2, 3]
)
result = geofacet(error_data, :state, (gl, data; kwargs...) -> begin
ax = Axis(gl[1, 1]; kwargs...)
barplot!(ax, [1], data.value, color = :orange)
ax.title = data.state[1]
end;
func_kwargs = (missing_regions = :skip,) # Gracefully skip invalid regions
)Core Data Types
GeoGrid
GeoFacetMakie.GeoGrid Type
GeoGridA geographical grid layout using StructArray for efficient storage. Stores region names and their (row, col) positions in a structure-of-arrays format.
This provides better memory efficiency and performance through:
Structure-of-Arrays (SOA) layout for better cache locality
Vectorized operations on grid data
Natural integration with DataFrames.jl ecosystem
Examples
# Create from vectors (backward compatible)
grid = StructArray{GridEntry}((
region = ["CA", "NY", "TX", "FL"],
row = [1, 1, 2, 2],
col = [1, 2, 1, 2],
name = ["California", "New York", "Texas", "Florida"],
metadata = [Dict{String,Any}(), Dict{String,Any}(), Dict{String,Any}(), Dict{String,Any}()]
))
# Access individual fields
regions = grid.region
rows = grid.row
cols = grid.col
names = grid.name
metadata = grid.metadata
# Iterate over entries
for grid_entry in grid
println("$(grid_entry.region) ($(grid_entry.name)) at ($(grid_entry.row), $(grid_entry.col))")
endGridEntry
GeoFacetMakie.GridEntry Type
GridEntryA single entry in a geographical grid, containing the region identifier, its position coordinates, display name, and optional metadata.
Fields
region::String: Region identifier/coderow::Int: Grid row position (≥ 1)col::Int: Grid column position (≥ 1)name::String: Display name for the region (defaults to region if not provided)metadata::Dict{String,Any}: Additional metadata from CSV columns
Constructors
# Basic constructor (backward compatible)
entry = GridEntry("CA", 1, 2)
# With custom name
entry = GridEntry("CA", 1, 2, "California")
# With name and metadata
entry = GridEntry("CA", 1, 2, "California", Dict("population" => 39538223))Iteration and Indexing
GridEntry supports iteration, unpacking, and indexing for backward compatibility:
entry = GridEntry("CA", 1, 2)
# Unpack core fields (backward compatible)
region, row, col = entry
# Index access (backward compatible)
region = entry[1] # "CA"
row = entry[2] # 1
col = entry[3] # 2
# Access new fields directly
name = entry.name # "CA" (defaults to region)
metadata = entry.metadata # Dict{String,Any}()
# Iterate over core fields (backward compatible)
for value in entry
println(value) # Prints "CA", 1, 2
end
# Convert to array (backward compatible)
values = collect(entry) # ["CA", 1, 2]
# Functional style
regions = map(e -> e[1], grid) # Extract all regionsWorking with Names and Metadata
# Create entry with custom display name
entry = GridEntry("CA", 1, 2, "California")
println(entry.name) # "California"
# Create entry with metadata (e.g., from CSV loading)
metadata = Dict("population" => 39538223, "area_km2" => 423970)
entry = GridEntry("CA", 1, 2, "California", metadata)
# Access metadata
pop = entry.metadata["population"] # 39538223
area = entry.metadata["area_km2"] # 423970
# Use in plotting functions (assuming you have access to the grid entry)
# Note: This is a conceptual example - in practice you'd need to look up
# the grid entry for the current region within your plot function
geofacet(data, :state, (gl, data; kwargs...) -> begin
ax = Axis(gl[1, 1]; kwargs...)
# Use region code for title (standard approach)
ax.title = data.state[1] # "CA"
# Or if you have access to the grid entry with display names:
# ax.title = entry.name # "California" instead of "CA"
lines!(ax, data.year, data.value)
end)
# Filter grids by metadata
large_states = filter(e -> get(e.metadata, "population", 0) > 10_000_000, grid)Usage Examples
Basic Geofaceted Plot
using GeoFacetMakie, DataFrames, CairoMakie
# Sample data
data = DataFrame(
state = ["CA", "TX", "NY", "FL"],
population = [39.5, 29.1, 19.8, 21.5],
year = [2023, 2023, 2023, 2023]
)
# Define plotting function
function plot_bars!(gl, data; kwargs...)
ax = Axis(gl[1, 1]; kwargs...)
barplot!(ax, [1], data.population, color = :steelblue)
ax.title = data.state[1]
end
# Create geofaceted plot
fig = geofacet(data, :state, plot_bars!;
figure_kwargs = (size = (800, 600),),
common_axis_kwargs = (ylabel = "Population (M)",))Multi-Axis Plot
# Multi-axis plot with different styling per axis
function plot_dual!(gl, data; processed_axis_kwargs_list)
ax1 = Axis(gl[1, 1]; processed_axis_kwargs_list[1]...)
ax2 = Axis(gl[2, 1]; processed_axis_kwargs_list[2]...)
barplot!(ax1, [1], data.population, color = :blue)
barplot!(ax2, [1], data.gdp, color = :red)
end
fig = geofacet(data, :state, plot_dual!;
axis_kwargs_list = [
(ylabel = "Population (M)",),
(ylabel = "GDP (B)", yscale = log10)
],
common_axis_kwargs = (titlesize = 12,))Custom Grid Usage
# Create a custom grid
custom_grid = GeoGrid(
["A", "B", "C", "D"],
[1, 1, 2, 2],
[1, 2, 1, 2]
)
# Use with geofacet
fig = geofacet(data, :region, plot_function!; grid = custom_grid)Plotting Function Interface
Function Signature
Plotting functions must follow this signature:
function my_plot!(gl::GridLayout, data::DataFrame; kwargs...)
# Create axis in the grid layout
ax = Axis(gl[1, 1]; kwargs...)
# Your plotting code here
lines!(ax, data.x, data.y)
# Set title to identify the region
ax.title = data.region[1]
return nothing
endMulti-Axis Functions
For plots with multiple axes (e.g., dual y-axes):
function dual_plot!(gl::GridLayout, data::DataFrame; processed_axis_kwargs_list)
# First axis
ax1 = Axis(gl[1, 1]; processed_axis_kwargs_list[1]...)
lines!(ax1, data.x, data.y1, color = :blue)
# Second axis
ax2 = Axis(gl[1, 1]; processed_axis_kwargs_list[2]...)
lines!(ax2, data.x, data.y2, color = :red)
ax1.title = data.region[1]
return nothing
endBest Practices
Always mutate: Function names should end with
!Use kwargs: Accept
kwargs...for axis stylingSet titles: Use
ax.title = data.region[1]to identify regionsHandle edge cases: Check for empty data, missing values
Return nothing: Explicitly return
nothing
Error Handling
Missing Regions
# Skip regions not in grid (default)
fig = geofacet(data, :state, plot_function!; missing_regions = :skip)
# Show empty facets for missing regions
fig = geofacet(data, :state, plot_function!; missing_regions = :empty)
# Throw error if regions are missing
fig = geofacet(data, :state, plot_function!; missing_regions = :error)Plot Function Errors
The geofacet function includes error handling for plot functions. If a plot function fails for a specific region, a warning is issued and plotting continues for other regions:
function potentially_failing_plot!(gl, data; kwargs...)
ax = Axis(gl[1, 1]; kwargs...)
if data.value[1] < 0 # This might fail for some regions
error("Negative values not supported")
end
barplot!(ax, [1], data.value)
end
# This will warn about failed regions but continue plotting others
fig = geofacet(data, :region, potentially_failing_plot!)Advanced Features
Axis Linking
# Link both X and Y axes across facets
fig = geofacet(data, :state, plot_function!; link_axes = :both)
# Link only Y axes (good for comparing magnitudes)
fig = geofacet(data, :state, plot_function!; link_axes = :y)
# Link only X axes (good for time series)
fig = geofacet(data, :state, plot_function!; link_axes = :x)
# No linking (default)
fig = geofacet(data, :state, plot_function!; link_axes = :none)Legend Creation
function plot_with_legend!(gl, data; kwargs...)
ax = Axis(gl[1, 1]; kwargs...)
barplot!(ax, [1], data.population, color = :blue, label = "Population")
lines!(ax, [0.5, 1.5], [data.gdp, data.gdp], color = :red, label = "GDP")
ax.title = data.state[1]
end
fig = geofacet(data, :state, plot_with_legend!;
legend_kwargs = (
title = "Metrics",
legend_position = (1, 4) # Row 1, Column 4
))Decoration Hiding
When axes are linked, inner decorations (tick labels, axis labels) are automatically hidden to reduce clutter:
# Inner decorations hidden automatically when axes are linked
fig = geofacet(data, :state, plot_function!;
link_axes = :y,
hide_inner_decorations = true, # Default
common_axis_kwargs = (
xlabel = "Index", # Only shown on bottom row
ylabel = "Value" # Only shown on left column
))See Also
Grid Operations - Working with geographic grids
Utilities - Helper functions and utilities
Quick Start Guide - Getting started tutorial