Menganimasikan Rute GPX di R

Menghidupkan jalur trekking dengan {gganimate}
r
gis
data-viz
gganimate
strava
Penulis

Ryan Avriandy

Diterbitkan

Sunday, November 1, 2026

Di tulisan sebelumnya, saya menutup dengan kalimat “kedepannya mungkin saya akan berbagi cara bagaimana menganimasikan rute tersebut”. Ternyata “kedepannya” itu butuh waktu tiga tahun. Tapi janji tetaplah janji, jadi kali ini kita akan melunasinya.

Kalau kamu pengguna Strava, kamu mungkin pernah melihat fitur Flyby, di mana jalur aktivitas kita diputar ulang seperti film pendek. Rasanya jauh lebih hidup dibanding melihat garis diam di atas peta. Kabar baiknya, kita bisa membuat versi sederhananya sendiri di R dengan paket {gganimate}, yang menambahkan dimensi waktu ke dalam plot ggplot2.

Data dan paket yang diperlukan

Kita masih menggunakan data yang sama, yaitu rute trekking di Bukit Paniisan. Unduh datanya melalui tautan berikut ini; data_trekking_gpx. Lalu pasang paket-paket di bawah ini. Paket gifski diperlukan agar {gganimate} bisa menyimpan animasi dalam format gif.

Membaca dan merapikan data GPX

Langkah ini adalah versi ringkas dari tulisan sebelumnya. Kita membaca titik koordinat, elevasi dan waktu, lalu menghitung jarak antar titik GPS berturut-turut dengan formula Haversine.

# Membaca data gpx
gpx_data <- htmlTreeParse(file = "Ngebukit.gpx", useInternalNodes = TRUE)

# Mengakses data koordinat, elevasi dan waktu
coords    <- xpathSApply(doc = gpx_data, path = "//trkpt", fun = xmlAttrs)
elevation <- xpathSApply(doc = gpx_data, path = "//trkpt/ele", fun = xmlValue)
datetime  <- xpathSApply(doc = gpx_data, path = "//trkpt/time", fun = xmlValue)

# Mengatur ke dalam dataframe
df <- data.frame(
  lon = as.numeric(coords["lon", ]),
  lat = as.numeric(coords["lat", ]),
  elevation = as.numeric(elevation),
  datetime = as_datetime(datetime, tz = "Asia/Jakarta")
)

# Menghitung jarak antar titik, jarak akumulasi dan waktu tempuh
df <- df %>%
  mutate(dist_m = distHaversine(cbind(lon, lat), cbind(lead(lon), lead(lat)))) %>%
  filter(!is.na(dist_m), dist_m > 0) %>%
  mutate(jarak_km = cumsum(dist_m) / 1000,
         menit = as.numeric(difftime(datetime, first(datetime), units = "mins")))

head(df)
       lon       lat elevation            datetime   dist_m    jarak_km
1 106.9304 -6.592982     481.4 2023-09-03 08:48:20 1.197857 0.001197857
2 106.9304 -6.592972     481.3 2023-09-03 08:48:21 1.055373 0.002253230
3 106.9304 -6.592963     481.3 2023-09-03 08:48:22 1.055373 0.003308603
4 106.9304 -6.592954     481.3 2023-09-03 08:48:23 1.055373 0.004363976
5 106.9304 -6.592945     481.3 2023-09-03 08:48:24 1.095177 0.005459154
6 106.9304 -6.592936     481.3 2023-09-03 08:48:25 3.202601 0.008661755
       menit
1 0.00000000
2 0.01666667
3 0.03333333
4 0.05000000
5 0.06666667
6 0.08333333

Data ini berisi hampir 4.000 titik, karena GPS merekam posisi setiap detik. Untuk animasi, sebanyak itu terlalu berlebihan dan hanya akan memperlambat proses rendering. Jadi kita ambil satu dari setiap sepuluh titik saja, tanpa mengubah bentuk jalurnya.

df_anim <- df %>% slice(seq(1, n(), by = 10))

nrow(df_anim)
[1] 381

Replay jalur di atas peta

