Mapas em R

Antes de começar

Antes de iniciar o tutorial, certifique-se de que os pacotes sf, mapview, tmap e raster são instalados.

Início

Dados espaciais são dados organizados por localização e permitem novos tipos de análise e visualização. Explorar mapas em R nos permite praticar e estender muitas das ferramentas que aprendemos nas últimas semanas - análises de dados com dplyr, joins e gráficos com ggplot2.

Mapeando Pontos Geográficos

Vamos começar a trabalhar com mapas a partir de um exemplo que, veremos, utilizará as ferramentas que aprendemos até então para produzir nossos primeiros mapas. Para tanto, vamos utilizar o cadastro de escolas que a Prefeitura Municipal de São Paulo disponibiliza aqui.

Nossa primeira tarefa é baixar os dados e faremos isso de forma inteligente e sem “cliques”. A partir do url do arquivo do cadastro, que guardaremos no objeto “url_cadatros_escolas”, faremos o download do arquivo e guardaremos o arquivo .csv baixado como o nome “escolas.csv”:

url_cadatros_escolas <- "http://dados.prefeitura.sp.gov.br/dataset/8da55b0e-b385-4b54-9296-d0000014ddd5/resource/39db5031-7238-4139-bcaa-e620a3180188/download/escolasr34fev2017.csv"

download.file(url_cadatros_escolas, "escolas.csv")

Vamos abrir o arquivo. Observe que estamos especificando o mesmo ‘endoding’ (‘LATIN1’) na qual o arquivo foi salvo para garantir que os dados sejam legíveis.

library(tidyverse)
escolas <- read_delim("escolas.csv",
                      delim = ";", 
                      locale=locale(encoding="LATIN1"))

Não há nada de extraordinário no arquivo, que se assemelha aos que vimos até então. Há, porém, uma dupla de variáveis que nos permite trabalhar “geograficamente” com os dados: LATITUDE e LONGITUDE. “Lat e Long” são a informação fundamental de um dos sistemas de coordenadas (coordinate reference system, CRS) mais utilizados para localização de objetos na superfície da terra.

Por uma razão desconhecida, a informação fornecida pela PMSP está em formato diferente do convencional. Latitudes são representadas por números entre -90 e 90, com 8 casas decimais, e Longitudes por números entre -180 e 180, também com 8 casas decimais. Em nosso par de variáveis, o separador de decimal está omitido e por esta razão faremos um pequena modificação na variável. Aproveitaremos também para renomear algumas variáveis de nosso interesse – como tipo da escola (CEI, EMEI, EMEF, CEU, etc) e o ano de início do funcionamento::

escolas <- escolas  %>%
  rename(lat = LATITUDE, lon = LONGITUDE, tipo = TIPOESC) %>% 
  mutate(lat = lat / 1000000, 
         lon = lon / 1000000,
         ano = as.numeric(substr(DT_INI_FUNC, 7, 10))) %>%
  filter(is.na(lat)==FALSE & is.na(lon)==FALSE)

Pronto! Temos agora uma informação geográfica das EMEFs e uma variável de interesse – ano – que utilizaremos para investigar a expansão da rede.

Para analisar estes dados como dados espaciais precisamos dizer a R quais são as variáveis de localização e transformá-lo em um objeto ‘simple features’ usando o biblioteca sf e a função st_as_sf para criar um objeto tipo “simple features”. Lembre-se de instalar o pacote antes de carregá-lo.

library(sf)
escolas <- escolas %>% st_as_sf(coords=c("lon","lat"), crs=4326)

O parâmetro ‘coords’ indica os colunas de latitude e longitude, e o ‘crs’ indica o sistema de coordenadas (a “projeção”) que queremos usar. Na verdade, o sistema de coordenadas não é uma opção nossa - precisamos especificar o mesmo sistema de coordenadas com o qual os dados foram salvos. Às vezes isso é difícil de saber. Aqui, como o longitude e latitude parece que eles estão em graus (entre -180 e 180) é provável que devemos usar o sistema “WGS84” (um sistema de coordenadas geográficas (não projetadas)). Um ‘shortcut’ para especificar o WGS84 é usar o numero 4326 no argumento ‘crs’. Para outros shortcuts para sistemas de coordenados que pode usar com dados salvados em outros sistemas de coordenados, pode aproveitar do site http://epsg.io/. Não se preocupe muito com sistemas de coordenadas - vamos discuti-los mais em aula.

