R: R, mapas e dados espaciais

Última atualização: 2026-08-26

Dados geoespaciais estão se tornando cada vez mais importantes em projetos do mundo real — como mapas de pandemia, mapas de calor do serviço de transporte Didi e áreas de cobertura de entregas da JD.com. Nesta aula, aprenderemos sobre visualização de mapas no R: usando o pacote sf para processar dados vetoriais, o ggplot2 para criar mapas e o Leaflet para construir mapas interativos.

Ao concluir esta aula, você será capaz de criar quatro tipos de mapas usando o R: mapas estáticos (ggplot2 + sf), mapas coropléticos, mapas de densidade de pontos e mapas interativos (leaflet).

1. O que você vai aprender



2. A história por trás de um mapa de calor de logística

(1) Desafio: os dados de vendas não dão uma noção de escala

Alice é responsável pelas vendas nacionais. O gerente perguntou: “Qual província tem as melhores vendas?” Depois de examinar a planilha por um tempo, o gerente disse: “Mostre-me um mapa.”

(2) Solução usando R

R
library(sf)
library(ggplot2)

# 1. View a Map of China
china <- st_read("china.shp")

# 2. Consolidate Sales Data
china_sales <- china |>
  left_join(sales_by_province, by = c("name" = "province"))

# 3. Line Chart of Categorical Statistics
ggplot(china_sales) +
  geom_sf(aes(fill = sales)) +
  scale_fill_gradient(low = "lightblue", high = "darkred") +
  labs(title = "National Sales Distribution", fill = "Sales")

3 linhas de código → um mapa das vendas na China com qualidade de publicação.



3. Fundamentos dos dados espaciais

(1) Vetorial x Raster

100%
graph LR
    A[Spatial Data] --> B[Vector Data<br/>Vector]
    A --> C[Raster Data<br/>Raster]
    B --> D[pt Point<br/>Line<br/>Polygon]
    C --> E[Pixel Pixel<br/>Satellite image/Temperature Map]
    
    style A fill:#fff3cd
    style B fill:#cce5ff
    style C fill:#d4edda

Esta aula aborda dados vetoriais (pontos, linhas e superfícies).

(2) O que é um shapefile?

Shapefile = padrão de dados vetoriais da ESRI, composto por quatro arquivos:

TEXT 📖 Somente leitura
data/
├── china.shp         # Geometric Information (Coordinates)
├── china.shx         # Shape Index
├── china.dbf         # Property Information (Name, Population, etc.)
└── china.prj         # Projection Information (Coordinate System)
⚠️ Observação: Os 4 arquivos devem ser colocados no mesmo diretório. O arquivo shp é o “arquivo principal”, e os demais são arquivos auxiliares.

GeoJSON

Os mapas modernos na web costumam usar GeoJSON (baseado em JSON):

JSON
{
  "type": "Feature",
  "properties": {"name": "Beijing", "value": 1000},
  "geometry": {
    "type": "Point",
    "coordinates": [116.4, 39.9]
  }
}


4. O pacote sf: Estrutura de Dados Espaciais

(1) Por que usar SF?

Bolsa Características Recomendação
sf Moderno, no estilo R tidyverse, com integração ao ggplot2 ⭐⭐⭐
sp Sintaxe básica do R no estilo antigo ❌ Não recomendado para código novo
raster Apenas para dados raster Para raster

(2) Instalação e carregamento

R
install.packages("sf")
library(sf)

(3) Ler o shapefile

R
library(sf)

# Read shapefile
china <- st_read("data/china.shp")
# Reading layer `china' from data source `data/china.shp' using driver `ESRI Shapefile'
# Simple feature collection with 34 features and 5 fields
# Geometry type: MULTIPOLYGON
# Dimension:     XY
# Bounding box:  xmin: 73.5 ymin: 3.5 xmax: 135 ymax: 53.5
# CRS:           4326  ← WGS84 Latitude and Longitude

# View Structure
print(china)
# It basically means data.frame More geometry col
# A tibble: 34 × 6
#   name        population   ...  geometry
#   <chr>            <int>        <MULTIPOLYGON>

# Geometric Information
st_geometry(china)
st_crs(china)              # Coordinate System
st_bbox(china)             # Bounding Box

(4) Leitura de GeoJSON

R
# Read GeoJSON
cities <- st_read("data/cities.geojson")
# or from URL
cities <- st_read("https://example.com/cities.geojson")

(5) Gravar shapefile / GeoJSON

