teqo/docs/plans/escala-relativa-mapa.md
Francisco Ignácio Fontoura Solla adc0f31f5b Sync all project docs with the deployed remodel and hardening; compile roadmap
- Compile docs/roadmap.md into a next-steps document (277→206 lines): R0–R5/M1–M5
  tables and the delivered map/list item paragraphs compressed into 'Já entregue';
  janelas, grafo and cortes list only open IDs; C10/C11/C8 fill-ins keep their
  original triggers; C5 moved under 'A validar'.
- Record the production deploy of the destructive remodel chain (2026-07-23 Vercel
  builds) across roadmap, AGENTS.md, the project notebook and plans — supersedes
  every 'deploy pendente'; smoke pós-deploy + onboarding remain the open blockers.
- Fix AGENTS.md internal contradictions: Município locked-decisions bullet (435,
  Camaçari whole, candidate role), campaignUser role enum, deploy-checklist item 7
  as historical, access/* structure note, election-aggregates artifact +
  election-tse revalidate runbook.
- Sync open plans to real identifiers (plaza*/nucleus* → municipality*): the
  intelligence program (master + E8–E15/B13/C12 with artifact/caching context and
  satisfied deps), B8 rescoped to Salvador ZE 1–19 only, WhatsApp D3–D5, D2, A6
  (now the insight layer over stateDeputy), RS+, onda-0 smoke step.
- Close items verified done in code: C8 F4 (DRY de forms) complete, filtros-auto
  shared pending shell, petition generateStaticParams select/depth; ledger the
  surviving user-visible 'Praça' copy in src as new P2 debt.
- Banner delivered praça-era plans (production note + rename pointer); mark
  remodelagem-pracas.md superseded and remodelagem-municipios.md delivered.
- TECH-DEBT smell statuses, TESTING RBAC row, README quality gates, campanha
  UI rules updated to current component names.
2026-07-24 02:04:45 -03:00

7.9 KiB
Raw Blame History

B13 — Escala relativa no mapa dos Municípios (quantis/LQ/rank + símbolo proporcional)

Status: rascunho Atualizado em: 2026-07-24 (refs sincronizadas pós-remodelagem Municípios + hardening) Item do roadmap: docs/roadmap.md (seção "Inteligência de campanha", B13; plano-mestre inteligencia-campanha.md) Impeccable: B — estende MunicipalityMapPanel/BahiaMap existentes (seletor de escala, camada de símbolos); sem rota nova Appetite: ~2 dias eng; sem migration Responsável: —

Design (Impeccable)

Âncoras: PRODUCT.md (princípio 5; anti-goal cartograma) / DESIGN.md (register product) · precedentes B10/B11/B12/B6 (BahiaMap, bahiaMapStyle.ts, MapFeatureReadout).

Na implementação: craft compacto → critique → polish.

  • Persona / contexto: coordenador em reunião projetando o mapa; "o mapa serve ao padrão espacial" (relatório D3).
  • Job principal: o mapa discrimina onde antes tudo era a mesma cor — leitura relativa ao próprio candidato, com o peso do eleitorado visível.
  • Estratégia de cor: sequencial de 5 classes (quantis) na paleta existente do tema; diverging continua exclusivo do compare (R4); bolhas com stroke branco (padrão choropleth da skill Mapbox).
  • Edit where you see: não (superfície de leitura).
  • Anti-goals: cartograma/geometria distorcida (anti-goal 4); rampa contínua 0–100% como única leitura; legenda com >7 classes; segundo componente de mapa.

Contexto

B11 entregou a escala "% dos válidos" com domínio fixo 0–100% — tecnicamente honesta e informacionalmente vazia para DF (topo estadual 4,71%; relatório D2). A cartografia manda: classes discretas (3–7), quantis para leitura e comparação em série (Brewer & Pickle), transformações relativas ao próprio candidato (quantis/LQ/rank), e peso visual por eleitorado ("land doesn't vote": interior gigante × Salvador com ~1/5 do eleitorado). A persona recomendou símbolo proporcional (bolha = votos em jogo, cor = classe) como o formato que "funciona em campo", descartando cartograma e tratando value-by-alpha como teste, não default (D3). BahiaMap já tem: layer estável + setStyle incremental (B6), scaleMax (B11), fitToKeys/interactiveKeys (B12), readout (B10).

Objetivos

  • Seletor de escala ganha modos relativos: Quantis (5) — default novo para métrica de candidato; LQ (diverging em torno de 1); Rank no município (1º / 2º–3º / 4º+); mantém Total (votos) e % dos válidos (B11).
  • Camada opcional de símbolos proporcionais (bolha por votos em jogo — válidos projetados E8; cor da bolha = classe E10) sobre o coropleto neutro; toggle no painel.
  • Legenda por classes com rótulos de faixa reais (quantis do conjunto filtrado atual, coerente com B7/B12).
  • Readout (MapFeatureReadout) mostra o valor bruto + a leitura relativa ("Q5 de 5 · LQ 2,8").
  • Compare mode (R4) continua absoluto/diverging — modos relativos desabilitados no compare (mesma regra do B11 com %).
  • Zonas SSA/CMS: agregação municipal atual permanece (B8 F2 é outro item); bolha usa o agregado municipal com zoneBreakdown no readout.

