Once we've finished mapping our hole or course, it's time to export all that hard work to a KML file. This can be done by clicking on the three vertical dots on the left side of the screen where your project resides. This project works best with geoJSON data, which we can easily convert our KML file to in the following steps. Now we are ready to head to R.
The packages we will need to prepare for the layout are: yes (to work with geospatial data), orderedverse (for data cleaning and plotting), rope (for string matching), and geojsonsf (to convert from KML to geoJSON). Our first step is to read the KML file, which can be done with the st_read() function of sf.
# load libraries
library(sf)
library(tidyverse)
library(stringr)
library(geojsonsf)kml_df <- st_read("/Users/adambeaudet/Downloads/erin_hills.kml")
Excellent! We should now have the KML data for our golf course in R. The data frame should have 2 columns: Name (project name, or course name in our case), and geometry (a list of all the individual points that make up the polygons we plotted). As briefly mentioned above, let's convert our KML data to geoJSON and also extract the course name and hole numbers.
# convert from KML to geoJSON
geojson_df <- st_as_sf(kml_df, "POLYGON")# extracting course name and hole number from polygon name
# assuming "course_hole_element" naming convention is used for polygons
geojson_df$course_name <- str_match(geojson_df$Name, “^(.+)_hole”)(,2)
geojson_df$hole_num <- gsub(“.*_hole_(\\d+)_.*”, “\\1”, geojson_df$Name)
In order for our maps to point north, we must project them in a way that preserves direction. We can do this with the st_transform() function.
# define a CRS for so map always points due north
crs <- "+proj=lcc +lat_1=33 +lat_2=45 +lat_0=39 +lon_0=-96 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"# transform data to CRS
geojson_df <- st_transform(geojson_df, crs)
We're almost ready to plot, but first we need to tell ggplot2 how each polygon should be colored. Below is the color palette my project uses, but feel free to customize it however you like.
Optional: In this step we can also calculate the centroids of our polygons with the st_centroid() function so we can overlay the hole number on each green.
geojson_df <- geojson_df %>%
mutate(color = case_when(
grepl(“_tee$”, Name) ~ “#57B740”,
grepl(“_bunker$”, Name) ~ “#EDE6D3”,
grepl(“_water$”, Name) ~ “#2243b6”,
grepl(“_fairway$”, Name) ~ “#57B740”,
grepl(“_green$”, Name) ~ “#86D14A”,
grepl(“_hazard$”, Name) ~ “#094d1d”
)) %>%
mutate(centroid = st_centroid(geometry))
We're officially ready to plot. We can use a combination of geom_sf(), geom_text()and even geom_point() if we want to get fancy and plot shots on top of our map. I usually remove the grid lines, axis labels, and legend for a cleaner look.
ggplot() +
geom_sf(data = geojson_df, aes(fill = color), color = "black") +
geom_text(data = filter(geojson_df, grepl("_green", Name)),
aes(x = st_coordinates(centroid)(, 1),
y = st_coordinates(centroid)(, 2),
label = hole_num),
size = 3, color = "black", fontface = "bold", hjust = 0.5, vjust = 0.5) +
scale_fill_identity() +
theme_minimal() +
theme(axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
plot.title = element_text(size = 16),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
theme(legend.position = "none") +
labs(title = 'Erin Hills | Hartford, WI')
And there you have it: a golf course laid out in R, what a concept!
To see other courses I have mapped out at the time of writing, you can visit my Shiny app: https://abodesy14.shinyapps.io/golfMapsR/
If you followed it, had fun doing it, or are intrigued, feel free to try mapping your favorite courses and creating a pull request for the golfMapsR repository I maintain: https://github.com/abodesy14/golfMapsR
With a little combined effort, we can create a small database of traceable golf courses around the world!