2026-09-15
sf for storing and processing spatial objectstmap for visualising spatial objectssf

Nearly 2 million trips 776 bike-share station locations 11,888 unique bikes| BikeID | tripid | station_name_D | station_name_O | lon_D | lon_O | lat_D | lat_O | dat_time_D | dat_time_O |
|---|---|---|---|---|---|---|---|---|---|
| 21481 | 1 | Broadway & W 49 St | W 52 St & 11 Ave | -73.98453 | -73.99393 | 40.76068 | 40.76727 | 2018-06-01 02:06:50 | 2018-06-01 01:57:20 |
| 19123 | 2 | W 41 St & 8 Ave | W 52 St & 11 Ave | -73.99003 | -73.99393 | 40.75641 | 40.76727 | 2018-06-01 02:10:43 | 2018-06-01 02:02:42 |
| 26983 | 3 | Broadway & W 58 St | W 52 St & 11 Ave | -73.98169 | -73.99393 | 40.76695 | 40.76727 | 2018-06-01 02:15:55 | 2018-06-01 02:04:23 |
| 26742 | 4 | W 31 St & 7 Ave | W 52 St & 11 Ave | -73.99160 | -73.99393 | 40.74916 | 40.76727 | 2018-06-01 03:11:59 | 2018-06-01 03:00:55 |
| 26386 | 5 | W 20 St & 11 Ave | W 52 St & 11 Ave | -74.00776 | -73.99393 | 40.74674 | 40.76727 | 2018-06-01 06:18:32 | 2018-06-01 06:04:54 |
| 27073 | 6 | W 24 St & 7 Ave | W 52 St & 11 Ave | -73.99530 | -73.99393 | 40.74488 | 40.76727 | 2018-06-01 06:24:26 | 2018-06-01 06:11:52 |
library(tidyverse)
library(here)
sm_tbl <-
here("data", "201806-citibike-tripdata.csv") %>%
read_csv() %>%
rename(
station_name_O = `start station name`,
station_name_D = `end station name`,
lon_O = `start station longitude`,
lat_O = `start station latitude`,
lon_D = `end station longitude`,
lat_D = `end station latitude`,
dat_time_O = starttime,
dat_time_D = stoptime,
BikeID = bikeid
)| BikeID | tripid | station_name_D | station_name_O | lon_D | lon_O | lat_D | lat_O | dat_time_D | dat_time_O |
|---|---|---|---|---|---|---|---|---|---|
| 21481 | 1 | Broadway & W 49 St | W 52 St & 11 Ave | -73.98453 | -73.99393 | 40.76068 | 40.76727 | 2018-06-01 02:06:50 | 2018-06-01 01:57:20 |
| 19123 | 2 | W 41 St & 8 Ave | W 52 St & 11 Ave | -73.99003 | -73.99393 | 40.75641 | 40.76727 | 2018-06-01 02:10:43 | 2018-06-01 02:02:42 |
| 26983 | 3 | Broadway & W 58 St | W 52 St & 11 Ave | -73.98169 | -73.99393 | 40.76695 | 40.76727 | 2018-06-01 02:15:55 | 2018-06-01 02:04:23 |
| 26742 | 4 | W 31 St & 7 Ave | W 52 St & 11 Ave | -73.99160 | -73.99393 | 40.74916 | 40.76727 | 2018-06-01 03:11:59 | 2018-06-01 03:00:55 |
| 26386 | 5 | W 20 St & 11 Ave | W 52 St & 11 Ave | -74.00776 | -73.99393 | 40.74674 | 40.76727 | 2018-06-01 06:18:32 | 2018-06-01 06:04:54 |
| 27073 | 6 | W 24 St & 7 Ave | W 52 St & 11 Ave | -73.99530 | -73.99393 | 40.74488 | 40.76727 | 2018-06-01 06:24:26 | 2018-06-01 06:11:52 |
library(ggplot2)
library(ggthemes)
numtrips <- sm_tbl %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
group_by(station_name_O, station_name_D, time_of_day) %>%
summarise(lon_O = first(lon_O),
lat_O = first(lat_O),
lon_D = first(lon_D),
lat_D = first(lat_D),
totaltrips = n()
)
numtrips %>%
ggplot()+
geom_segment(aes(x=lon_O, y=lat_O,xend=lon_D, yend=lat_D, alpha=totaltrips))+
#Here is the magic bit that sets line transparency - essential to make the plot readable
scale_alpha_continuous(trans = "log10", range = c(0.005, 0.04), guide='none')+
facet_wrap(time_of_day~.)+
#Set black background, ditch axes
scale_x_continuous("", breaks=NULL)+
scale_y_continuous("", breaks=NULL) +
theme_tufte()numtrips <- sm_tbl %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
group_by(station_name_O, time_of_day) %>%
summarise(lon_O = first(lon_O),
lat_O = first(lat_O),
totaltrips = n()
)
numtrips %>%
ggplot()+
geom_point(aes(x=lon_O, y=lat_O, size=totaltrips))+
scale_size_continuous(range = c(0.01, 2), guide='none')+
facet_wrap(time_of_day~.)+
#Set black background, ditch axes
scale_x_continuous("", breaks=NULL)+
scale_y_continuous("", breaks=NULL) +
theme_tufte()+
labs(main = "Popularity of origin stations, June 2018")numtrips <- sm_tbl %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
group_by(station_name_D, time_of_day) %>%
summarise(lon_D = first(lon_D),
lat_D = first(lat_D),
totaltrips = n()
)
numtrips %>%
ggplot()+
geom_point(aes(x=lon_D, y=lat_D, size=totaltrips))+
scale_size_continuous(range = c(0.01, 2), guide='none')+
facet_wrap(time_of_day~.)+
#Set black background, ditch axes
scale_x_continuous("", breaks=NULL)+
scale_y_continuous("", breaks=NULL) +
theme_tufte()+
labs(main = "Popularity of destination stations, June 2018")library(maptiles)
library(terra)
library(tidyterra)
bbox <- c(left = -74.06, bottom = 40.64, right = -73.89, top = 40.89)
bbox_sf <- sf::st_bbox(c(xmin = unname(bbox["left"]), ymin = unname(bbox["bottom"]),
xmax = unname(bbox["right"]), ymax = unname(bbox["top"])), crs = 4326) %>%
sf::st_as_sfc()
g1 <- get_tiles(bbox_sf, provider = carto_positron, zoom = 10, crop = TRUE, apikey = carto_key)
ggplot() +
geom_spatraster_rgb(data = g1) +
geom_point(aes(x=lon_D, y=lat_D, size=totaltrips), data = numtrips)+
scale_size_continuous(range = c(0.01, 2), guide='none')+
facet_wrap(time_of_day~.)+
#Set black background, ditch axes
scale_x_continuous("", breaks=NULL)+
scale_y_continuous("", breaks=NULL) +
theme_tufte()tmap (but need to get serious)sfsfsfGEOS (projected) and s2geometry (unprojected)sf ecosystem

