flowchart TD
%% Node Definitions
DEM[(Raw DEM Elevation)]
subgraph A[Spatial Processing]
direction TB
BLUR[(Focal Blur / Smoothing)]
CONT[(Extract Contour Polygons)]
SHIFT[(Geometry Translation)]
end
subgraph B[Aesthetic Rendering]
direction TB
SHADOW[Render Base Shadows]
FILL[Color Thievery / Gradient]
EDGE[Crisp Edge Highlights]
TEX[Paper Texture Overlay]
end
OUT[Pseudo-3D Map]
%% Connections
DEM ==> BLUR
BLUR ==> CONT
CONT ==> SHIFT
CONT ==> FILL
SHIFT ==> SHADOW
SHADOW ==> EDGE
FILL ==> EDGE
EDGE ==> TEX
TEX ==> OUT
%% Classes
classDef output fill:#f9f,stroke:#333,stroke-width:2px;
class OUT output;
Contours
Examples
Pseudo-3D Terrain Generation
John Nelson’s brilliant ArcGIS Pro tutorial demonstrates how to create a stunning, tactile, “trippy” AI-generated aesthetic using real elevation data. The workflow relies on heavily blurring a Digital Elevation Model (DEM), extracting contour polygons, and using a drop-shadow rendering “hack” to create a pseudo-3D isometric look.
Because the methodology is highly geometric, it’s an instructive exercise in spatial data manipulation to replicate this exact style entirely using open-source R packages.
Background The pseudo-3D map is generated by generalizing high-resolution elevation data so the resulting contours are sweeping and smooth. Then, rather than rendering 3D scenes with heavy ray-tracing, we manually shift the 2D polygon geometries to simulate light and depth.
Our workflow to generate this surface will look like so:
To make this problem computationally tractable while keeping the elegant sweeping lines, we apply a focal smoothing window over the DEM. This replicates the “Blur” step in the tutorial, washing out tiny terrain irregularities.
# 1. Blur the DEM for smooth, generalized contours
# Using a 9x9 moving window for an aggressive smoothing effect
w <- matrix(1/81, nc = 9, nr = 9)
r_smoothed <- terra::focal(r_dem, w, fun = mean, na.policy = "omit")
# 2. Creating contour polygons from the DEM
# We extract polygons rather than lines to fill them with color later
contour_polys <- terra::as.polygons(r_smoothed, trunc = FALSE, round = FALSE, breaks = seq(90, 200, by = 5))
contours_sf <- sf::st_as_sf(contour_polys) |>
dplyr::rename(elevation = 1) |>
dplyr::mutate(layer_id = dplyr::row_number())
# 3. Ferris Bueller style summary of the hack:
# We copy the geometries and physically shift their coordinates down and right
# to create an artificial "drop shadow" layer.
shift_x <- 15
shift_y <- -15
shadows_sf <- contours_sf
sf::st_geometry(shadows_sf) <- sf::st_geometry(shadows_sf) + c(shift_x, shift_y)
sf::st_crs(shadows_sf) <- sf::st_crs(contours_sf) # Re-assign CRS after shiftWith the raw geometry prepared, we can execute the rendering. The tutorial emphasizes “Color Thievery” and crisp edge highlights. In {ggplot2}, we stack our geometries: the shifted shadows go down first with a dark, semi-transparent fill, followed by the true contours topped with a crisp, bright stroke.
# Color thievery: Let's use a trippy, vibrant palette
trippy_pal <- colorRampPalette(c("#0d1b2a", "#1b263b", "#415a77", "#778da9", "#e0e1dd", "#ff9f1c", "#e71d36"))(nrow(contours_sf))
p_map <- ggplot2::ggplot() +
# The Hack: Isolating the shading into a separate layer
ggplot2::geom_sf(data = shadows_sf, fill = "#111111", color = NA, alpha = 0.35) +
# The true contours with a crisp edge highlight (color = "#ffffff")
ggplot2::geom_sf(data = contours_sf, ggplot2::aes(fill = elevation), color = "#ffffff", linewidth = 0.3) +
ggplot2::scale_fill_gradientn(colors = trippy_pal, guide = "none") +
ggplot2::theme_void() +
ggplot2::theme(
panel.background = ggplot2::element_rect(fill = "#f4f1de", color = NA),
plot.background = ggplot2::element_rect(fill = "#f4f1de", color = NA),
plot.margin = ggplot2::margin(20, 20, 20, 20)
)
p_map