# 📊 Schema de Dados - Busco Rumo

## Campo por Campo

### Identidade
```javascript
{
  id: "sao_paulo_sp",        // string: kebab-case de nome_uf (único)
  nome: "São Paulo",         // string: nome oficial do município
  uf: "SP"                   // string (2): sigla do estado
}
```

| Campo | Tipo | Fonte | Atualização |
|-------|------|-------|------------|
| `id` | string | Manual (gerado de nome_uf) | Raro |
| `nome` | string | IBGE API | Anual (IBGE) |
| `uf` | string(2) | IBGE API | Fixa |

---

### Localização Geográfica
```javascript
{
  regiao: "Sudeste",          // enum: Norte, Nordeste, Centro-Oeste, Sudeste, Sul
  bioma: "mata_atlantica",    // enum: amazonia, cerrado, mata_atlantica, caatinga, pantanal, pampa
  clima: "ameno",             // enum: frio, ameno, quente, semiarido
  lat: -23.5505,              // float: latitude IBGE
  lon: -46.6333               // float: longitude IBGE
}
```

| Campo | Tipo | Fonte | Atualização | Nota |
|-------|------|-------|------------|------|
| `regiao` | enum | Mapa UF→Regiao | Fixa | Regra: estado define região |
| `bioma` | enum | Mapa UF→Bioma | Fixa | **Simplificado**: 1 bioma por estado |
| `clima` | enum | Amplitude térmica | Fixa | **frio** se temp_min<10 · **quente** se temp_max>32 · senão **ameno** (semiárido preservado) — ver `scripts/melhorar_clima.py` |
| `lat`/`lon` | float | IBGE API localidades | Fixa | Dados geográficos |

**Mapas referência:**
```python
# UF → Região
UF_REGIAO = {
  'AC':'Norte', 'AL':'Nordeste', 'AM':'Norte', 'AP':'Norte',
  'BA':'Nordeste', 'CE':'Nordeste', 'DF':'Centro-Oeste', 'ES':'Sudeste',
  'GO':'Centro-Oeste', 'MA':'Nordeste', 'MG':'Sudeste', 'MS':'Centro-Oeste',
  'MT':'Centro-Oeste', 'PA':'Norte', 'PB':'Nordeste', 'PE':'Nordeste',
  'PI':'Nordeste', 'PR':'Sul', 'RJ':'Sudeste', 'RN':'Nordeste',
  'RO':'Norte', 'RR':'Norte', 'RS':'Sul', 'SC':'Sul',
  'SE':'Nordeste', 'SP':'Sudeste', 'TO':'Norte'
}

# UF → Bioma (simplificado)
UF_BIOMA = {
  'AC':'amazonia', 'BA':'caatinga', 'DF':'cerrado', 'ES':'mata_atlantica',
  'MG':'mata_atlantica', 'MS':'pantanal', 'PA':'amazonia', 'RJ':'mata_atlantica',
  'RS':'pampa', 'SP':'mata_atlantica',
  # ... todos os 27 estados
}
```

---

### Indicadores Socioeconômicos
```javascript
{
  pop: 11451245,                    // integer: população total (Censo 2022)
  idh: 0.805,                       // float: 0.0–1.0 (indicador interno IDH-E, NÃO é o IDHM oficial - ver seção abaixo)
  ideb: 6.2,                        // float: 0–10 (INEP 2021)
  pib: 45000,                       // integer: PIB per capita R$ (IBGE 2021)
  temp: 19,                         // integer: temperatura média °C
  temp_min: 14,                     // integer: mínima estimada °C
  temp_max: 28,                     // integer: máxima estimada °C
  crescimento_pct: 0.3,             // float ou null: crescimento populacional 2010–2022 (%)
  crescimento_fonte: "IBGE",        // enum: "IBGE" (real) | null (indisponível)
  crescimento_status: "censitario", // enum: "censitario" | "indisponivel"
  crescimento_classificacao: "baixo", // enum: "retracao"|"baixo"|"moderado"|"elevado"|"desconhecida"
  pop_2010: 11253503,               // int: só quando crescimento_status == "censitario"
  pop_2022: 11451999,               // int: só quando crescimento_status == "censitario"
  pop_2025_estimativa: 11904961,    // int ou ausente: estimativa IBGE 2025 (SIDRA 6579) - campo SEPARADO, nunca combinado com o crescimento censitário
  pop_2025_estimativa_status: "oficial_ibge"
}
```