R
# Write shapefile
st_write(china, "output/china_new.shp")

# Write GeoJSON
st_write(china, "output/china.geojson")


5. Sistema de Coordenadas CRS

(1) O que é o CRS?

CRS (Sistema de Referência de Coordenadas) — Mapeamento da superfície “esférica” da Terra em um “plano”:

100%
graph LR
    A[Earth's Surface] -->|WGS84 4326<br/>Latitude and Longitude| B[Cartesian coordinates]
    A -->|Web Mercator 3857<br/>m| B
    A -->|China 2000<br/>m| B

(2) Códigos CRS comuns

EPSG Nome Finalidade
4326 WGS84 GPS / Geral (Mais comum)
3857 Web Mercator Google Maps, OpenStreetMap
4490 CGCS2000 Sistema Nacional de Coordenadas da China
32650 UTM 50N Local de alta precisão

(3) Transformação do sistema de coordenadas

R
# View Current CRS
st_crs(china)
# Coordinate Reference System:
#   EPSG: 4326
#   proj4string: "+proj=longlat +datum=WGS84 ..."

# Convert to Web Mercator (3857)
china_3857 <- st_transform(china, 3857)

# Convert to China 2000
china_4490 <- st_transform(china, 4490)

(4) Extração de latitude e longitude

R
# Extracting Latitude and Longitude from Geometric Objects
coords <- st_coordinates(china)
# X = Longitude, Y = Latitude


6. Criação de mapas com o ggplot2 e o geom_sf

(1) Mapa de base

R
library(ggplot2)
library(sf)

ggplot(china) +
  geom_sf() +
  labs(title = "Map of China")

(2) Mapas coloridos (coloração por atributo)

R
# Color by Province
ggplot(china) +
  geom_sf(aes(fill = name)) +
  labs(title = "Provinces of China", fill = "Province") +
  theme_minimal()

(3) Simplificação geométrica (otimização de desempenho)

R
# World Map (>100,000 coordinate points) use st_simplify to simplify
china_simple <- st_simplify(china, dTolerance = 1000)

# Drawing (10x faster)
ggplot(china_simple) + geom_sf()

(4) Personalização do tema

R
ggplot(china) +
  geom_sf(fill = "lightblue", color = "white", linewidth = 0.3) +
  labs(title = "Map of China") +
  theme_void() +              # Hide Axes
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "none"
  )


7. Mapa coroplético

(1) Combinar dados + Criar um gráfico categórico

R
# Simulated Sales Data
sales_by_province <- data.frame(
  name = c("Beijing", "Shanghai", "Guangdong", "Jiangsu", "Zhejiang", "Sichuan", "Hubei", "Shaanxi"),
  sales = c(5000, 4500, 4000, 3500, 3000, 2500, 2000, 1500)
)

# Merge into Map
china_sales <- china |>
  left_join(sales_by_province, by = "name")

# Draw a categorical bar chart
ggplot(china_sales) +
  geom_sf(aes(fill = sales), color = "white", linewidth = 0.3) +
  scale_fill_gradient(
    low = "lightyellow",
    high = "darkred",
    na.value = "gray90"
  ) +
  labs(title = "National Sales Distribution", fill = "Sales (10,000 yuan)") +
  theme_void() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
    legend.position = "right"
  )

(2) Categorização (Contínua → Discreta)

R
# Use cut to divide continuous values into 5 levels
china_sales <- china_sales |>
  mutate(sales_level = cut(sales,
                            breaks = c(0, 1000, 2000, 3000, 5000),
                            labels = c("Low", "Mid", "High", "Very high"),
                            include.lowest = TRUE))

ggplot(china_sales) +
  geom_sf(aes(fill = sales_level), color = "white", linewidth = 0.3) +
  scale_fill_brewer(palette = "YlOrRd", na.value = "gray90") +
  labs(title = "National Sales Grades", fill = "Level") +
  theme_void()


8. Gráfico de densidade de pontos

R
# City Points (Latitude and Longitude + Sales)
cities <- data.frame(
  name = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen"),
  lon = c(116.4, 121.5, 113.3, 114.1),
  lat = c(39.9, 31.2, 23.1, 22.5),
  sales = c(5000, 4500, 4000, 3000)
) |>
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

# Draw a point on the map
ggplot() +
  geom_sf(data = china, fill = "lightgray", color = "white") +
  geom_sf(data = cities, aes(size = sales), color = "red", alpha = 0.7) +
  scale_size_continuous(range = c(2, 12), name = "Sales") +
  labs(title = "Map of City Sales Locations") +
  theme_void()


