Public
Edited
Aug 20, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tabla = Inputs.table(data)
Insert cell
srcColumn = d3.map(data, d => d.src)
Insert cell
Insert cell
urlsFiltradas = {

function caminoSeleccionado(camino){
const filtradas = data
.filter(d => d.camino === camino)
.map(d => d.url);
return filtradas
}
return caminoSeleccionado("Putumayo")
}
Insert cell
panelFromScenes = async (data, scenes, start) => {
const newScenes = JSON.parse(JSON.stringify(scenes));
await setScenes(data, newScenes);
showPanel(newScenes, start);
return newScenes
}
Insert cell
scenes = (
{
"start": {
"viz":{ "filter": [], "type": "compare", "x": 116, "y": 110},
"title": "Sobreviviencias de la V lámina de Vues", "text": "",
},
}
)
Insert cell
setScenes = async (data, scenes) => {
const msg = d3.select("#panel-container").append("div").text("Loading Atlas...");
// v 0.3.0
// sintaxis = {filter: () => {}, type: pack, scatter, compare, x:, y:}
const keys = Object.keys(scenes);
const vizFunction = {'pack': vanillaCanvasPack, 'scatter': vanillaCanvasScatter, 'compare': vanillaCanvasCompare}

for (let k of keys) {
if (scenes[k].viz !== undefined) {
const {filter, type, x, y} = scenes[k].viz;
let filtered = data;
for (let f of filter) {
const fn = filterFn(f)
filtered = fn(filtered);
}
const {viz, areas} = await vizFunction[type](filtered, x, y);
scenes[k].image = viz;
scenes[k].areas = areas;
}

let newText = "";
if (scenes[k].meta) {
newText += getMeta(data[scenes[k].meta]);
scenes[k].image = data[scenes[k].meta].url;
delete scenes[k].meta;
}
if (scenes[k].title) {
newText += `<h3>${scenes[k].title}</h3>`;
delete scenes[k].title;
}
scenes[k].text = newText + (scenes[k].text || "");

if (scenes[k].info) { scenes[k].text +=`<a href=${scenes[k].info[1]} target="_blank">${scenes[k].info[0]}</a>` }
}

function getMeta(d) {
return `<div class="meta-container"><p><b>Title:</b> ${d.obras}</p><p><b>Date:</b> ${d.fecha}</p><p><b>Author:</b> ${d.autor}</p><p><b>Source:</b> ${d.fuente}</p><p><b>Description:</b> ${d.descripcion}</p><a class="repo-link" href="${d.repositorio}" target="_blank">Repository</a></div>`
}

function filterFn(f) {
if (f[1] === "=" || f[1] === "==" || f[1] === "===") {
const condition = /true|True/i.test(""+f[2]) ? true : /false|False/i.test(""+f[2]) ? false : f[2];
return data => data.filter(d => d[f[0]] == condition)
} else if (f[1] === "<") {
return data => data.filter(d => d[f[0]] < f[2])
} else if (f[1] === ">") {
return data => data.filter(d => d[f[0]] > f[2])
} else {
return data => data
}
}

for (let d of data) {
scenes[`ind_${d.n}`] = {
image: d.url,
text: getMeta(d),
options: [{ btn: "<<<", scene: `ind_${d.n}`, back: true }]
}
}

msg.remove();

return scenes
}
Insert cell
showPanel = (scenes, start) => {
let prevScene = start;
const a = new aventuraModule.Aventura('en', {
adventureContainer: "panel-container",
typewriterSpeed: 0,
sceneCallbackStart: (scene) => {
if (prevScene === scene.key) return
if (scene.options === undefined) {
scene.options = [{ btn: "<<<", scene: prevScene, back: true }];
} else {
if (scene.options[0].back) {
scene.options[0].scene = prevScene;
} else {
scene.options.unshift({ btn: "<<<", scene: prevScene, back: true });
}
}

if (!scene.key.includes("ind_")) {
prevScene = scene.key;
}
}
})
a.setScenes(scenes).testScenes().startAdventure(start)
return scenes
}
Insert cell
processedScenes = panelFromScenes(data, scenes, 'start')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vanillaCanvasScatter = async (data, vx, vy) => {
const size = 50;
const margin = {l: 0.2 * width, r: 0.1 * width, t: 0.1 * height, b: 0.1 * height};
const wm = width - margin.l - margin.r;
const hm = height - margin.t - margin.b;
const filtered = data;

const domainX = [...new Set(filtered.map(d => d[vx]))];
const domainY = [...new Set(filtered.map(d => d[vy]))];
const scaleX = d3.scalePoint().domain(domainX).range([0, wm]).padding(0.5).round(true);
const scaleY = d3.scalePoint().domain(domainY).range([0, hm]).padding(0.5).round(true);

const imgs = [];
for (let d of filtered) {
const img = new Image();
img.setAttribute('crossorigin', 'anonymous');
img.src = d.url;
await new Promise(r => { img.onload = () => { r(true) }});
imgs.push({ url: d.url, x: scaleX(d[vx]), y: scaleY(d[vy]), img: img, r: size, n: d.n });
}

const simulation = d3.forceSimulation(imgs)
.force("charge", d3.forceManyBody().strength(5))
.force("collide", d3.forceCollide(10))
.tick(200)

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
ctx.fillStyle = "#313131";
ctx.fillRect(0, 0, width, height);

ctx.strokeStyle = "white";
ctx.font = "14px serif";
ctx.textBaseline = "middle";
ctx.textAlign = "center";

for (let d of domainX) {
ctx.fillStyle = "white";
ctx.fillText(d, margin.l + scaleX(d), margin.t + hm + (margin.b / 2));
}
ctx.beginPath();
ctx.moveTo(margin.l, margin.t + hm);
ctx.lineTo(margin.l + wm, margin.t + hm);
ctx.stroke();

ctx.textAlign = "right";
for (let d of domainY) {
ctx.fillStyle = "white";
ctx.fillText(d, margin.l - 5, margin.t + scaleY(d));
}
ctx.beginPath();
ctx.moveTo(margin.l, margin.t);
ctx.lineTo(margin.l, margin.t + hm);
ctx.stroke();

ctx.strokeStyle = "black";

const areas = [];
for (let i of imgs) {
const w = size;
const h = w * i.img.height / i.img.width;
const j = Math.random() * 100;
const x = i.x + margin.l + j;
const y = i.y + margin.t // + Math.random() * 10;

ctx.save();
ctx.translate(-w/2, -h/2);
ctx.drawImage(i.img, x, y, w, h);
ctx.restore();

const area = {
x, y, w, h,
btn: "",
scene: `ind_${i.n}`,
tooltip: ""
}
areas.push(area);
}

return {viz: canvas.toDataURL('image/png'), areas}
}
Insert cell
vanillaCanvasPack = async (data, h1, h2) => {
const filtered = data;
const groups = d3.rollup(filtered, v => v.length, d => d[h1], d => d[h2])
const childrenAccessorFn = ([ key, value ]) => value.size && Array.from(value)
const root = d3.hierarchy(groups, childrenAccessorFn)
.sum(([,value]) => value)
.sort((a, b) => b.value - a.value)

const scheme = [...d3.schemeGreys[4]].reverse();
// scheme[0] = "#313131";
const desc = root.descendants();

const color = (v) => scheme[v];

d3.pack(root)
.size([width, height])
.padding(20)
(root)
const imgs = [];
for (let f of filtered) {
for (let d of root.leaves()) {
if (f[h1] === d.parent.data[0] && f[h2] === d.data[0]) {
const img = new Image();
img.setAttribute('crossorigin', 'anonymous');
img.src = f.url;
await new Promise(r => { img.onload = () => { r(true) }});
imgs.push({ url: f.url, x: d.x, y: d.y, r: 25, img: img, n: f.n });
}
}
}
const simulation = d3.forceSimulation(imgs)
.force("collide", d3.forceCollide(20))
.tick(200)
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
ctx.fillStyle = "#313131";;
ctx.fillRect(0, 0, width, height);
ctx.font = "12px serif";
ctx.textBaseline = "middle";
ctx.textAlign = "center";

for (let d of desc) {
ctx.save();
ctx.translate(d.x, d.y);
ctx.strokeStyle = "black";
ctx.fillStyle = color(d.depth);
ctx.beginPath();
ctx.ellipse(0, 0, d.r, d.r, 0, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();

ctx.fillStyle = "black";
ctx.fillText(d.depth === 1 || d.depth === 2 ? d.data[0] : "", 0, -d.r - 5);
ctx.restore();
}

const areas = [];
for (let i of imgs) {
const w = parseInt(50);
const h = parseInt(w * i.img.height / i.img.width);
const j = (Math.random() - 0.5) * 100;
const x = i.x // + j;
const y = i.y // + j;
ctx.save();
ctx.translate(-w/2, -h/2);
ctx.drawImage(i.img, x, y, w, h);
ctx.restore();

const area = {
x, y, w, h,
btn: "",
scene: `ind_${i.n}`, // ¿Cómo definir esto?
tooltip: ""
}
areas.push(area);
}

return {viz: canvas.toDataURL('image/png'), areas}
// return canvas
}
Insert cell
vanillaCanvasCompare = async (data, h1, h2) => {
const filtered = [data.find(d => d.n == h1), data.find(d => d.n == h2)];

const imgs = [];
for (let f of filtered) {
const img = new Image();
img.setAttribute('crossorigin', 'anonymous');
img.src = f.url;
await new Promise(r => { img.onload = () => { r(true) }});
imgs.push({ url: f.url, img: img, n: f.n });
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
ctx.fillStyle = "#313131";
ctx.fillRect(0, 0, width, height);
ctx.font = "12px serif";
ctx.textBaseline = "middle";
ctx.textAlign = "center";

const areas = [];
let count = 0;
for (let i of imgs) {
const m = parseInt(width / 2) * 0.1
const w = parseInt(width / 2) - m;
const h = parseInt(w * i.img.height / i.img.width);
const x = (count * (w + m)) + (w/2) + (m/2);
const y = height/2;
ctx.save();
ctx.translate(-w/2, -h/2);
ctx.drawImage(i.img, x, y, w, h);
ctx.restore();

const area = {
x, y, w, h,
btn: "",
scene: `ind_${i.n}`, // ¿Cómo definir esto?
tooltip: ""
}
areas.push(area);
count++;
}

return {viz: canvas.toDataURL('image/png'), areas}
// return canvas
}
Insert cell
Insert cell
Insert cell
Insert cell
<!-- <style>
#storygeneraldiv {
font-family: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif;
box-sizing: border-box;
background: white;
margin: auto;
max-width: 900px;
border: solid 1px black;
border-radius: 5px;
/* box-shadow: 1px 1px 5px black; */
}

/* .storydiv {
display: grid;
grid-template-columns: 4fr 2fr 1fr;
} */

.storyp {
min-height: 40px;
max-width: 100%;
padding: 0px 10px;
font-size: 18px;
text-align: justify;
}
.storyp h3 {
font-size: 30px;
text-align: left;
max-width: 100%;
margin: 5px 0px;
font-weight: 400;
}
.storyp p{
}

.storyp a{
text-decoration: none;
color:#3fab9b;
font-weight: 300px;
margin: 10px 0px;
}
.storyp a:hover {
text-decoration: none;
color:#FFFFFF;
background:#3fab9b;
}
.storyp img {
width: 50%;
justify-content: center;
margin: auto;
border: 20px;
display: block;
padding: 20px;
}

.meta-container p {
max-width: 100%;
margin: 0px;
padding: 0px;
}

.meta-container {
/* max-width: 100%; */
margin: 20px 0px;
}


.storybutton-container {
text-align: center;
}
.storybutton {
font-family: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif;
padding: 10px;
background:#FFFFFF;
color:#3fab9b;
border: solid 1px black;
cursor: pointer;
/* box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19); */
/* border-radius: 12px; */
margin: 5px 1em 5px 5px;
font-size: 20px;
font-weight: 600;
}
.storybutton:hover {
color:#FFFFFF;
background:#3fab9b;
/* box-shadow: 0 6px 20px 0 rgba(0,0,0,0.19), 0 8px 16px 0 rgba(0,0,0,0.2); */
}

.storyimage-container {
box-sizing: border-box;
position: relative;
padding: 10px;
max-width: 100%;
max-height: 80vh;
margin: auto;
display: flex;
}

.storyimage {
justify-content: center;
max-width: 100%;
max-height: 80vh;
margin: auto;
border: 1px solid black;
border-radius: 5px;
display: block;
}

.storyimage-area {
box-sizing: border-box;
position: absolute;
cursor: pointer;
text-align: center;
font-size: 10px;
border: solid black 1px;
}

.storyimage-area:hover {
background-color:#FFFFFF;
opacity: 0.4;
}
@media screen and (max-device-width: 400px) {
#storygeneraldiv {
max-width:50%;
}
.storyimage {
max-width: 50%;
}
.storyp {
font-size: 7vw;
}
.storybutton {
font-size: 10vw;
}
}
</style> -->
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