Can tmap internally cast points to lines
Amazing work on Version 4!!
My Question: I have trajectory data that I want to plot. I store the data as points, since each location sample has metadata (e.g. timestamp). However, I would like to have the points joined by direct lines to see the order of the location samples.
Currently, I cast the data myself using the approach below. I then realized that some tmap-layers do spatial operations internally, e.g. calculation of the centroid. Is this also possible with POINT to LINESTRING casting?
library(sf)
library(tmap)
set.seed(42)
# create some sample data
traj <- data.frame(time = Sys.time()+1:10, x = rnorm(10), y = rnorm(10)) |>
st_as_sf(coords = c("x","y"))
# plot the data as points
p <- tm_shape(traj) + tm_dots()
# p (uncommented for brevity)
# plot the data as lines
traj_lines <- st_cast(st_combine(traj), "LINESTRING")
p +
tm_shape(traj_lines) + tm_lines()
Very interesting use case!
If I understood correctly, you would like a tm_connect_the_dots as in
It should be quite easy to create a layer function for this. The same as tm_lines, but indeed with a spatial operation that create a polyline strings.
Open questions (also tagging @Nowosad @luukvdmeer @agila5 @Robinlovelace):
- What is a good layer name?
tm_trajectories? (Ortm_paths,tm_routes, ...?) - What to do with MULTI-features? What we could do: if there are only 'POINT's, connect them. If there are MULTIPOINTs, connect each MULTIPOINT feature with a separate line.
- We can a variable similar to
plot.orderto decide the order of points. By default the order in which they appear, but should also be possible to use another variable (e.g. timestamp).
Last but not least: Would it be a good idea to:
- add this layer function in base tmap4,
- create an extension package for this,
- add this to
tmap.networks?
tm_path() with a nod to https://ggplot2.tidyverse.org/reference/geom_path.html?
- Keeping up with the naming of the other layers' functions --
tm_paths()seems good (plural) - I think that, in general, it would be preferable to put such functions in external (extension) packages, and then if they become popular/generally useful -- to incorporate them into tmap
- I do not know if it is better to have a new extension package or to add this to tmap.networks
- Also -- in this case,
tm_paths()could be quite experimental (e.g., I can see some possible customizations, like adding arrows to the paths, bundling similar paths, etc.)
Yes, agree with tm_paths.
I think that, in general, it would be preferable to put such functions in external (extension) packages, and then if they become popular/generally useful -- to incorporate them into tmap ... Also -- in this case, tm_paths() could be quite experimental (e.g., I can see some possible customizations, like adding arrows to the paths, bundling similar paths, etc.)
You could also argue the other way round: put this (as experimental) in tmap, and if it becomes popular and larger (with customuzations) migrate it to another package. (And if it doesn't, keep it in tmap).
But generally speaking I also prefer to put it in an extension package.
Probably tmap.networks makes most sense, because it is strongly correlated to network analysis tools/methods, like routing, and edge bundling. Putting tm_paths in another package say tmap.routes may add confusion.
Yes, agree with
tm_paths. ... But generally speaking I also prefer to put it in an extension package. ... Probably tmap.networks makes most sense...
This all sounds good to me! Should I try and implement this in tmap.networks? It just might be a while till I find the time...
Hi everyone! I like the idea and I also want to point out that, to some extent, this idea is already available in sfnetworks v1.0: https://luukvdmeer.github.io/sfnetworks/dev/articles/sfn02_create_represent.html#from-spatial-points
For example:
# packages
library(sfnetworks)
library(tmap.networks)
library(tmap)
library(sf)
# Sample data
traj <- data.frame(x = 1:10 + rnorm(10, 0, 0.1), y = 1:10 + rnorm(10, 0, 0.1)) |>
st_as_sf(coords = c("x","y"))
# Build network from points connecting them in sequence
sfn <- as_sfnetwork(traj, "sequence")
# Plot it
tm_shape(sfn) +
tm_network()
(not sure why but I cannot create a reprex from that code, will add another issue in tmap.networks repo)
This is not the first time something cool is already unknowingly implemented sf_networks!
Quick question to see if this fits different scenarios with trajectory data. Would it be possible to respect a grouping column, e.g. id (see below):
(I'm using dplyr because I don't know how to st_union an sfobject by a column!)
library(sf)
library(tmap)
library(dplyr)
set.seed(42)
# create some sample data (with IDs!)
traj <- data.frame(time = Sys.time()+1:10, x = rnorm(10), y = rnorm(10), id = rep(c("A","B"),each = 5)) |>
st_as_sf(coords = c("x","y"))
# cast the data to lines, respecting the IDs
traj_lines <- traj |>
group_by(id) |>
summarise(do_union = TRUE) |>
st_cast("LINESTRING")
p +
tm_shape(traj_lines) + tm_lines(col = "id")