Public
Edited
Apr 2, 2023
1 fork
Insert cell
Insert cell
Insert cell
chart = {
const root = d3.hierarchy(finalJson);

root.x0 = dy / 2;
root.y0 = 0;
root.descendants().forEach((d, i) => {
d.id = i;
d._children = d.children;
if (d.depth && d.data.name.length !== 7) d.children = null;
});


const svg = d3.create("svg")
.attr("viewBox", [-margin.left, -margin.top, width, dx])
.style("font", "10px sans-serif")
.style("user-select", "none");

const strokeWidthBase = 20;
const gLink = svg.append("g")
.attr("fill", "none")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5);

const colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, 1]);

const gNode = svg.append("g")
.attr("cursor", "pointer")
.attr("pointer-events", "all");
function selectImage(node) {
// console.log(node.data);
let imgTopic = getImgTid(node.data.name);
if (imgTopic !== null) {
// console.log(imgTopic);
let idxofImage = getIndexOfObjectWithTopic(imgTopic);
dispatch.call("circle-clicked", null, idxofImage);
// console.log(`Hola at caller ${idxofImage}`);
// myVariable is not null, do something
}
// return imgTopic;
}

function update(source) {
const duration = d3.event && d3.event.altKey ? 2500 : 250;
const nodes = root.descendants().reverse();
const links = root.links();



// Compute the new tree layout.
tree(root);

let left = root;
let right = root;
root.eachBefore(node => {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
});

const height = right.x - left.x + margin.top + margin.bottom;

const transition = svg.transition()
.duration(duration)
.attr("viewBox", [-margin.left, left.x - margin.top, width, height])
.tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));

let imageTopicClicked;
// Update the nodes…
const node = gNode.selectAll("g")
.data(nodes, d => d.id);

// Enter any new nodes at the parent's previous position.
const nodeEnter = node.enter().append("g")
.attr("transform", d => `translate(${source.y0},${source.x0})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
.on("click", (event, d) => {
d.children = d.children ? null : d._children;
update(d);
selectImage(d)
})
// Add mouseover and mouseout events
.on("mouseover", function (event, d) {
// Get all ancestors of the node
const ancestors = d.ancestors();
// Select all links on the path to the node and make them thicker
gLink.selectAll("path")
.filter(link => ancestors.includes(link.source) && ancestors.includes(link.target))
.attr("stroke-width", 6)
.attr("stroke", "red")
.attr("stroke-opacity", 1);
})
.on("mouseout", function (event, d) {
// Reset the stroke width of all links
gLink.selectAll("path")
.attr("stroke-width", d => d.target.data.similarity ?
(d.target.data.similarity * strokeWidthBase) : 1.5)
.attr("stroke", d => d.target.data.similarity ?
colorScale(d.target.data.similarity) : "#ccc")
.attr("stroke-opacity", 0.4);
});

// console.log(`After ${imageTopicClicked}`);
nodeEnter.append("circle")
.attr("r", 2.5)
.attr("fill", d => d._children ? "#555" : "#999")
.attr("stroke-width", 10);

nodeEnter.append("text")
.attr("dy", "0.31em")
.attr("x", d => d._children ? -6 : 6)
.attr("text-anchor", d => d._children ? "end" : "start")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.attr("stroke", "white");
// Add title element to show labels information
nodeEnter.append("title")
.text(d => {
let labelStr = d.data.labels ? d.data.labels.join(", ") : "";
let simStr = d.data.similarity ? "\nSimilarity: " + d.data.similarity : "";
return labelStr + simStr;
});


// Transition nodes to their new position.
const nodeUpdate = node.merge(nodeEnter).transition(transition)
.attr("transform", d => `translate(${d.y},${d.x})`)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);

// Transition exiting nodes to the parent's new position.
const nodeExit = node.exit().transition(transition).remove()
.attr("transform", d => `translate(${source.y},${source.x})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);

const diagonal = d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x);
// Update the links…
const link = gLink.selectAll("path")
.data(links, d => d.target.id);

// Enter any new links at the parent's previous position.
const linkEnter = link.enter().append("path")
.attr("d", d => {
const o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
}).attr("stroke", d => d.target.data.similarity ? colorScale(d.target.data.similarity) : "#ccc")
.attr("stroke-width", d => d.target.data.similarity ? (d.target.data.similarity * strokeWidthBase) : 1.5)
.attr("fill", "none")
.attr("stroke-linejoin", "round");

// Transition links to their new position.
link.merge(linkEnter).transition(transition)
.attr("d", diagonal)
.attr("stroke", d => d.target.data.similarity ?
colorScale(d.target.data.similarity) : "#ccc")
.attr("stroke-width", d => d.target.data.similarity ? (d.target.data.similarity * strokeWidthBase) : 1.5);


// Transition exiting nodes to the parent's new position.
link.exit().transition(transition).remove()
.attr("d", d => {
const o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
});

// Stash the old positions for transition.
root.eachBefore(d => {
d.x0 = d.x;
d.y0 = d.y;
});
}

update(root);

return svg.node();
}
Insert cell
Insert cell
Insert cell
dispatch = d3.dispatch("circle-clicked");

