Introduction to Spatial Data in R

· 6 min read · Updated March 16, 2026 · beginner
spatial sf r gis beginner terra

Spatial data represents information about locations and geographic features on Earth. Whether you are analyzing customer locations, environmental measurements, or election results, spatial data lets you uncover patterns that would be invisible in traditional tabular data. This tutorial introduces you to working with spatial data in R using modern packages.

Why Spatial Data Matters

Geographic context adds powerful dimensions to your analysis:

  • Location matters: Real estate prices, disease rates, and election results all vary by geography
  • Spatial relationships: Proximity to hospitals, schools, or pollution sources affects outcomes
  • Visualization: Maps communicate patterns more intuitively than tables

R has become a powerful platform for spatial analysis, with packages that integrate seamlessly with the tidyverse.

Installing Required Packages

Two packages form the foundation of modern spatial data in R:

# Install sf for vector data (points, lines, polygons)
install.packages("sf")

# Install terra for raster data (grids, images)
install.packages("terra")

# Install tidyverse for data manipulation
install.packages("tidyverse")

# Load them
library(sf)
library(terra)
library(tidyverse)

The sf package implements Simple Features—a standardized way to represent geographic vector data. The terra package handles raster data (gridded data like satellite imagery or climate layers).

Understanding Vector Data

Vector data represents geographic features using points, lines, and polygons:

Geometry TypeExamplesUse Case
PointsCities, sampling locationsDiscrete locations
LinesRoads, rivers, bordersLinear features
PolygonsCountries, counties, parcelsBounded areas

The sf package stores vector data in data frames with a special geometry column:

# Create a simple point
point <- st_point(c(-122.4194, 37.7749))  # San Francisco
print(point)
# POINT (-122.4194 37.7749)

# Create a geometry list-column
sf_points <- st_sfc(list(point), crs = 4326)
sf_df <- st_as_sf(data.frame(name = "San Francisco"), geometry = sf_points)
print(sf_df)
# Simple feature collection with 1 feature and 1 field
# Geometry type: POINT
# Dimension: XY
# Bounding box: -122.42 -122.42 37.77 37.77
# Geodetic CRS: WGS 84

Creating Spatial Data from Scratch

You can create spatial data directly in R:

# Create multiple points (cities)
cities <- tibble(
  name = c("San Francisco", "Los Angeles", "San Diego"),
  lat = c(37.7749, 34.0522, 32.7157),
  lon = c(-122.4194, -118.2437, -117.1611)
)

# Convert to sf object
cities_sf <- st_as_sf(cities, coords = c("lon", "lat"), crs = 4326)

print(cities_sf)
# Simple feature collection with 3 features and 1 field
# Geometry type: POINT
# Bounding box: -118.24 -117.16 32.72 37.77
# Geodetic CRS: WGS 84

Working with Polygons

Polygons represent areas—countries, states, counties, or any bounded region:

# Create a simple rectangle (bounding box)
bbox <- st_bbox(c(xmin = -125, ymin = 24, xmax = -66, ymax = 50), crs = 4326)
rectangle <- st_as_sfc(bbox)

# Or use st_polygon to define vertices manually
polygon_coords <- list(rbind(
  c(-122, 37),
  c(-122, 38),
  c(-121, 38),
  c(-121, 37),
  c(-122, 37)
))
polygon <- st_polygon(polygon_coords)
print(polygon)
# POLYGON ((-122 37, -122 38, -121 38, -121 37, -122 37))

Reading Spatial Data

In practice, you will read spatial data from files. The sf package supports many formats:

# Read a shapefile (common GIS format)
# counties <- st_read("counties.shp")

# Read GeoJSON
# cities <- st_read("cities.geojson")

# Read from a URL
# url <- "https://raw.githubusercontent.com/ropensci/geojsonio/main/inst/examples/us_cities.geojson"
# cities <- st_read(url)

# For this example, load built-in NC data (North Carolina counties)
library(geosphere)
nc <- st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)

print(nc)
# Simple feature collection with 100 features and 14 fields
# Geometry type: MULTIPOLYGON
# Dimension: XY
# Bounding box: -84.32 33.84 -75.47 36.59
# Geodetic CRS: NAD27

Coordinate Reference Systems

Every spatial dataset needs a coordinate reference system (CRS) that defines how coordinates map to locations on Earth. Two main types exist:

  • Geographic CRS: Uses latitude/longitude (degrees). Example: WGS84 (EPSG:4326)
  • Projected CRS: Uses meters or feet. Example: UTM, Albers Equal Area
# Check the CRS of your data
st_crs(nc)
# Coordinate Reference System:
# User input: NAD27 
# wkt: ...

