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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more