Public
Edited
Dec 18, 2022
2 forks
Importers
10 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof mapC = mapboxD3({
d3Background: "#f4ebc97d",
features: votingPosts.features,
mapboxOptions: {
// Bogotá 4.7110° N, 74.0721° W
center: [-74.1221, mywidth > 600 ? 4.631 : 4.651],
zoom: mywidth > 600 ? 10.5 : 9.8,
style: "mapbox://styles/jguerrag/cjuerw13z2vja1fndc9wzk7eo",
scrollZoom: mywidth > 600 ? true : false,
maxBounds: [
[-75.36349309199994, 3.98574147100004],
[-73.51973278599996, 5.823891889000038]
]
},
r: (d) => rScale(d?.data?.value),
id: (d) => d.properties.OBJECTID,
height: myheight,
width: mywidth,
collide,
alphaRestart: 0.9,
parentInvalidation: invalidation,
stroke: "white",
strokeWidth: 0.5
})
Insert cell
title = {
let renderedCandidates;

const renderCandidateTitle = (c, i = 0, all = []) => {
const scale = colors[c] || defaultColor;
return htl.html`<h3><span style="color: ${scale(
(defaultDomain[1] - defaultDomain[0]) * 0.6
)}">${c}</span> ${i < all.length - 1 ? "vs" : ""}</h3>`;
};

if (attr === "Ganador") {
renderedCandidates = candidatesToCompare.map(renderCandidateTitle);
} else if (attr === "Igual Tamaño") {
renderedCandidates = "";
} else if (attr === "Total") {
renderedCandidates = "por puesto";
} else {
renderedCandidates = renderCandidateTitle(attr);
}
let mainHeader = "";
if (attr === "Ganador") {
if (candidatesToCompare.length >= 1) {
mainHeader = htl.html`<h2>¿En donde ganó?</h2>`;
} else if (candidatesToCompare.length === 1) {
mainHeader = htl.html`<h2>Total de Votos</h2>`;
} else {
mainHeader = htl.html`<h2>Seleccione al menos un candidato</h2>`;
}
} else {
if (attr === "Igual Tamaño") {
mainHeader = htl.html`<h2>Todos los puestos</h2>`;
} else {
mainHeader = htl.html`<h2>Total de Votos</h2>`;
}
}

return htl.html`<div id="title" style="position: absolute;
top: 20px;
left: 20px;
pointer-events: none;">${mainHeader} ${renderedCandidates}</div>`;
}
Insert cell
Insert cell
mywidth = width
Insert cell
myheight = mywidth * (mywidth > 600 ? 0.7 : 1.4)
Insert cell
rScale = d3.scaleSqrt().domain(defaultDomain).range([0, 30])
Insert cell
update = {
// const path = mapC.path;
// const dots = mapC.dots;
// const map = mapC.map;
// const hoverObj = mapC.hoverObj;

votingPosts.features = votingPosts.features.map((d) => {
d.foci = mapC.path.centroid(d);
const candidatesVotes = candidatesToCompare.map((c) => d.data[c]);
d.data.iWinner = d3.maxIndex(candidatesVotes);
d.data.Ganador = candidatesVotes[d.data.iWinner];
// if (d.data) {
// d.data.r = d.data[attrR];
// } else {
// console.log("data is undefined", d);
// }
return d;
});
const features = includeCorferias
? votingPosts.features
: votingPosts.features.filter(
(d) => d.properties.PVONOMBRE !== "CORFERIAS"
);
mapC.features = features;
mapC.simulation && mapC.simulation.alpha(0.3).restart();
mapC.fill = selectiveColorScale;
rScale
.domain([
0,
d3.max(
includeCorferias
? bogotaByPost
: bogotaByPost.filter((d) => d.Num_puesto !== 1 && d.Zona !== 90),
(d) => d["Gustavo Petro"]
)
])
.range([1, maxR]);
mapC.r = (d) => d.data && rScale(d?.data[attrR.attr]);
// mapC.delay = (d, i) => d.foci[0] * 2;
mapC.delay = (d, i) => i * 1;
// mapC.width = mywidth;
// mapC.height = myheight;
mapC.update();

// console.log("map update", attrR.attr, features.length, "myheight", myheight);
// return update;
}
Insert cell
function tipcontent(_d) {
const d = _d.data;
return htl.html`
<h3>${d.Puesto} <small>${d.num_mesas} mesas</small></h3>
<table>
${candidates
.filter((d) => d !== "Igual Tamaño")
.map(
(c) =>
htl.html`<tr><td>${c}</td>
<td style="font: console; text-align:right">${fmtN(d[c])}</td></tr>`
)}
</table>`;
}
Insert cell
function selectiveColorScale(d) {
if (d.data === undefined) {
console.log("undefined data", d);
return;
}
let scale = defaultColor;
if (Object.keys(colors).includes(attr)) {
// one of the candidates
scale = colors[attr];
} else if (attr === "Ganador") {
scale = colors[candidatesToCompare[d.data.iWinner]];
}
// console.log(scale(d.data[attr]), d);
return scale ? scale(d.data[attr]) : defaultColor(d.data[attr]);
}
Insert cell
r = d3
.scaleSqrt()
.domain(defaultDomain)
.range([1, maxR])
Insert cell
defaultDomain = [
0,
d3.max(
bogotaByPost.filter((d) => d.Num_puesto !== 1 && d.Zona !== 90),
(d) => d["Total"] * 1.2
)
// d3.max(
// includeCorferias
// ? bogotaByPost
// : bogotaByPost.filter((d) => d.Num_puesto !== 1 && d.Zona !== 90),
// (d) => d["Gustavo Petro"]
// )
]
Insert cell
// bogotaByPost.filter((d) => d.Puesto.indexOf("PUESTO CENSO") !== -1)