Decisões travadas

  • Quantis do conjunto filtrado como default relativo (discriminam por construção; recomendação clássica para séries). Rejeitado: Jenks como default (menos estável entre filtros/anos — quebra a comparabilidade; pode entrar depois como opção); domínio dinâmico min–max contínuo (melhora pouco e perde classes nomeáveis).
  • Símbolo proporcional em vez de cartograma/value-by-alpha como peso de eleitorado. Operador navega pela geografia real; bolha codifica volume+status em 2 canais legíveis (relatório D3). Rejeitado: cartograma (anti-goal travado); value-by-alpha como default (canal sutil para decisão rápida — pode virar teste A/B futuro).
  • Nenhum componente de mapa novo — tudo entra por BahiaMap/bahiaMapStyle.ts (B6 manteve o layer estável exatamente para isso). Rejeitado: fork do painel para "mapa estratégico" separado.
  • i18n e naming: scaleMode estendido (absolute | percentValid | quantile | lq | rank), symbolLayer, computeQuantileBreaks; labels pt-BR ("Quantis", "Padrão próprio (LQ)", "Posição no município", "Bolhas por votos em jogo").

Questões em aberto

  • Bolha dimensiona por válidos projetados ou por votos em jogo da meta (E8)? Opções: válidos projetados | meta | eleitorado. Recomendação: válidos projetados (estável, independe de meta editada; a meta já aparece na fila E9).
  • Quantis calculados sobre o conjunto filtrado ou o estado inteiro? Recomendação: conjunto filtrado (coerente com B7/B12 — o mapa acompanha a lista); nota na legenda quando filtrado.

Abordagem proposta

flowchart LR
    Bundle["MunicipalityMapBundle<br/>(municipalityMapData.ts)"]
    Pot["municipalityPotential (E8)"]
    Class["computeDfTerritorialClass (E10)"]
    Breaks["computeQuantileBreaks / lq / rank<br/>(bahiaMapStyle.ts)"]
    Map["BahiaMap<br/>(fills + symbol layer)"]
    Readout["MapFeatureReadout"]
    Bundle --> Breaks
    Pot --> Breaks
    Class --> Map
    Breaks --> Map
    Map --> Readout

Componentes:

  • src/lib/bahiaMapStyle.ts: breaks de quantis/LQ/rank puros (unit-testáveis); paleta de 5 classes do tema.
  • src/components/campaign/BahiaMap.tsx: camada L.circleMarker por município (raio ∝ √votos, padrão cartográfico), regida pelo mesmo pathByKeyRef/restyle incremental de B6; hover/tap integram o readout existente.
  • src/components/campaign/MunicipalityMapPanel.tsx: seletor de escala estendido + toggle de bolhas; regras de compare inalteradas.
  • src/utilities/municipalityMapData.ts: expõe métricas relativas no bundle (LQ, rank, classe) — deriva de E8/E10 sem query nova. Desde o hardening 2026-07-23, o TIPO do bundle vive em municipalityMapContract.ts (módulo client-safe; municipalityMapData.ts é o loader server-only que o re-exporta) — estender o bundle = editar os dois; anos históricos vêm do artefato bahiaElectionAggregates.ts.
  • Sem migration.

Dependências

  • Duras: E8 (válidos projetados/LQ), E10 (classe para a cor da bolha). Suaves: E14 (bolha pode encodar nível no futuro), B8 F2 (polígonos de zona — independente).
  • Reusa: B6/B10/B11/B12 inteiros; municipalitiesByIbgeCode; canonicalMapKeysKey.

Não escopo

  • Rollup/mapa por TI (E12); polígonos das Municípios-zona (B8 F2); export/print do mapa; value-by-alpha (adiado abaixo).

Rabbit holes

  • Perf com 417 bolhas + hover. B6 resolveu para fills; markers precisam do mesmo tratamento incremental — se o frame cair, bolhas só a partir de zoom N ou top-K por votos. Não otimizar antes de medir.
  • Legenda de quantis com faixas confusas quando o filtro retorna <10 municípios. Degradar para 3 classes ou valores diretos; não inventar interpolação.
  • Rank exigir todos os candidatos por município em memória. municipalityCandidateComparison.ts já pagina/limita; rank v1 = posição do Solla entre os top-N carregados, documentado.

Adiado com gatilho

  • Value-by-alpha (opacidade ∝ eleitorado). Gatilho: teste com o time em reunião real pedir alternativa às bolhas.
  • Roll-off como métrica de mapa. Gatilho: I-A entrar no motor (E11 fase 2) e o time pedir a leitura espacial.

Referências

  • docs/roadmap.md (Inteligência de campanha, B13) · plano-mestre
  • docs/research/relatorio-entrevista-persona-campanha.md D2/D3 (escalas, símbolo proporcional, "não vá" do cartograma) · compêndio §8.2
  • docs/plans/escala-percentual-mapa-pracas.md (B11), docs/plans/escala-dry-pos-b3.md (B6), docs/plans/aproximar-mapa-pracas.md (B12), docs/plans/hover-mapa-pracas.md (B10)
  • src/components/campaign/BahiaMap.tsx, src/components/campaign/MunicipalityMapPanel.tsx, src/lib/bahiaMapStyle.ts, src/utilities/municipalityMapData.ts
  • AGENTS.md — mapa (B6/B11/B12 as-built), naming