4
votes

I currently have a dataframe containing player name, time, latitude, longitude and speed in m/s. I want to map out a players heat map, but my issue is with converting latitude and longitude to a position x and position y on a football pitch image.

Currently can map out the lat and lon path like image 1 below but the values are not in the direction of a flat football pitch but in the direction similar to image 2

enter image description here

enter image description here

I ideally would like to convert the lat and lon values to a position x and position y on a football pitch and output a plot similar to the below.

enter image description here

The attempt I have taken so far is below, I took the max and min(lat and lon) values from google maps for the pitch for the top left, bottom left, top right and bottom right. Calculated the pitch length and pitch width, then added two new columns to the df using the below formulas. But this has not resolved my problem any ideas I would be very much grateful thanks.

#pitch dimensions taken from google
top_left_lat <- 51.662233
top_left_lon <- -0.273183
top_right_lat <- 51.662518
top_right_lon <- -0.272164
bottom_left_lat <- 51.661337
bottom_left_lon <- -0.272539
bottom_right_lat <- 51.661630
bottom_right_lon <- -0.271528

#calculate pitch length
pitch_length <- acos(cos(deg2rad(90 - top_left_lat)) * cos(deg2rad(90 - bottom_left_lat))
                      + sin(deg2rad(90 - top_left_lat)) * sin(deg2rad(90 - bottom_left_lat))
                      * cos(deg2rad(top_left_lon - bottom_left_lon))) * 6371

pitch_length

#calculate pitch width
pitch_width <- acos(cos(deg2rad(90 - top_left_lat)) * cos(deg2rad(90 - top_right_lat))
                     + sin(deg2rad(90 - top_left_lat)) * sin(deg2rad(90 - top_right_lat))
                     * cos(deg2rad(top_left_lon - top_right_lon))) * 6371

pitch_width


#convert lat lon to pos x and y on a pitch
a <- mutate(a, posX = (pitch_width/360)*(180 + a$Lon))
a <- mutate(a, posY = (pitch_length/180)*(90 - a$Lat))  
1
If you have lat/long for two perpendicular edges of the field, could you do the math (geometry) to convert the lat/long for the player's position to distance from those edges? So, basically, converting to x/y via triangulation? - ulfelder
For some reason when I try to map the pitch using your lat/lon values I find the pitch to be in the middle of the A128. - Chris
@Chris apologies the minus sign was missing in front of the lat and lon values - Ciaran Sheelan

1 Answers

1
votes

Not the best solution but I was able to do the necessary transformations in sf:

First I took your pitch coordinates and converted them to planar coordinates (27700) and took a random sample to represent your gps data:

library(dplyr)
library(sf)
pts <- data.frame(lat = c(top_left_lat,bottom_left_lat, bottom_right_lat, top_right_lat),
           lng = c(top_left_lon,bottom_left_lon, bottom_right_lon, top_right_lon )) %>%
  st_as_sf(coords = c('lng', 'lat'), crs = 4326) %>%
  st_transform(27700)

poly <- pts %>%
  st_union() %>%
  st_convex_hull() 

centroid <- st_centroid(poly)

set.seed(2020)
path <- st_sample(poly, 20) %>%
  st_union() %>%
  st_cast("LINESTRING")


st_transform(poly, 4326) %>% 
  ggplot() +
  geom_sf(fill = NA, col = "black") +
  geom_sf(data = st_transform(path,4326), col = 'red')

initial plot

Then calculated the angle of the pitch and rotated our data by that angle (rot function found in sf vignette):

# get angle of pitch
se <- st_coordinates(pts[1,])
ne <- st_coordinates(pts[2,])
dy <- ne[2] - se[2]
dx <- ne[1] - se[1]

angle = atan(dy/dx) 

# rotate 
rot <- function(a) matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2) 

poly2 <- (poly - centroid) * rot(angle) + centroid
path2 <- (path -  centroid) * rot(angle)+ centroid

Finally translate to get bottom left at (0,0):

# translate to 0,0
xmin <- st_bbox(poly2)[1]
ymin <- st_bbox(poly2)[2]

pitch <- poly2 - c(xmin, ymin)
positions <- path2 - c(xmin, ymin)

## fetch the x,y coordinates:
position_xy <- positions %>%
  st_coordinates() %>%
  as.data.frame()

pitch_xy <- pitch %>%
  st_cast("LINESTRING") %>%
  st_coordinates %>%
  as.data.frame

position_xy %>%
  ggplot() +
  geom_path(aes(x = X, y= Y), col= 'red') +
  geom_path(data = pitch_xy ,aes(x = X, y = Y)) 

enter image description here