| Campo | Tipo | Fonte | Última atualização | Nota |
|-------|------|-------|-------------------|------|
| `pop` | int | IBGE Censo 2022 | Censo 2022 | ~5570 municípios |
| `idh` | float | Cálculo interno (educação/longevidade/PIB, todos IBGE) | componentes de anos distintos | **NÃO é o IDHM oficial do PNUD/Atlas Brasil** — ver seção abaixo |
| `ideb` | float | INEP 2021 | 2021 | ~300 sem dados (fallback: capital) |
| `pib` | int | IBGE (2021) | 2021 | Estimado por município |
| `temp` | int | Histórico (por estado) | Histórico | Média anual |
| `temp_min`/`temp_max` | int | Estimado por bioma/tipo | Fixa | Base da categorização de `clima` — `melhorar_clima.py` |
| `crescimento_pct` | float/null | IBGE (Censo 2010 vs 2022, SIDRA 200/9514) | 2010 e 2022 | Ver [CRESCIMENTO.md](CRESCIMENTO.md) — só municípios comparáveis (join por código IBGE via DTB) |
| `crescimento_fonte` | enum | — | — | `IBGE` (5.565 municípios) ou `null` (5 criados após 2010, indisponível) |
| `pop_2010`/`pop_2022` | int | IBGE (SIDRA 200/9514, oficial) | Censo | Presentes só nos municípios censitariamente comparáveis |
| `pop_2025_estimativa` | int | IBGE (SIDRA 6579, Estimativas de População) | 2025 | Campo distinto do Censo — nunca usado pra calcular crescimento |

### IDH-E: como é calculado (indicador interno, não oficial)
**Fase 2 complementar (2026-07-27): este campo NÃO é o IDHM oficial publicado pelo Atlas Brasil/PNUD.** É um indicador
próprio (chamado de "IDH-E" na interface), calculado a partir de 3 componentes IBGE:

IDH-E = (componente_educacao + componente_longevidade + componente_renda) / 3
- **Educação**: anos de escolaridade (não é a fórmula oficial do IDHM, que combina escolaridade esperada + escolaridade média da população adulta)
- **Longevidade**: expectativa de vida (Censo)
- **Renda**: PIB per capita bruto (não é a fórmula oficial do IDHM, que usa logaritmo da renda per capita com piso/teto normalizados)

**Por que não é o IDHM oficial**: o IDHM (Atlas Brasil/PNUD) só tem edição municipal completa para os anos censitários
(2000, 2010) com metodologia própria de normalização. Não existe uma atualização oficial 2022 publicada pelo PNUD no
momento desta auditoria. Em vez de reaproveitar o IDHM 2010 (desatualizado) ou inventar uma extrapolação, o produto usa
este indicador interno simplificado — sinalizado como estimado, não oficial, na interface e no componente de
proveniência (`proveniencia_shared.js`).

**Cobertura**: ~99% dos municípios (componentes: educação/longevidade IBGE Censo, PIB per capita IBGE 2021)

### IDEB: Fallback para municípios pequenos
```python
if c.ideb is None:
  # Usar IDEB da capital do estado
  c.ideb = IDEB_POR_CAPITAL[c.uf]
```

**Municípios sem IDEB**: ~300 (geralmente pop < 5000, sem escola estadual)

### Segurança e Saneamento (Fase de segurança, 2026-07-28)

Reativa o que a Fase 2 complementar (2026-07-27) tinha suspendido — `det_homicidios`/`mtur_seguranca` (planilha MTur sem
ano/licença/fonte documentados) continuam **mortos, nunca lidos** por nenhum dos 3 apps. Os campos abaixo são novos,
com fonte oficial e proveniência completa.

```javascript
{
  seguranca_taxa_homicidios: 6.16,        // float ou null: taxa por 100 mil hab. (Atlas da Violência 2026, Ipea/FBSP)
  seguranca_tendencia: "queda",           // enum: "queda" | "estavel" | "alta" (média dos últimos 3 anos vs. 3 anos anteriores)
  seguranca_status: "oficial",            // enum: "oficial" | "fallback_regional"
  seguranca_fonte: "Atlas da Violência 2026 (Ipea/FBSP), série municipal",
  seguranca_ano: "2019-2024",             // janela usada p/ taxa + tendência
  saneamento_indice_pct: 76.4,            // float ou null: média de água/esgoto/resíduos atendidos (0-100)
  saneamento_status: "oficial",           // enum: "oficial" | "fallback_regional"
  saneamento_fonte: "SINISA 2024 (Ministério das Cidades) via Instituto Água e Saneamento",
  saneamento_ano: "2024"
}
```

