Advanced Analysis with Swiss Data
swiss_data.Rmd
This vignette demonstrates an advanced workflow for analyzing and correcting a map with complex, non-linear distortions. While the first vignette showed a general correction, this one focuses on a more challenging real-world scenario.
We will use the swiss_cps
dataset, which is included
with the mapAI
package. The key feature of this dataset is
that the gross, linear distortions have already been removed with a
Helmert transformation. The remaining errors are subtle, non-linear, and
spatially variable—a perfect test case for the more advanced models in
mapAI
.
Our goal is to model these non-linear residuals and visualize the effect of the correction on a reference line grid.
# Load the necessary libraries for our workflow
library(mapAI)
library(sf)
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE
library(ggplot2)
library(magrittr) # For the %>% pipe operator
Step 1: Load and Explore the Data
We begin by loading the swiss_cps
dataset. This
sf
object contains 343 control points where the
source
coordinates have been pre-aligned, so we can focus
on the residual dx
and dy
errors.
# Load the built-in Swiss control points dataset
data(swiss_cps)
# Inspect the data structure
# Note that dx and dy are relatively small, representing the non-linear errors.
dplyr::glimpse(swiss_cps)
#> Rows: 343
#> Columns: 8
#> $ Index <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18…
#> $ source_x <dbl> 612293.0, 612225.0, 615470.3, 616339.8, 616927.1, 617571.9, 6…
#> $ source_y <dbl> 267353.6, 270412.7, 270195.1, 269367.5, 269507.6, 269273.5, 2…
#> $ target_x <dbl> 611375.9, 611573.1, 615840.8, 616129.6, 617031.0, 618168.7, 6…
#> $ target_y <dbl> 267719.1, 270370.6, 270463.6, 269380.6, 268932.1, 269179.3, 2…
#> $ dx <dbl> -917.1063, -651.9320, 370.4737, -210.1946, 103.8862, 596.7662…
#> $ dy <dbl> 365.468249, -42.050863, 268.478200, 13.144895, -575.530782, -…
#> $ geometry <POINT [m]> POINT (612293 267353.6), POINT (612225 270412.7), POINT…
# Let's visualize the residual displacement vectors
plot_displacement(swiss_cps, title = "Residual Non-Linear Distortions")
The plot
shows that even after a global alignment, there are clear spatial
patterns in the remaining errors, which a simple linear model could not
fix.
Step 2: Create a Reference Grid to Deform
To best visualize the distortion, we will create a regular grid of lines that covers the extent of our control points. This will serve as our “source map.” We will then apply our correction model to this grid to see how it is “un-warped.”
# Create a grid of polygons covering the data extent
grid_polygons <- sf::st_make_grid(swiss_cps, n = c(15, 15))
# Convert the polygon boundaries to a MULTILINESTRING object
# This gives us a clean line grid for visualization.
source_grid <- sf::st_cast(grid_polygons, "MULTILINESTRING") %>%
sf::st_sf() # Convert to a full sf object
# Plot the original, regular grid
plot(sf::st_geometry(source_grid), main = "Original, Regular Reference Grid")
Step 3: Train a Non-Linear Model
Because the linear components of the distortion have already been
removed from the swiss_cps
data, using a
helmert
or lm
model would be ineffective. This
is the perfect scenario to use a Generalized Additive Model
(gam
), which is designed to capture exactly these
kinds of smooth, non-linear spatial patterns.
# Train the GAM model to learn the residual distortion patterns
gam_model <- train_pai_model(swiss_cps, method = "gam")
#> Training 'gam' model...
Step 4: Apply the Correction to the Grid
Now, we use our trained gam_model
to correct the regular
line grid we created. The apply_pai_model
function will
transform every vertex in the grid according to the learned
distortion.
# Apply the model to our source grid
corrected_grid <- apply_pai_model(gam_model, source_grid)
#> Applying PAI model to map features...
#> Correction complete.
Step 5: Visualize the Corrected Grid
This is the most intuitive part of the analysis. We overlay the corrected grid on the original grid. The correction will transform the regular, straight-lined grid to a warped one, in order to correct it.
# For easy plotting, add a 'status' column to each grid
source_grid$status <- "Original (Regular)"
corrected_grid$status <- "Corrected (Warped by model)"
# Combine them into a single sf object for ggplot2
comparison_data <- rbind(
source_grid[, "status"],
corrected_grid[, "status"]
)
ggplot(comparison_data) +
geom_sf(aes(color = status, linetype = status), linewidth = 0.6) +
scale_color_manual(
name = "Grid Status",
values = c("Original (Regular)" = "grey50",
"Corrected (Warped by model)" = "#e41a1c")
) +
scale_linetype_manual(
name = "Grid Status",
values = c("Original (Regular)" = "dashed",
"Corrected (Warped by model)" = "solid")
) +
labs(
title = "Visualizing the Non-Linear Correction",
subtitle = "The GAM model transforms the regular shape to warped grid"
) +
theme_minimal()
Step 6: In-Depth Distortion Analysis
Finally, let’s use the advanced analysis functions to quantify the distortion that the model learned. We will analyze the distortion on a dense grid of points and visualize the results.
# 1. Create a dense grid of POINTS for analysis
analysis_points <- sf::st_make_grid(swiss_cps, n = c(25, 25)) %>%
sf::st_centroid() %>%
sf::st_sf()
# 2. Analyze the distortion using our trained GAM model
distortion_results <- analyze_distortion(gam_model, analysis_points)
#> Calculating distortion metrics for gam model...
#> Finalizing metrics from derivatives...
#> Distortion analysis complete.
# 3. Visualize the results for two key metrics
plot_shear <- plot_distortion_surface(
distortion_results,
metric = "max_shear",
diverging = FALSE,
palette = "magma"
) +
labs(title = "Maximum Angular Distortion (°)")
#> Regular grid detected. Creating a surface plot with geom_raster().
plot_area <- plot_distortion_surface(
distortion_results,
metric = "log2_area_scale",
diverging = TRUE
) +
labs(title = "Log-Areal Distortion (log2)")
#> Regular grid detected. Creating a surface plot with geom_raster().
# Combine plots using the patchwork library
if (requireNamespace("patchwork", quietly = TRUE)) {
plot_shear / plot_area
} else {
plot_shear
}
#> Warning: Raster pixels are placed at uneven horizontal intervals and will be shifted
#> ℹ Consider using `geom_tile()` instead.
#> Raster pixels are placed at uneven horizontal intervals and will be shifted
#> ℹ Consider using `geom_tile()` instead.
#> Raster pixels are placed at uneven horizontal intervals and will be shifted
#> ℹ Consider using `geom_tile()` instead.
#> Raster pixels are placed at uneven horizontal intervals and will be shifted
#> ℹ Consider using `geom_tile()` instead.
This final analysis provides a quantitative map of the distortion,
revealing complex patterns of shearing and scaling across the map extent
that the gam
model successfully identified and
corrected.