---
title: "Painel Criminalidade - Veículos MG (BPTran)"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
theme: cosmo
self_contained: true
source_code: embed
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE)
library(tidyverse)
library(DT)
library(janitor)
library(sf)
library(geobr)
library(viridis)
library(htmltools)
library(stringi)
# 1) Verificar arquivos
arq_roubos <- "veiculos_roubados.csv"
arq_furtos <- "veiculos_furtados.csv"
if (!file.exists(arq_roubos) || !file.exists(arq_furtos)) {
stop("ERRO: Coloque 'veiculos_roubados.csv' e 'veiculos_furtados.csv' na mesma pasta do .Rmd.")
}
# 2) Ler CSV com separador ; (ponto-e-vírgula)
ler_csv_pv <- function(arquivo) {
readr::read_delim(
file = arquivo,
delim = ";",
locale = locale(encoding = "Latin1"),
show_col_types = FALSE,
trim_ws = TRUE
)
}
roubos <- ler_csv_pv(arq_roubos) %>% mutate(Natureza = "Roubo")
furtos <- ler_csv_pv(arq_furtos) %>% mutate(Natureza = "Furto")
dados <- bind_rows(roubos, furtos) %>%
janitor::clean_names() %>%
mutate(
registros = suppressWarnings(as.numeric(registros)),
mes = suppressWarnings(as.integer(mes)),
ano_fato = suppressWarnings(as.integer(ano_fato)),
# cod_ibge: limpa tudo, garante 7 dígitos (padrão municipio ibge)
cod_ibge = stringr::str_replace_all(as.character(cod_ibge), "[^0-9]", ""),
cod_ibge = stringr::str_pad(cod_ibge, width = 7, side = "left", pad = "0")
)
# 3) Checar colunas mínimas
campos_obrig <- c("registros", "municipio", "cod_ibge", "mes", "ano_fato", "natureza")
faltando <- setdiff(campos_obrig, names(dados))
if (length(faltando) > 0) {
stop(paste("ERRO: faltam colunas no CSV:", paste(faltando, collapse = ", ")))
}
# 4) Filtrar período
dados_recentes <- dados %>%
filter(ano_fato >= 2023) %>%
mutate(data_base = as.Date(paste(ano_fato, mes, "01", sep = "-")))
# 5) Totalizadores
total_roubo <- dados_recentes %>%
filter(natureza == "Roubo") %>%
summarise(t = sum(registros, na.rm = TRUE)) %>%
pull(t)
total_furto <- dados_recentes %>%
filter(natureza == "Furto") %>%
summarise(t = sum(registros, na.rm = TRUE)) %>%
pull(t)
# 6) DataTables PT-BR (sem depender de internet/CDN)
dt_lang_ptbr <- list(
sEmptyTable = "Nenhum dado disponível na tabela",
sInfo = "Mostrando _START_ até _END_ de _TOTAL_ registros",
sInfoEmpty = "Mostrando 0 até 0 de 0 registros",
sInfoFiltered = "(filtrado de _MAX_ registros no total)",
sLengthMenu = "Mostrar _MENU_ registros",
sLoadingRecords = "Carregando...",
sProcessing = "Processando...",
sSearch = "Pesquisar:",
sZeroRecords = "Nenhum registro encontrado",
oPaginate = list(sNext = "Próximo", sPrevious = "Anterior", sFirst = "Primeiro", sLast = "Último")
)
# 7) Mapa de MG por municipio (join pelo cod_ibge)
mapa_mg <- geobr::read_municipality(code_muni = "all", year = 2020, showProgress = FALSE) %>%
dplyr::filter(code_state == 31) %>%
dplyr::mutate(code_muni = stringr::str_pad(as.character(code_muni), width = 7, side = "left", pad = "0"))
# --- 1) Criar chave de municipio no MAPA (sem acento / caixa alta) ---
lookup_municipios <- mapa_mg %>%
sf::st_drop_geometry() %>%
transmute(
code_muni = stringr::str_pad(as.character(code_muni), width = 7, side = "left", pad = "0"),
municipio_mapa = name_muni,
chave_muni = stringr::str_squish(
stringi::stri_trans_general(toupper(name_muni), "Latin-ASCII")
)
) %>%
distinct(chave_muni, .keep_all = TRUE)
# --- 2) Criar chave de municipio nos DADOS (sem acento / caixa alta) ---
dados_recentes <- dados_recentes %>%
mutate(
chave_muni = stringr::str_squish(
stringi::stri_trans_general(toupper(municipio), "Latin-ASCII")
)
)
# --- 3) Colar o code_muni no seu dado pelo nome do municipio ---
dados_com_codigo <- dados_recentes %>%
left_join(lookup_municipios %>% select(code_muni, chave_muni), by = "chave_muni")
# --- 4) Agregar e juntar ao mapa ---
dados_mapa <- dados_com_codigo %>%
group_by(code_muni) %>%
summarise(total = sum(registros, na.rm = TRUE), .groups = "drop")
mapa_final <- mapa_mg %>%
mutate(code_muni = stringr::str_pad(as.character(code_muni), width = 7, side = "left", pad = "0")) %>%
left_join(dados_mapa, by = "code_muni")
```
Visão Geral {data-icon="fa-tachometer"}
=======================================================================
Row {data-height=230}
-----------------------------------------------------------------------
### INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA - IFSULDEMINAS
```{r results='asis'}
HTML('
<div style="background-color:#f5f5f5; padding:15px; border-radius:8px;">
<div style="display:flex; justify-content:space-between; align-items:flex-start;">
<div>
<strong>Polícia Militar de Minas Gerais</strong><br>
<strong>BPTran</strong><br><br>
<strong>Relatório:</strong> Roubos e Furtos de Veículos<br>
<strong>Período:</strong> 2023 a 2025<br><br>
<strong>Discente:</strong> Marcelo José Gomes da Silva<br>
<strong>Nº Polícia:</strong> 129.894-2<br>
<strong>Email:</strong> marcelo.gomes@alunos.ifsuldeminas.edu.br<br>
</div>
<div style="text-align:right;">
<img src="logo.png" style="height:110px;">
</div>
</div>
</div>
')
```
### Total de Roubos
```{r}
flexdashboard::valueBox(format(total_roubo, big.mark="."), icon = "fa-exclamation-triangle", color = "danger")
```
### Total de Furtos
```{r}
flexdashboard::valueBox(format(total_furto, big.mark="."), icon = "fa-car", color = "warning")
```
Row {data-height=520}
-----------------------------------------------------------------------
### Tabela Interativa (2023–2025)
```{r}
tabela_exibicao <- dados_recentes %>%
select(municipio, ano_fato, mes, natureza, registros, risp, rmbh) %>%
arrange(desc(ano_fato), desc(mes))
DT::datatable(
tabela_exibicao,
colnames = c("municipio", "Ano", "Mês", "Natureza", "Registros", "RISP", "RMBH"),
options = list(pageLength = 10, scrollX = TRUE, language = dt_lang_ptbr)
)
```
Gráficos Estatísticos {data-icon="fa-chart-bar"}
=======================================================================
Row {data-height=520}
-----------------------------------------------------------------------
### Evolução Mensal (Linha)
```{r}
evolucao <- dados_recentes %>%
group_by(data_base, natureza) %>%
summarise(total = sum(registros, na.rm = TRUE), .groups = "drop")
ggplot(evolucao, aes(x = data_base, y = total, color = natureza)) +
geom_line(linewidth = 1.1) +
geom_point() +
labs(
title = "Evolução mensal de ocorrências",
x = "Data",
y = "Total de ocorrências",
color = "Natureza"
) +
theme_minimal()
```
### Comparativo Geral (Barras)
```{r}
comparativo <- dados_recentes %>%
group_by(natureza) %>%
summarise(total = sum(registros, na.rm = TRUE), .groups = "drop")
ggplot(comparativo, aes(x = natureza, y = total, fill = natureza)) +
geom_col() +
labs(
title = "Total acumulado (2023–2025)",
x = "Natureza",
y = "Total"
) +
theme_minimal() +
theme(legend.position = "none")
```
Row {data-height=520}
-----------------------------------------------------------------------
### Top 10 municipios (Ranking)
```{r}
top10 <- dados_recentes %>%
group_by(municipio) %>%
summarise(total = sum(registros, na.rm = TRUE), .groups = "drop") %>%
slice_max(order_by = total, n = 10)
ggplot(top10, aes(x = reorder(municipio, total), y = total)) +
geom_col() +
coord_flip() +
labs(
title = "10 municipios com mais registros (2023–2025)",
x = "",
y = "Ocorrências"
) +
theme_minimal()
```
### Sazonalidade (Acumulado por Mês)
```{r}
sazonal <- dados_recentes %>%
group_by(mes) %>%
summarise(total = sum(registros, na.rm = TRUE), .groups = "drop") %>%
arrange(mes)
ggplot(sazonal, aes(x = factor(mes), y = total)) +
geom_col() +
labs(
title = "Ocorrências por mês (acumulado 2023–2025)",
x = "Mês (1=Jan ... 12=Dez)",
y = "Total"
) +
theme_minimal()
```
Mapa Temático {data-icon="fa-map"}
=======================================================================
Row {data-height=900}
-----------------------------------------------------------------------
### Mapa por municipio (MG) — total de registros (2023–2025)
```{r}
ggplot(mapa_final) +
geom_sf(aes(fill = total), color = NA) +
scale_fill_viridis_c(
option = "magma",
direction = -1,
name = "Total",
na.value = "grey90"
) +
labs(
title = "Distribuição espacial — Minas Gerais (2023–2025)",
subtitle = "Total de registros por municipio"
) +
theme_void()
```