| Campo | Tipo | Fonte | Cobertura | Nota |
|-------|------|-------|-----------|------|
| `seguranca_*` | ver acima | Atlas da Violência 2026 (Ipea/FBSP) — `dados-api/series-values/20/4` (municípios) e `/20/3` (estados) | 5.530/5.570 municípios diretos (99,3%); 40 com fallback estadual | Script: `scripts/baixar_seguranca_atlas_violencia.py`. Segurança é usada no **Busco Rumo e no Dicas Outdoor** (badge "Refúgio Seguro" + ordenação) |
| `saneamento_*` | ver acima | SINISA 2024 (Ministério das Cidades), consolidado por página pública do Instituto Água e Saneamento | 5.272/5.570 municípios diretos (94,6%); 298 com fallback estadual | Script: `scripts/baixar_saneamento_sinisa.py`. Saneamento é usado **só no Busco Rumo** (não aparece no Dicas Outdoor) |

**Fallback estadual**: quando o município não tem série própria, o valor vem da média/série oficial da UF (nunca uma
invenção) e `*_status` fica `"fallback_regional"` — sempre marcado como tal na UI (nunca disfarçado de dado
municipal direto), mesmo padrão já usado no IDEB acima.

**Uso no score do Busco Rumo**: ambos entram como fator base sempre aplicado em `recommendCities()` (`bussola.html`)
e como dimensão `violencia_letal` no motor de compatibilidade (`index.html`, `compatScoreDetalhado`) — nunca via
`det_homicidios`/`mtur_seguranca`.

**Exibição no card (2026-07-29):**
- **Busco Rumo**: chips 🛡️ Segurança e 🚰 Saneamento na faixa de sinais do card, coloridos pelos mesmos limiares do
  score de Qualidade (ver seção "Qualidade" abaixo); `*` no rótulo + tooltip quando o valor é fallback estadual.
  Detalhe completo (fonte/ano/confiança) no modal, via `proveniencia_shared.js` (`ProveShared.render('seguranca'|'saneamento', ...)`).
- **Dicas Outdoor**: só o índice de Segurança aparece no card (mesma cor/fallback); saneamento não aparece (fora de
  escopo deste app). O modal interno **não** mostra o detalhe de proveniência de segurança/crescimento/IDH-E — esse
  conteúdo é do Busco Rumo; o card interno do Outdoor prioriza atividades outdoor (trilhas/esportes).

### Qualidade (calcScore): fórmula final com Segurança pública e Saneamento (Fase de Qualidade, 2026-07-28)

`calcScore()`/`scoreComponents()` é o indicador de **"Qualidade"** (0–100) mostrado em todo card de cidade no Busco
Rumo (`index.html`) e no Dicas Outdoor (`outdoor/index.html`) — formula duplicada nos dois arquivos (não há módulo
compartilhado pra esse trecho, mesma convenção do resto do repo). É **diferente** de:
- `compatScoreDetalhado()` (Busco Rumo) — "Compatibilidade" (`card_compat`, "combina com você"), preferência
  PESSOAL que só pontua quando o usuário ativa uma persona. Tem sua própria dimensão `violencia_letal` (segurança),
  independente da Qualidade — os dois scores aparecem em rótulos diferentes na mesma tela, sem se sobrepor.
- `recommendCities()` (Bússola, `bussola.html`) — motor de recomendação do questionário, não tem um número
  "Qualidade" 0-100; segurança/saneamento já entram lá como fator base (ver seção acima). A Bússola não ganhou uma
  segunda pontuação nesta fase — o fator base já era a camada certa.

**Busco Rumo (`index.html`) — 7 componentes, soma 100:**

