teqo-backup/src/utilities/municipalityMapContract.ts
Francisco Ignácio Fontoura Solla 2c30944593 B8+: escala das malhas em unidades reais, contrato do map key e degradacao
F1 — a tolerancia de simplificacao e os digitos de quantizacao viraram
politica em unidades de solo em scripts/lib/topology.mjs, consumida pelos
dois scripts de geometria. Os quantis 0.35 copiados cortavam as 19 ZE de
Salvador a ~0.6 m2 e os 417 municipios a ~1.78 km2; com tolerancia
absoluta e a grade de ~103 m do estado, a malha das zonas caiu de 480 para
30.2 pontos/feature. bahia-municipalities segue byte-identico e o tripwire
de bytes deu lugar a pontos/feature.

F2 — featureMapKey passou a ser asserido sobre features reais das tres
malhas comprometidas (417 codarea, 27 code, 19 slug), e o comentario de
MAP_KEY_PROPERTIES agora diz o que o codigo faz: as propriedades sao
mutuamente exclusivas, nao ordenadas por especificidade.

F3 — a malha das zonas e um detalhe sobre o mapa, nao o mapa: se o chunk
falha, Salvador cai no poligono municipal que ja esta desenhado embaixo e
o resto da Bahia continua funcionando, com o motivo logado. A ressalva de
aproximacao saiu do gate de comparacao e passou a depender de
bundle.hasZoneMunicipalities, derivado no servidor de municipality.kind.

F4 — orfaos deletados: os dois acessores sem chamador, isZoneMapKey (o
servidor ja sabe a regra), o wrapper simplifyTopology e o helper
polygonCount com um branch inalcancavel. MunicipalityMapNavigation
colapsou em string | undefined e MunicipalitiesByMapKey em
Record<string, string>.
2026-07-27 01:59:36 -03:00

142 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { FederalCompetitiveRank } from '@/lib/bahiaElectionAggregates'
import { HISTORICAL_SERIES_YEARS } from '@/lib/electionResults'
import type { VoteEstimateScenario } from '@/lib/voteEstimate'
import type { MunicipalitiesByMapKey } 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) → map key → (sollaVotes − otherVotes). */
diffByYear: Record<string, Record<string, number>>
}
/**
* Every record below is keyed by MAP KEY, not by IBGE codarea: since B8 F2 the
* map paints one polygon per catalog unit, so Salvador's 19 zones are addressed
* by slug (`salvador-ze-3`) and every other município by its codarea. See
* `mapKeyForMunicipality`.
*/
export type MunicipalityMapBundle = {
/** year (as string) → map key → value. 2026 = cenário médio (central). */
valuesByYear: Record<string, Record<string, number>>
/** 2026 totals per estimate scenario (map key → votes). */
values2026ByScenario: Record<VoteEstimateScenario, Record<string, number>>
/** year (as string) → map key → 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>
/** map key → E10 territorial class of that unit. */
territorialClassByMapKey: Record<string, MunicipalityTerritorialClass>
/**
* map key → his placement among the federal-deputy candidates voted there. The
* TSE artifact ranks by codarea only, so Salvador's zones all carry the city's
* position (the legend says so).
*/
competitiveRankByYear: Record<string, Record<string, FederalCompetitiveRank>>
/** map key → projected 2026 valid votes (E8) — the size of the proportional symbol. */
projectedValidVotesByMapKey: Record<string, number>
/** map key → the municipality it opens on click. */
municipalitiesByMapKey: MunicipalitiesByMapKey
/**
* Whether the actor's scope includes a zone municipality, i.e. whether the
* approximate ZE mesh is painted at all — which is what the map's provenance
* caveat is about. Published rather than re-derived on the client so `kind`
* stays the single answer to "is this a zona": reading it off the key's shape
* would be a second encoding of the rule in `mapKeyForMunicipality`, and
* `zoneBreakdown` is scheduled for deletion by the fill-in that removes the
* textual zone list.
*/
hasZoneMunicipalities: boolean
/** Zone municipalities in scope (Salvador) with per-year values. */
zoneBreakdown: MunicipalityZoneBreakdownRow[]
candidateName: string
comparison: MunicipalityMapComparison | null
}