Hands-on Exercise 9b

Visualising Geospatial Point Data

Author

Yee Weicong Mark

Published

June 15, 2026

Modified

June 18, 2026

22 Visualising Geospatial Point Data

22.1 Overview

Proportional symbol maps, also known as graduated symbol maps, use the visual variable of size to represent differences in the magnitude of a discrete, abruptly changing phenomenon such as counts of people. Like choropleth maps, they come in classed and unclassed versions. The classed ones are known as range-graded or graduated symbols. The unclassed ones are called proportional symbols, where the area of each symbol is proportional to the value of the mapped attribute. This chapter creates a proportional symbol map showing the number of wins by Singapore Pools outlets with the tmap package.

22.1.1 Learning Outcome

By the end of this hands-on exercise you will be able to:

  1. import an aspatial data file into R,
  2. convert it into a simple point feature data frame and assign an appropriate projection reference,
  3. plot interactive proportional symbol maps.

22.2 Getting Started

The tmap package and its related packages are installed and loaded into R.

pacman::p_load(sf, tmap, tidyverse)

22.3 Geospatial Data Wrangling

22.3.1 The Data

The dataset is called SGPools_svy21, in csv file format. It has seven columns. The XCOORD and YCOORD columns are the x and y coordinates of Singapore Pools outlets and branches, given in the Singapore SVY21 Projected Coordinate System.

22.3.2 Data Import and Preparation

read_csv() of readr imports SGPools_svy21.csv into R as a tibble data frame called sgpools.

sgpools <- read_csv("data/aspatial/SGPools_svy21.csv")

The imported data is examined with list().

list(sgpools)
[[1]]
# A tibble: 306 × 7
   NAME           ADDRESS POSTCODE XCOORD YCOORD `OUTLET TYPE` `Gp1Gp2 Winnings`
   <chr>          <chr>      <dbl>  <dbl>  <dbl> <chr>                     <dbl>
 1 Livewire (Mar… 2 Bayf…    18972 30842. 29599. Branch                        5
 2 Livewire (Res… 26 Sen…    98138 26704. 26526. Branch                       11
 3 SportsBuzz (K… Lotus …   738078 20118. 44888. Branch                        0
 4 SportsBuzz (P… 1 Sele…   188306 29777. 31382. Branch                       44
 5 Prime Serango… Blk 54…   552542 32239. 39519. Branch                        0
 6 Singapore Poo… 1A Woo…   731001 21012. 46987. Branch                        3
 7 Singapore Poo… Blk 64…   370064 33990. 34356. Branch                       17
 8 Singapore Poo… Blk 88…   370088 33847. 33976. Branch                       16
 9 Singapore Poo… Blk 30…   540308 33910. 41275. Branch                       21
10 Singapore Poo… Blk 20…   560202 29246. 38943. Branch                       25
# ℹ 296 more rows
NoteThings to Learn from the Code Chunk Above

The sgpools data is a tibble data frame rather than a common R data frame.

22.3.3 Creating an sf Data Frame from an Aspatial Data Frame

st_as_sf() of sf converts the sgpools data frame into a simple feature data frame.

sgpools_sf <- st_as_sf(sgpools,
                       coords = c("XCOORD", "YCOORD"),
                       crs= 3414)
NoteThings to Learn from the Code Chunk Above

The coords argument takes the x-coordinate column name first, then the y-coordinate column name. The crs argument takes the coordinate system in EPSG format. EPSG 3414 is the Singapore SVY21 Projected Coordinate System.

The basic information of sgpools_sf is displayed with the code chunk below.

list(sgpools_sf)
[[1]]
Simple feature collection with 306 features and 5 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 7844.194 ymin: 26525.7 xmax: 45176.57 ymax: 47987.13
Projected CRS: SVY21 / Singapore TM
# A tibble: 306 × 6
   NAME                         ADDRESS POSTCODE `OUTLET TYPE` `Gp1Gp2 Winnings`
 * <chr>                        <chr>      <dbl> <chr>                     <dbl>
 1 Livewire (Marina Bay Sands)  2 Bayf…    18972 Branch                        5
 2 Livewire (Resorts World Sen… 26 Sen…    98138 Branch                       11
 3 SportsBuzz (Kranji)          Lotus …   738078 Branch                        0
 4 SportsBuzz (PoMo)            1 Sele…   188306 Branch                       44
 5 Prime Serangoon North        Blk 54…   552542 Branch                        0
 6 Singapore Pools Woodlands C… 1A Woo…   731001 Branch                        3
 7 Singapore Pools 64 Circuit … Blk 64…   370064 Branch                       17
 8 Singapore Pools 88 Circuit … Blk 88…   370088 Branch                       16
 9 Singapore Pools Anchorvale … Blk 30…   540308 Branch                       21
10 Singapore Pools Ang Mo Kio … Blk 20…   560202 Branch                       25
# ℹ 296 more rows
# ℹ 1 more variable: geometry <POINT [m]>
NoteThings to Learn from the Code Chunk Above

sgpools_sf is a point feature class. Its epsg ID is 3414. The bounding box gives the spatial extent of the geospatial data, and a new geometry column has been added.

22.4 Drawing Proportional Symbol Map

The view mode of tmap creates an interactive proportional symbol map. The code chunk below turns on the interactive mode.

tmap_mode("view")

22.4.1 An Interactive Point Symbol Map

The code chunk below creates an interactive point symbol map.

tm_shape(sgpools_sf) +
  tm_bubbles(fill = "red",
           size = 1,
           col = "black",
           lwd = 1)

22.4.2 Making It Proportional

A numerical variable is assigned to the size visual attribute to draw a proportional symbol map. The code chunk below assigns the variable Gp1Gp2 Winnings to size.

tm_shape(sgpools_sf) +
  tm_bubbles(fill = "red",
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1)

22.4.3 Giving It a Different Colour

The colour visual attribute improves the proportional symbol map further. The code chunk below uses the OUTLET TYPE variable as the colour attribute.

tm_shape(sgpools_sf) +
  tm_bubbles(fill = "OUTLET TYPE",
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1)

22.4.4 Faceted Maps with Synchronised Views

The view mode of tmap also works with faceted plots. The sync argument in tm_facets() produces multiple maps with synchronised zoom and pan settings.

tm_shape(sgpools_sf) +
  tm_bubbles(fill = "OUTLET TYPE",
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1) +
  tm_facets(by= "OUTLET TYPE",
            nrow = 1,
            sync = TRUE)
TipReading the Plot

The two synchronised panels separate the branch outlets from the main outlets while keeping the same zoom and pan. Panning one panel moves the other, so the spatial spread of each outlet type can be compared directly.

The tmap viewer is switched back to plot mode before the session ends.

tmap_mode("plot")

22.5 Reference

Kam, T. S. (2025). R for Visual Analytics, Chapter 22, Visualising Geospatial Point Data. Singapore Management University.

🕹️ LEVEL COMPLETE 🕹️

★ ★ ★ ★ ★

CHAPTER 22 CLEARED!

+1000 XP · ACHIEVEMENT UNLOCKED: Point Plotter 📍

Press any key to continue…