# Transform to a different CRS (WGS84)
nc_wgs84 <- st_transform(nc, 4326)
st_crs(nc_wgs84)
# Coordinate Reference System:
# User input: EPSG:4326 
# wkt: ...

# Create a projected CRS for area calculations (meters)
nc_albers <- st_transform(nc, "ESRI:102003")  # USA Contiguous Albers Equal Area

Basic Spatial Operations

The sf package provides many spatial operations:

# Calculate area (requires projected CRS)
nc_albers <- nc_albers %>%
  mutate(area_km2 = as.numeric(st_area(geometry) / 1e6))

print(nc_albers %>% select(NAME, area_km2))
# # A tibble: 100 × 2
#    NAME         area_km2
# 1  Ashe          1155. 
# 2  Alleghany     251. 
# ...

# Calculate centroid (center point of each polygon)
centroids <- st_centroid(nc_albers)

# Buffer around points (create polygons around points)
buffer_100km <- st_buffer(nc_albers, dist = 100000)  # 100 km in meters

Spatial Relationships

You can test relationships between spatial objects:

# Create a point (Raleigh, NC - the capital)
raleigh <- st_point(c(-78.6382, 35.7796)) %>%
  st_sfc(crs = 4326) %>%
  st_as_sf()

# Find which county Raleigh is in
raleigh_county <- nc %>%
  filter(st_within(raleigh, ., sparse = FALSE))

print(raleigh_county$NAME)
# "Wake"

# Calculate distance from Raleigh to each county centroid
centroids <- st_centroid(nc)
raleigh <- st_transform(raleigh, st_crs(centroids))

distances <- st_distance(raleigh, centroids)
nc <- nc %>% mutate(distance_to_raleigh_km = as.numeric(distances) / 1000)

print(nc %>% select(NAME, distance_to_raleigh_km) %>% arrange(distance_to_raleigh_km) %>% head(5))
# # A tibble: 5 × 2
#   NAME    distance_to_raleigh_km
# 1 Wake                     0.0000028
# 2 Durham                  28.4   
# 3 Orange                  39.7   
# ...

Introduction to Raster Data

Raster data represents continuous surfaces as grids of cells. Each cell holds a value (temperature, elevation, land cover):

# Create a simple raster from scratch
r <- rast(nrows = 10, ncols = 10, 
          xmin = 0, xmax = 10, ymin = 0, ymax = 10,
          crs = "EPSG:4326")

# Fill with values (temperature in Celsius)
values(r) <- runif(ncell(r), 10, 30)

print(r)
# class       : SpatRaster 
# dimensions : 10, 10, 1  (nrow, ncol, nlyr)
# resolution : 1, 1  (x, y)
# extent     : 0, 10, 0, 10  (xmin, xmax, ymin, ymax)
# coord. ref. : EPSG:4326 
# source     : memory 
# names      : layer 
# values     : 10.1, 29.9  (min, max)

# Raster statistics
global(r, "mean")
#         mean
# layer 20.03

# Read elevation data (example with built-in raster)
elevation <- rast(system.file("ex/elev.tif", package = "terra"))
print(elevation)

Basic Spatial Visualization

Create maps with ggplot2 using geom_sf():

# Map North Carolina counties
ggplot(nc) +
  geom_sf(aes(fill = AREA)) +
  scale_fill_viridis_c(name = "Area (sq. deg)") +
  labs(title = "North Carolina Counties by Area",
       subtitle = "Using sf and ggplot2") +
  theme_minimal()

You can also plot sf objects directly:

# Quick plot with sf
plot(nc["AREA"])

Combine vector and raster data:

# Crop raster to vector extent
nc_ext <- ext(nc)
elevation_nc <- crop(elevation, nc_ext)

# Plot both together
plot(elevation_nc, main = "Elevation in NC region")
plot(st_geometry(nc), add = TRUE, col = NA, border = "white")

What You Have Learned

This tutorial covered foundational concepts for spatial data in R:

ConceptDescription
Vector dataPoints, lines, polygons (sf package)
Raster dataGridded data (terra package)
Coordinate reference systemsHow coordinates map to locations
Spatial operationsBuffer, centroid, distance
Spatial relationshipsWithin, intersects, distance tests
Visualizationggplot2 with geom_sf()

See Also

Next Steps

Continue your spatial analysis journey:

  • Working with sf and terra — Deep dive into both packages
  • Geocoding and Mapping in R — Convert addresses to coordinates and create maps
  • Spatial Joins in R — Combine spatial datasets based on location

These tutorials build on the foundation established here to unlock more advanced spatial analysis techniques.