Insert cell
diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x)
Insert cell
tree = d3.tree().nodeSize([dx, dy])
Insert cell
// data = FileAttachment("flare-2.json").json()
Insert cell
// groupedData.get(1)
Insert cell
groupedData = d3.group(imageCsv, d => d.topic);
Insert cell
filteredImageTopics = new Map([...groupedData].filter(([key, value]) => uniqueImageTopics.includes(key)));
Insert cell
randomImageByTopicsMap = new Map(
Array.from(filteredImageTopics.entries()).map(([key, values]) => {
if (values.length < imagePerTopics) {
return [key, values];
}
return [key, d3.shuffle(values).slice(0, imagePerTopics)];
})
);
Insert cell
flattenedRandomSelection = Array.from(randomImageByTopicsMap, ([key, values]) =>
values.map(value => ({ ...value }))
).flatMap(array => array);
Insert cell
// flattenedRandomSelection[0].img_url
Insert cell
// randomImageByTopics = JSON.parse(JSON.stringify(Object.fromEntries(randomImageByTopicsMap)));

Insert cell
imageCsv = d3.csv(
"https://raw.githubusercontent.com/AI-EnabledSoftwareEngineering-AISE/RE4VIS/main/data/images_df.csv",
d3.autoType
)
Insert cell
Insert cell
// `${JSON.stringify(uniqueImageTopics)} end of file`
Insert cell
uniqueImageTopics = Array.from(new Set(filteredData.flatMap(obj => obj.children.map(child => child.img_tid))));

Insert cell
filteredData = sampleData.children.map(child => {
if (child.children) {
let filteredChildren = child.children.filter(grandchild => {
return grandchild.similarity && grandchild.similarity >= threshold;
});
return {...child, children: filteredChildren};
} else {
return child;
}
});
Insert cell
sampleData = d3.json("https://raw.githubusercontent.com/AI-EnabledSoftwareEngineering-AISE/RE4VIS/main/data/flare_v1.json")
Insert cell
avgSimilarity = sampleData.children.map(entry => {
const sum = entry.children.reduce((acc, child) => acc + child.similarity, 0);
const avg = sum / entry.children.length || 0; // to avoid division by zero
return { id: entry.id, avgSimilarity: avg };
});
Insert cell
avgSimilarities = avgSimilarity.map(d => d.avgSimilarity).sort()
Insert cell
d3.quantileSorted(avgSimilarities, 0.5)
Insert cell
similarities = sampleData.children.flatMap(d => d.children.map(c => c.similarity)).sort()
Insert cell
d3.quantileSorted(similarities, 0.25)
Insert cell
maxSimilarity = d3.max(sampleData.children, d => d3.max(d.children, child => child.similarity));

Insert cell
minSimilarity = d3.min(sampleData.children, d => d3.min(d.children, child => child.similarity));
Insert cell
stepValue = (maxSimilarity - minSimilarity) / 10
Insert cell
initialValue = minSimilarity + 7* stepValue
Insert cell
// Collect the similarity values into an array
// similarities = [];
Insert cell
// function traverseData(data) {
// if (data.hasOwnProperty('similarity')) {
// similarities.push(data.similarity);
// }
// if (data.hasOwnProperty('children')) {
// for (let child of data.children) {
// traverseData(child);
// }
// }
// }
Insert cell
// traverseData(sampleData);
Insert cell
// similarities
Insert cell
// maxSimilarity = Math.max(...similarities);
Insert cell
// minSimilarity = Math.min(...similarities);
Insert cell
finalJson = {
return {
name:"",
children:filteredData
}
}
Insert cell
getIndexOfObjectWithTopic(7)
Insert cell
function getIndexOfObjectWithTopic(topic) {
for (let i = 0; i < flattenedRandomSelection.length; i++) {
if (flattenedRandomSelection[i].topic === topic) {
return i;
}
}
return -1; // topic not found in any object in data
}
Insert cell
getImgTid(1)
Insert cell
function getImgTid(name) {
let maxSim = -Infinity;
let imgTid = null;
if (name === '') {
// Search for entry with the highest similarity
finalJson.children.forEach(child => {
child.children.forEach(grandchild => {
if (grandchild.similarity > maxSim) {
maxSim = grandchild.similarity;
imgTid = grandchild.img_tid;
}
});
});
} else {
const foundGrandchild = finalJson.children.flatMap(child => child.children).find(grandchild => grandchild.id == name);
if (foundGrandchild) {
imgTid = foundGrandchild.img_tid;
} else {
const foundChild = finalJson.children.find(child => child.name == name);
if (foundChild) {
foundChild.children.forEach(grandchild => {
if (grandchild.similarity > maxSim) {
maxSim = grandchild.similarity;
imgTid = grandchild.img_tid;
}
});
}
}
}
return imgTid;
}
Insert cell
getImgTid(71)
Insert cell
dx = 10
Insert cell
dy = width / 6
Insert cell
margin = ({top: 10, right: 120, bottom: 10, left: 40})
Insert cell
d3 = require("d3@6")
Insert cell
// import {quantileSorted} from "d3-array"
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