| Componente | Peso | Campo fonte | Fórmula de normalização (0–1) |
|---|---|---|---|
| IDH-E | 34 | `idh` | `max(0, (idh-0.5)/0.37)` |
| IDEB | 24 | `ideb` | `max(0,(ideb-3)/7)` (neutro 0.4 se ausente) |
| PIB per capita | 15 | `pib` | `min(1, pib/80000)` |
| Natureza | 6 | `tipo` (montanha/praia) | 1 se montanha/praia, senão 0.3 |
| Turismo | 6 | `tipo` (turística) | 1 se turística, senão 0.2 |
| **Segurança pública** | **8** | `seguranca_taxa_homicidios`/`seguranca_status` | `max(0, min(1, 1 - taxa/50))` |
| **Saneamento** | **7** | `saneamento_indice_pct`/`saneamento_status` | `max(0, min(1, indice/100))` |

**Dicas Outdoor (`outdoor/index.html`) — 6 componentes, soma 100 (sem saneamento — fora de escopo deste app):**

| Componente | Peso | Fórmula |
|---|---|---|
| IDH-E | 37 | igual acima |
| IDEB | 26 | igual acima |
| PIB per capita | 16 | igual acima |
| Natureza | 6 | igual acima |
| Turismo | 6 | igual acima |
| **Segurança pública** | **9** | igual acima |

Os pesos dos 5 componentes antigos foram **encolhidos proporcionalmente** (não zerados nem escolhidos ao acaso) pra
abrir espaço aos novos e ainda somar 100: Busco Rumo aplicou fator 0,85 (100 → 85, sobrando 15 pra
segurança+saneamento); Dicas Outdoor aplicou fator 100/93 sobre a versão de 6 componentes (sem saneamento) pra
redistribuir o que sobrou sem o campo de saneamento.

**Fallback estadual dentro da Qualidade**: como a cobertura de `seguranca_status`/`saneamento_status` já é ~100%
(nenhum município fica sem valor, graças ao fallback), o componente sempre entra no cálculo — mas quando
`status==='fallback_regional'` o valor normalizado é **amortecido a 50% da distância até o neutro (0.5)**:
`valor_final = 0.5 + (valor_bruto - 0.5) * 0.5`. Isso reduz o peso prático do fallback sem zerá-lo nem tratá-lo como
dado municipal — e o breakdown (`renderScoreBreakdown`) sempre mostra "(estadual)" ao lado da nota quando isso
acontece, além do aviso completo no modal de proveniência (`proveniencia_shared.js`, chaves `seguranca`/`saneamento`
do `REGISTRY`, confiança cai pra "média" nesse caso).

**`PERSONA_W.seguranca`** (bônus quando o usuário ativa a persona/chip "Segurança" no comparador de perfis) foi
**corrigido** nesta fase: antes bonificava IDH-E/IDEB/PIB/Natureza (usando IDH-E como proxy de criminalidade — o
mesmo erro que a Fase 2 já tinha proibido no texto, só não tinha sido corrigido aqui). Agora bonifica exclusivamente
o componente `s_seguranca` real (`seguranca:{seguranca:2.0}`) — sem dupla contagem com os outros componentes.

### PIB: Estimado por população
```python
# IBGE fornece PIB por estado, não por município
pib_municipio = (pop_municipio / pop_estado) * pib_estado
```

**Limitações**: 
- Não reflete PIB real (ex: município com indústria vs. agricultura)
- Apenas SP, RJ, MG têm PIB granular no IBGE
- Para demais: proporção pela população é fallback razoável

---

### Mudanças Climáticas
```javascript
{
  mc: "neutro",               // enum: positivo, neutro, risco_calor, risco_seca, risco_inundacao, risco_desmatamento
  desmat_ha_ano: 0            // integer: hectares desmatados/ano (MapBiomas)
}
```

| Campo | Tipo | Fonte | Atualização | Nota |
|-------|------|-------|------------|------|
| `mc` | enum | Mapa UF→MC + dados municipais | Anual | Risco climático por estado |
| `desmat_ha_ano` | int | MapBiomas município | ~2 anos atrás | Apenas alguns municípios |

**Classificação MC por estado:**
```python
UF_MC = {
  'AC':'risco_desmatamento',   # Amazônia
  'AL':'risco_inundacao',      # Litoral
  'BA':'risco_seca',           # Sertão
  'DF':'neutro',               # Planalto
  'MG':'neutro',               # Interior
  'SP':'neutro',               # Centro-sul
  # ... (ver scripts/gerar_cidades.py)
}
```

**Desmatamento (MapBiomas):**
- Apenas municípios em biomas com risco (Amazônia, Cerrado)
- Arquivo: `scripts/mapbiomas_desmat_municipio.xlsx`
- Processado por: `scripts/enriquecer_desmatamento.py`
- Dados: ha/ano (hectares desmatados por ano)

