policymap/api/templates/render_network.html

196 lines
6.7 KiB
HTML
Raw Normal View History

2025-03-12 20:47:11 -07:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Render Network</title>
<!-- Include D3.js -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
#chart {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: pointer; /* Change cursor to indicate interactivity */
}
.link {
fill: none;
stroke: #999;
stroke-opacity: 0.6;
}
text {
font-size: 12px;
}
/* Tooltip styles */
.tooltip {
position: absolute;
padding: 10px;
background-color: rgba(255, 255, 255, 0.9);
border: 1px solid #ccc;
pointer-events: none; /* Make sure the tooltip doesn't interfere with mouse events */
}
</style>
</head>
<body>
<div id="chart"></div>
<!-- Tooltip container -->
<div class="tooltip" style="display: none;"></div>
<script type="text/javascript">
// Get container dimensions
const chartContainer = d3.select("#chart");
let width = parseInt(chartContainer.style("width"));
let height = parseInt(chartContainer.style("height"));
// Create SVG container
const svg = chartContainer.append("svg")
.attr("width", width)
.attr("height", height);
// Define simulation
const simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-500)) // Adjust charge strength for better distribution
.force("center", d3.forceCenter(width / 2, height / 2));
// Fetch network data from Flask endpoint
d3.json('/person_network').then(function(data) {
2025-03-14 03:35:23 -07:00
if (data.error) {
console.error(data.error);
return;
}
2025-03-12 20:47:11 -07:00
const nodesData = data.nodes;
const linksData = data.links;
console.log("Nodes:", nodesData);
console.log("Links:", linksData);
// Append links to the SVG container
const link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(linksData)
.enter()
.append("line")
.attr("class", "link");
// Append nodes to the SVG container
const node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodesData)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", d => d.group === 1 ? 8 : 6) // Larger circles for Person nodes
.style("fill", d => d.group === 1 ? "green" : "lightblue") // Green for Person, light blue for others
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", showTooltip);
// Append labels to the SVG container
const label = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(nodesData)
.enter()
.append("text")
.text(d => {
if (d.labels.includes('Person')) {
return d.name || 'Person';
} else if (['Legislation', 'Bill', 'Law'].some(label => d.labels.includes(label))) {
return d.title || 'Title';
}
2025-03-14 03:35:23 -07:00
// Fallback for other types
return Object.keys(d).filter(key => key !== 'labels').map(key => d[key]).join(', ') || 'Node';
2025-03-12 20:47:11 -07:00
})
.attr("dx", 10) // Offset from node
.attr("dy", 4); // Offset from node
// Initialize simulation with nodes and links
simulation.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData);
function ticked() {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("cx", d => Math.max(10, Math.min(width - 10, d.x)))
.attr("cy", d => Math.max(10, Math.min(height - 10, d.y)));
label.attr("x", d => d.x)
.attr("y", d => d.y);
}
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
// Show tooltip on node click
function showTooltip(event, d) {
const tooltip = d3.select(".tooltip");
tooltip.style("display", "block")
.html(Object.entries(d).map(([key, value]) => `<strong>${key}:</strong> ${value}`).join("<br />"))
.style("left", `${event.pageX}px`)
.style("top", `${event.pageY}px`);
}
// Hide tooltip on document click
d3.select(document).on("click.tooltip", function() {
const tooltip = d3.select(".tooltip");
if (!d3.event.target.classList.contains("node")) {
tooltip.style("display", "none");
}
});
}).catch(function(error) {
console.error("Error fetching data:", error);
});
// Handle window resize
window.addEventListener('resize', () => {
width = parseInt(chartContainer.style("width"));
height = parseInt(chartContainer.style("height"));
svg.attr("width", width)
.attr("height", height);
simulation.force("center")
.x(width / 2)
.y(height / 2);
});
</script>
</body>
</html>