Qual tipo de objeto é emef agora?

class(escolas)

Temos vários resultados aqui - é um objeto ‘simple features’, mas também um tbl (tibble) e data.frame! Isso significa que podemos aplicar todas as funções do dplyr com dados espaciais também. Por exemplo, selecionaremos apenas as linhas referentes a EMEF (Escolas Municipal de Ensino Fundamental) usando filter e as variáveis NOMESC e SITUACAO:

emef <- escolas  %>%
  filter(tipo == "EMEF") %>%
  select(NOMESC,SITUACAO)

Vamos olhar em detalhe o conteúdo de nosso objeto:

emef

Quando olhamos num objeto simple features, tem vários coisas importantes a observar:
1. Na descrição imprimido em Rstudio, podemos ver ‘geometry type: POINT’ - isso significa que cada elemento espacial é um ponto único (também pode ser um polígono ou linha);
2. O CRS (e o código epsg);
3. O número de ‘features’ (unidades espaciais) e ‘fields’ (variáveis/colunas);
4. No data.frame mesmo, temos duas colunas de NOMESC e SITUACAO, e uma terceira de ‘geometry’ - isso é uma coluna especial que contém as informações de localização para cada unidade. Aqui são as coordenadas dos nossos pontos.

Visualisando mapas

Lembra que nossos gráficos em ggplot foram conectados diretamente com um data.frame? Como um objeto de ‘simple features’ é um data.frame, podemos usar ggplot para criar mapas! Para visualizar o nosso mapa, vamos usar uma camada de geometria especial (e espacial) se chama geom_sf, e o formato dos parâmetros são os mais simples possíveis: branco!

emef %>% ggplot() +
  geom_sf()

Já criou o seu primeiro mapa! O eixo x é o longitude e o eixo y é o latitude. Debaixo, vamos ver como a representar outros aspectos de nosso data.frame em mapas. Agora, tem um outro abordagem para criar um mapa interativo, com um mapa de terra no fundo. Usamos o pacote ‘mapview’ e, de novo, não precisamos especificar nenhum parâmetro.

library(mapview)
emef %>% mapview()

Exercício

Criar um mapa de escolas de tipo “EMEBS” (Escolas Municipais de Educação Bilíngue para Surdos).

Georeferencing

Um outro fonte de dados espaciais é georeferenciar um endereço usando uma ferramenta de busca como o open street maps (mas fácil a acessar do que o google maps), por exemplo com a biblioteca tmaptools do pacote tmap.

Vamos agora criar um novo data frame a partir dos dados do cadastro da PMSP que contém apenas os CEUs. Vamos juntar algumas informações de endereço e agregar a elas o texto “, Sao Paulo, Brazil”

ceu <- escolas  %>%
  filter(tipo == "CEU") %>%
  mutate(endereco = paste(ENDERECO, NUMERO, ", Sao Paulo, Brazil"))

Com a função geocode_OSM, procuraremos a latitude e longitude dos 46 CEUs. Vamos ver o exemplo do primeiro CEU, criando um objeto tipo ‘sf’ com o nosso sistema de referência de coordenadas (WGS84 = epsg 4326) para ficar consistente:

library(tmaptools)
ceu_geocoded <- geocode_OSM(ceu$endereco, projection=4326, as.sf=T)

Podemos mapear os resultados:

ceu_geocoded %>% mapview()

Agora, podemos comparar as localizações no banco de dados original da PMSP com os nossos resultados de geocodificação, aplicando o nosso conhecimento do ggplot2.

ceu %>% ggplot() + 
  geom_sf(color="blue") +
  geom_sf(data=ceu_geocoded, color="red",
          alpha=0.5)

Simples, não! Alguns erros, mas a maioria dos conjuntos de coordenados parecem bem pertos. O principal problema da função geocode_OSM - e por que você pode ver menos de 46 pontos geocodificados em seu mapa - é que ele depende muito na qualidade de endereços. ### Exercício Use a função geocode_OSM para geolocalizar três endereços com os quais você está familiarizado em São Paulo e criar um mapa com ggplot e um mapa interativa com mapview.

Trabalhando com Polígonos

Áreas administrativas são geralmente representadas como polígonos em mapas. Em geral, obtemos esses polígonos como ‘shapefiles’ produzidos por uma agência oficial. Podemos abrir qualquer tipo de shapefile (pontos, linhas ou polígonos) com a função read_sf. Vamos abrir um shapefile (simplificado) de IBGE dos municipios do Brasil.

download.file("https://github.com/JonnyPhillips/Curso_R/raw/master/Brazil_s.zip",destfile="Brazil_s.zip")
unzip("Brazil_s.zip")
municipios <- read_sf("Brazil_s.shp")

Como podemos visualizar este mapa? Exatamente o mesmo que antes (pode demorar para abrir):

municipios %>% ggplot() +
  geom_sf()

Enquanto trabalhando com dados espaciais de diversas fontes (ex. as escolas e os municipios), é essencial ter certeza que estamos trabalhando com a mesma projeção para todos os objetos - caso contrário nossos mapas podem não aparecer corretamente e nossas medidas espaciais serão imprecisas. Dar uma olhada na CRS/projeção de ‘municipios’ - está escrito “+proj=longlat +ellps=GRS80 +no_defs” - em contraste de CRS/projeção de ‘escolas’ - “+proj=longlat +datum=WGS84 +no_defs”. O parte ‘WGS84’ é o mais comum então vamos transformar os dados dos municipios para a mesma projeção usando a função st_transform e o shortcut 4326 para WGS84. Agora estamos tudo pronto para fazer análises ou mapas incorporando as duas camadas espaciais.

municipios <- municipios %>% st_transform(4326)

Joins Não-Espaciais

Se abre o objeto municipios vai ver os detalhes de CRS/projeção e o data.frame dele mesmo, incluindo a coluna de ‘geometry’ e também todas as colunas normais, incluindo o código municipal (CD_GEOCODM). Isso é uma oportunidade para nós - se tivermos dados não-espaciais de todos os municípios, podemos simplesmente ‘join’ estes dados com o nosso shapefile e, em seguida, podemos visualizar mapas dessas variáveis.

Por exemplo, vamos baixar os dados eleitorais de 2010 para cada município com o percentagem de voto da Dilma no segundo turno.

download.file("https://github.com/JonnyPhillips/FLS6397_2019/raw/master/data/Dilma_2010_segundo_turno.csv",destfile="Dilma_2010_segundo_turno.csv")

Dilma_2010 <- read_csv("Dilma_2010_segundo_turno.csv")

Agora, se quisermos tornar esses dados eleitorais espaciais podemos fazer um left_join com o nosso shapefile, contanto que, como normal, os nomes e tipo de colunas chaves - o código do IBGE - nos dois bancos de dados são os mesmos.

municipios <- municipios %>% rename("COD_MUN_IBGE"="CD_GEOCODM") %>% 
  mutate(COD_MUN_IBGE=as.numeric(COD_MUN_IBGE)) %>%
  left_join(Dilma_2010,by="COD_MUN_IBGE")

Quantas colunas o nosso objeto municipios tem agora? Mais! - os dados eletorais também.

municipios

Para visualizar a coluna ‘pct_voto’ num mapa, podemos trabalhar em ggplot como normal. Para polígonos, colocamos o nome de coluna para o parâmetro fill ( color com pontos e linhas). Um mapa de todo o Brasil pode ser esmagador, então vamos nos concentrar em São Paulo com um filter.

municipios %>% filter(UF=="SP") %>% 
  ggplot() +
  geom_sf(aes(fill=pct_voto))

Vamos destacar as fronteiras em branco, aplicar um tema e tirar as linhas de longitude/latitude.

municipios %>% filter(UF=="SP") %>% 
  ggplot() +
  geom_sf(aes(fill=pct_voto),color="white") +
  theme_classic() +
  coord_sf(datum=NA)

Além disso, podemos alterar as escalas de cores como normal em ggplot.

municipios %>% filter(UF=="SP") %>% 
  ggplot() +
  geom_sf(aes(fill=pct_voto),color="white") +
  theme_classic() +
  coord_sf(datum=NA) +
  scale_fill_gradient(low="white",high="red")

Podemos colocar os pontos dos CEUs no mesmo mapa? Sim - apenas especificamos o parâmetro de ‘data’ para uma nova camada dentro de geom_sf. (Isto assume que nós confirmamos que ambas as camadas têm a mesma projeção, como confirmamos acima).

municipios %>% filter(UF=="SP") %>% 
  ggplot() +
  geom_sf(aes(fill=pct_voto),color="white") +
  theme_classic() +
  scale_fill_gradient(low="white",high="red") +
  geom_sf(data=ceu,color="dark green") +
  coord_sf(datum=NA)

Exercício

Crie um mapa do percentagem de voto de Dilma nos municipios de Tocantins no segundo turno da eleição presidencial de 2010.

Joins Espaciais

O mundo espacial abre um novo tipo de join entre diversas bancos de dados - joins espaciais que são definido pela localização semelhante e não por uma chave comum nas tabelas de dados. Existe diversas tipos de joins espaciais mas vamos focar sobre um join entre uma camada de polígonos e uma camada de pontos. Queremos pegar os dados de polígono (município) em que fica cada ponto (escola). Vamos usar st_join para fazer um join espacial e usar o tipo de comparação de st_contains (que simplesmente significa que um join existe quando um ponto é dentro de um polígono). (Lembra-se que é importante confirmar que ambas as camadas têm a mesma projeção, como confirmamos acima).

joined <- ceu %>% st_join(municipios,st_intersects)

Agora, o objeto ‘joined’ contém os dados de escolas e colunas adicionais com dados dos polígonos em que cada escola existe. Isso facilita comparações entre cada escola e os dados eleitorais da sua região, mas também podemos simplesmente contar o número de escolas em cada polígono (município). Claro que neste caso, a maioria das escolas estão localizadas na PMSP.

joined %>% group_by(NM_MUNICIP) %>% count()

Outras operações espaciais

O pacote simple features inclui muitas metodologias espaciais. Como exemplo, podemos calcular a distancia entre cada um das escolas CEU (em metros) com st_distance. O resultado é um matriz (simétrico) com distancias entre escolas.

ceu %>% st_distance()

Rasters

Existe um outro formato para dados espaciais que não é baseado em formas geométricas (polígonos, pontos e linhas), mas em uma grade regular com valores específicos em cada célula x, y - isto é um ‘raster’ e para trabalhar com ele usamos o pacote ‘raster’. Vamos usar o código debaixo para abrir um arquivo raster de densidade populacional no Camboja, que é simplesmente um imagem com extensão .tif.

library(raster)
download.file("https://github.com/JonnyPhillips/Curso_R/raw/master/khm_popdenr_landscan_2011.zip",destfile="khm_popdenr_landscan_2011.zip")
unzip("khm_popdenr_landscan_2011.zip")
cambodia <- raster("khm_popdenr_landscan_2011.tif")

Para visualizar o nosso raster, precisamos transformar ele em um data.frame simples e usar o ggplot com a geometria de geom_tile.

cambodia %>% as("SpatialPixelsDataFrame") %>% 
  as.data.frame() %>% 
  ggplot() + 
  geom_tile(aes(x=x,y=y,fill=khm_popdenr_landscan_2011))

Este mapa parece bem chato porque os dados são altamente ‘skewed’, com grandes outliers apenas na capital. Frequentemente com rasters é útil transformá-los em uma escala de log para visualizar. Vamos também limpar o fundo e adicionar uma escala de cores.

cambodia %>% as("SpatialPixelsDataFrame") %>% 
  as.data.frame() %>% 
  ggplot() + 
  geom_tile(aes(x=x,y=y,fill=khm_popdenr_landscan_2011)) +
  coord_equal() +
  theme_void() +
  scale_fill_gradient(low="white",high="red",na.value="white", trans="log")

Também podemos criar mapas de raster interativos com mapview. (Adicionamos uma pequena quantidade ao log para evitar valores infinitos e erros).

log(cambodia+0.00001) %>% 
  brick() %>% 
  mapview(layer.name="khm_popdenr_landscan_2011")

Exercício:

Vá a uma das duas fontes de mapas indicadas – Prefeitura de São Paulo ou Centro de Estudos da Metrópole (CEM) – importe uns arquivos espaciais e produza um mapa. Dependendo do tema que você escolher, você produzirá mapas com polígonos (por exemplo, mapas de divisões políticas ou administrativas), linhas (ruas, ciclovias, etc), pontos (unidades de saúde, escolas, etc) ou rasters.