Data Science II

Session 1: Quarto for Spatial Reporting

Bogdan G. Popescu

Tec de Monterrey

GIS Is More Than Making Maps

Making the map is usually the last step. The method is everything before it.

A spreadsheet can tell you how many clinics Yucatán has. Only location can tell you:

  • How far is the nearest one from each village?
  • How many people live within 8 km of one?
  • Where is the gap that the next clinic should fill?

The new idea this term is joining tables by geometry rather than by a shared id.

The Sessions

You learn So you can judge
1 Quarto for spatial reporting what a reader actually receives
2 coordinates and CRS whether an analysis measures in metres or degrees
3 sf objects, joins, choropleths whether a map shows a rate, or just population
4 joins by location, distance, buffers whether nearest and within mean what you intended
5 rasters and zonal statistics whether resolution and aggregation fit the question

Everything is done in R and Quarto rather than in ArcGIS or QGIS.

Where This Is Going

All of these come from the data in your data/ folder, and you will build them yourself.

Ask AI to Do the Analysis

“Calculate the distance from every Mexican municipality to the nearest hospital.”

You will get fifteen lines of clean, well-commented sf code in about four seconds.

It will run. It will produce 2,469 numbers.

The question is whether those numbers mean what you think they mean.

The Questions It Did Not Ask

  • are the layers in degrees or metres — and are the distances therefore meaningful? (today)
  • does nearest mean the centroid, the boundary, or where people actually live? (Session 4)
  • did the join quietly duplicate municipalities? (Session 4)
  • what happened to the islands? (today)
  • is a straight line what you want, or travel time on a road?

None of these is a coding question. Every one of them changes the answer, and none of them throws an error.

Note

By the end of these sessions you should be able to ask for an analysis like this, read what comes back, say where it is wrong, fix it, and defend the version you hand in.

What Does “Nearest” Mean?

Tizimín, one municipality in Yucatán.

What Does “Nearest” Mean?

It contains 129 clinics.

What Does “Nearest” Mean?

Ask for the distance to the nearest hospital. The obvious code takes the centroid.

What Does “Nearest” Mean?

It answers 30 km.

What Does “Nearest” Mean?

But 126 of the 129 are right there, in the town.

The code is right. The question was wrong: nobody lives at the centroid.

One Centroid, 129 Distances

The centroid has a distance to every clinic, not just the nearest.

Which Distance Do You Report?

30.0 km to the nearest clinic
33.6 km median of all 129
33.8 km mean of all 129
41.8 km to the farthest

Nearest, median or mean — they are all between 30 and 42 km. Picking a different statistic does not help.

The problem is not which summary we take. It is where we measured from.

Nobody Lives at the Centroid

The centroid’s square kilometre holds one person. The town’s holds 3,875.

So Ask About Communities, Not Polygons

Every populated square kilometre of Tizimín, grouped by how many people live in it.

So Ask About Communities, Not Polygons

50,070 people are 0.3 km from a clinic. 26,156 are 22.8 km away. That is the finding.

That Is the Point of This Course

The computer did not get 30 km wrong. We asked it to measure from a square kilometre where one person lives.

Every correction along the way was a question, not a function: measure from where people are, group by community rather than by polygon.

And the answer was never one number — 0.3 km for 50,070 people, 22.8 km for 26,156.

Over these sessions you should be able to ask for an analysis like this, see what is wrong with it, fix it, and defend what you hand in.

What You’ll Learn Today

  1. Quarto as the container for spatial work — built from scratch, click by click
  2. The four Quarto settings that matter for maps
  3. Why the Earth’s shape forces us to think about coordinate reference systems
  4. Geographic vs. projected systems, and what every projection distorts
  5. Vector layers: points, lines, polygons
  6. Reading and mapping a real shapefile of Mexico

By the end, you will have a Quarto document that renders a map of Mexico.

Before We Start: Your Folder