---

### Características da Cidade
```javascript
{
  tipo: ["capital", "industrial"],
  esportes: ["futebol", "trekking", "cicloturismo"]
}
```

#### Tipo de Cidade (arrays)
Pode ter múltiplos valores:
```javascript
tipo: string[]  // um ou mais de:
[
  'capital',                   // Capital de estado
  'praia',                     // Litoral (até 50km da costa)
  'montanha',                  // Altitude > 800m OU relevo serrano
  'interior',                  // Não-litoral, não-capital
  'turistica',                 // Fluxo turístico — 399 cidades (curadoria MTur)
  'universitaria',             // Polo universitário — dado real INEP, 698 cidades (corrigido 2026-07-17)
  'agro',                      // Agricultura/pecuária (PIB agro alto)
  'industrial'                 // Industrial (PIB ind alto)
  // 'comercio_servicos' — DESCONTINUADO como filtro (registros legados podem mantê-lo)
]
```

**Como é determinado?**
1. **Capital**: Verificar se é capital_uf
2. **Praia**: Lista oficial de municípios litorâneos do IBGE 2021 — 286 cidades (`fix_dados_v13.py`)
3. **Montanha**: Altitude > 700m (SRTM) OU relevo serrano (IBGE)
4. **Turística**: praia c/ pop>3k + montanha + curadoria MTur — 399 cidades (`fix_dados_v13.py`)
5. **Agro/Industrial**: PIB setorial IBGE
6. **Universitária (tag em `tipo`)**: dado real do INEP (`tem_universidade`, ver campos abaixo), corrigido via `scripts/corrigir_tag_universitaria.py` em 2026-07-17 — 698 municípios (570 ganharam a tag, 5 perderam por não terem IES real, 128 já estavam corretos). **`gerar_cidades.py` ainda tem a heurística antiga** (lista hardcoded + população ≥ 300 mil, 133 cidades, com falsos-positivos) — se o pipeline completo for rodado do zero nesse gerador, `patch_universidade_cities_data.py` (grava `tem_universidade`) entra logo após a base (passo 6d), e `corrigir_tag_universitaria.py` (corrige a tag `tipo` a partir do campo real) roda **por último** (passo 21b), **depois** de todos os `fix_*` que reescrevem `tipo[]` — caso contrário a base regride para o proxy antigo. Ordem canônica completa: ver a tabela `STAGES` em `build.py` / [MANUTENCAO.md](MANUTENCAO.md) (ou simplesmente `python build.py --run`).

#### Universidade (campos próprios, INEP — 2026-07-17)
```javascript
{
  tem_universidade: true,           // bool — pelo menos 1 IES ativa no município
  universidade_publica: true,       // bool — Federal/Estadual/Municipal
  universidade_privada: true,       // bool — com ou sem fins lucrativos
  qtd_ies: 21,                      // int — total de instituições
  qtd_ies_forte: 5                  // int — só Universidade/Centro Universitário/IF (exclui faculdade isolada)
}
```
**Fonte:** Censo da Educação Superior 2024 (INEP), `MICRODADOS_ED_SUP_IES_2024.CSV` — 2.561 IES em 698 municípios, cruzado por código IBGE (campo `ibge`) via `scripts/patch_universidade_cities_data.py`. Agregação intermediária em `scripts/ies_por_municipio.json`. Usado no chip do card de avaliação (`index.html`, `renderCityProfile()`) e no campo `"uni"` de `bussola_cities.json` (Bússola de Mudança).

#### Esportes Disponíveis (arrays)

> **⚠️ Atualização 2026-07-15:** `mountain_bike` e `ciclismo_estrada` saíram
> **permanentemente** do produto (pedido do usuário). `trekking`/`hiking`/
> `cicloturismo`/`bikepacking` saíram do Wikiloc (que continua os alimentando
> na tabela abaixo como histórico de origem, v16/v17) e **hoje vêm de fontes
> oficiais curadas** (Trilhas SP, Ciclorrotas SP, Peregrinação, Rede de
> Trilhas) via `scripts/sync_atividades_oficiais.py` — ver
> [RUNBOOK_ATIVIDADES_OFICIAIS.md](RUNBOOK_ATIVIDADES_OFICIAIS.md). A tabela
> e a narrativa v13-v16 abaixo são mantidas como **histórico** de como os
> dados chegaram até aqui; para o estado atual dessas 4 atividades, ver o
> runbook.

