Skip to content

Commit 250e6b4

Browse files
authored
Merge pull request #44 from dieghernan/dev
Add Antarctic images
2 parents 4f6fa41 + b42b67c commit 250e6b4

28 files changed

+1297
-11
lines changed
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
---
2+
title: "Mapping Antarctica"
3+
subtitle: "Cool maps from the South Pole"
4+
tags:
5+
- r_bloggers
6+
- beautiful_maps
7+
- rstats
8+
- rspatial
9+
- maps
10+
- ggplot2
11+
- sf
12+
- giscoR
13+
output:
14+
html_document:
15+
md_document:
16+
variant: gfm
17+
preserve_yaml: true
18+
header_img: xxx.png
19+
---
20+
21+
```{r setup, include=FALSE}
22+
knitr::opts_chunk$set(
23+
collapse = TRUE,
24+
comment = "#>",
25+
warning = FALSE,
26+
message = FALSE,
27+
dev = "ragg_png",
28+
dpi = 300,
29+
tidy = "styler",
30+
out.width = "100%",
31+
fig.cap = ""
32+
)
33+
rm(list = ls())
34+
```
35+
36+
Creating maps with **R** is usually straightforward, but representations that
37+
cross the [International Date
38+
Line](https://en.wikipedia.org/wiki/International_Date_Line) or that use polar
39+
projections can be tricky.
40+
41+
Different spatial-data providers use different conventions: some break
42+
geometries at certain longitudes (for example, cutting the Chukchi Peninsula),
43+
while others omit portions of the data. These inconsistencies can produce
44+
awkward artifacts near the poles.
45+
46+
In this post I fix the [GISCO (European
47+
Commission)](https://ec.europa.eu/eurostat/web/gisco) shapefile for Antarctica
48+
and produce clean orthographic maps. I walk through the manual corrections and
49+
then create a few example maps.
50+
51+
```{r}
52+
# Libraries
53+
library(tidyverse)
54+
library(sf)
55+
library(giscoR)
56+
library(ggrepel)
57+
library(rmapshaper)
58+
```
59+
60+
## Fixing the geometry
61+
62+
First, we obtain the GISCO Antarctica polygon and transform it to an
63+
orthographic projection centered on the South Pole.
64+
65+
```{r 202510_init}
66+
antarct <- gisco_get_countries(year = 2024, resolution = 1, country = "ATA") %>%
67+
select(NAME = NAME_ENGL) |>
68+
# Ortho proj centered in the South Pole
69+
st_transform(crs = "+proj=ortho +lat_0=-90 +lon_0=0")
70+
71+
ggplot(antarct) +
72+
geom_sf(fill = "lightblue")
73+
```
74+
75+
The shapefile contains a visible "lollipop" cut that looks unnatural in an
76+
orthographic projection. I correct it manually by:
77+
78+
1. Identify the polygon that represents the main Antarctic landmass.
79+
2. Convert that polygon to a sequence of coordinates (points).
80+
3. Remove the small sequence of points that create the artifact.
81+
4. Rebuild the polygon from the cleaned coordinates and replace the broken
82+
geometry with the corrected one.
83+
84+
We convert polygons to point coordinates and inspect them to find the offending
85+
sequence:
86+
87+
```{r 202510_init_ant}
88+
# Identify the max
89+
ant_explode <- antarct |>
90+
st_cast("POLYGON")
91+
92+
nrow(ant_explode)
93+
94+
# Max polygon
95+
96+
ant_max <- ant_explode[which.max(st_area(ant_explode)), ]
97+
98+
coords <- st_coordinates(ant_max) |>
99+
as_tibble() |>
100+
# Add id for points
101+
mutate(np = row_number())
102+
103+
104+
ggplot(coords, aes(X, Y)) +
105+
geom_point(size = 0.05, color = "darkblue") +
106+
geom_text(aes(label = np), check_overlap = TRUE) +
107+
coord_equal()
108+
```
109+
110+
From the plotted indices, we can see the problematic points fall roughly in the
111+
range 8200–9200. We inspect that interval in detail to select the exact indices
112+
to remove.
113+
114+
```{r 202510_test_points}
115+
test <- coords |>
116+
filter(np %in% seq(8200, 9200))
117+
118+
test |>
119+
ggplot(aes(X, Y)) +
120+
geom_point(size = 0.05, color = "darkblue") +
121+
geom_text(aes(label = np), check_overlap = TRUE)
122+
```
123+
124+
::: {.alert .alert-warning .p-3 .mx-2 .mb-3}
125+
Note: This cleaning is tailored to this specific shapefile and may need to be
126+
repeated for other shapefiles. The approach is straightforward but depends on
127+
the particular geometry and projection.
128+
:::
129+
130+
```{r 202510_final_sol, out.width="50%", fig.show="hold"}
131+
# Final solution after some iterations...
132+
133+
test |>
134+
filter(np %in% seq(8289, 9130)) |>
135+
ggplot(aes(X, Y)) +
136+
geom_point(color = "darkblue") +
137+
labs(title = "To remove")
138+
139+
140+
test |>
141+
filter(!np %in% seq(8289, 9130)) |>
142+
ggplot(aes(X, Y)) +
143+
geom_point(color = "darkblue") +
144+
labs(title = "To keep")
145+
```
146+
147+
After removing the offending points, we rebuild the polygon and reconstitute the
148+
full Antarctica shape from the corrected piece plus the remaining polygons.
149+
150+
```{r 202510_good_pol}
151+
# From coordinates to polygon
152+
newpol <- coords |>
153+
as.data.frame() |>
154+
filter(!np %in% seq(8289, 9130)) |> # Removing offending points
155+
select(X, Y) |>
156+
as.matrix() |>
157+
list() |>
158+
st_polygon() |>
159+
st_sfc() |>
160+
st_set_crs(st_crs(ant_max))
161+
162+
ant_max_fixed <- st_sf(st_drop_geometry(ant_max), geometry = newpol)
163+
164+
# Regenerate initial shape
165+
antarctica_fixed <- bind_rows(
166+
ant_max_fixed,
167+
ant_explode[-which.max(st_area(ant_explode)), ]
168+
) |>
169+
group_by(NAME) |>
170+
summarise(m = 1) |>
171+
select(-m) |>
172+
st_make_valid()
173+
174+
antarctica_fixed
175+
176+
ggplot(antarctica_fixed) +
177+
geom_sf(fill = "lightblue")
178+
```
179+
180+
## Plotting examples
181+
182+
With the corrected shape we can produce maps. Below are a few examples based on
183+
proposed Antarctic flag designs.
184+
185+
### Graham Bartram's proposal (1996)
186+
187+
A simple rendition of Bartram's original concept:
188+
189+
```{r 202510_bartram}
190+
bbox <- st_bbox(antarctica_fixed) # For limits on the panel
191+
192+
antarctica_fixed |>
193+
ggplot() +
194+
geom_sf(fill = "white", color = NA) +
195+
theme(
196+
panel.background = element_rect(fill = "#009fdc"),
197+
panel.grid = element_blank(),
198+
axis.text = element_blank(),
199+
axis.ticks = element_blank()
200+
) +
201+
labs(title = "Graham Bartram's proposal") +
202+
coord_sf(
203+
xlim = c(bbox[c(1, 3)]) * 1.8,
204+
ylim = c(bbox[c(2, 4)]) * 1.4
205+
)
206+
```
207+
208+
### Emblem of the Antarctic Treaty
209+
210+
This example uses graticules to create a concentric "bullseye" pattern around
211+
Antarctica. Generating such graticules and merging meridians requires a few
212+
extra steps to avoid small gaps near the pole.
213+
214+
```{r 202510_treaty1}
215+
# Need graticules
216+
grats <- giscoR::gisco_get_countries() |>
217+
st_transform(st_crs(antarctica_fixed)) |>
218+
# Specify the cuts of the graticules
219+
st_graticule(
220+
lat = c(-80, -70, -60),
221+
lon = seq(-180, 180, 30),
222+
ndiscr = 10000,
223+
margin = 0.000001
224+
)
225+
226+
227+
ggplot(grats) +
228+
geom_sf(color = "darkblue")
229+
```
230+
231+
We merge meridians so the area around the South Pole is filled. `st_graticule()`
232+
can leave a tiny hole at the pole; we fix this by joining complementary
233+
meridians.
234+
235+
```{r 202510_fixgrats}
236+
# Merge meridians
237+
merid <- lapply(seq(-180, 0, 30), function(x) {
238+
df <- grats |>
239+
filter(type == "E") |>
240+
filter(degree %in% c(x, x + 180))
241+
242+
df2 <- df |>
243+
st_geometry() |>
244+
st_cast("MULTIPOINT") |>
245+
st_union() |>
246+
st_cast("LINESTRING")
247+
248+
sf_x <- st_sf(
249+
degree = x,
250+
type = "E",
251+
geometry = df2
252+
)
253+
}) |> bind_rows()
254+
255+
256+
grats_end <- merid |>
257+
bind_rows(grats |>
258+
filter(type != "E"))
259+
```
260+
261+
We then cut and color the resulting graticules so they form the emblem-like
262+
pattern.
263+
264+
```{r 202510_treaty2}
265+
# Cut since some grats should be colored differently
266+
267+
antarctica_simp <- rmapshaper::ms_simplify(antarctica_fixed, keep = 0.005)
268+
grats_yes <- st_intersection(grats_end, antarctica_simp)
269+
grats_no <- st_difference(grats_end, antarctica_simp)
270+
271+
antarctica_simp |>
272+
ggplot() +
273+
geom_sf(fill = "white", color = NA) +
274+
theme(
275+
panel.background = element_rect(fill = "#072b5f"),
276+
panel.grid = element_blank(),
277+
axis.text = element_blank(),
278+
axis.ticks = element_blank()
279+
) +
280+
geom_sf(data = grats_yes, color = "#072b5f", linewidth = 1) +
281+
geom_sf(data = grats_no, color = "white", linewidth = 1) +
282+
coord_sf(
283+
xlim = c(bbox[c(1, 3)]) * 1.8,
284+
ylim = c(bbox[c(2, 4)]) * 1.4
285+
) +
286+
labs(title = "Emblem of the Antarctic Treaty")
287+
```
288+
289+
### Antarctica Flag Redesigned
290+
291+
In 2024, Graham Bartram presented a refreshed version of his flag that preserves
292+
the white Antarctic outline but fills the blue field with many tiny, colorful
293+
dots representing microplastics - even in remote regions. Because the design
294+
relies on randomness, we approximate it using the following procedure:
295+
296+
1. Sample random points across the Antarctic polygon.
297+
2. Build Voronoi polygons from those points, then apply a small negative buffer
298+
to create gaps.
299+
3. Randomly sample the resulting polygons to increase visual noise.
300+
4. Color polygons so larger areas remain white while smaller polygons use
301+
magenta/pink tones.
302+
303+
```{r 202510_redesign}
304+
# Maximum chunk of Antarctica, the one that we fixed
305+
306+
ant_max_fixed
307+
308+
set.seed(2024)
309+
# Sample, Voronoi and negative buffer
310+
plastics <- st_sample(ant_max_fixed, 3000) |>
311+
st_union() |>
312+
st_voronoi(envelope = st_geometry(ant_max_fixed)) |>
313+
st_collection_extract() |>
314+
st_buffer(dist = -10000)
315+
316+
317+
# Keep only those properly included in the outline
318+
319+
toinc <- st_contains_properly(ant_max_fixed, plastics, sparse = FALSE) |>
320+
as.vector()
321+
322+
# Select random chunks
323+
plastic_end <- plastics[toinc, ] |>
324+
st_as_sf() |>
325+
slice_sample(prop = 0.75)
326+
327+
ggplot(plastic_end) +
328+
geom_sf(fill = "darkblue")
329+
330+
331+
# Random coloring
332+
333+
plastic_end$area <- st_area(plastic_end) |> as.double()
334+
335+
plastic_end$fill <- sample(c("#ff00ec", "#9e00ec"), nrow(plastic_end), replace = TRUE)
336+
plastic_end$fill <- ifelse(plastic_end$area > quantile(plastic_end$area, probs = 0.4),
337+
"white",
338+
plastic_end$fill
339+
)
340+
341+
bbox2 <- st_bbox(plastic_end)
342+
ggplot() +
343+
geom_sf(data = plastic_end, aes(fill = fill), color = NA) +
344+
scale_fill_identity() +
345+
theme(
346+
panel.background = element_rect(fill = "#009fdc"),
347+
panel.grid = element_blank(),
348+
axis.text = element_blank(),
349+
axis.ticks = element_blank()
350+
) +
351+
labs(title = "New redesign") +
352+
coord_sf(
353+
xlim = c(bbox2[c(1, 3)] * 1.8),
354+
ylim = c(bbox2[c(2, 4)]) * 1.4
355+
)
356+
```

_Rmd/2025-10-24-mapping-antarctica.html

Lines changed: 717 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)