Insert cell
defaultColor = d3
.scaleSequential(adjustInterpolator((t) => d3.interpolateInferno(1 - t)))
.domain(defaultDomain)
Insert cell
colors = ({
"Gustavo Petro": d3
.scaleSequential(adjustInterpolator(d3.interpolateReds))
.domain(defaultDomain),
"Rodolfo Hernández": d3
.scaleSequential(adjustInterpolator(d3.interpolatePuRd))
.domain(defaultDomain),
"Federico Gutiérrez": d3
.scaleSequential(adjustInterpolator(d3.interpolateBlues))
.domain(defaultDomain),
"Sergio Fajardo": d3
.scaleSequential(adjustInterpolator(d3.interpolateGreens))
.domain(defaultDomain),
Otros: defaultColor
})
Insert cell
adjustInterpolator = (t) => (d) => t(croppedScale(d))
Insert cell
croppedScale = d3.scaleLinear().domain([0, 1]).range([colorAdjust, 1])
Insert cell
mutable hovered = null
Insert cell
mapBogotaByPost = new Map(bogotaByPost.map((d) => [getKey(d), d]))
Insert cell
function getKey(d) {
return fmt2(d.Zona) + fmt3(d.Num_puesto);
}
Insert cell
function getKeyPost(d) {
return fmt2(d.properties.LOCCODIGO) + fmt3(d.properties.PVONPUESTO);
}
Insert cell
fmt2 = d3.format("02d")
Insert cell
fmt3 = d3.format("03d")
Insert cell
fmtN = d3.format(",d")
Insert cell
candidates = [
"Gustavo Petro",
"Federico Gutiérrez",
"Rodolfo Hernández",
"Sergio Fajardo",
"John Milton Rodríguez",
"Luis Pérez",
"Enrique Gómez",
"Ingrid Betancourt",
"Votos en Blanco",
"Votos no Marcados",
"Votos Nulos",
"Total",
"Igual Tamaño"
]

// candidates = Object.keys(bogotaByPost[0]).slice(7).filter(d => d!=="iWinner")
Insert cell
infoCols = Object.keys(bogotaByPost[0]).slice(0, 6)
Insert cell
import { navio } from "@john-guerra/navio"
Insert cell
bogotaByPost = FileAttachment(
"Escrutinios-primera-vuelta-presidencial-bogota_agregados_por_puesto@2.csv"
)
.csv({ typed: true })
.then((res) => {
const candidates = Object.keys(res[0]).slice(7);
for (let d of res) {
d.Total = candidates.reduce((p, c) => p + d[c], 0);
d["Igual Tamaño"] = 1000;
// done in update now
// d.iWinner = d3.maxIndex(candidates.map((c) => d[c]));
// d.Ganador = candidates.map((c) => d[c])[d.iWinner];
}
return res;
})
Insert cell
votingPosts = FileAttachment("Bogota_puestos_de_votacion_2022.geojson")
.json()
.then((res) => {
for (let d of res.features) {
d.data = mapBogotaByPost.get(getKeyPost(d));
if (d.data) d.data.value = d.data["Gustavo Petro"];
}
res.features = res.features.sort((a, b) => d3.descending(a.Total, b.Total));
return res;
})
Insert cell
mapboxgl = {
const gl = await require("mapbox-gl");
if (!gl.accessToken) {
gl.accessToken =
"pk.eyJ1Ijoiamd1ZXJyYWciLCJhIjoiY2pueW9uMGhzMDA3NDN3bXp5b2Rqbzk1YiJ9.3scgV-j4d394mvOUqtz1XQ";
const href = await require.resolve("mapbox-gl/dist/mapbox-gl.css");
document.head.appendChild(html`<link href=${href} rel=stylesheet>`);
}
return gl;
}
Insert cell
import { legend } from "@john-guerra/color-legend"
Insert cell
tippytheme = "light"
Insert cell
import { mapboxD3, link } with {
tipcontent,
tippytheme
} from "@john-guerra/mapbox-d3"
Insert cell
link
Insert cell
d3 = require("d3@7.6")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more