As 21 atividades outdoor vêm de **dados reais de uso do Wikiloc** (`fix_atividades_wikiloc_v16.py`); as demais têm **critério geográfico discriminante** (`fix_esportes_v14.py`/`v15`).
Removidos por serem universais: ~~natação~~ (100%) e ~~hipismo~~ (95%). Retirados do dashboard na v16: ~~esqui~~, ~~tênis~~, ~~sandboard~~, ~~stand-up paddle~~, ~~surf~~, ~~buggy~~, ~~rappel~~.
```javascript
esportes: string[]  // um ou mais de:
[
  // ── 20 códigos vindos do Wikiloc (uso registrado, filtro de qualidade — v16) ──
  // 21 slugs do CSV → 20 códigos (parapente funde em asa_delta_parapente).
  'via_ferrata',               // Wikiloc (16)
  'canionismo',                // Wikiloc (49)
  'escalada',                   // Wikiloc (28)
  'espeleologia',              // Wikiloc (68)
  'mergulho',                   // Wikiloc (9)
  'canoagem',                   // Wikiloc — caiaque/canoa (37)
  'birdwatching',              // Wikiloc — observação de aves (44)
  'observacao_fauna',          // Wikiloc — observação de fauna (30)
  'balonismo',                 // Wikiloc (22)
  'asa_delta_parapente',       // Wikiloc — asa delta + parapente (44)
  'vela',                       // Wikiloc — veleiro (37)
  'cavalgada',                 // Wikiloc (31)
  'jet_ski',                    // Wikiloc (20)
  'kite_windsurf',             // Wikiloc — kitesurf (22)
  'kite_ski',                   // Wikiloc (8)
  'alpinismo',                 // Wikiloc — montanhismo (42)
  'bikepacking',               // Wikiloc (40)
  'remo',                       // Wikiloc (32)
  'trekking',                   // Wikiloc — trilha ≥20 km, TrailRank≥50 (96)
  'hiking',                     // Wikiloc — trilha 2–19,9 km, TrailRank≥50 (186)
  // ── atividades com critério geográfico próprio (não-Wikiloc) ──
  'futebol',                    // Estádio profissional + capitais (255)
  'mountain_bike',             // Serra/turística/praia (256)
  'pesca_esportiva',           // Litoral + grandes rios/reservatórios (356)
  'rafting',                    // Rio de corredeira + operação comercial (32, lista curada)
  'bodyboard',                 // Litoral (ondas pequenas) (115)
  'ciclismo_estrada',          // Estradas bem sinalizadas (1327)
  'pico_brasil'                 // 36 cidades-portal dos pontos mais altos
]
// SAÍRAM do dashboard (v16): esqui_snowboard, tenis, sandboard, stand_up_paddle,
// surf, buggy, rappel_tirolesa. Universais já removidos: natacao, hipismo.
```

**Como é determinado?**
1. **Base inicial** por inferências de tipo/bioma em `gerar_cidades.py`:
   - Montanha → escalada, rappel, asa delta (trekking saiu daqui em 2026-07-15, ver aviso acima)
   - Praia → surf, mergulho, kite, stand up paddle
   - Rio/lago → canoagem, rafting, pesca
2. **Curadoria geográfica** aplicada por `fix_esportes_v14.py`:
   - Futebol → lista de clubes profissionais (CBF Séries A/B/C/D) + 27 capitais
   - Pesca → litoral + grandes bacias/represas (São Francisco, Amazonas, Paraná, Tocantins-Araguaia, Pantanal)
   - Kite/Sandboard/Buggy → confinados a `tipo:praia`
   - ~~Trekking/MTB → restritos a `montanha`/`turistica`/`praia`~~ (histórico — desde 2026-07-15 `trekking` não é mais tocado aqui e `mountain_bike` saiu do produto)
   - Natação e Hipismo → removidos (universais)
3. **Curadoria v15** (`fix_rafting_praia_v15.py`):
   - Rafting → lista factual de ~32 destinos com rio de corredeira e operação comercial (Brotas-SP, Três Coroas-RS, Apiúna-SC, Jaciara-MT, Itacaré-BA, Presidente Figueiredo-AM…). Antes em ~700 cidades (quase todo PR+SC) por erro de heurística regional.
   - Segmento MTur `sol_praia` → removido de 666 cidades sem litoral; agora só em `tipo:praia`.
