
Tutorial 5: Spatial Join
climasus4r Team
17/10/2026
Source:vignettes/05-juncao-espacial.Rmd
05-juncao-espacial.RmdObjective
By the end of this tutorial you will be able to assign a
geometry to each health record — linking the time series built
in the previous stage to Brazil’s official geographic
units (municipalities, biomes, health regions, metropolitan
regions, urban areas, indigenous lands, postal codes) with
sus_spatial_join(), and to integrate gridded
environmental data into the health pipeline with
sus_grid_join().
The spatial join is the fifth stage of the
climasus4r pipeline. It turns an “ordinary” table (health ×
date × municipal code) into an object of class
sf with explicit geometry — a requirement
for mapping, neighborhood computations and — critical to our driving
case — merging with socioeconomic indicators (Tutorial 6) and gridded
climate data (Tutorial 8).
Scientific context
Climate–health studies are, by nature, spatial: the exposure (temperature, pollution) and the outcome (deaths, hospitalizations) occur in concrete places. Linking a health record to its geographic unit allows mapping inequalities, controlling for contextual confounders, and defining the unit of analysis. This choice is not neutral: the Modifiable Areal Unit Problem (MAUP; Openshaw, 1984) shows that statistical results can change depending on the scale (municipality vs. census tract) and the partitioning (health regions vs. metropolitan regions) adopted.
In Brazil, official geometries come from IBGE and
are accessed through the geobr package (Pereira et al.,
2019). climasus4r wraps this access in
sus_spatial_join(), adding geometry
caching and code normalization — a
sensitive point, since the IBGE municipal code has 7
digits (including a check digit), while the Health Information
Systems usually record 6 digits. The function aligns
the two representations automatically. The coordinate reference system
adopted is SIRGAS 2000 (EPSG:4674), the official
Brazilian standard, reprojected to WGS84 (EPSG:4326) when centroids are
computed.
Geographer’s hat. Three concerns run through this stage: (i) the CRS (always use SIRGAS 2000 / WGS84 consistently); (ii) the difference between 6- and 7-digit IBGE codes (resolved by the function); and (iii) the MAUP — the scale and the delineation chosen affect inference, so document them.
Driving case. We are investigating temperature and outcomes in older adults (60+) in the São Paulo Metropolitan Region, 2014–2019. At this stage, we link the respiratory-death series (output of Tutorial 4) to the municipalities of the São Paulo Metropolitan Region (RMSP), obtaining the spatial object that Tutorial 6 (Socioeconomic Linkage) will use.
Data used
| Input | Source | Role in the case |
|---|---|---|
caso_serie.rds |
Tutorial 4 (series aggregated by municipality × date) | Health input |
| Municipal geometries | IBGE via geobr::read_municipality()
|
RMSP polygons |
| Climate grid (demonstration) | synthetic (real climate enters in T8) | Illustrates sus_grid_join()
|
The input is the object
dados/caso_serie.rds (loaded with an
file.exists() guard; in its absence, the script uses a
minimal synthetic example). The output is
dados/caso_espacial.rds, which feeds
Tutorial 6.
Full pipeline
The blocks below reproduce what the script
scripts/tutorial_05_juncao_espacial.R executes. Start by
loading the package:
library(climasus4r)Load the health series (output of Tutorial 4)
# A etapa anterior salvou a série agregada (óbitos respiratórios × município × data).
caso_serie <- readRDS("dados/caso_serie.rds")
serie_saude <- if (is.data.frame(caso_serie)) caso_serie else caso_serie$serieLink the series to municipalities with
sus_spatial_join()
sus_spatial_join() automatically
detects the municipal code column
(join_col = NULL), downloads the geometries from
geobr (with caching) and returns an
sf object at
stage = "spatial". The default level is
"munic":
caso_espacial <- sus_spatial_join(
df = serie_saude,
level = "munic", # município (padrão)
join_col = NULL, # auto-detecção da coluna geográfica
lang = "pt",
use_cache = TRUE, # reaproveita geometrias já baixadas
cache_dir = "~/.climasus4r_cache/spatial",
verbose = TRUE
)The function accepts many other geographic
levels — simply change level:
# Bioma (chapéu ambiental):
sf_bioma <- sus_spatial_join(serie_saude, level = "biomes", lang = "pt")
# Região metropolitana:
sf_metro <- sus_spatial_join(serie_saude, level = "metro_area", lang = "pt")
# Geocodificação por CEP (somente sistemas SIH e CNES, gera POINTs):
sf_cep <- sus_spatial_join(df_sih, level = "cep", lang = "pt")Integrate gridded environmental data with
sus_grid_join()
sus_grid_join() is the bridge between
the environmental pipeline (sus_grid_*, which produces
objects at stage = "climate") and the health pipeline. It
merges the two tables on the space-time key
c("code_muni", "date") (real climate data is introduced in
Tutorial 8; here the usage is illustrative):
# grid_clim é a saída de uma função sus_grid_*() (ERA5, CHIRPS, ...), stage = "climate"
caso_clima <- sus_grid_join(
health_data = caso_espacial,
grid_data = grid_clim,
by = c("code_muni", "date"), # chave espaço-temporal
type_out = NULL, # herda o type do grid
lang = "pt",
verbose = TRUE
)
sus_meta(caso_clima, "stage") # "climate"Temporal granularity. If health data are monthly and the grid is daily, joining on
dateproduces manyNAs. In that case, aggregate the grid first (withsus_climate_aggregate()), or, for annual grids (e.g., PRODES), join only onby = c("code_muni")— the annual value is replicated for every month.
At the end of this stage we have caso_espacial — the
health series with geometry, saved for Tutorial 6.
Function characteristics
sus_spatial_join()
Links health data to Brazil’s official geometries
(geobr) or geocodes postal codes (CEP). Requires a
climasus_df at minimum stage
"stand" and returns an sf object at
stage = "spatial".
| Argument | Essential? | Description |
|---|---|---|
df |
yes |
climasus_df (at least standardized) with a geographic
identifier column |
level |
yes | Geographic unit. "munic" (default);
"biomes", "health_region",
"amazon", "semiarid",
"conservation_units", "disaster_risk_area",
"indigenous_land", "urban_area",
"metro_area", "urban_concentrations",
"pop_arrangements", "schools",
"health_facilities", "neighborhood",
"cep"
|
join_col |
optional | Column with the geographic code; NULL (default)
auto-detects |
use_cache / cache_dir
|
recommended | Geometry cache at ~/.climasus4r_cache/spatial
|
lang / verbose
|
optional | Language ("pt", "en", "es")
and progress messages |
System restrictions: "cep" and
"neighborhood" only for SIH/CNES;
"health_facilities" only for CNES.
Return: an sf object
(climasus_df, stage = "spatial",
type = level) with the original columns plus
geometry — POLYGON/MULTIPOLYGON for area
levels, POINT for
"cep"/"health_facilities".
Code normalization. Accepts municipal codes with
6 or 7 digits (the check digit is handled
automatically); CEP is left-padded with zeros (8 characters); all keys
are coerced to text. The area join is an inner_join — codes
with no matching geometry are dropped (see Figure
3).
sus_grid_join()
Merges the output of any sus_grid_*() (at
stage = "climate") with a health climasus_df
(at stage "spatial", "aggregate",
or "census"), producing a climasus_df at
stage = "climate".
| Argument | Essential? | Description |
|---|---|---|
health_data |
yes | Health climasus_df with code_muni and
date
|
grid_data |
yes |
climasus_df at stage = "climate" (output
of sus_grid_*) |
by |
optional | Join key; default c("code_muni", "date"); use
c("code_muni") for an annual grid |
type_out |
optional | Overrides the resulting type; NULL
inherits it from the grid |
lang / verbose
|
optional | Language and messages |
Return: climasus_df
(stage = "climate") with the health columns plus the
environmental variables; uses left_join,
so health rows with no matching grid receive NA. The
sf geometry is dropped before the join (the join is
tabular, on the key). The function warns about
column-name collisions and about temporal-granularity
mismatches.
Formalization
The area join can be seen as a mapping from the municipal key (IBGE code) to the geometry of the set of geometries :
where is a health record and its municipal code. The match rate (quality control in Figure 3) is
the fraction of input codes
that find a matching geometry in
.
For sus_grid_join(), the fraction of rows with missing
climate is
,
directly reported in the completion message.
Illustrated results

