mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:45:18 +00:00
O /simplify da escala relativa do mapa achou um bug semantico real em computeAggregateTerritorialClass — somava o headroom dos slugs e comparava contra a mediana de UMA unidade, classificando um grupo grande como expansao quando ele e marginal. A mediana agora escala por slugs.length, com teste. As bolhas sairam do centro do bounding box para o centroide real e ganharam pane proprio (z-index 450), porque o bringToFront do hover as escondia atras do poligono. Legenda e pintura passaram a compartilhar o mesmo predicado, a copy dos quantis parou de prometer faixas de igual largura, e o subtitulo do painel parou de dizer "em 2026" com 2022 selecionado (defeito do B11). Fonte unica onde havia duas: NO_DATA_FILL, o raio das bolhas e formatPlacementOrdinal (ex-formatMunicipalityVoteRank, agora client-safe em electionFormat.ts). Os quatro debitos maiores que o cleanup viraram o fill-in B13+ (docs/plans/escala-dry-pos-b13.md); a flakiness dos e2e, reproduzida em main limpo, foi para o ledger com a causa afinada.
122 lines
5.3 KiB
TypeScript
122 lines
5.3 KiB
TypeScript
import type { FederalCompetitiveRank } from '@/lib/bahiaElectionAggregates'
|
||
import { HISTORICAL_SERIES_YEARS } from '@/lib/electionResults'
|
||
import type { VoteEstimateScenario } from '@/lib/voteEstimate'
|
||
import type { MunicipalitiesByIbgeCode } from '@/utilities/municipalityMapNavigation'
|
||
import type { MunicipalityTerritorialClass } from '@/utilities/municipalityTerritorialClass'
|
||
|
||
/**
|
||
* Client-safe contract of the municipality map: the bundle shape the server
|
||
* assembles and the year/scale vocabulary the client panel renders. Keep this
|
||
* module free of Payload/Next-server imports — client components import these
|
||
* VALUES, so anything reachable from here ships in the browser bundle
|
||
* (`municipalityMapData.ts` is the server-only side).
|
||
*/
|
||
|
||
export const MUNICIPALITY_MAP_YEARS = [...HISTORICAL_SERIES_YEARS, 2026] as const
|
||
export type MunicipalityMapYear = (typeof MUNICIPALITY_MAP_YEARS)[number]
|
||
|
||
/**
|
||
* B13: the first three modes are RELATIVE to the candidate himself and are
|
||
* what actually discriminate on a federal-deputy map — his best município
|
||
* gives him ~5% of its valid votes, so `percentValid` and `absolute` paint
|
||
* Bahia in one shade plus Salvador. The two absolute modes stay because they
|
||
* answer "how many votes, really", which the relative ones deliberately hide.
|
||
*/
|
||
export type MunicipalityMapScaleMode =
|
||
| 'quantile'
|
||
| 'lq'
|
||
| 'competitiveRank'
|
||
| 'percentValid'
|
||
| 'absolute'
|
||
|
||
export const MUNICIPALITY_MAP_SCALE_MODES = [
|
||
'quantile',
|
||
'lq',
|
||
'competitiveRank',
|
||
'percentValid',
|
||
'absolute',
|
||
] as const satisfies readonly MunicipalityMapScaleMode[]
|
||
|
||
export const DEFAULT_MUNICIPALITY_MAP_SCALE_MODE: MunicipalityMapScaleMode = 'quantile'
|
||
|
||
export const municipalityMapScaleModeLabels: Record<MunicipalityMapScaleMode, string> = {
|
||
quantile: 'Quantis (5 faixas)',
|
||
lq: 'Padrão próprio (LQ)',
|
||
competitiveRank: 'Posição no município',
|
||
percentValid: '% dos válidos',
|
||
absolute: 'Total (votos)',
|
||
}
|
||
|
||
/**
|
||
* What the colour means, printed with the legend the scale selector describes.
|
||
*
|
||
* Deliberately NOT the canonical E18 `oneLiner` the list's column descriptions
|
||
* quote: three of these five scales have a glossary entry and two do not, so
|
||
* quoting would fork the voice inside one record — and the legend already
|
||
* carries two to four more sentences after this one, where a standalone
|
||
* definition ("Divide os municípios em cinco faixas…") reads as padding. The
|
||
* "Saiba mais" link next to the note is the path to the full entry.
|
||
*/
|
||
export const municipalityMapScaleModeHints: Record<MunicipalityMapScaleMode, string> = {
|
||
quantile: 'Cinco faixas com o mesmo número de municípios.',
|
||
lq: 'Força local comparada ao padrão estadual do próprio candidato.',
|
||
competitiveRank: 'Colocação dele entre os candidatos a deputado federal votados ali.',
|
||
percentValid:
|
||
'Participação nos votos válidos — honesta, mas comprime quase tudo numa faixa estreita.',
|
||
absolute: 'Votos em números absolutos — favorece os municípios grandes.',
|
||
}
|
||
|
||
/** Scales that need TSE results and therefore have no 2026 reading. */
|
||
export const isMunicipalityMapScaleModeAvailable = (
|
||
mode: MunicipalityMapScaleMode,
|
||
year: MunicipalityMapYear,
|
||
): boolean => mode !== 'competitiveRank' || year !== 2026
|
||
|
||
export const municipalityMapYearLabels: Record<MunicipalityMapYear, string> = {
|
||
2014: '2014 (TSE)',
|
||
2018: '2018 (TSE)',
|
||
2022: '2022 (TSE)',
|
||
2026: '2026 (estimativas)',
|
||
}
|
||
|
||
type MunicipalityZoneBreakdownRow = {
|
||
slug: string
|
||
name: string
|
||
votesByYear: Record<string, number>
|
||
votes2026ByScenario: Record<VoteEstimateScenario, number>
|
||
}
|
||
|
||
export type MunicipalityMapComparison = {
|
||
candidateNumber: number
|
||
candidateName: string
|
||
/** TSE year (as string) → codarea → (sollaVotes − otherVotes). */
|
||
diffByYear: Record<string, Record<string, number>>
|
||
}
|
||
|
||
export type MunicipalityMapBundle = {
|
||
/** year (as string) → codarea → value. 2026 = cenário médio (central). */
|
||
valuesByYear: Record<string, Record<string, number>>
|
||
/** 2026 totals per estimate scenario (codarea → votes). */
|
||
values2026ByScenario: Record<VoteEstimateScenario, Record<string, number>>
|
||
/** year (as string) → codarea → votosValidos (federal T1). 2026 reuses 2022. */
|
||
validVotesByYear: Record<string, Record<string, number>>
|
||
/**
|
||
* year (as string) → his own STATEWIDE share of valid votes: the "1×" the LQ
|
||
* scale divides by. Statewide, never the share of the municipalities on
|
||
* screen, so an advisor's map and the list's "Classe" column measure against
|
||
* the same standard. 2026 reuses the 2022 standard, like `validVotesByYear`.
|
||
*/
|
||
statewideShareByYear: Record<string, number>
|
||
/** codarea → E10 territorial class, aggregated over the slugs in scope. */
|
||
territorialClassByCode: Record<string, MunicipalityTerritorialClass>
|
||
/** codarea → his placement among the federal-deputy candidates voted there. */
|
||
competitiveRankByYear: Record<string, Record<string, FederalCompetitiveRank>>
|
||
/** codarea → projected 2026 valid votes (E8) — the size of the proportional symbol. */
|
||
projectedValidVotesByCode: Record<string, number>
|
||
/** IBGE codarea → accessible municipality slugs for map click navigation. */
|
||
municipalitiesByIbgeCode: MunicipalitiesByIbgeCode
|
||
/** Zone municipalities in scope (Salvador) with per-year values. */
|
||
zoneBreakdown: MunicipalityZoneBreakdownRow[]
|
||
candidateName: string
|
||
comparison: MunicipalityMapComparison | null
|
||
}
|