4. **Curadoria v16 — atividades Wikiloc** (`fix_atividades_wikiloc_v16.py`, fonte `wikiloc_cidades.csv`):
   - **20 códigos** outdoor vêm de **dados reais de uso do Wikiloc** (plataforma de trilhas alimentada por usuários — evidência de uso, não cadastro oficial), agora com **filtro de qualidade** (`MIN_TRILHAS` por atividade + `min_km`/`max_km`/`min_trailrank` para trekking/hiking — ver [WIKILOC_COMO_RASPAR.md](WIKILOC_COMO_RASPAR.md)). É a **fonte única de verdade**: limpa os códigos-alvo e reaplica do CSV.
   - 21 slugs do CSV → 20 códigos (parapente funde em asa_delta_parapente). **Trekking** (≥20 km) e **hiking** (2–19,9 km) saem do mesmo slug `trekking`, separados por distância.
   - O filtro removeu falsos positivos (ex.: birdwatching 237→44, cavalgada 255→31, balonismo 68→22). 5 atividades novas: alpinismo, bikepacking, remo, kite_ski, observacao_fauna.
   - Saem do dashboard: esqui_snowboard, tenis, sandboard, stand_up_paddle, surf, buggy, rappel_tirolesa.

---

### Notas
```javascript
{
  nota: "Município serrano no sul da Mantiqueira, ~1.100m de altitude...",
  nota_en: "Mountain town in the southern Mantiqueira range...",
  nota_es: "Municipio serrano en el sur de la Mantiqueira...",
  nota_zh: "位于南马蒂克拉山脉的山区市镇...",
  nota_ru: "Горный городок на юге хребта Мантикейра...",
}
```

| Campo | Tipo | Fonte | Atualização |
|-------|------|-------|------------|
| `nota` | string | `scripts/gen_notas.py` (5.558 cidades, orientado a dados) + curadoria manual em `NOTAS_I18N` no `index.html` (12 cidades) | Ao rodar `gen_notas.py` |

Nota = Descrição para usuários entenderem a vocação da cidade (turismo, natureza, isolamento
etc.), **sempre com um ponto positivo e um "Ponto de atenção" negativo**. Para as 5.558 cidades
sem curadoria manual, o texto é 100% derivado dos indicadores do próprio dataset (IDH, IDEB,
PIB, crescimento, renda, saúde, segurança, conectividade, turismo, desmatamento) — sem fatos
inventados. As 12 cidades curadas (fatos reais conhecidos) ficam em `NOTAS_I18N` dentro do
`index.html`, não em `cities_data.js`. Ver [RUNBOOK_IDIOMAS.md](RUNBOOK_IDIOMAS.md) seção
"Notas de cidade" para como editar/traduzir.

---

## Exemplo Completo

```javascript
{
  id: "goncalves_mg",
  nome: "Gonçalves",
  uf: "MG",
  regiao: "Sudeste",
  bioma: "mata_atlantica",
  clima: "frio",
  lat: -22.3,
  lon: -45.4,
  
  pop: 1100,
  idh: 0.695,
  ideb: 5.2,
  pib: 15000,
  temp: 14,
  
  mc: "neutro",
  desmat_ha_ano: 0,
  
  tipo: ["montanha", "turistica", "interior"],
  esportes: ["trekking", "escalada", "mountain_bike", "camping"],
  
  nota: "Município serrano no sul da Mantiqueira, ~1.100m de altitude. Neblina frequente no inverno, frio real de maio a agosto. Turismo rural e de aventura consolidado."
}
```

---

## Atualização de Dados

### Workflow de Atualização

```
[1] Check IBGE
    ↓
[2] python scripts/gerar_cidades.py
    ├─ Busca API IBGE localidades
    ├─ Busca IDEB (INEP)
    ├─ Busca PIB por estado
    └─ Gera cities_data.js
    ↓
[3] python scripts/enriquecer_desmatamento.py (se houver dados novos)
    ├─ Read: mapbiomas_desmat_municipio.xlsx
    └─ Merge em cities_data.js
    ↓
[4] python scripts/enriquecer_mc_munic.py (se houver mudanças)
    ├─ Recalcula risco climático
    └─ Merge em cities_data.js
    ↓
[5] Validação
    ├─ Conferir ~10 cidades aleatórias
    ├─ Testar scoring
    └─ Testar filtros
    ↓
[6] Commit e reload
```