Figure 1. Choropleth map of respiratory deaths by municipality after the spatial join (RMSP, 2014–2019). Each polygon now carries the health outcome — the basis for mapping inequalities and cross-referencing with contextual indicators.

Figure 2. Monthly series preserved after the join. The spatial join adds geometry without altering the temporal structure: the time dimension remains intact for modeling (Tutorial 9).

Figure 3. Coverage diagnostic of the join. Because
sus_spatial_join() uses inner_join, municipal
codes with no matching geometry in geobr are dropped.
Checking this rate is an essential quality control (it avoids silent
loss of records).
| Item | Valor |
|---|---|
| Funcao | sus_spatial_join() |
| Nivel (level) | munic (municipio) |
| Coluna de juncao | code_muni (auto-detectada) |
| Sistema de referencia (CRS) | definido |
| Linhas antes da juncao | 359 |
| Linhas depois da juncao | 359 |
| Municipios distintos | 5 |
| Tipo de geometria | MULTIPOLYGON (municipio) |
| code_muni | name_muni | abbrev_state | date |
|---|---|---|---|
| 350950 | Campinas | SP | 2014-01-01 |
| 350950 | Campinas | SP | 2014-02-01 |
| 350950 | Campinas | SP | 2014-03-01 |
| 350950 | Campinas | SP | 2014-04-01 |
| 350950 | Campinas | SP | 2014-05-01 |
| 350950 | Campinas | SP | 2014-06-01 |
| 350950 | Campinas | SP | 2014-07-01 |
| 350950 | Campinas | SP | 2014-08-01 |
| 350950 | Campinas | SP | 2014-09-01 |
| 350950 | Campinas | SP | 2014-10-01 |
References
- Openshaw, S. (1984). The Modifiable Areal Unit Problem. CATMOG 38. Norwich: Geo Books.
- Pereira, R. H. M., Gonçalves, C. N., et al. (2019). geobr: Loads Shapefiles of Official Spatial Data Sets of Brazil. GitHub: https://github.com/ipeaGIT/geobr.
- Pereira, R. H. M., & Barbosa, R. J. (2023). censobr: Download Data from Brazil’s Population Census. R package v0.4.0. DOI: 10.32614/CRAN.package.censobr.
- IBGE — Instituto Brasileiro de Geografia e Estatística. Geocentric reference system SIRGAS 2000.
Navigation
- Back to tutorial hub
- Previous: Tutorial 4 — Variable Creation and Aggregation
- Next: Tutorial 6 — Socioeconomic Linkage
```