9. Mapa interativo do Leaflet

(1) Instalação e noções básicas

R
install.packages("leaflet")
library(leaflet)

# Basic Interactive Map
m <- leaflet() |>
  addTiles() |>  # OpenStreetMap Base Map
  setView(lng = 116.4, lat = 39.9, zoom = 4)  # Beijing as the center

m  # Show in RStudio Viewer

(2) Adicionar uma marca (rótulo)

R
# Add a city
leaflet(cities) |>
  addTiles() |>
  addMarkers(
    lng = ~lon, lat = ~lat,
    popup = ~paste0("<b>", name, "</b><br>Sales: ", sales, " 10,000 yuan"),
    label = ~name
  )

(3) Adicionar círculos (por tamanho)

R
leaflet(cities) |>
  addTiles() |>
  addCircles(
    lng = ~lon, lat = ~lat,
    radius = ~sqrt(sales) * 1000,  # By Sales Radius
    color = "red",
    fillOpacity = 0.5,
    popup = ~paste0(name, ": ", sales, "10,000 yuan")
  )

(4) Coloração em tons

R
# Use sf Data
leaflet(china_sales) |>
  addTiles() |>
  addPolygons(
    fillColor = ~colorNumeric("YlOrRd", sales)(sales),
    fillOpacity = 0.7,
    color = "white",
    weight = 1,
    popup = ~paste0("<b>", name, "</b><br>Sales: ", sales, " 10,000 yuan")
  ) |>
  addLegend(pal = colorNumeric("YlOrRd", china_sales$sales),
            values = ~sales, title = "Sales")

(5) Salvar como HTML

R
# Save as a standalone HTML file (Shareable)
library(htmlwidgets)
saveWidget(m, "interactive_map.html", selfcontained = FALSE)


10. Exemplo completo: Mapa de calor das vendas nacionais

A seguir, apresentamos um exemplo de um fluxo de trabalho completo que reúne todos os conceitos relacionados a mapas abordados nesta aula.

▶ Exemplo: 4 mapas de vendas de cidades (estáticos + interativos)

R 📖 Somente leitura
# ============================================
# 4 City Sales Map
# Features: ggplot2 Static Map + leaflet Interactive Map
# ============================================

library(sf)
library(ggplot2)
library(dplyr)
library(leaflet)
library(htmlwidgets)

# 1. Prepare data
# 4 City Points (Latitude and Longitude)
cities <- data.frame(
  name = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen"),
  lon = c(116.4074, 121.4737, 113.2644, 114.0579),
  lat = c(39.9042, 31.2304, 23.1291, 22.5431),
  sales = c(5000, 4500, 4000, 3000),
  region = c("Northern China", "East China", "South China", "South China")
) |>
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

# Simplified Polygons of China's Provinces (Sample data here)
china_provinces <- data.frame(
  name = c("Beijing", "Shanghai", "Guangdong Province"),
  lat = c(39.9, 31.2, 23.1),
  lon = c(116.4, 121.5, 113.3)
)

# 2. Static Map: ggplot2 + sf
p_static <- ggplot() +
  # Simplify: use geom_point Replace geom_sf (no shapefile)
  borders(database = "world", regions = "China",
          fill = "lightgray", color = "white") +
  geom_sf(data = cities, aes(size = sales, color = region),
          alpha = 0.7) +
  geom_text(data = as.data.frame(st_coordinates(cities)),
            aes(X, Y, label = cities$name),
            vjust = -1.5, size = 4, fontface = "bold") +
  scale_size_continuous(range = c(3, 12), name = "Sales (10,000 yuan)") +
  scale_color_brewer(palette = "Set1", name = "Region") +
  labs(title = "China 4 Sales Distribution in Major Cities",
       subtitle = "Data: 2024 Q4",
       caption = "Base Map: Natural Earth | Chart: R + ggplot2") +
  coord_sf(xlim = c(100, 125), ylim = c(20, 45)) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
    legend.position = "bottom"
  )

print(p_static)

# 3. Save Static Image
ggsave("static_sales_map.png", p_static,
       width = 10, height = 8, dpi = 300)
cat("=== The static map has been saved: static_sales_map.png ===\n")

# 4. Interactive Map: leaflet
cities_df <- as.data.frame(st_coordinates(cities)) |>
  rename(lon = X, lat = Y) |>
  cbind(name = cities$name, sales = cities$sales, region = cities$region)