### Passo 1: Regenerar com dados novos

```bash
# Terminal
cd scripts/
python gerar_cidades.py

# Verifica se rodou bem:
# - Output: ../cities_data.js (~7MB)
# - Mensagens: "Processando X cidades..." "Salvo com sucesso"
```

**Tempo estimado**: 2–5 minutos (depende da API IBGE)

### Passo 2: Enriquecer desmatamento

```bash
# Se há novo arquivo MapBiomas
python enriquecer_desmatamento.py

# Output:
# - desmat_por_municipio.json
# - cities_data.js atualizado
```

### Passo 3: Validação

```javascript
// No console do navegador

// Conferir total de cidades
console.log(window.CITIES_DB.length)  // deve ser ~5500–5570

// Conferir índices mínimos
const idhs = CITIES_DB.map(c => c.idh).sort((a,b) => a-b)
console.log('Min IDH:', idhs[0], 'Max:', idhs[idhs.length-1])
// Esperado: Min: ~0.50, Max: ~0.87

// Score de uma capital (ex: São Paulo)
const sp = CITIES_DB.find(c => c.id === 'sao_paulo_sp')
console.log('SP Score:', calcScore(sp))
// Esperado: ~70–80 pontos

// Conferir desmatamento (algumas cidades)
const comDesmat = CITIES_DB.filter(c => c.desmat_ha_ano > 0)
console.log('Cidades com desmatamento:', comDesmat.length)
// Esperado: 20–200 (depende do MapBiomas)
```

---

## Versionamento de Dados

Manter um histórico:

```
cities_data.js (current)
├─ cities_data_2026-06-23_backup.js
├─ cities_data_2026-06-16_backup.js
├─ cities_data_2026-06-09_backup.js
└─ ...
```

**Razão**: Fácil reverter se houver erro de atualização.

---

## FAQ sobre Dados

### P: Onde vem cada dado?

| Dado | Fonte | Frequência |
|------|-------|-----------|
| Municípios (ID, UF) | IBGE API | Fixa |
| População | IBGE Censo 2022 | Anual (próximo 2030) |
| IDH | PNUD + IBGE Censo 2022 | ~2 anos |
| IDEB | INEP Prova Brasil | Anual (jan–mar) |
| PIB | IBGE Contas Nacionais | Anual (2–3 anos depois) |
| Temperatura | Histórico climático INMET | Fixa (média) |
| Desmatamento | MapBiomas | Anual (2–3 anos depois) |
| Renda per capita (`renda_pc`) | IBGE Censo 2022 (SIDRA tabela 10295) | ~10 anos (próximo censo) |
| Desemprego (`desemprego_pct`) | IBGE Censo 2022 (SIDRA tabela 6580) | ~10 anos (próximo censo) |

### P: Por que nem todas cidades têm IDEB?

IDEB é apenas para municípios com escolas estaduais testadas. ~300 municípios pequenos (pop < 5000) não têm IDEB oficial.

**Fallback**: Usamos IDEB da capital do estado como proxy.

### P: O PIB é real ou estimado?

**Estimado** para ~5200 municípios. Apenas SP, RJ, MG têm PIB oficial granular.

**Método**: (pop_municipio / pop_estado) × pib_estado

**Imprecisão**: Até 200–300% em cidades com indústrias concentradas.

### P: Como é o risco climático?

Por estado (simplificado):

1. **Amazônia** (AC, AM, AP, PA, RO, RR) → risco_desmatamento
2. **Cerrado** (DF, GO, MS, MT, TO) → risco_seca
3. **Caatinga** (AL, BA, CE, PB, PE, PI, RN, SE) → risco_seca
4. **Litoral** (cidades praia) → risco_inundacao
5. **Sul** (PR, RS, SC) → risco_inundacao
6. **Sudeste** (ES, MG, RJ, SP) → neutro

**Refinamento futuro**: Usar dados municipais de risco climático (IPCC, SEEG).

### P: Posso fazer download dos dados?

Sim! `cities_data.js` é um arquivo JSON (após remover `window.CITIES_DB = `).

```bash
# Extract pure JSON from cities_data.js
cat cities_data.js | sed 's/^window\.CITIES_DB = //; s/;$//' > cities.json

# Agora é um JSON válido para importar em qualquer ferramenta
```

---

**Last updated**: 2026-06-23
