0

I have a dataframe with many (100+) pairs of coordinates

[lat1] [long2] [lat3] [long4] [..]  [..]
30.12    70.25    32.21    70.25  ..  ..
31.21    71.32    32.32    75.2   ..  ..
32.32    70.25    31.23    75.0   ..  ..

This function draws a line connecting the coordinates in the first two columns of the dataframe

lines(mapproject(x=data$long2, y=data$lat1), col=3, pch=20, cex=.1)

I need to perform this function on every pair of lat/lon coordinates so that there is a new/unconnected line drawn for each one

Looking at this example Operate on every two columns in a matrix I think I need to create a list, how do I do that for each column pair?

Then, I can use lapply as described here https://nicercode.github.io/guides/repeating-things/ - how should I wrap my lines() call within a function?

Full script:

library(maps)
library(mapproj)

data <- read.csv("data.csv")

map('world', proj='orth', fill=TRUE, col="#f2f2f2", border=0, orient=c(90, 0, 0))

lines(mapproject(x=data$long, y=data$lat), col=3, pch=20, cex=.1)

UPDATED QUESTION

With some help from the comments I've tried a method that performs sapply on every two columns, which seems to work as desired. This is open to improvements

df <- data.frame(X1 = c(0, 10, 20), 
                   Y2 = c(80, 85, 90), 
                   X3 = c(3, 10, 15), 
                   Y4 = c(93, 100, 105), 
                   X5 = c(16, 20, 35),
                   Y6 = c(100, 105, 130))


map('world', proj='orth', fill=TRUE, col="#f2f2f2", border=0, orient=c(90, 0, 0))

sapply(seq(1,5,by=2),function(i) lines(mapproject(x = (df[,i]), y = (df[,(i+1)])), col = 3))
user3821345
  • 648
  • 1
  • 11
  • 33

2 Answers2

0

REPLY TO UPDATED QUESTION

This update to my original answer doesn't rely on any of the contents of the column names, but it does rely on the columns being ordered in (latitude, longitude) pairs. Since ?mapproject indicates that it expects the coordinate pairs in the reverse order, I switch them in the function call.

I also don't assume a number of columns.

library(dplyr)
library(stringr)
library(maps)
library(mapproj)

lat_lons <- data.frame(lat1 = c(30, 31, 32), 
                       lon1 = c(70, 71, 70), 
                       lat2 = c(32, 32, 31), 
                       lon2 = c(70, 75, 75), 
                       lat3 = c(33, 33, 33),
                       lon3 = c(72, 73, 74))

ints <- seq(1, ncol(lat_lons) - 1)

map_lines <- function(int) {

  lines(mapproject(x = df[, int + 1], y = df[, int]), col = 3, pch = 20, cex = .1)

}

map('world', proj = 'orth', fill = TRUE, col = "#f2f2f2", border = 0, orient = c(90, 0, 0))

sapply(ints, map_lines)

REPLY TO ORIGINAL QUESTION

I think this does what you want:

library(dplyr)
library(stringr)
library(maps)
library(mapproj)

lat_lons <- data.frame(lat1 = c(30, 31, 32), 
                       lon1 = c(70, 71, 70), 
                       lat2 = c(32, 32, 31), 
                       lon2 = c(70, 75, 75), 
                       lat3 = c(33, 33, 33),
                       lon3 = c(72, 73, 74))

ints <- names(lat_lons) %>% str_extract("[0-9]") %>% unique()

map_lines <- function(int) {

  df <- select(lat_lons, matches(int))
  lines(mapproject(x = df[, 2], y = df[, 1]), col = 3, pch = 20, cex = .1)

}

map('world', proj = 'orth', fill = TRUE, col = "#f2f2f2", border = 0, orient = c(90, 0, 0))

sapply(ints, map_lines)

First, get the unique set of integers from the data frame column names. Then, for each integer, apply a function that selects the 2 columns matching that integer and call lines.

The function map_lines doesn't return anything, but adds the lines to an active plot as a side effect. Not an ideal way to do things (I'd rather use ggplot2 and assemble the pieces in a list before making the plot), but it works.

Ajar
  • 1,786
  • 2
  • 15
  • 23
  • This is a good answer too, but I ran into an issue whereby I have unusable column names, i.e X1, Y2, X3, Y4....where X1 and Y2 are a match. What do you think of doing it this way? sapply(seq(1,449,by=2),function(i) lines(mapproject(x = (df[,i]), y = (df[,(i+1)])), col = 3, pch = 20, cex = .1)) – user3821345 Aug 22 '18 at 11:25
  • Sure! I updated my answer to remove assumptions about what the column names contain, while still not making an assumption about how many columns there are. – Ajar Aug 23 '18 at 13:48
0

Your edit works for me, but I would probably do it this way, because it generalizes better.

library(maps)
library(mapproj)

df <- data.frame(X1 = c(0, 10, 20), 
                 Y2 = c(80, 85, 90), 
                 X3 = c(3, 10, 15), 
                 Y4 = c(93, 100, 105), 
                 X5 = c(16, 20, 35),
                 Y6 = c(100, 105, 130))

draw_lines <- function(df){

#Make sure we get the order right for iterating over length
 x  <- sort(grep("X", names(df), value = TRUE), decreasing = FALSE)
 y  <- sort(grep("Y", names(df), value = TRUE), decreasing = FALSE)

#Check for vector lengths to be the same
stopifnot(length(x) == length(y))

#I dont want to print anything to console
invisible(lapply(1 : length(x), function(j){
         lines(mapproject(x = df[, x[j]], y = df[, y[j]]), col = 3, pch = 20, cex = .1)
         })
         )
}

And then:

map('world', proj='orth', fill=TRUE, col="#f2f2f2", border=0, orient=c(90, 0, 0))
draw_lines(df = df)

Hope this helps

mmn
  • 150
  • 1
  • 7
  • This is a good answer but I ran into an issue whereby I have unusable column names, i.e X1, Y2, X3, Y4....where X1 and Y2 are a match. What do you think of doing it this way? sapply(seq(1,449,by=2),function(i) lines(mapproject(x = (df[,i]), y = (df[,(i+1)])), col = 3, pch = 20, cex = .1)) – user3821345 Aug 22 '18 at 11:25
  • Should work here, when you can guarantee that your columns are sorted exactly how you need it. Are you sure that x = df[, i] and y = df[, i+1]. Just asking because you wrote in your question lines(mapproject(x=data$long2, y=data$lat1), col=3, pch=20, cex=.1). You should maybe edit your question. – mmn Aug 22 '18 at 11:59