m_interactive <- leaflet(cities_df) |>
  addTiles() |>
  setView(lng = 114, lat = 33, zoom = 4) |>
  addCircleMarkers(
    lng = ~lon, lat = ~lat,
    radius = ~sqrt(sales) / 5,
    color = ~case_when(
      region == "Northern China" ~ "red",
      region == "East China" ~ "blue",
      region == "South China" ~ "green"
    ),
    fillOpacity = 0.6,
    stroke = TRUE,
    weight = 2,
    popup = ~paste0(
      "<div style='font-family: Arial; font-size: 14px;'>",
      "<b>", name, "</b><br>",
      "Region: ", region, "<br>",
      "Sales: ", sales, " 10,000 yuan<br>",
      "Longitude: ", round(lon, 2), "<br>",
      "Latitude: ", round(lat, 2),
      "</div>"
    ),
    label = ~paste0(name, ": ", sales, "10,000 yuan")
  ) |>
  addLegend(
    "bottomright",
    colors = c("red", "blue", "green"),
    labels = c("Northern China", "East China", "South China"),
    title = "Region"
  ) |>
  addScaleBar(position = "bottomleft")

# 5. Show (in RStudio Viewer)
m_interactive

# 6. Save as HTML
saveWidget(m_interactive, "interactive_sales_map.html",
           selfcontained = TRUE)
cat("=== The interactive map has been saved: interactive_sales_map.html ===\n")

# 7. Output Statistics
cat("\n=== Sales Statistics ===\n")
print(cities_df |> select(name, region, sales))
83 linhas de lógica (limite de 40, somente leitura)

Resultado esperado:


❓ Perguntas Frequentes

P: O que devo escolher, sf ou sp? R: sf (Simple Features) é o padrão moderno para dados espaciais no R, segue o estilo do tidyverse e se integra perfeitamente ao dplyr e ao ggplot2. Use sf para códigos novos; o código antigo do sp não é mais mantido.**

P: Como faço para escolher entre 4326 e 3857? R: 4326 (latitude e longitude WGS84) é para uso geral + GPS; 3857 (Web Mercator) é para serviços de mapas + Google/OSM. O ggplot2 + sf normalmente usa 4326, enquanto o Leaflet usa 3857 por padrão.**

P: Onde posso baixar o shapefile? R:

P: O que devo fazer se o mapa exibir caracteres chineses distorcidos? R: Adicione family = "SimHei" a theme(). Como alternativa, borders(database = "world", regions = "China") já inclui os nomes em chinês por padrão.

P: Como faço para criar um mapa 3D? R: Use o pacote rayshader (que converte gráficos do ggplot2 para 3D) ou o gráfico de dispersão 3D do plotly combinado com um fundo de mapa. Não entraremos em detalhes sobre isso nesta aula.

P: O que devo escolher para mapas — Leaflet ou ggplot2? R:


📖 Resumo


📝 Exercícios

  1. Exercício básico: Baixe o arquivo GeoJSON das províncias da China no Alibaba Cloud DataV, importe-o usando st_read() e utilize ggplot() + geom_sf() para desenhar um mapa básico da China (sem preencher com cores).

  2. Questão básica: Pinte o mapa da questão anterior por província (usando aes(fill = name)) e, em seguida, adicione theme_void() e um desenho temático.

  3. Exercício básico: Use borders(database = "world", regions = "China") + geom_point(data = cities, aes(lon, lat, size = sales)) para traçar um mapa das cidades chinesas, com coordenadas personalizadas de latitude e longitude para quatro cidades.

  4. Exercício avançado: Simule dados de vendas para 5 cidades (incluindo latitude/longitude, valores de vendas e categorias) e use o Leaflet para criar um mapa interativo: ① Relacione os valores de vendas ao tamanho dos círculos; ② Relacione as categorias às cores; ③ Adicione dicas de ferramenta para exibir detalhes; ④ Adicione uma legenda e uma escala. Salve como HTML.

  5. Desafio: Fluxo de trabalho completo — Baixe os dados GeoJSON das províncias da China e simule dados de vendas para as 31 províncias: ① Faça a junção no mapa usando um LEFT JOIN ② Use cut() para dividir em 5 categorias ③ Crie um gráfico estatístico categorizado usando o ggplot2 ④ Formate o gráfico (título/subtítulo/fonte chinesa) ⑤ Crie uma versão interativa usando o Leaflet (incluindo uma legenda) ⑥ Salve como PNG e HTML. Faça uma captura de tela e salve-a.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%