Kunci dari animasi ini adalah fungsi transition_reveal(), yang “membuka” data sedikit demi sedikit mengikuti suatu variabel, dalam hal ini jarak tempuh (jarak_km). Agar jalur lengkapnya tetap terlihat sebagai bayangan abu-abu, kita masukkan lapisan tersebut dengan data yang tidak memiliki kolom jarak_km, sehingga {gganimate} membiarkannya diam.

Warna jalur menunjukkan elevasi, sehingga kita bisa melihat kapan mulai menanjak dan kapan mulai turun.

p_rute <- ggplot(df_anim, aes(x = lon, y = lat)) +
  geom_path(data = select(df, lon, lat), colour = "grey85", linewidth = 1) +
  geom_path(aes(colour = elevation), linewidth = 1.2) +
  geom_point(aes(colour = elevation), size = 4) +
  scale_colour_viridis_c(option = "magma", name = "Elevasi (m)") +
  coord_quickmap() +
  labs(title = "Trekking Bukit Paniisan",
       subtitle = "Jarak: {round(frame_along, 2)} km",
       x = NULL, y = NULL) +
  theme_minimal() +
  transition_reveal(jarak_km)

animate(p_rute, nframes = 100, fps = 10, width = 600, height = 600, units = "px", res = 96,
        renderer = gifski_renderer())

Perhatikan bagian subtitle. Teks di dalam kurung kurawal {} akan diperbarui di setiap frame, dan frame_along adalah nilai jarak pada frame tersebut. Karena rute ini pulang-pergi melalui jalur yang sama, di paruh kedua animasi kamu akan melihat titiknya kembali turun menyusuri jalur yang sudah dilewati.

Profil elevasi yang berjalan

Animasi kedua mungkin lebih menarik bagi para pendaki, yaitu profil elevasi. Di sini kita bisa merasakan kembali “penderitaan” tanjakan sepanjang dua kilometer pertama, dari sekitar 480 m hingga puncaknya di sekitar 830 m dpl.

p_elev <- ggplot(df_anim, aes(x = jarak_km, y = elevation)) +
  geom_area(data = select(df, jarak_statis = jarak_km, elevation),
            aes(x = jarak_statis), stat = "identity", fill = "grey90") +
  geom_line(colour = "#118230", linewidth = 1) +
  geom_point(colour = "#CF4446", size = 3) +
  coord_cartesian(ylim = c(min(df$elevation) - 20, max(df$elevation) + 40)) +
  labs(title = "Profil elevasi",
       subtitle = "Jarak: {round(frame_along, 2)} km | Elevasi: {round(approx(df$jarak_km, df$elevation, frame_along)$y)} m",
       x = "Jarak (km)", y = "Elevasi (m)") +
  theme_bw() +
  transition_reveal(jarak_km)

animate(p_elev, nframes = 100, fps = 10, width = 700, height = 400, units = "px", res = 96,
        renderer = gifski_renderer())

Trik kecil di bagian subtitle: fungsi approx() mencari nilai elevasi pada jarak frame_along, sehingga angka elevasinya ikut berubah seiring titik merah bergerak.

Menyimpan animasi

Setelah puas, animasi bisa disimpan untuk dibagikan di media sosial. Format gif paling mudah dibagikan, sedangkan mp4 ukurannya jauh lebih kecil. Untuk mp4, pasang terlebih dahulu paket av.

# Menyimpan sebagai gif
anim_save("rute_paniisan.gif", animation = p_rute,
          nframes = 100, fps = 10, width = 600, height = 600, units = "px", res = 96)

# Menyimpan sebagai mp4
anim_save("rute_paniisan.mp4", animation = p_rute,
          nframes = 100, fps = 10, width = 600, height = 600, units = "px", res = 96,
          renderer = av_renderer())

Penutup

Dengan beberapa baris tambahan, garis diam di atas peta berubah menjadi cerita perjalanan yang bisa diputar ulang. Cara yang sama juga bisa dipakai untuk hal yang lebih “serius”, misalnya menganimasikan pergerakan satwa dari data kalung GPS di Movebank, yang pernah saya singgung di tulisan tentang mengakses data ekologi.

Coba gunakan data GPX dari aktivitas kamu sendiri, entah itu trekking, lari pagi, atau bersepeda keliling kota. Siapa tahu, animasi rute kamu jadi lebih menarik dibanding Flyby di Strava.