|
|
|
|
|
function forceEdgeColors() { |
|
|
if (!sigmaInstance) return; |
|
|
|
|
|
|
|
|
let nodeInfo = {}; |
|
|
let nodeTypeCount = { paper: 0, author: 0, organization: 0, unknown: 0 }; |
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
|
|
|
if (nodeTypeCount[node.type || 'unknown'] < 3) { |
|
|
console.log(`Node ${node.id}: type=${node.type}, color=${node.color}`); |
|
|
} |
|
|
nodeTypeCount[node.type || 'unknown']++; |
|
|
|
|
|
nodeInfo[node.id] = { |
|
|
color: node.color || '#aaa', |
|
|
type: node.type || 'unknown' |
|
|
}; |
|
|
}); |
|
|
|
|
|
console.log("Node type counts:", nodeTypeCount); |
|
|
|
|
|
let edgeTypeCount = { |
|
|
paperAuthor: 0, |
|
|
paperOrg: 0, |
|
|
other: 0 |
|
|
}; |
|
|
|
|
|
|
|
|
let edgeColors = {}; |
|
|
sigmaInstance.iterEdges(function(edge) { |
|
|
const sourceNode = nodeInfo[edge.source]; |
|
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
|
|
if (!sourceNode || !targetNode) { |
|
|
console.log(`Missing node info for edge ${edge.id}: source=${edge.source}, target=${edge.target}`); |
|
|
return; |
|
|
} |
|
|
|
|
|
let newColor; |
|
|
let edgeType = ''; |
|
|
|
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
|
edgeTypeCount.paperAuthor++; |
|
|
|
|
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
|
newColor = authorNode.color; |
|
|
edgeType = 'paper-author'; |
|
|
} |
|
|
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
|
edgeTypeCount.paperOrg++; |
|
|
|
|
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
|
newColor = paperNode.color; |
|
|
edgeType = 'paper-org'; |
|
|
} |
|
|
|
|
|
else { |
|
|
edgeTypeCount.other++; |
|
|
|
|
|
newColor = sourceNode.color; |
|
|
edgeType = 'other'; |
|
|
} |
|
|
|
|
|
|
|
|
edgeColors[edge.id] = { |
|
|
color: newColor, |
|
|
type: edgeType |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
|
if (edgeColors[edge.id]) { |
|
|
const colorInfo = edgeColors[edge.id]; |
|
|
edge.color = colorInfo.color; |
|
|
|
|
|
|
|
|
if (edgeTypeCount[colorInfo.type === 'paper-author' ? 'paperAuthor' : |
|
|
colorInfo.type === 'paper-org' ? 'paperOrg' : 'other'] <= 3) { |
|
|
console.log(`Edge ${edge.id} (${colorInfo.type}): color=${colorInfo.color}`); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
console.log("Edge type counts:", edgeTypeCount); |
|
|
|
|
|
|
|
|
sigmaInstance.draw(); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
sigmaInstance.refresh(); |
|
|
}, 50); |
|
|
} |
|
|
|
|
|
|
|
|
function logNodeColors() { |
|
|
if (!sigmaInstance) return; |
|
|
|
|
|
console.log("Current node colors by type:"); |
|
|
let typeColors = {}; |
|
|
|
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
if (node.type) { |
|
|
if (!typeColors[node.type]) { |
|
|
typeColors[node.type] = { |
|
|
configColor: (config.nodeTypes && config.nodeTypes[node.type]) |
|
|
? config.nodeTypes[node.type].color |
|
|
: (nodeTypes[node.type] ? nodeTypes[node.type].color : 'none'), |
|
|
nodeCount: 0, |
|
|
colorCounts: {} |
|
|
}; |
|
|
} |
|
|
|
|
|
typeColors[node.type].nodeCount++; |
|
|
|
|
|
if (!typeColors[node.type].colorCounts[node.color]) { |
|
|
typeColors[node.type].colorCounts[node.color] = 0; |
|
|
} |
|
|
typeColors[node.type].colorCounts[node.color]++; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
for (const type in typeColors) { |
|
|
console.log(`Node type: ${type}`); |
|
|
console.log(` Config color: ${typeColors[type].configColor}`); |
|
|
console.log(` Node count: ${typeColors[type].nodeCount}`); |
|
|
console.log(' Actual colors used:'); |
|
|
for (const color in typeColors[type].colorCounts) { |
|
|
console.log(` ${color}: ${typeColors[type].colorCounts[color]} nodes`); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function forceNodeColorsFromConfig() { |
|
|
console.log("Forcibly applying node colors from config settings"); |
|
|
|
|
|
if (!sigmaInstance) { |
|
|
console.error("Cannot apply node colors, sigma instance not initialized"); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
let configNodeTypes = config.nodeTypes || nodeTypes; |
|
|
|
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
if (node.type && configNodeTypes[node.type]) { |
|
|
|
|
|
const configColor = configNodeTypes[node.type].color; |
|
|
console.log(`Setting node ${node.id} color to ${configColor} based on type ${node.type}`); |
|
|
node.color = configColor; |
|
|
|
|
|
|
|
|
if (configNodeTypes[node.type].size) { |
|
|
node.size = configNodeTypes[node.type].size; |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
|
|
|
setTimeout(function() { |
|
|
forceEdgeColors(); |
|
|
|
|
|
logNodeColors(); |
|
|
}, 100); |
|
|
|
|
|
console.log("Node colors have been forcibly applied from config"); |
|
|
} |
|
|
|
|
|
|
|
|
function debugPaperOrgEdges() { |
|
|
console.log("Debugging paper-organization edge coloring"); |
|
|
|
|
|
|
|
|
let nodeInfo = {}; |
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
nodeInfo[node.id] = { |
|
|
color: node.color || '#aaa', |
|
|
type: node.type || 'unknown', |
|
|
label: node.label || node.id |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
let paperOrgEdges = []; |
|
|
sigmaInstance.iterEdges(function(edge) { |
|
|
const sourceNode = nodeInfo[edge.source]; |
|
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
|
|
|
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
|
const orgNode = sourceNode.type === 'organization' ? sourceNode : targetNode; |
|
|
|
|
|
paperOrgEdges.push({ |
|
|
edgeId: edge.id, |
|
|
edgeColor: edge.color, |
|
|
paperNodeId: paperNode.id, |
|
|
paperNodeLabel: paperNode.label, |
|
|
paperNodeColor: paperNode.color, |
|
|
orgNodeId: orgNode.id, |
|
|
orgNodeLabel: orgNode.label, |
|
|
orgNodeColor: orgNode.color, |
|
|
edgeSourceIsOrg: sourceNode.type === 'organization' |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
console.log(`Found ${paperOrgEdges.length} paper-organization edges`); |
|
|
console.log("Sample of paper-organization edges:", paperOrgEdges.slice(0, 5)); |
|
|
|
|
|
|
|
|
let correctlyColored = 0; |
|
|
let incorrectlyColored = 0; |
|
|
|
|
|
for (const edge of paperOrgEdges) { |
|
|
if (edge.edgeColor === edge.paperNodeColor) { |
|
|
correctlyColored++; |
|
|
} else { |
|
|
incorrectlyColored++; |
|
|
console.log(`Incorrectly colored edge: ${edge.edgeId} (color: ${edge.edgeColor}, should be: ${edge.paperNodeColor})`); |
|
|
} |
|
|
} |
|
|
|
|
|
console.log(`Edge coloring stats: ${correctlyColored} correct, ${incorrectlyColored} incorrect`); |
|
|
|
|
|
return paperOrgEdges; |
|
|
} |
|
|
|
|
|
|
|
|
function initializeGraph(data) { |
|
|
graph = data; |
|
|
console.log("Initializing graph with nodes:", graph.nodes.length, "edges:", graph.edges.length); |
|
|
|
|
|
try { |
|
|
|
|
|
sigmaInstance = sigma.init(document.getElementById('sigma-canvas')); |
|
|
|
|
|
|
|
|
sigmaInstance.mouseProperties({ |
|
|
maxRatio: 32, |
|
|
minRatio: 0.5, |
|
|
mouseEnabled: true, |
|
|
mouseInertia: 0.8 |
|
|
}); |
|
|
|
|
|
|
|
|
for (let i = 0; i < graph.nodes.length; i++) { |
|
|
let node = graph.nodes[i]; |
|
|
|
|
|
|
|
|
if (!node.type && node.id) { |
|
|
const idParts = node.id.split('_'); |
|
|
if (idParts.length >= 2) { |
|
|
node.type = idParts[0]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let nodeColor; |
|
|
if (node.type && config.nodeTypes && config.nodeTypes[node.type]) { |
|
|
nodeColor = config.nodeTypes[node.type].color; |
|
|
} else if (node.type && nodeTypes[node.type]) { |
|
|
nodeColor = nodeTypes[node.type].color; |
|
|
} else { |
|
|
nodeColor = '#666'; |
|
|
} |
|
|
|
|
|
|
|
|
if (i < 5) { |
|
|
console.log(`Adding node: id=${node.id}, type=${node.type}, color=${nodeColor}`); |
|
|
} |
|
|
|
|
|
sigmaInstance.addNode(node.id, { |
|
|
label: node.label || node.id, |
|
|
x: node.x || Math.random() * 100, |
|
|
y: node.y || Math.random() * 100, |
|
|
size: node.size || 1, |
|
|
color: nodeColor, |
|
|
type: node.type |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
for (let i = 0; i < graph.edges.length; i++) { |
|
|
let edge = graph.edges[i]; |
|
|
sigmaInstance.addEdge(edge.id, edge.source, edge.target, { |
|
|
size: edge.size || 1 |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
sigmaInstance.drawingProperties({ |
|
|
labelThreshold: config.sigma?.drawingProperties?.labelThreshold || 8, |
|
|
defaultLabelColor: config.sigma?.drawingProperties?.defaultLabelColor || '#000', |
|
|
defaultLabelSize: config.sigma?.drawingProperties?.defaultLabelSize || 14, |
|
|
defaultEdgeType: config.sigma?.drawingProperties?.defaultEdgeType || 'curve', |
|
|
defaultHoverLabelBGColor: config.sigma?.drawingProperties?.defaultHoverLabelBGColor || '#002147', |
|
|
defaultLabelHoverColor: config.sigma?.drawingProperties?.defaultLabelHoverColor || '#fff', |
|
|
borderSize: 2, |
|
|
nodeBorderColor: '#fff', |
|
|
defaultNodeBorderColor: '#fff', |
|
|
defaultNodeHoverColor: '#fff', |
|
|
edgeColor: 'source', |
|
|
defaultEdgeColor: '#ccc', |
|
|
minEdgeSize: 0.5, |
|
|
maxEdgeSize: 2 |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.graphProperties({ |
|
|
minNodeSize: config.sigma?.graphProperties?.minNodeSize || 1, |
|
|
maxNodeSize: config.sigma?.graphProperties?.maxNodeSize || 8, |
|
|
minEdgeSize: config.sigma?.graphProperties?.minEdgeSize || 0.5, |
|
|
maxEdgeSize: config.sigma?.graphProperties?.maxEdgeSize || 2, |
|
|
sideMargin: 50 |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.draw(); |
|
|
|
|
|
|
|
|
forceNodeColorsFromConfig(); |
|
|
|
|
|
|
|
|
applyNodeStyles(); |
|
|
|
|
|
|
|
|
forceEdgeColors(); |
|
|
|
|
|
|
|
|
initFilters(); |
|
|
|
|
|
|
|
|
bindEvents(); |
|
|
} catch (e) { |
|
|
console.error("Error initializing sigma instance:", e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function applyNodeStyles() { |
|
|
if (!sigmaInstance) return; |
|
|
try { |
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
|
|
|
if (node.type && config.nodeTypes && config.nodeTypes[node.type]) { |
|
|
|
|
|
node.color = config.nodeTypes[node.type].color; |
|
|
node.size = config.nodeTypes[node.type].size; |
|
|
} else if (node.type && nodeTypes[node.type]) { |
|
|
|
|
|
node.color = nodeTypes[node.type].color; |
|
|
node.size = nodeTypes[node.type].size; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.draw(2, 2, 2, 2); |
|
|
|
|
|
|
|
|
forceEdgeColors(); |
|
|
} catch (e) { |
|
|
console.error("Error applying node styles:", e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function colorNodesByAttribute(attribute) { |
|
|
if (!sigmaInstance) return; |
|
|
|
|
|
console.log("Coloring nodes by attribute:", attribute); |
|
|
|
|
|
let values = {}; |
|
|
let valueCount = 0; |
|
|
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
let value = n[attribute] || 'unknown'; |
|
|
if (!values[value]) { |
|
|
values[value] = true; |
|
|
valueCount++; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
let valueColors = {}; |
|
|
let i = 0; |
|
|
let palette = config.colorPalette || colors; |
|
|
|
|
|
for (let value in values) { |
|
|
valueColors[value] = palette[i % palette.length]; |
|
|
i++; |
|
|
} |
|
|
|
|
|
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
let value = n[attribute] || 'unknown'; |
|
|
n.originalColor = valueColors[value]; |
|
|
n.color = valueColors[value]; |
|
|
}); |
|
|
|
|
|
|
|
|
let nodeInfo = {}; |
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
nodeInfo[node.id] = { |
|
|
color: node.color || '#aaa', |
|
|
type: node.type || 'unknown' |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
|
const sourceNode = nodeInfo[edge.source]; |
|
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
|
edge.originalColor = authorNode.color; |
|
|
edge.color = authorNode.color; |
|
|
} |
|
|
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
|
edge.originalColor = paperNode.color; |
|
|
edge.color = paperNode.color; |
|
|
} |
|
|
|
|
|
else { |
|
|
edge.originalColor = targetNode.color; |
|
|
edge.color = targetNode.color; |
|
|
} |
|
|
}); |
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
|
|
|
updateColorLegend(valueColors); |
|
|
} |
|
|
|
|
|
|
|
|
function nodeActive(nodeId) { |
|
|
|
|
|
var node = null; |
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
if (n.id == nodeId) { |
|
|
node = n; |
|
|
} |
|
|
}); |
|
|
|
|
|
if (!node) { |
|
|
console.error("Node not found:", nodeId); |
|
|
return; |
|
|
} |
|
|
|
|
|
sigmaInstance.detail = true; |
|
|
selectedNode = node; |
|
|
|
|
|
|
|
|
let nodeInfo = {}; |
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
nodeInfo[n.id] = { |
|
|
color: n.color || '#aaa', |
|
|
type: n.type || 'unknown' |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
var neighbors = {}; |
|
|
sigmaInstance.iterEdges(function(e) { |
|
|
if (e.source == nodeId || e.target == nodeId) { |
|
|
neighbors[e.source == nodeId ? e.target : e.source] = { |
|
|
name: e.label || "", |
|
|
type: e.source == nodeId ? nodeInfo[e.target].type : nodeInfo[e.source].type, |
|
|
color: e.color |
|
|
}; |
|
|
|
|
|
e.originalColor = e.color; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
if (n.id == nodeId) { |
|
|
n.originalColor = n.color; |
|
|
n.size = n.size * 1.5; |
|
|
} else if (neighbors[n.id]) { |
|
|
n.originalColor = n.color; |
|
|
} else { |
|
|
n.originalColor = n.originalColor || n.color; |
|
|
n.color = greyColor; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.iterEdges(function(e) { |
|
|
if (e.source == nodeId || e.target == nodeId) { |
|
|
|
|
|
e.originalColor = e.color; |
|
|
} else { |
|
|
e.originalColor = e.originalColor || e.color; |
|
|
e.color = greyColor; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
|
|
|
var connectionList = []; |
|
|
for (var id in neighbors) { |
|
|
var neighbor = null; |
|
|
sigmaInstance.iterNodes(function(n) { |
|
|
if (n.id == id) { |
|
|
neighbor = n; |
|
|
} |
|
|
}); |
|
|
|
|
|
if (neighbor) { |
|
|
connectionList.push('<li><a href="#" data-node-id="' + id + '">' + (neighbor.label || id) + '</a></li>'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
$('#attributepane').show().css('display', 'block'); |
|
|
|
|
|
|
|
|
$('.nodeattributes .name').text(node.label || node.id); |
|
|
|
|
|
let dataHTML = ''; |
|
|
for (let attr in node) { |
|
|
if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' && |
|
|
attr !== 'label' && attr !== 'originalColor' && attr !== 'hidden' && |
|
|
typeof node[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' && |
|
|
attr !== 'displaySize') { |
|
|
dataHTML += '<div><strong>' + attr + ':</strong> ' + node[attr] + '</div>'; |
|
|
} |
|
|
} |
|
|
|
|
|
if (dataHTML === '') { |
|
|
dataHTML = '<div>No additional attributes</div>'; |
|
|
} |
|
|
|
|
|
$('.nodeattributes .data').html(dataHTML); |
|
|
$('.nodeattributes .link ul').html(connectionList.length ? connectionList.join('') : '<li>No connections</li>'); |
|
|
|
|
|
|
|
|
$('.nodeattributes .link ul li a').click(function(e) { |
|
|
e.preventDefault(); |
|
|
var id = $(this).data('node-id'); |
|
|
nodeActive(id); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error("Error updating attribute pane:", e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function nodeNormal() { |
|
|
if (sigmaInstance) { |
|
|
sigmaInstance.detail = false; |
|
|
selectedNode = null; |
|
|
|
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
node.color = node.originalColor || node.color; |
|
|
|
|
|
if (node.type && config.nodeTypes && config.nodeTypes[node.type]) { |
|
|
node.size = config.nodeTypes[node.type].size; |
|
|
} else if (node.type && nodeTypes[node.type]) { |
|
|
node.size = nodeTypes[node.type].size; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
|
edge.color = edge.originalColor || edge.color; |
|
|
}); |
|
|
|
|
|
|
|
|
$('#attributepane').css('display', 'none'); |
|
|
|
|
|
|
|
|
sigmaInstance.draw(2, 2, 2, 2).refresh(); |
|
|
|
|
|
|
|
|
forceEdgeColors(); |
|
|
} |
|
|
} |