new node graph
This commit is contained in:
Pablo Revilla
2025-04-01 22:06:29 -07:00
parent ada666da44
commit e0080ce811
+108 -161
View File
@@ -8,68 +8,99 @@
#mynetwork { #mynetwork {
width: 100%; width: 100%;
height: 100vh; height: 100vh;
max-width: 2000px; border: 1px solid #ddd;
max-height: 2000px; background-color: #f8f9fa;
border: 1px solid lightgray; border-radius: 10px;
background-color: white; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
} }
.legend { .search-container {
position: absolute; position: absolute;
bottom: 10px; top: 10px;
left: 10px; left: 10px;
background-color: rgba(255, 255, 255, 0.8); z-index: 10;
padding: 5px; display: flex;
border-radius: 5px; gap: 5px;
}
.search-container input {
padding: 8px;
border-radius: 8px;
border: 1px solid #ccc; border: 1px solid #ccc;
font-size: 12px; }
color: #333;
.search-container button {
padding: 8px 12px;
border-radius: 8px;
border: none;
background-color: #007bff;
color: white;
cursor: pointer;
}
.search-container button:hover {
background-color: #0056b3;
} }
#node-info { #node-info {
position: absolute; position: absolute;
bottom: 10px; bottom: 10px;
right: 10px; right: 10px;
background-color: rgba(255, 255, 255, 0.9); background-color: rgba(255, 255, 255, 0.95);
padding: 12px;
border-radius: 8px;
border: 1px solid #ccc;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
font-size: 14px;
color: #333;
width: 260px;
max-height: 250px;
overflow-y: auto;
}
#legend {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.9);
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
border: 1px solid #ccc; border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-size: 14px; font-size: 14px;
color: #333; }
width: 250px;
max-height: 200px; .legend-box {
overflow-y: auto; display: inline-block;
width: 12px;
height: 12px;
margin-right: 5px;
border-radius: 3px;
} }
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div style="position: absolute; top: 10px; left: 10px; z-index: 10;"> <div class="search-container">
<input type="text" id="node-search" placeholder="Search node..." <input type="text" id="node-search" placeholder="Search node...">
style="padding: 5px; border-radius: 5px; border: 1px solid #ccc;"> <button onclick="searchNode()">Search</button>
<button onclick="searchNode()"
style="padding: 5px 10px; margin-left: 5px; border-radius: 5px; border: 1px solid #ccc; background-color: #3388ff; color: white;">
Search
</button>
</div> </div>
<div id="mynetwork"></div> <div id="mynetwork"></div>
<!-- Legend --> <div id="node-info">
<div class="legend"> <b>Long Name:</b> <span id="node-long-name"></span><br>
<div><span style="background-color: #ff5733; width: 20px; height: 20px; display: inline-block; border-radius: 50%; margin-right: 5px;"></span> Traceroute</div> <b>Short Name:</b> <span id="node-short-name"></span><br>
<div><span style="background-color: #3388ff; width: 20px; height: 20px; display: inline-block; border-radius: 50%; margin-right: 5px;"></span> NeighborInfo</div> <b>Role:</b> <span id="node-role"></span><br>
<b>Hardware Model:</b> <span id="node-hw-model"></span>
</div> </div>
<!-- Node Information Panel --> <div id="legend">
<div id="node-info"> <div><span class="legend-box" style="background-color: #ff5733;"></span> <span style="color: black;">Traceroute</span></div>
<b>Long Name: </b> <span id="node-long-name"></span></br> <div><span class="legend-box" style="background-color: #3388ff;"></span> <span style="color: black;">Neighbor</span></div>
<b>Short Name: </b><span id="node-short-name"></span></br>
<b>Role: </b><span id="node-role"></span></br>
<b>Hardware Model: </b><span id="node-hw-model"></span>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
// Initialize chart
var chart = echarts.init(document.getElementById('mynetwork')); var chart = echarts.init(document.getElementById('mynetwork'));
var nodes = [ var nodes = [
@@ -77,21 +108,17 @@
{ {
name: `{{ node.node_id }}`, name: `{{ node.node_id }}`,
value: `{{ node.long_name | tojson }}`, value: `{{ node.long_name | tojson }}`,
symbol: 'rect', symbol: 'circle',
symbolSize: [5, 5], symbolSize: 15,
itemStyle: { color: '#007bff' },
label: { label: {
show: true, show: true,
position: 'inside', position: 'right',
color: '#000', color: '#333',
padding: [5, 10], fontSize: 12,
formatter: function(params) { formatter: function(params) {
// Remove quotes around the long_name return params.data.value.replace(/^\"(.*)\"$/, '$1');
return params.data.value.replace(/^"(.*)"$/, '$1'); // Remove surrounding quotes }
},
backgroundColor: '#f0f0f0',
borderColor: '#999',
borderWidth: 1,
borderRadius: 5
}, },
long_name: `{{ node.long_name | tojson }}`, long_name: `{{ node.long_name | tojson }}`,
short_name: `{{ node.short_name | tojson }}`, short_name: `{{ node.short_name | tojson }}`,
@@ -108,42 +135,17 @@
target: `{{ edge.to }}`, target: `{{ edge.to }}`,
originalColor: `{{ edge.originalColor }}`, originalColor: `{{ edge.originalColor }}`,
lineStyle: { lineStyle: {
color: '#d3d3d3', color: '#d3d3d3', // Default gray color
width: 2, width: 2
opacity: 0.5
} }
}{% if not loop.last %},{% endif %} }{% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
]; ];
var option = { var option = {
backgroundColor: 'white', backgroundColor: '#f8f9fa',
tooltip: { tooltip: { show: false },
formatter: function(params) { animation: false,
if (params.dataType === 'node') {
return (params.data.long_name || 'Unknown') + ' - ' + (params.data.short_name || 'Unknown');
} else {
return ''; // Hide tooltip for edges
}
}
},
animationDuration: 0, // Disable all animation
animationEasing: 'linear', // Make easing linear to avoid any animation effect
legend: {
data: ['Traceroute', 'NeighborInfo'],
selectedMode: false,
left: 'center',
bottom: '5%',
orient: 'vertical',
textStyle: {
fontSize: 12,
color: '#333'
},
itemWidth: 10,
itemHeight: 10,
padding: [5, 15]
},
series: [ series: [
{ {
type: 'graph', type: 'graph',
@@ -151,57 +153,48 @@
data: nodes, data: nodes,
links: edges, links: edges,
roam: true, roam: true,
force: { force: { repulsion: 200, edgeLength: [80, 120] }
repulsion: 100, // Reduced repulsion for more static graph
edgeLength: [100, 150], // Shorten edge length to keep nodes closer
gravity: 0.03 // Lower gravity to prevent too much movement
},
lineStyle: {
width: 2,
curveness: 0
},
edgeLabel: { show: false }, // Hides edge labels
tooltip: { show: false } // Disables tooltips for edges
} }
] ]
}; };
chart.setOption(option); chart.setOption(option);
// Event listener for node clicks function updateSelectedNode(selectedNode) {
var updatedEdges = edges.map(edge => ({
...edge,
lineStyle: {
color: (edge.source === selectedNode || edge.target === selectedNode) ? edge.originalColor : '#d3d3d3',
width: (edge.source === selectedNode || edge.target === selectedNode) ? 4 : 2
}
}));
var updatedNodes = nodes.map(node => ({
...node,
itemStyle: {
color: node.name === selectedNode ? '#ff0000' : '#007bff'
}
}));
chart.setOption({ series: [{ links: updatedEdges, data: updatedNodes }] });
var nodeData = nodes.find(n => n.name === selectedNode);
if (nodeData) {
document.getElementById('node-long-name').innerText = nodeData.long_name;
document.getElementById('node-short-name').innerText = nodeData.short_name;
document.getElementById('node-role').innerText = nodeData.role;
document.getElementById('node-hw-model').innerText = nodeData.hw_model;
}
}
chart.on('click', function(params) { chart.on('click', function(params) {
if (params.dataType === 'node') { if (params.dataType === 'node') {
var selectedNode = params.data.name; updateSelectedNode(params.data.name);
var updatedEdges = edges.map(edge => {
if (edge.source === selectedNode || edge.target === selectedNode) {
return {
...edge,
lineStyle: {
color: edge.originalColor,
width: 2,
opacity: 1
}
};
} else {
return edge;
}
});
chart.setOption({
series: [{ links: updatedEdges }]
});
document.getElementById('node-long-name').innerText = params.data.long_name;
document.getElementById('node-short-name').innerText = params.data.short_name;
document.getElementById('node-role').innerText = params.data.role;
document.getElementById('node-hw-model').innerText = params.data.hw_model;
} }
}); });
function searchNode() { function searchNode() {
var searchQuery = document.getElementById('node-search').value.toLowerCase().trim(); var searchQuery = document.getElementById('node-search').value.toLowerCase().trim();
if (!searchQuery) return; if (!searchQuery) return;
var foundNode = nodes.find(node => var foundNode = nodes.find(node =>
@@ -211,53 +204,7 @@
); );
if (foundNode) { if (foundNode) {
// Update the node's label background color to light blue updateSelectedNode(foundNode.name);
chart.setOption({
series: [{
data: nodes.map(node => ({
...node,
label: {
...node.label,
backgroundColor: node.name === foundNode.name ? '#add8e6' : node.label.backgroundColor
}
}))
}]
});
// Simulate node click behavior
var updatedEdges = edges.map(edge => {
if (edge.source === foundNode.name || edge.target === foundNode.name) {
return {
...edge,
lineStyle: {
color: edge.originalColor,
width: 2,
opacity: 1
}
};
} else {
return edge;
}
});
chart.setOption({
series: [{ links: updatedEdges }]
});
// Update the node info panel
document.getElementById('node-long-name').innerText = foundNode.long_name;
document.getElementById('node-short-name').innerText = foundNode.short_name;
document.getElementById('node-role').innerText = foundNode.role;
document.getElementById('node-hw-model').innerText = foundNode.hw_model;
// Recenter the graph on the found node
chart.dispatchAction({
type: 'moveTo',
seriesIndex: 0,
dataIndex: nodes.indexOf(foundNode),
zoom: 1.5 // Adjust zoom level as needed
});
} else { } else {
alert("Node not found!"); alert("Node not found!");
} }