mirror of https://github.com/MISP/misp-galaxy
Fix [graph] links + pairwise distance using Fruchterman-Reingold
(https://arxiv.org/pdf/1201.3011.pdf)pull/929/head
parent
ce55d8799d
commit
8f93eb9ed6
|
@ -4,12 +4,20 @@ document$.subscribe(function () {
|
||||||
const NODE_COLOR = "#69b3a2";
|
const NODE_COLOR = "#69b3a2";
|
||||||
const Parent_Node_COLOR = "#ff0000";
|
const Parent_Node_COLOR = "#ff0000";
|
||||||
|
|
||||||
function parseFilteredTable(tf) {
|
|
||||||
|
function parseFilteredTable(tf, allData) {
|
||||||
var data = [];
|
var data = [];
|
||||||
tf.getFilteredData().forEach((row, i) => {
|
tf.getFilteredData().forEach((row, i) => {
|
||||||
data.push({ source: row[1][0], target: row[1][1], level: row[1][2] });
|
sourcePath = allData[row[0] - 2].sourcePath;
|
||||||
}
|
targetPath = allData[row[0] - 2].targetPath;
|
||||||
);
|
data.push({
|
||||||
|
source: row[1][0],
|
||||||
|
sourcePath: sourcePath,
|
||||||
|
target: row[1][1],
|
||||||
|
targetPath: targetPath,
|
||||||
|
level: row[1][2]
|
||||||
|
});
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,31 +26,57 @@ document$.subscribe(function () {
|
||||||
table.querySelectorAll("tr").forEach((row, i) => {
|
table.querySelectorAll("tr").forEach((row, i) => {
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
var cells = row.querySelectorAll("td");
|
var cells = row.querySelectorAll("td");
|
||||||
data.push({ source: cells[0].textContent, target: cells[1].textContent, level: cells[2].textContent });
|
var sourceAnchor = cells[0].querySelector("a");
|
||||||
|
var sourcePath = sourceAnchor ? sourceAnchor.getAttribute("href") : null;
|
||||||
|
var targetAnchor = cells[1].querySelector("a");
|
||||||
|
var targetPath = targetAnchor ? targetAnchor.getAttribute("href") : null;
|
||||||
|
data.push({
|
||||||
|
source: cells[0].textContent,
|
||||||
|
target: cells[1].textContent,
|
||||||
|
sourcePath: sourcePath,
|
||||||
|
targetPath: targetPath,
|
||||||
|
level: cells[2].textContent
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processNewData(newData) {
|
function processNewData(newData) {
|
||||||
|
var nodePaths = {};
|
||||||
|
newData.forEach(d => {
|
||||||
|
nodePaths[d.source] = d.sourcePath || null;
|
||||||
|
nodePaths[d.target] = d.targetPath || null;
|
||||||
|
});
|
||||||
var newNodes = Array.from(new Set(newData.flatMap(d => [d.source, d.target])))
|
var newNodes = Array.from(new Set(newData.flatMap(d => [d.source, d.target])))
|
||||||
.map(id => ({ id }));
|
.map(id => ({
|
||||||
|
id,
|
||||||
|
path: nodePaths[id]
|
||||||
|
}));
|
||||||
|
|
||||||
var newLinks = newData.map(d => ({ source: d.source, target: d.target }));
|
var newLinks = newData.map(d => ({ source: d.source, target: d.target }));
|
||||||
return { newNodes, newLinks };
|
return { newNodes, newLinks };
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterTableAndGraph(tf, simulation) {
|
function filterTableAndGraph(tf, simulation, data) {
|
||||||
var filteredData = parseFilteredTable(tf);
|
var filteredData = parseFilteredTable(tf, data);
|
||||||
var { newNodes, newLinks } = processNewData(filteredData);
|
var { newNodes, newLinks } = processNewData(filteredData);
|
||||||
|
|
||||||
simulation.update({ newNodes: newNodes, newLinks: newLinks });
|
simulation.update({ newNodes: newNodes, newLinks: newLinks });
|
||||||
}
|
}
|
||||||
|
|
||||||
function createForceDirectedGraph(data, elementId) {
|
function createForceDirectedGraph(data, elementId) {
|
||||||
// Extract nodes and links
|
var nodePaths = {};
|
||||||
|
data.forEach(d => {
|
||||||
|
nodePaths[d.source] = d.sourcePath || null;
|
||||||
|
nodePaths[d.target] = d.targetPath || null;
|
||||||
|
});
|
||||||
|
|
||||||
var nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])))
|
var nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])))
|
||||||
.map(id => ({ id }));
|
.map(id => ({
|
||||||
|
id,
|
||||||
|
path: nodePaths[id]
|
||||||
|
}));
|
||||||
|
|
||||||
var links = data.map(d => ({ source: d.source, target: d.target }));
|
var links = data.map(d => ({ source: d.source, target: d.target }));
|
||||||
|
|
||||||
|
@ -51,16 +85,18 @@ document$.subscribe(function () {
|
||||||
.style("opacity", 0);
|
.style("opacity", 0);
|
||||||
|
|
||||||
// Set up the dimensions of the graph
|
// Set up the dimensions of the graph
|
||||||
var width = 1000, height = 1000;
|
var width = 800, height = 1000;
|
||||||
|
|
||||||
var svg = d3.select(elementId).append("svg")
|
var svg = d3.select(elementId).append("svg")
|
||||||
.attr("width", width)
|
.attr("width", width)
|
||||||
.attr("height", height);
|
.attr("height", height);
|
||||||
|
|
||||||
// Create a force simulation
|
// Create a force simulation
|
||||||
|
linkDistance = Math.sqrt((width * height) / nodes.length);
|
||||||
|
|
||||||
var simulation = d3.forceSimulation(nodes)
|
var simulation = d3.forceSimulation(nodes)
|
||||||
.force("link", d3.forceLink(links).id(d => d.id).distance(10))
|
.force("link", d3.forceLink(links).id(d => d.id).distance(linkDistance))
|
||||||
.force("charge", d3.forceManyBody().strength(-20))
|
.force("charge", d3.forceManyBody().strength(-50))
|
||||||
.force("center", d3.forceCenter(width / 2, height / 2))
|
.force("center", d3.forceCenter(width / 2, height / 2))
|
||||||
.alphaDecay(0.02); // A lower value, adjust as needed
|
.alphaDecay(0.02); // A lower value, adjust as needed
|
||||||
|
|
||||||
|
@ -106,6 +142,11 @@ document$.subscribe(function () {
|
||||||
.style("opacity", 0);
|
.style("opacity", 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Apply links on nodes
|
||||||
|
node.on("dblclick", function (event, d) {
|
||||||
|
location.href = d.path;
|
||||||
|
});
|
||||||
|
|
||||||
// Define drag behavior
|
// Define drag behavior
|
||||||
var drag = d3.drag()
|
var drag = d3.drag()
|
||||||
.on("start", dragstarted)
|
.on("start", dragstarted)
|
||||||
|
@ -150,7 +191,6 @@ document$.subscribe(function () {
|
||||||
|
|
||||||
return Object.assign(svg.node(), {
|
return Object.assign(svg.node(), {
|
||||||
update({ newNodes, newLinks }) {
|
update({ newNodes, newLinks }) {
|
||||||
// Process new nodes and maintain the existing ones
|
|
||||||
const oldNodesMap = new Map(node.data().map(d => [d.id, d]));
|
const oldNodesMap = new Map(node.data().map(d => [d.id, d]));
|
||||||
nodes = newNodes.map(d => Object.assign(oldNodesMap.get(d.id) || {}, d));
|
nodes = newNodes.map(d => Object.assign(oldNodesMap.get(d.id) || {}, d));
|
||||||
|
|
||||||
|
@ -189,6 +229,14 @@ document$.subscribe(function () {
|
||||||
.style("opacity", 0);
|
.style("opacity", 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Apply links on nodes
|
||||||
|
node.on("dblclick", function (event, d) {
|
||||||
|
console.log("Node: " + d.id);
|
||||||
|
console.log(d);
|
||||||
|
console.log("Source Path: " + d.sourcePath);
|
||||||
|
location.href = d.path;
|
||||||
|
});
|
||||||
|
|
||||||
// Process new links
|
// Process new links
|
||||||
const oldLinksMap = new Map(link.data().map(d => [`${d.source.id},${d.target.id}`, d]));
|
const oldLinksMap = new Map(link.data().map(d => [`${d.source.id},${d.target.id}`, d]));
|
||||||
links = newLinks.map(d => Object.assign(oldLinksMap.get(`${d.source.id},${d.target.id}`) || {}, d));
|
links = newLinks.map(d => Object.assign(oldLinksMap.get(`${d.source.id},${d.target.id}`) || {}, d));
|
||||||
|
@ -214,7 +262,6 @@ document$.subscribe(function () {
|
||||||
document.querySelectorAll("table").forEach((table, index) => {
|
document.querySelectorAll("table").forEach((table, index) => {
|
||||||
var graphHeader = table.querySelector("th.graph");
|
var graphHeader = table.querySelector("th.graph");
|
||||||
if (graphHeader) {
|
if (graphHeader) {
|
||||||
// Initialize TableFilter for the table
|
|
||||||
var tf = new TableFilter(table, {
|
var tf = new TableFilter(table, {
|
||||||
base_path: "../../../../01_attachements/modules/tablefilter/",
|
base_path: "../../../../01_attachements/modules/tablefilter/",
|
||||||
highlight_keywords: true,
|
highlight_keywords: true,
|
||||||
|
@ -259,7 +306,7 @@ document$.subscribe(function () {
|
||||||
|
|
||||||
// Listen for table filtering events
|
// Listen for table filtering events
|
||||||
tf.emitter.on(['after-filtering'], function () {
|
tf.emitter.on(['after-filtering'], function () {
|
||||||
filterTableAndGraph(tf, simulation);
|
filterTableAndGraph(tf, simulation, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue