7  Mapas

Nos dias atuais, é muito comum nos depararmos com dados georreferenciados. O crescimento de dados espaciais é relativamente recente. Porém, já experimentamos seus efeitos no dia a dia.

Visualizações de dados espaciais é fundamental para quem trabalha com análise de dados.

7.1 Shapefile

Se o intuito é representar o espaço de interesse, precisamos inicialmente discutir sobre o shapefile.

Um shapefile é um formato popular de arquivo contendo dados geoespaciais em forma de vetor usado por Sistemas de Informações Geográficas (SIG). Shapefiles espaciais descrevem geometrias: pontos, linhas e polígonos. Entre outras coisas, essas geometrias podem representar Poços, Rios, cidades, respectivamente.

O formato shapefile define a geometria e os atributos dos recursos referenciados geograficamente em três ou mais arquivos com extensões de arquivo específicas que devem ser armazenadas no mesmo espaço de trabalho do projeto. Eles são:

  • .shp - O arquivo principal que armazena a geometria do recurso, obrigatório,

  • .shx - O arquivo de índice que armazena o índice da geometria do recurso, obrigatório,

  • .dbf - A tabela do dBASE que armazena as informações de atributos dos recursos, obrigatório,

  • .prj, .sbn, .sbx, .fbn, .fbx, .ain, .aih, .atx, .ixs, .mxs, .xml, .cpg são outras extensões possíveis.

