Las elecciones presidenciales de Uruguay ocurrieron en 2024 y esta visualización muestra con distintos grafos (mapas, barras) e interacciones varias los resultados finales y como se fueron dando en el país, en los distintos departamentos, a nivel de los partidos políticos y en el balotaje.

Los objetivos de este mini proyecto van desde lo técnico a lo comunicacional; en lo técnico es poner en uso tecnologías, librerías y conceptos que se destacan por su eficiencia al momento de ser usados para implementaciones de software.
En el campo del UX, la idea es experimentar con formas de visualizar los datos en pantallas reducidas, hacerlo dinámico, legible y entendible (tanto en la interacción como en el contenido)
La mayoría de las personas miran información desde sus teléfono móviles, si las visualizaciones no se adaptan a estas restricciones, ninguna comunicación será adecuada y se habrá desperdiciado tiempo, dinero y espacio
A nivel comunicacional el objetivo es sobrepasar esa limitante de la parcialidad y la subjetividad al dar prioridad, exclusividad casi total, al mensaje que los datos visualizados emiten
En temas de política es muy difícil comunicar, siempre hay una subjetividad. Eliminar en lo posible eso ofrece la posibilidad de generar diálogos más sensatos, más civiles.



Qué tecnología se uso?
Es una conjunción de varias tecnologías, la cuidada y meticulosa implementación de la arquitectura y el código necesario para que la aplicación funcione eficientemente y de forma clara:
- svelte usando el framework svelte-kit se implementó el frontend, es fácil de entender y relativamente simple de implementar si se sigue buenas prácticas de ingeniería de software
<script>
import * as Styles from './styles';
export let imageName;
export let title;
export let description;
export let linkLabel;
export let link;
export let linkClass = '';
export let target = '_blank';
</script>
<div class={`${Styles.card}`}>
<div class={`${Styles.imageContainer}`}>
<img
class={`${Styles.image}`}
src={`/images/${imageName}`}
/>
</div>
<div class={`${Styles.body}`}>
<div class={`${Styles.content}`}>
<h3 class={`${Styles.title}`}>{title}</h3>
<p class={`${Styles.description}`}>
{description}
</p>
<div class={`${Styles.linkContainer}`}>
<a class={`${Styles.link} ${linkClass}`} href={link} {target}>
{linkLabel}
</a>
</div>
</div>
</div>
</div>
- tailwindcss y preline para los estilos de la aplicación, es versátil, mantenible y, una opinión muy subjetiva, espectacular para usar.
Algo que me gusta hacer imitando lo que hace StyledComponents es exportar los estilos desde './styles' y evito llenar de líneas el código del componente
export const xTickLabel = `
invisible
text-xs
font-noto
font-light
md:visible
`;
export const yTickLabel = `
text-[0.6rem]
font-noto
font-light
md:text-xs
`;
export const numOfVotesLabel = `
text-xs
font-noto
sm:text-sm
lg:text-lg
lg:tracking-wider
`;
export const percentageLabel = `
text-[0.6rem]
font-noto
font-light
sm:text-xs
lg:text-sm
`;
Luego está layercake que es un framework gráfico, se hace cargo de las medidas y cálculos necesarios para posicionar y renderizar los componentes - que se implementan de forma independiente, ya sean SVG, WebGL, Canvas o HTML.
<script>
import { LayerCake } from 'layercake';
import { data24, data19 } from '$data';
import { CirclePack, DetailsSlider, Header, observedYear } from '$lib';
import * as Styles from './styles';
const dataOptions = {
2019: data19,
2024: data24
};
let isOpen = false;
let dataForParty = null;
let selectedParty = null;
let dataForPartyToUse = null;
const setDataForParty = () => {
dataForPartyToUse = selectedParty
? dataOptions[$observedYear].find((d) => d.accronym === selectedParty)
: null;
dataForParty = dataForPartyToUse;
};
const onToggleYearViz = (year) => {
setDataForParty();
};
const onToggleDetails = (shouldOpen, selParty = null) => {
isOpen = shouldOpen;
selectedParty = selParty;
setDataForParty();
};
$: dataForParty = dataForPartyToUse;
</script>
<div class={Styles.container} class:h-full={!isOpen} class:h-[32%]={isOpen}>
<Header
header="Resultados por Lemas"
description="(Seleccioná un partido para más detalles)"
onToggleViz={onToggleYearViz}
/>
<LayerCake data={dataOptions[$observedYear]}>
<CirclePack {onToggleDetails} />
<DetailsSlider
{isOpen}
selectedData={dataForParty?.data || []}
title={dataForParty?.party}
description={dataForParty?.party && dataForParty?.party !== 'otros'
? '(Sublemas más votados)'
: ''}
onClose={() => onToggleDetails(false)}
/>
</LayerCake>
</div>
Y la cereza del pastel es d3js una librería de bajo nivel que es usada para los cálculos de posicionamiento, escala (tanto visual como cuantitativamente) y demás transformaciones necesarias para generar las visualizaciones.
En lugar de usar d3js para graficar los components, se lo usa para los cálculos, los components son elementos SVG (i.e.dot.svelte
) que reciben en susprops
todo lo necesario para renderizar
<script>
import { getContext } from 'svelte';
import { Svg } from 'layercake';
import { stratify, pack, hierarchy } from 'd3-hierarchy';
import Circle from './circle.svelte';
const { width, height, data } = getContext('LayerCake');
export let sortBy = (a, b) => b.numOfVotes - a.numOfVotes;
export let spacing = 0;
export let onToggleDetails;
$: dataset = [...$data, { party: 'URUGUAY', accronym: 'UY' }];
$: stratifier = stratify()
.id((d) => d.accronym)
.parentId((d) => (d.accronym === 'UY' ? '' : d.parent || 'UY'));
$: packer = pack().size([$width, $height]).padding(spacing);
$: stratified = stratifier(dataset);
$: root = hierarchy(stratified)
.sum((d) => d.data.numOfVotes || 1)
.sort(sortBy);
$: packed = packer(root);
$: descendants = packed.descendants();
const descendantData = (d) => {
const { color: partyColor, accronym, percentage } = d.data.data;
return {
x: d.x,
y: d.y,
r: d.r,
partyColor,
accronym,
percentage
};
};
</script>
<Svg>
{#each descendants as d}
{#if d.depth > 0}
<Circle {...descendantData(d)} {onToggleDetails} />
{/if}
{/each}
</Svg>
Y los datos?
La Corte Electoral del Uruguay pone a disposición, como datos abiertos, todo lo concerniente a las elecciones: desgloce de votos, información de los partidos políticos, sus sublemas y los candidatos correspondientes, el plan circuital, y mucho más.
para este proyecto específico se utilizo la data que incluye el número de votos de un partido político en la mesaserie
de la comisión receptora de votocrv
del departamento X
Con librerias como pandas
y numpy
(del mundo python
) se extrajo, organizó y se le dió una estructura consistente con lo que la aplicación esperaba para poder renderizar los datos de forma visual.