| BikeID | tripid | station_name_D | station_name_O | lon_O | lat_O | dat_time_D | dat_time_O | geometry |
|---|---|---|---|---|---|---|---|---|
| 21481 | 1 | Broadway & W 49 St | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:06:50 | 2018-06-01 01:57:20 | POINT (-73.98453 40.76068) |
| 19123 | 2 | W 41 St & 8 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:10:43 | 2018-06-01 02:02:42 | POINT (-73.99003 40.75641) |
| 26983 | 3 | Broadway & W 58 St | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:15:55 | 2018-06-01 02:04:23 | POINT (-73.98169 40.76695) |
| 26742 | 4 | W 31 St & 7 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 03:11:59 | 2018-06-01 03:00:55 | POINT (-73.9916 40.74916) |
| 26386 | 5 | W 20 St & 11 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 06:18:32 | 2018-06-01 06:04:54 | POINT (-74.00776 40.74674) |
| 27073 | 6 | W 24 St & 7 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 06:24:26 | 2018-06-01 06:11:52 | POINT (-73.9953 40.74488) |
| station_name_O | geometry |
|---|---|
| Willoughby St & Fleet St | POINT (-73.98433 40.68021) |
| Adelphi St & Myrtle Ave | POINT (-73.9813 40.69197) |
| Douglass St & 4 Ave | POINT (-73.97632 40.68383) |
| Douglass St & 3 Ave | POINT (-73.97945 40.66106) |
| BikeID | tripid | geometry | orig_geom |
|---|---|---|---|
| 21481 | 1 | POINT (-73.98453 40.76068) | POINT (-73.99393 40.76727) |
| 19123 | 2 | POINT (-73.99003 40.75641) | POINT (-73.99393 40.76727) |
| 26983 | 3 | POINT (-73.98169 40.76695) | POINT (-73.99393 40.76727) |
| 26742 | 4 | POINT (-73.9916 40.74916) | POINT (-73.99393 40.76727) |
| 26386 | 5 | POINT (-74.00776 40.74674) | POINT (-73.99393 40.76727) |
| 27073 | 6 | POINT (-73.9953 40.74488) | POINT (-73.99393 40.76727) |
| BikeID | orig_geom | buffer_geom | geometry |
|---|---|---|---|
| 21481 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-73.98453 40.76068) |
| 19123 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-73.99003 40.75641) |
| 26983 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-73.98169 40.76695) |
| 26742 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-73.9916 40.74916) |
| 26386 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-74.00776 40.74674) |
| 27073 | POINT (-73.99393 40.76727) | POLYGON ((-73.99393 40.7672... | POINT (-73.9953 40.74488) |
| station_name_D | dat_time_D | geometry |
|---|---|---|
| Broadway & W 49 St | 2018-06-01 02:06:50 | POINT (-73.98453 40.76068) |
| W 41 St & 8 Ave | 2018-06-01 02:10:43 | POINT (-73.99003 40.75641) |
| Broadway & W 58 St | 2018-06-01 02:15:55 | POINT (-73.98169 40.76695) |
| W 31 St & 7 Ave | 2018-06-01 03:11:59 | POINT (-73.9916 40.74916) |
| BikeID | tripid | hr | time_of_day | geometry |
|---|---|---|---|---|
| 21481 | 1 | 1 | Midnight - 6AM | POINT (-73.98453 40.76068) |
| 19123 | 2 | 2 | Midnight - 6AM | POINT (-73.99003 40.75641) |
| 26983 | 3 | 2 | Midnight - 6AM | POINT (-73.98169 40.76695) |
| 26742 | 4 | 3 | Midnight - 6AM | POINT (-73.9916 40.74916) |
| 26386 | 5 | 6 | Midnight - 6AM | POINT (-74.00776 40.74674) |
| 27073 | 6 | 6 | Midnight - 6AM | POINT (-73.9953 40.74488) |
scard_sf %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
group_by(time_of_day) %>%
summarise(totaltrips = n())| totaltrips | time_of_day | geometry |
|---|---|---|
| 6 | Midnight - 6AM | MULTIPOINT ((-73.99003 40.7... |
| 32 | 6AM - 10AM | MULTIPOINT ((-74.00278 40.7... |
| 12 | 10AM - 4PM | MULTIPOINT ((-73.99667 40.7... |
| BikeID | tripid | station_name_D | station_name_O | lon_O | lat_O | dat_time_D | dat_time_O | orig_geom |
|---|---|---|---|---|---|---|---|---|
| 21481 | 1 | Broadway & W 49 St | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:06:50 | 2018-06-01 01:57:20 | POINT (-73.99393 40.76727) |
| 19123 | 2 | W 41 St & 8 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:10:43 | 2018-06-01 02:02:42 | POINT (-73.99393 40.76727) |
| 26983 | 3 | Broadway & W 58 St | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 02:15:55 | 2018-06-01 02:04:23 | POINT (-73.99393 40.76727) |
| 26742 | 4 | W 31 St & 7 Ave | W 52 St & 11 Ave | -73.99393 | 40.76727 | 2018-06-01 03:11:59 | 2018-06-01 03:00:55 | POINT (-73.99393 40.76727) |
scard_sf %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
st_drop_geometry() %>%
group_by(time_of_day) %>%
summarise(totaltrips = n())| time_of_day | totaltrips |
|---|---|
| Midnight - 6AM | 91060 |
| 6AM - 10AM | 425052 |
| 10AM - 4PM | 649292 |
| 4PM - 8PM | 601275 |
| 8PM - Midnight | 143179 |
tmaptmap againtmap_mode("plot")
numtrips <- sm_tbl %>%
filter(station_name_O != station_name_D) %>%
mutate(hr = hour(dat_time_O),
time_of_day = hr %>% cut(breaks=c(0,6,10,16,20,24), include.lowest = TRUE, labels=c("Midnight - 6AM", "6AM - 10AM", "10AM - 4PM", "4PM - 8PM", "8PM - Midnight"))
) %>%
group_by(station_name_D, time_of_day) %>%
summarise(lon_D = first(lon_D),
lat_D = first(lat_D),
totaltrips = n()
) %>%
st_as_sf(coords = c("lon_D", "lat_D"), crs=4326)
numtrips %>%
tm_shape()+
tm_bubbles(size = "totaltrips",
col="red",
border.col = "red",
title.size = "# of trips",
legend.size.is.portrait = TRUE
)+
tm_facets(by = "time_of_day") +
tm_layout(
legend.outside.size = 0.2,
main.title= 'Popularity of trip destination',
main.title.position = c("center", 'top'))tmap can create interactive mapssf| BikeID | station_name_O | station_name_D | geometry |
|---|---|---|---|
| 21481 | W 52 St & 11 Ave | Broadway & W 49 St | LINESTRING (-73.99393 40.76... |
| 19123 | W 52 St & 11 Ave | W 41 St & 8 Ave | LINESTRING (-73.99393 40.76... |
| 26983 | W 52 St & 11 Ave | Broadway & W 58 St | LINESTRING (-73.99393 40.76... |
| 26742 | W 52 St & 11 Ave | W 31 St & 7 Ave | LINESTRING (-73.99393 40.76... |
| 26386 | W 52 St & 11 Ave | W 20 St & 11 Ave | LINESTRING (-73.99393 40.76... |
| 27073 | W 52 St & 11 Ave | W 24 St & 7 Ave | LINESTRING (-73.99393 40.76... |
$`1`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.8
$`2`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.8
$`3`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.8
$`4`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.7
$`5`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.7
$`6`
# A tibble: 1 × 4
lon_O lat_O lon_D lat_D
<dbl> <dbl> <dbl> <dbl>
1 -74.0 40.8 -74.0 40.7
$`1`
LINESTRING (-73.99393 40.76727, -73.98453 40.76068)
$`2`
LINESTRING (-73.99393 40.76727, -73.99003 40.75641)
$`3`
LINESTRING (-73.99393 40.76727, -73.98169 40.76695)
$`4`
LINESTRING (-73.99393 40.76727, -73.9916 40.74916)
$`5`
LINESTRING (-73.99393 40.76727, -74.00776 40.74674)
$`6`
LINESTRING (-73.99393 40.76727, -73.9953 40.74488)
[,1] [,2]
[1,] 121.3643 31.16001
[2,] 121.4226 31.18858
LINESTRING (121.3643 31.16001, 121.4226 31.18858)
Geometry set for 6 features
Geometry type: LINESTRING
Dimension: XY
Bounding box: xmin: -74.00776 ymin: 40.74488 xmax: -73.98169 ymax: 40.76727
Geodetic CRS: WGS 84
First 5 geometries:
LINESTRING (-73.99393 40.76727, -73.98453 40.76...
LINESTRING (-73.99393 40.76727, -73.99003 40.75...
LINESTRING (-73.99393 40.76727, -73.98169 40.76...
LINESTRING (-73.99393 40.76727, -73.9916 40.74916)
LINESTRING (-73.99393 40.76727, -74.00776 40.74...
| BikeID | station_name_O | station_name_D | geometry |
|---|---|---|---|
| 21481 | W 52 St & 11 Ave | Broadway & W 49 St | LINESTRING (-73.99393 40.76... |
| 19123 | W 52 St & 11 Ave | W 41 St & 8 Ave | LINESTRING (-73.99393 40.76... |
| 26983 | W 52 St & 11 Ave | Broadway & W 58 St | LINESTRING (-73.99393 40.76... |
| 26742 | W 52 St & 11 Ave | W 31 St & 7 Ave | LINESTRING (-73.99393 40.76... |
| 26386 | W 52 St & 11 Ave | W 20 St & 11 Ave | LINESTRING (-73.99393 40.76... |
| 27073 | W 52 St & 11 Ave | W 24 St & 7 Ave | LINESTRING (-73.99393 40.76... |
Reading layer `districts' from data source
`/Users/kaza/Desktop/website_new/website/static/slides-src/spatial_with_sf/data/districts.shp'
using driver `ESRI Shapefile'
Simple feature collection with 262 features and 11 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -74.25559 ymin: 40.49613 xmax: -73.70001 ymax: 40.91553
Geodetic CRS: WGS 84
| borocode | boroname | countyfips | nta2020 | ntaname | ntaabbrev | ntatype | cdta2020 | cdtaname | shape_leng | shape_area | geometry |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 3 | Brooklyn | 047 | BK0101 | Greenpoint | Grnpt | 0 | BK01 | BK01 Williamsburg-Greenpoint (CD 1 Equivalent) | 28919.56 | 35321808 | MULTIPOLYGON (((-73.93213 4... |
| 3 | Brooklyn | 047 | BK0102 | Williamsburg | Wllmsbrg | 0 | BK01 | BK01 Williamsburg-Greenpoint (CD 1 Equivalent) | 28134.08 | 28852853 | MULTIPOLYGON (((-73.95814 4... |
| 3 | Brooklyn | 047 | BK0103 | South Williamsburg | SWllmsbrg | 0 | BK01 | BK01 Williamsburg-Greenpoint (CD 1 Equivalent) | 18250.28 | 15208961 | MULTIPOLYGON (((-73.95024 4... |
| 3 | Brooklyn | 047 | BK0104 | East Williamsburg | EWllmsbrg | 0 | BK01 | BK01 Williamsburg-Greenpoint (CD 1 Equivalent) | 43184.80 | 52267407 | MULTIPOLYGON (((-73.92406 4... |
….. and many more
tmap againTell a story about
In the raw dataset things are a little/lot more messy