Alguns arquivos podem ser obtidos no site do IBGE (https://www.ibge.gov.br/geociencias/downloads-geociencias.html).

Acessem o site e sigam o seguinte caminho: Organização do territorio >> malhas territoriais. Vocês terão acesso:

  • malhas de setores censitários,

  • malhas municipais,

  • malhas do Brasil por município.

Atividade: Baixem o arquivo do Estado do Rio de Janeiro do ano de 2016 contendo os municípios. Vejam quantos arquivos foram baixados e quais suas extensões.

7.1.1 Importando Shapefile

Para importamos um shapefile para o R usaremos a função st_read do pacote sf.

A seguir, vamos apresentar os principais argumentos da função st_read:

  - dsn - o nome do arquivo a ser importado (com extensão .shp).

# Carregando o pacote
library(sf)

# Importando o shapefile
recife = st_read(dsn = "Bairros_Recife.shp")
Linking to GEOS 3.11.0, GDAL 3.5.3, PROJ 9.1.0; sf_use_s2() is TRUE
Reading layer `Bairros_Recife' from data source 
  `/Users/jonyarrais/Documents/VisualizacaoDados/Bases/Cap7/Bairros_Recife.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 94 features and 10 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 277547.5 ymin: 9098032 xmax: 295287.7 ymax: 9123296
Projected CRS: SIRGAS 2000 / UTM zone 25S

Para plotarmos o shape, vamos inicialmemte recorrer ao ggplot2.

# Carregando o pacote
library(ggplot2)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
## Para plotar somente o contorno do objeto recife
ggplot(data = recife) +
  geom_sf(fill = "White")

# Selecionando o shape do bairro de Boa Viagem
Boa_viagem = recife |>  
  filter(EBAIRRNOME == "BOA VIAGEM")

# Visualizando o objeto Boa_viagem
Boa_viagem
Simple feature collection with 1 feature and 10 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 288679.6 ymin: 9098032 xmax: 292546.5 ymax: 9104302
Projected CRS: SIRGAS 2000 / UTM zone 25S
  CBAIRRCODI VBAIRROID EBAIRRNOME CRPAAACODI CMICROCODI TBAIRRULAT CEMPRECODI
1        205         0 BOA VIAGEM          6          1 1899-12-30          0
  AUSUACMATR EBAIRRNO_1 EBAIRRLINK                       geometry
1          0 Boa Viagem       <NA> MULTIPOLYGON (((289794.1 90...
# Plotando o shape de Boa Viagem
ggplot(data = Boa_viagem) +
  geom_sf() +
  theme_light()

# Unindo poligonos no shapefile
novo_recife = recife |>  
   group_by(CRPAAACODI) |>  
   summarize(n = n())

# Visualizando o novo objeto
novo_recife
Simple feature collection with 6 features and 2 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 277547.5 ymin: 9098032 xmax: 295287.7 ymax: 9123296
Projected CRS: SIRGAS 2000 / UTM zone 25S
# A tibble: 6 × 3
  CRPAAACODI     n                                                      geometry
       <dbl> <int>                                                 <POLYGON [m]>
1          1    11 ((291904.5 9106181, 291809.5 9106087, 291804.2 9106082, 2916…
2          2    18 ((290782.8 9111147, 290674.9 9111202, 290670.2 9111204, 2906…
3          3    29 ((288186.9 9110691, 288112.3 9110726, 288077.5 9110745, 2880…
4          4    12 ((287295.9 9108372, 287294 9108364, 287288.6 9108349, 287228…
5          5    16 ((287565.4 9103874, 287544.5 9103870, 287505.3 9103870, 2874…
6          6     8 ((292546.5 9104121, 292509.4 9104020, 292477.6 9103925, 2924…
# Plotando o novo objeto
ggplot(data = novo_recife) +
  geom_sf(fill = "White") +
  theme_light()

Determinados shapifiles podem ser porduzidos com uma quantidade muito grande de pontos para definir os contornos da região de interesse. Nestes casos, os mapas que serão plotados serão pesados e provavelmente demoram a carregar. Existe uma forma de deixar esses arquivos mais leves nestes cenários.

# Carregando o pacote
library(rmapshaper)
The legacy packages maptools, rgdal, and rgeos, underpinning the sp package,
which was just loaded, will retire in October 2023.
Please refer to R-spatial evolution reports for details, especially
https://r-spatial.org/r/2023/05/15/evolution4.html.
It may be desirable to make the sf package available;
package maintainers should consider adding sf to Suggests:.
The sp package is now running under evolution status 2
     (status 2 uses the sf package in place of rgdal)
# Simplificando o shape
Recife_simples = ms_simplify(input = recife,
                             keep = 0.1,
                             keep_shapes = TRUE)

O argumento input é o shape a ser simplificado, o keep é a proporção dos pontos que você deseja manter e o keep_shapes = TRUE impede que características pequenas dos polígonos sumam, no caso de uma grande simplificação.

# Plotando o objeto simplificado
ggplot(data = Recife_simples) +
  geom_sf(fill = "White") +
  theme_light()

7.2 Mapa de pontos

Quando possuímos as coordenadas (lat/long) da ocorrência de um evento de interesse, uma visualização apropriada é a criação de um mapa de pontos. Neste mapa é possível avaliar a existência de aglomerações dos pontos em algumas localidades da região de interesse.

Atividade: Criem um projeto chamado MapaNY. Importem o arquivo nyshape.shp e armazenem em um objeto chamado NYShp.

Reading layer `nyshape' from data source 
  `/Users/jonyarrais/Documents/VisualizacaoDados/Bases/Cap7/nyshape.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 71 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74.25559 ymin: 40.49612 xmax: -73.70001 ymax: 40.91553
Geodetic CRS:  WGS84(DD)
# Visualizando o objeto
NYShp
Simple feature collection with 71 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74.25559 ymin: 40.49612 xmax: -73.70001 ymax: 40.91553
Geodetic CRS:  WGS84(DD)
First 10 features:
   shape_leng boro_cd shape_area                       geometry
1    51549.56     311  103177785 MULTIPOLYGON (((-73.97299 4...
2    65821.88     313   88195686 MULTIPOLYGON (((-73.98372 4...
3    52245.83     312   99525500 MULTIPOLYGON (((-73.9714 40...
4    37008.10     304   56663217 MULTIPOLYGON (((-73.89647 4...
5    62239.83     209  114266297 MULTIPOLYGON (((-73.83979 4...
6    83998.36     212  154896607 MULTIPOLYGON (((-73.79386 4...
7    35875.71     206   42664311 MULTIPOLYGON (((-73.87185 4...
8    47817.46     208   92071724 MULTIPOLYGON (((-73.89663 4...
9    32820.40     226   50566411 MULTIPOLYGON (((-73.8679 40...
10   43326.85     317   93810207 MULTIPOLYGON (((-73.90755 4...

Atividade: Importem o arquivo NYPD.csv e armazenem em um objeto chamado NYPD.

O arquivo contém dados sobre crimes ocorridos na cidade de Nova York. Entre as variáveis disponibilizadas, podemos citar o tipo do crime (TYPE), as coordenadas (latitude e longitude), entre outras.

# Visualizando o objeto
NYPD
# A tibble: 407 × 6
   LAW_CAT_CD TYPE      BORO_NM         Day Longitude Latitude
   <chr>      <chr>     <chr>         <dbl>     <dbl>    <dbl>
 1 FELONY     VANDALISM QUEENS            1     -73.7     40.7
 2 FELONY     ROBBERY   BRONX             1     -73.9     40.9
 3 FELONY     ROBBERY   QUEENS            3     -73.8     40.7
 4 FELONY     ROBBERY   BROOKLYN          4     -74.0     40.7
 5 FELONY     ROBBERY   QUEENS            2     -73.8     40.7
 6 FELONY     VANDALISM QUEENS            5     -73.8     40.7
 7 FELONY     ROBBERY   STATEN ISLAND     6     -74.2     40.6
 8 FELONY     ROBBERY   QUEENS            3     -73.7     40.7
 9 FELONY     ROBBERY   BRONX             2     -73.9     40.9
10 FELONY     MURDER    QUEENS            3     -73.8     40.7
# ℹ 397 more rows

Vamos fazer o mapa de pontos dos crimes na cidade de Nova York usando o ggplot2.

#Plotando as localizações dos delitos
ggplot(data = NYShp) +
  geom_sf(fill = "White") +
  geom_point(data = NYPD,
             mapping = aes(x = Longitude,
                           y = Latitude),
             colour = 'Dark Blue') +
  theme_light()

Vale ressaltar que o data indicado na função ggplot é o shape, já no geom_point foi preciso especificar um data diferente, a base de dados.

Ë possível criar mapas de pontos indicando os diferentes tipos de crime, por exemplo.

# Mapa de pontos indicando o tipo de crime com o ggplot2 
mapa1 = ggplot(data = NYShp) +
  geom_sf(fill = "White") +
  geom_point(data = NYPD,
             aes(x = Longitude,
                 y = Latitude,
                 colour = TYPE),
             size = 0.8) +
  theme_light()


mapa1

Atividade: Com base no conteúdo da disciplina visto até o momento, crie o código necessário para reproduzir a visualização abaixo.

7.3 Mapa coroplético

Mapas coropléticos são usados em cenários no qual a variável de interesse foi observada de forma agregada para alguma partição em áreas do espaço de interesse. Por exemplo, suponha que foram observados o número de roubos de carros nos bairros da cidade do Rio de Janeiro. Neste cenário, a região de interesse é o Rio de Janeiro, que está particionada em seus bairros e a variável foi observada de forma agregada paea cada área.

Atividade: Importe o arquivo Vendas_agregada_bairro.csv e guarde em um objeto chamado base_venda.

A base fornece o registro do número de produtos vendidos (por semestre e no ano todo) por bairro de um determinado produto, entre outras.

Rows: 94 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (1): no_bairro_residencia
dbl (5): num.vendas.1sem, num.vendas.2sem, num.vendas, gasto, porc_panfletagem

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Visualinado a base de dados
base_venda
# A tibble: 94 × 6
   no_bairro_residencia num.vendas.1sem num.vendas.2sem num.vendas gasto
   <chr>                          <dbl>           <dbl>      <dbl> <dbl>
 1 PAU FERRO                          1               0          1  4460
 2 SANTO ANTONIO                      6               2          8  4590
 3 PAISSANDU                         11               4         15  4369
 4 ILHA DO LEITE                     18               1         19  4614
 5 SOLEDADE                           1              20         21  4788
 6 JAQUEIRA                          20               4         24  4652
 7 TORREAO                            3              27         30  4794
 8 CABANGA                            6              25         31  4647
 9 SANTANA                           12              21         33  4852
10 DERBY                             20              14         34  4832
# ℹ 84 more rows
# ℹ 1 more variable: porc_panfletagem <dbl>

Para criarmos um mapa coropléticos precisamos inicialmente acrescentar a variável de interesse ao shape. Faremos isso, fazendo um merge da base com o shape. Neste caso, usamos o left_join passando como base inicial o shape, para garantir que a base que será retornada terá exatamente as linhas que já existem no shape e usamos os nomes dos bairros para fazer a junção.

#Fazendo a relacao dos dois data frames
recife = left_join(x = recife, 
                   y = base_venda, 
                   by = c("EBAIRRNOME" = "no_bairro_residencia"))

# Visualizando o objeto
recife
Simple feature collection with 94 features and 15 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 277547.5 ymin: 9098032 xmax: 295287.7 ymax: 9123296
Projected CRS: SIRGAS 2000 / UTM zone 25S
First 10 features:
   CBAIRRCODI VBAIRROID         EBAIRRNOME CRPAAACODI CMICROCODI TBAIRRULAT
1          19         0             RECIFE          1          1 1899-12-30
2          27         0      SANTO ANTONIO          1          2 1899-12-30
3          35         0           SAO JOSE          1          2 1899-12-30
4          43         0 ILHA JOANA BEZERRA          1          3 1899-12-30
5          51         0            CABANGA          1          2 1899-12-30
6          60         0            COELHOS          1          3 1899-12-30
7          78         0      ILHA DO LEITE          1          2 1899-12-30
8          86         0          BOA VISTA          1          2 1899-12-30
9          94         0          PAISSANDU          1          2 1899-12-30
10        108         0        SANTO AMARO          1          1 1899-12-30
   CEMPRECODI AUSUACMATR         EBAIRRNO_1 EBAIRRLINK num.vendas.1sem
1           0          0             Recife       <NA>              39
2           0          0      Santo Antônio       <NA>               6
3           0          0           São José       <NA>             163
4           0          0 Ilha Joana Bezerra       <NA>             187
5           0          0            Cabanga       <NA>               6
6           0          0            Coelhos       <NA>             123
7           0          0      Ilha do Leite       <NA>              18
8           0          0          Boa Vista       <NA>              98
9           0          0          Paissandú       <NA>              11
10          0          0        Santo Amaro       <NA>             495
   num.vendas.2sem num.vendas gasto porc_panfletagem
1               42         81  4811        0.7075714
2                2          8  4590        0.1632857
3               58        221  4980        0.7220000
4              271        458  5082        0.7294286
5               25         31  4647        0.2832857
6              157        280  5022        0.7262857
7                1         19  4614        0.2788571
8              117        215  4988        0.7181429
9                4         15  4369        0.1738571
10             274        769  5208        0.9671429
                         geometry
1  MULTIPOLYGON (((294617.4 91...
2  MULTIPOLYGON (((293353.3 91...
3  MULTIPOLYGON (((292458 9107...
4  MULTIPOLYGON (((290464.9 91...
5  MULTIPOLYGON (((291566 9106...
6  MULTIPOLYGON (((291634.3 91...
7  MULTIPOLYGON (((291634.2 91...
8  MULTIPOLYGON (((291721.3 91...
9  MULTIPOLYGON (((291140.4 91...
10 MULTIPOLYGON (((294038.3 91...

A seguir, apresentamos o mapa coroplético do número de produtos vendidos no ano.

#Mapa coropletico do numero de vendas de produto
ggplot(data = recife,
       mapping = aes(fill = num.vendas)) +
  geom_sf()

Como alterar características deste gráfico?

#Modificando o mapa coropletico
ggplot(data = recife,
       mapping = aes(fill = num.vendas)) +
  geom_sf() +
  scale_fill_gradient(name = "Vendas", 
                      low = "White", 
                      high = "Red") +
  theme_light() +
  theme(legend.title = element_text(size = 16,
                                    colour = "Red",
                                    face = "bold"),
        legend.text = element_text(size = 10,
                                    colour = "Red"))

O pacote tmap é outro pacote bastante útil para a criação de mapas coropléticos.

#Carregando pacote tmap
library(tmap)

#Construindo o mapa coropletico para vendas
tm_shape(shp = recife) + 
  tm_fill(col = "num.vendas",
          palette = "Greens",
          title = "Vendas")

## tm_shape - cria um elemento tmap que especifica um objeto espacial
## tm_fill - cria um elemento tmap que desenha e preenche o mapa
#Argumentos:
#shp - um objeto da classe sf
#col - a variavel/atributo que deseja plotar
#palette - a paleta de cores
#title - Nome que deseja mostrar no titulo da legenda


#Transformando em uma geometria valida
recife = st_make_valid(x = recife)

## st_make_valid - transforma uma geometria invalida em uma valida
#Argumentos:
#x - um objeto da classe sf

#Modificando o numero de intervalos
tm_shape(shp = recife) + 
  tm_fill(col = "num.vendas",
          n = 4,
          palette = "Greens",
          title = "Vendas")

#Definindo os limites e o numero de intervalos
mapa = tm_shape(shp = recife) + 
  tm_fill(col = "num.vendas",
          breaks = quantile(recife$num.vendas, probs = seq(0,1,.1)),
          palette = "Greens",
          title = "Vendas") +
  tm_borders(lwd = 0.75,
             lty = "solid")

## tm_borders - desenha as bordas que separam os poligonos
#Argumentos:
#lwd - espessura da borda
#lty - tipo de linha


#Visualizando o mapa
mapa

#Mudando o estilo do mapa com tm_style (pre-definido)
mapa + 
  tm_style("cobalt")

#Mudando o estilo do mapa com tm_layout (criar)
mapa + 
  tm_layout(title = "2018",
            legend.outside = TRUE,
            legend.text.color = "Dark Green")

## tm_layout - especifica configuracoes para o layout do mapa
#Argumentos:
#title - um titulo acima da legenda
#legend.outside - para a legenda aparecer ao lado de fora do mapa
#legend.text.color - cor da legenda

#Incluindo texto nos mapas
mapa +
  tm_text(text = "EBAIRRNOME",
          size = "num.vendas",
          legend.size.show = FALSE) +
  tm_layout(legend.outside = TRUE)

## tm_text - especifica nomes para os poligonos
#Argumentos:
#text - variavel que deseja aparecer como texto nos poligonos 
#size - numero ou uma variavel que vai determinar o tamanho da fonte
#legend.size.show - se deseja apresentar a legenda para os diferentes tamanhos da letra

#Grafico para mais de uma variavel
tm_shape(shp = recife) + 
  tm_fill(col = c("num.vendas.1sem","num.vendas.2sem"),
          title = c("1 Sem", "2 Sem"),
          palette = "Reds") +
  tm_facets(nrow = 1) +
  tm_borders() +
  tm_layout(legend.position = c("left","bottom"))

#Colocando todos os graficos em uma mesma escala
tm_shape(shp = recife) + 
  tm_fill(col = c("num.vendas.1sem","num.vendas.2sem"),
          title = "Vendas",
          palette = "Reds") +
  tm_facets(nrow = 1,
            free.scales = FALSE) +
  tm_borders() +
  tm_layout(panel.labels = c("1 Sem","2 Sem"))

7.3.1 Mapas interativos com o tmap

No tmap, é possível contruir mapas interativos usando a função tmap_mode com argumento view. Todos os mapas criados após definirmos tmap_mode("view") serão mapas interativos. Para voltarmos a criar mapas estáticos é preciso definir tmap_mode("plot").

#Definindo o tipo do mapa como interativo
tmap_mode("view")

tm_shape(shp = recife) + 
  tm_fill(col = "num.vendas",
          breaks = quantile(recife$num.vendas, probs = seq(0,1,.2)),
          palette = "Greens",
          title = "Vendas")

No botão, do lado esquerdo, é possível mudar o mapa do fundo.

Ao passar o mouse sobre os bairros serão apresentados os valores da variável. Para melhorar a visualização, vamos criar uma variável que apresenta o nome do bairro e o valor de produtos vendidos para que ao passarmos o mouse sobre os bairros seja exibido o nome e o valor da venda.

#Ativando pacote
library(stringr)

#Melhorando o grafico
recife = recife |> 
  mutate(nome_casos = str_c(EBAIRRNOME, " - ", num.vendas))

tm_shape(shp = recife) + 
  tm_fill(col = "num.vendas",
          breaks = quantile(recife$num.vendas, probs = seq(0,1,.2)),
          palette = "Greens",
          title = "Vendas",
          id = "nome_casos")