Every line of code in these slides runs on your machine, provided your folders look like this:

ciencia_datos_2/
├── data/
│   ├── gadm41_MEX_shp/       <- Mexico boundaries (GADM)
│   ├── denue/                <- DENUE point data
│   └── mex_formats/          <- the same states as .geojson and .gpkg
└── session1/
    └── my_notes.qmd          <- you work here

Code in these slides reads ../data/... — up one level, then into data. Keep that shape and everything copies and pastes without edits.

Quarto: Three Layers

Every .qmd file has exactly three kinds of content:

  1. YAML header — metadata and output settings, between --- fences
  1. Markdown — your prose, headings, tables, links
  1. Code chunks — R that runs when you render

Let’s build one together, from an empty RStudio window.

Step 1: Open RStudio

Nothing open yet — just the Console on the left and an empty Environment on the right.

Step 2: File → New File → Quarto Document

Note Quarto Document and Quarto Presentation are separate entries. Today: Document.

Step 3: The New Document Dialog

Step 4: The Visual Editor

RStudio gives you a template document. Headings look like headings; code chunks look like boxes.

Step 5: The Source Editor

The same file, shown as raw text. ## Quarto is a heading; ```{r} opens a chunk.

Visual or Source?

Visual

  • Easier for prose
  • Tables and links via toolbar
  • Good when writing the report

Source

  • You see exactly what is in the file
  • Necessary for chunk options
  • Good when debugging

Note

Everything in these slides is written in Source. When something goes wrong, Source is where you find out why.

Step 6: Clear the Template

Delete the sample text. Keep the YAML header, one heading, and one chunk.

Step 7: Save It

Warning

Save before you render. An unsaved document has no folder, so relative paths like data/... have nothing to be relative to.

Step 8: Now It Has a Name

The tab now reads example.qmd, and the tooltip shows the Render shortcut: Shift+Cmd+K (Shift+Ctrl+K on Windows).

Step 9: Write Some Text

Plain prose between the chunks. This is the part that makes it a report and not a script.

Step 10: Comment Your Code

Inside a chunk, # starts a comment — exactly as in any R script.

Step 11: Run the Chunk

The green ▶ runs the chunk. Output appears inline, under the chunk, and in the Console.

Step 12: Insert Another Chunk

The circled button inserts a new chunk — or press Option+Cmd+I (Alt+Ctrl+I on Windows). Learn this shortcut; you will use it constantly.

Step 13: Render

Step 14: The Result

Prose, code, and output — one document, reproducible from source. That is the whole point.

The YAML Header

---
title: "Access to Health Services"
author: "Your Name"
date: "2026-09-15"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.
  • format: html — what gets produced
  • toc: true — table of contents
  • toc-depth: 2 — how many heading levels
  • toc-title: — rename it

YAML is whitespace-sensitive. Two spaces of indentation mean something; a tab breaks it.

What toc-depth Does

The same document, rendered three times. Only toc-depth changes.

toc-depth: 1

toc-depth: 2

toc-depth: 3

Each level deeper adds the next rank of headings — #, then ##, then ###. The default is 3.

Code Chunks

Embedding Code

This is how we include code in our documents.

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
summary(mtcars)
```

Code Chunks

Embedding Code

Both the code and its output appear.

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
#| echo: true
#| eval: true
summary(mtcars)
```

Code Chunks

Embedding Code

Only the output, with the code hidden.

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
#| echo: false
#| eval: true
summary(mtcars)
```

Code Chunks

Embedding Code

Only the code, without running it.

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
#| echo: true
#| eval: false
summary(mtcars)
```

Code Chunks

Embedding Code

