Public
Edited
Feb 28
Insert cell
titulo = html`
<style>
.titulo {
font-family: 'Inter', sans-serif;
}
</style>

<div class="titulo">
${md`# Mapas del texto sobre el Perro de Roulin`}
</div>
`

Insert cell
pathClima = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSLlPx4wNTSYqqWO_bHzl_m1sGfLo4jJuAwx5F7OwWHsxRjJzvyUL1RFcIAv4eApAHA3J7DAueaK_uw/pub?output=csv"
Insert cell
pathPerros = "https://docs.google.com/spreadsheets/d/e/2PACX-1vRVBt_mVyXz3-Sfv1vzmST12wq27NK90CoJAYf3nC4Rcr50U4jDcqqrkHo4crkpYwou04WSIlzKMvJw/pub?output=csv"
Insert cell
imgUrl = "https://lh3.googleusercontent.com/d/"
Insert cell
csvString = fetch(pathClima).then((response) => response.text())
Insert cell
csvPerros = fetch(pathPerros).then((response) => response.text())
Insert cell
dataClima = d3.csvParse(csvString, d3.autoType)
Insert cell
dataPerros = d3.csvParse(csvPerros, d3.autoType).map(e => ({...e, url: imgUrl + e.id, otros: e.otros === "False" ? false : e.otros == "True" ? true : e.otros}))
Insert cell
altitudes = {
// Crear el contenedor general con FLEXBOX usando `html`
const container = html`<div style="
display: flex;
gap: 20px;
align-items: center;
justify-content: center;
width: 100%;
max-width: 900px;
background-color: white;
"></div>`;

// Crear un div para el gráfico a la izquierda
const chartContainer = html`<div style="
width: 60%;
min-width: 700px;
"></div>`;

// Crear un div para la tarjeta a la derecha
const infoContainer = html`<div style="
width: 30%;
min-width: 250px;
border: 1px solid #ccc;
border-radius: 8px;
background: white;
box-shadow: 2px 2px 10px rgba(0,0,0,0.2);
padding: 10px;
font-family: 'Inter', sans-serif;
"></div>`;

// Agregar los divs al contenedor general
container.append(chartContainer, infoContainer);

// Dimensiones del gráfico
const width = 700; // Espacio para la gráfica
const height = 500;
const margin = { top: 40, right: 30, bottom: 50, left: 60 };

// Crear el SVG dentro del `chartContainer`
const svg = d3.select(chartContainer).append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

// Crear escalas
const xScale = d3.scaleBand()
.domain(dataClima.map(d => d.Lugar))
.range([margin.left, width - margin.right - 50])
.padding(0.4);

const yScale = d3.scaleLinear()
.domain([0, d3.max(dataClima, d => d["Altitud (msnm)"])])
.nice()
.range([height - margin.bottom, margin.top]);

// Agregar ejes
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale));

svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale));

// SELECCIÓN DEL PUNTO POR DEFECTO
const selectedPoint = dataClima[0]; // Seleccionamos el primer punto
let selectedLugar = selectedPoint.Lugar;

// Crear elementos dentro del `infoContainer`
const imageElement = d3.select(infoContainer).append("img")
.attr("width", "100%")
.style("object-fit", "contain")
.style("display", "none")
.style("margin-bottom", "5px");

const textElement = d3.select(infoContainer).append("div")
.style("font-size", "14px")
.style("line-height", "1.4");

// 🔹 Función para actualizar la tarjeta con la información del punto seleccionado
function updateCard(lugar) {
const selectedData = dataClima.find(d => d.Lugar === lugar);
if (!selectedData) return;

const matchingImages = dataPerros.filter(p => p.lugar === lugar);
const imageUrl = matchingImages.length > 0 ? matchingImages[0].url : null;
const obra = matchingImages.length > 0 ? matchingImages[0].obras : "Sin imagen registrada";
const autor = matchingImages.length > 0 ? matchingImages[0].autor : "";
const fecha = matchingImages.length > 0 ? matchingImages[0].fecha : "";
const fuente = matchingImages.length > 0 ? matchingImages[0].fuente : "";

// Actualizar contenido de la tarjeta
textElement.html(`
<strong>${selectedData.Lugar}</strong><br>
Altitud: ${selectedData["Altitud (msnm)"]} msnm<br>
Clima: ${selectedData.Clima}<br>
<hr>
<strong>Obra:</strong> ${obra} <br>
<strong>Autor:</strong> ${autor} <br>
<strong>Fecha:</strong> ${fecha} <br>
<strong>Fuente:</strong> ${fuente} <br>
`);

if (imageUrl) {
imageElement.attr("src", imageUrl).style("display", "block");
} else {
imageElement.style("display", "none");
}
}

// Inicializar la tarjeta con el primer punto
updateCard(selectedLugar);

// Puntos en la gráfica
svg.selectAll("circle")
.data(dataClima)
.enter()
.append("circle")
.attr("cx", d => xScale(d.Lugar) + xScale.bandwidth() / 2)
.attr("cy", d => yScale(d["Altitud (msnm)"]))
.attr("r", 6)
.attr("fill", d => (d.Lugar === selectedLugar ? "orange" : "steelblue")) // Resalta el seleccionado
.style("cursor", "pointer")
.on("mouseover", function(event, d) {
d3.select(this).transition().duration(200).attr("r", 8);
})
.on("mouseout", function(event, d) {
d3.select(this).transition().duration(200).attr("r", 6);
})
.on("click", function(event, d) {
console.log(`🟢 Click en: ${d.Lugar}`); // Debug en Observable

// Actualizar el punto seleccionado
selectedLugar = d.Lugar;
updateCard(selectedLugar);

// Actualizar colores de los puntos
svg.selectAll("circle")
.attr("fill", p => (p.Lugar === selectedLugar ? "orange" : "steelblue"));
});

return container;
}

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