Revision 9e94b1c651a3ba27934bc790271261099fb360ad authored by Laura Garrison on 10 June 2024, 08:28:14 UTC, committed by Laura Garrison on 10 June 2024, 08:28:14 UTC
1 parent 9bef561
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="biomed_reset.css">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<div id="main">
<header>Visual Conceptual Metaphor Explorer</header>
<div id="buttons-wrapper">
<a href="/BIOMED" style="background-color: #019488">Biomedicine</a>
<a href="/CLIMATE" style="background-color: #3fccbf">Climate</a>
<a href="/SPACE" style="background-color: #7accc4">Space</a>
<a href="/ANTHRO" style="background-color: #a7c7c4">Anthropology</a>
</div>
<div id="columns">
<div style="display: flex; flex-direction:column;">
<div class="row header">Distribution</div>
<div class="row">
<span><b>Domain:</b> </span><span id="primary-domain-title"></span>
<span><b>Secondary Domain(s):</b> </span><span id="secondary-domain-title"></span>
</div>
<div id="scatterplot">
<svg id="scatterplot-svg">
</svg>
</div>
</div>
<div id="preview">
<div class="row header">General Information</div>
<div class="row row-textual"><span><b>Name:</b> </span><span id="name-title"></span></div>
<div class="row row-textual"><span><b>Author:</b> </span><span id="author-title"></span></div>
<div class="row row-textual"><span><b>Link:</b> </span><a href="" id="link-title"></a></div>
<div class="row" style="min-height: 0;"><img id="preview-img" src="" /></div>
</div>
<div id="piechart">
<div class="row header">Conceptual metaphor analysis</div>
<div class="row row-textual"><span><b>Supporting textual metaphor:</b> </span><span id="text-metaphor"></span></div>
<div class="row row-textual"><span><b>Type:</b> </span><span id="type-metaphor"></span></div>
</div>
</div>
</div>
<script>
window.onload = function () {
function PieChart(data, {
name = ([x]) => x, // given d in data, returns the (ordinal) label
value = ([, y]) => y, // given d in data, returns the (quantitative) value
title, // given d in data, returns the title text
width = 640, // outer width, in pixels
height = 400, // outer height, in pixels
innerRadius = 0, // inner radius of pie, in pixels (non-zero for donut)
outerRadius = Math.min(width, height) / 2, // outer radius of pie, in pixels
labelRadius = (innerRadius * 0.2 + outerRadius * 0.8), // center radius of labels
format = ",", // a format specifier for values (in the label)
names, // array of names (the domain of the color scale)
colors, // array of colors for names
stroke = innerRadius > 0 ? "none" : "white", // stroke separating widths
strokeWidth = 1, // width of stroke separating wedges
strokeLinejoin = "round", // line join of stroke separating wedges
padAngle = stroke === "none" ? 1 / outerRadius : 0, // angular separation between wedges, in radians
} = {}) {
// Compute values.
const N = d3.map(data, name);
const V = d3.map(data, value);
const I = d3.range(N.length).filter(i => !isNaN(V[i]));
// Unique the names.
if (names === undefined) names = N;
names = new d3.InternSet(names);
// Chose a default color scheme based on cardinality.
if (colors === undefined) colors = d3.schemeSpectral[names.size];
if (colors === undefined) colors = d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), name.size);
// Construct scales.
const color = d3.scaleOrdinal(names, colors);
// Compute titles.
if (title === undefined) {
const formatValue = d3.format(format);
title = i => `${N[i]}\n${formatValue(V[i])}`;
} else {
const O = d3.map(data, d => d);
const T = title;
title = i => T(O[i], i, data);
}
// Construct arcs.
const arcs = d3.pie().padAngle(padAngle).sort(null).value(i => V[i])(I);
const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius);
const arcLabel = d3.arc().innerRadius(labelRadius).outerRadius(labelRadius);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
svg.append("g")
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("stroke-linejoin", strokeLinejoin)
.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => color(N[d.data]))
.attr("d", arc)
.append("title")
.text(d => title(d.data));
svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${arcLabel.centroid(d)})`)
.selectAll("tspan")
.data(d => {
const lines = `${title(d.data)}`.split(/\n/);
return (d.endAngle - d.startAngle) > 0.25 ? lines : lines.slice(0, 1);
})
.join("tspan")
.attr("x", 0)
.attr("y", (_, i) => `${i * 1.1}em`)
.attr("font-weight", (_, i) => i ? null : "bold")
.text(d => d);
return Object.assign(svg.node(), { scales: { color } });
}
// append the svg object to the body of the page
const svg_column = document.querySelector("#scatterplot");
/* Globals */
let data = undefined;
let rectangles = undefined;
let chosenImg = '';
let piechartValues = undefined;
const regenerate = () => {
while (svg_column.firstChild) {
svg_column.removeChild(svg_column.lastChild);
}
d3.select("#scatterplot").append("svg");
const svg = d3.select("#scatterplot > svg");
const margin = { top: 20, right: 10, bottom: 45, left: 80 };
const width = svg_column.clientWidth - margin.left - margin.right;
const height = svg_column.clientHeight - margin.top - margin.bottom;
const svg_viewport = svg
.attr("width", svg_column.clientWidth)
.attr("height", svg_column.clientHeight)
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// Add X axis
const x = d3.scaleLinear()
.domain([0, 90])
.range([0, width]);
svg_viewport.append("g")
.attr("transform", `translate(0, ${height})`)
.call(
d3.axisBottom(x)
.tickValues([10, 20, 30, 40, 50, 60, 70, 80, 90])
.tickFormat((v) => {
// you can completley override anything in this formatting
// for example
if (v <= 10) return 'nanosec';
if (v <= 20) return 'sec';
if (v <= 30) return 'min';
if (v <= 40) return 'hours';
if (v <= 50) return 'days';
if (v <= 60) return 'weeks';
if (v <= 70) return 'months';
if (v <= 80) return 'years';
//if (v <= 10) return '' + v + '⁻³ \n ns'
//if (v <= 20) return '' + v + '\n s';
//if (v <= 30) return '' + v + '\n m';
//return v + '\n s';
})
);
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 0)
.attr("x", -margin.top)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("SPATIAL COVERAGE");
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width + margin.left + margin.right)
.attr("y", height + 55)
.text("TEMPORAL COVERAGE");
// Add Y axis
const y = d3.scaleLinear()
.domain([0, 100])
.range([height, 0]);
svg_viewport.append("g")
.call(
d3.axisLeft(y)
.tickValues([10, 20, 30, 40, 50, 60, 70, 80, 90])
.tickFormat((v) => {
switch (v) {
case 10: return 'molecules';
case 20: return 'viruses';
case 30: return 'cells';
case 40: return 'bacteria';
case 50: return 'tissues';
case 60: return 'organs';
case 70: return 'organ systems';
case 80: return 'organism';
case 90: return 'population';
}
})
);
if (!data || !rectangles) return;
const setPiechart = (values) => {
if (!values) return;
const piechart_column = document.querySelector("#piechart");
document.querySelectorAll('#piechart svg').forEach(e => e.remove());
let chart = PieChart(values, {
name: d => d.type,
value: d => d.percentage,
width: piechart_column.clientWidth,
height: piechart_column.clientHeight,
names: ['Structural', 'Ontological', 'Orientational', 'Imagistic']
});
piechart_column.insertBefore(chart, document.querySelector('#piechart .row:nth-child(2)'));
};
setPiechart(piechartValues);
let Tooltip = d3.select("#scatterplot")
.append("div")
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
.style("z-index", "999")
.style("position", "fixed")
.attr('pointer-events', 'none');
document.querySelectorAll('rect').forEach(e => e.remove());
for (let i = 0; i < rectangles.length; i++) {
const positionX = parseFloat(rectangles[i].xfrom);
const positionY = parseFloat(rectangles[i].yfrom);
const positionXTo = parseFloat(rectangles[i].xto);
const positionYTo = parseFloat(rectangles[i].yto);
console.log(positionX, positionXTo);
console.log(positionX < positionXTo);
console.log(positionY < positionYTo);
console.log(height - Math.abs(y(positionYTo - positionY)));
svg_viewport
.append('rect')
.attr('x', x(positionX))
.attr('y', y(positionYTo))
.attr('width', x(positionXTo) - x(positionX))
.attr('height', height - Math.abs(y(positionYTo - positionY)))
//.attr('stroke', 'black')
.attr('fill', rectangles[i].color)
//.attr('opacity', 0.2)
.attr('pointer-events', 'none');
}
// Points Tooltip
for (let i = 0; i < data.length; i++) {
const positionX = data[i].x.split('-')[0];
const positionY = data[i].y.split('-')[0];
const positionXTo = data[i].x.split('-')[1];
const positionYTo = data[i].y.split('-')[1];
svg_viewport
.append('circle')
.attr('cx', x(positionX))
.attr('cy', y(positionY))
.attr('r', 10.0)
.attr('class', 'circle-base')
.on("mouseover", () => {
Tooltip.style("opacity", 1);
Tooltop.style("display", "block")
.attr('pointer-events', 'none');
})
.on("mousemove", (event) => {
Tooltip
.html(data[i].name)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 20) + "px")
.style("display", "block");
})
.on("mouseleave", () => {
Tooltip.style("opacity", 0);
Tooltip.style("display", "none")
.attr('pointer-events', 'none');
})
.on('click', (event) => {
// Change state of each circled & set current to different CSS class
document.querySelectorAll("svg circle").forEach((node) => {
node.classList = ['circle-base'];
});
document.querySelectorAll("svg circle")[i].classList = ['circle-click'];
// Set Image OR copyright notice and link
document.querySelector("#preview-img").setAttribute('src', data[i].imageURL);
// Set Domains, authors, etc...
document.querySelector("#primary-domain-title").innerHTML = data[i].primarydomain;
document.querySelector("#secondary-domain-title").innerHTML = data[i].secondarydomain;
document.querySelector("#name-title").innerHTML = data[i].name;
document.querySelector("#author-title").innerHTML = data[i].author;
document.querySelector("#link-title").innerHTML = data[i].url;
document.querySelector("#link-title").href = data[i].url;
document.querySelector("#type-metaphor").innerHTML = data[i].typeOfMetaphor;
document.querySelector("#text-metaphor").innerHTML = data[i].textMetaphor;
console.log(data[i]);
// Configure piechart
const types = data[i].type.split('|');
const percentages = data[i].percentages.split('|');
let values = [];
for (let i = 0; i < types.length; i++) {
if (percentages[i] > 0) {
values.push({ type: types[i], percentage: percentages[i] })
}
}
piechartValues = values;
setPiechart(values);
});
}
};
window.onresize = () => {
regenerate();
};
regenerate();
d3.csv("biomed.csv").then((d) => {
data = d;
regenerate();
});
d3.csv("biomed_rectangles.csv").then((d) => {
rectangles = d;
regenerate();
});
};
</script>
</body>
</html>
Computing file changes ...