Many times you will have seen annoying library messages:

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
#| echo: true
#| eval: false
summary(mtcars)
```

```{r}
library(dplyr)
```

Code Chunks

Embedding Code

To keep them out of the document, you should do:

---
title: "Statistical Analysis Report"
author: "Your Name"
date: "2024-11-05"
format: html
toc: true
toc-title: "Contents"
---

# Introduction

This is an example.

This is an equation: $E=mc^{2}$

```{r}
#| echo: true
#| eval: false
summary(mtcars)
```

```{r}
#| warning: false
#| message: false
library(dplyr)
```

Installing What You Need

Run these once, in the Console — not in your document. One line at a time:

install.packages("sf")          # spatial objects: the core of this course
install.packages("tidyverse")   # brings ggplot2, dplyr, readr and friends

Warning

sf is the one that can fail. It needs GDAL, GEOS and PROJ underneath. On Mac and Windows the binary from CRAN includes them; on Linux install libgdal-dev, libgeos-dev and libproj-dev first.

Installing What You Need

The rest, in the order you will meet them:

install.packages("spData")        # example world data          (today)
install.packages("cowplot")       # plot themes                 (today)
install.packages("ggspatial")     # map layers and scale bars   (today)
install.packages("patchwork")     # plots side by side          (Session 3)
install.packages("classInt")      # choropleth class breaks     (Session 3)
install.packages("viridis")       # colour scales               (Session 3)
install.packages("leaflet")       # interactive maps            (Session 3)
install.packages("terra")         # rasters                     (Session 5)
install.packages("tidyterra")     # rasters with ggplot         (Session 5)
install.packages("exactextractr") # zonal statistics            (Session 5)

sf Is Worse Than dplyr

It reports the geometry type, the bounding box and the full CRS on every single read.

```{r}
#| warning: false
#| message: false
mex <- st_read("../data/gadm41_MEX_shp/gadm41_MEX_1.shp",
               quiet = TRUE)
```

Important

You need both. quiet = TRUE silences sf’s own reporting; the chunk options silence R’s warnings and messages. They are different channels.

A First Map of Mexico

library(sf)
library(ggplot2)

#Step1: Read it, quietly
mex <- st_read(
  "../data/gadm41_MEX_shp/gadm41_MEX_1.shp",
  quiet = TRUE)

#Step2: Draw it
ggplot() +
  geom_sf(data = mex)

Chunk Options for Maps

This is the part that is not in a normal Quarto tutorial.

```{r}
#| fig-width: 8       # plot canvas, in inches
#| fig-height: 5      # taller than wide is usually wrong for Mexico
#| out-width: "100%"  # how big it appears in the document
#| fig-dpi: 150       # resolution; 96 for slides, 150+ for print
```

Chunk Options for Maps

A wide canvas. Mexico fills it.

```{r}
#| warning: false
#| message: false
#| fig-width: 8
#| fig-height: 5
ggplot() + geom_sf(data = mex)
```

Chunk Options for Maps

A tall canvas. Same map, same code — but smaller, with space wasted.

```{r}
#| warning: false
#| message: false
#| fig-width: 4
#| fig-height: 6
ggplot() + geom_sf(data = mex)
```

embed-resources: The Trap

You build an interactive map. It works on your laptop. You email your reader the .html file on its own, the way you would send any attachment.

They see a blank white box.

Why It Happens

An HTML file by default links to its JavaScript, CSS, and images, which sit in a _files folder next to it. Send the HTML alone and every link breaks.

---
format:
  html:
    embed-resources: true
---

This inlines everything into a single, larger, self-sufficient file.

Important

Set embed-resources: true, then send the .html file on its own. Nothing else needs to travel with it: no _files folder, no images. For anything you send to anyone this setting is not optional. We build a leaflet map in Session 3, which is usually when it bites.

What Have We Learned?

  • Built a Quarto document from an empty RStudio window, and rendered it
  • Code chunks: echo and eval decide what the reader sees
  • warning and message keep package chatter out of a report
  • fig-width and fig-height set the canvas a map is drawn on
  • embed-resources: true, then send the single .html file
  • An sf layer reads quietly with quiet = TRUE, and geom_sf() draws it