Nasdanika ECharts Graph model can be used to generate ECharts graphs such as this Maven dependency graph. The below snippet shows how to use the model.
Graph graph = GraphFactory.eINSTANCE.createGraph();
Item coreCategory = GraphFactory.eINSTANCE.createItem();
coreCategory.setName("Core");
graph.getCategories().add(coreCategory);
// More categories...
// Add nodes and links
GraphUtil.forceLayout(graph, 2000, 1600);
GraphSeries graphSeries = new org.icepear.echarts.charts.graph.GraphSeries()
.setSymbolSize(24)
.setDraggable(true)
.setLayout("none")
.setLabel(new SeriesLabel().setShow(true).setPosition("right"))
.setLineStyle(new GraphEdgeLineStyle().setColor("source").setCurveness(0))
.setRoam(true)
.setEdgeSymbol(new String[] { "none", "arrow" })
.setEmphasis(new GraphEmphasis().setFocus("adjacency")); // Line style width 10?
graph.configureGraphSeries(graphSeries);
org.icepear.echarts.Graph echartsGraph = new org.icepear.echarts.Graph()
.setTitle("Maven Dependencies")
.setLegend()
.addSeries(graphSeries);
Engine engine = new Engine();
String chartJSON = engine.renderJsonOption(echartsGraph);
System.out.println(chartJSON);
String chartHTML = Context
.singleton("chart", chartJSON)
.compose(Context.singleton("graphContainerId", "graph-container"))
.interpolateToString(GRAPH_TEMPLATE);
Files.writeString(new File("docs/index.html").toPath(), chartHTML);
Graph template:
<html>
<head>
<title>Module dependency</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
</head>
<body>
<div class="container-fluid">
<div id="graph-container-${graphContainerId}" class="row" style="height:80vh;width:100%">
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
var dom = document.getElementById("graph-container-${graphContainerId}");
var myChart = echarts.init(dom, null, {
render: "canvas",
useDirtyRect: false
});
var option = ${chart};
option.tooltip = {};
option.series[0].tooltip = {
formatter: function(arg) {
return arg.value ? arg.value.description : null;
}
};
myChart.setOption(option);
myChart.on("dblclick", function(params) {
if (params.value) {
if (params.value.link) {
window.open(params.value.link, "_self");
} else if (params.value.externalLink) {
window.open(params.value.externalLink);
}
}
});
window.addEventListener("resize", myChart.resize);
});
</script>
</body>
</html>
You can also generate 3D Force Graphs from the graph model leveraging Nasdanika 3D Force Graph Java Bindings.
The below code generated this Maven dependency graph:
HTMLPage page = HTMLFactory.INSTANCE.page();
ForceGraph3D forceGraph3D = GraphUtil.asForceGraph3D(graph);
forceGraph3D.getFactory().cdn(page);
String forceGraphContainerId = "force-graph";
forceGraph3D
.elementId(forceGraphContainerId)
.nodeAutoColorBy("'group'")
.nodeVal("'size'")
.linkDirectionalArrowLength(3.5)
.linkDirectionalArrowRelPos(1)
.addExtraRederer("new CSS2DRenderer()")
.nodeThreeObject(
"""
node => {
const nodeEl = document.createElement('div');
nodeEl.textContent = node.name;
nodeEl.style.color = node.color;
nodeEl.className = 'node-label';
return new CSS2DObject(nodeEl);
}
""")
.nodeThreeObjectExtend(true)
.onNodeDragEnd(
"""
node => {
node.fx = node.x;
node.fy = node.y;
node.fz = node.z;
}
""");
page.body(HTMLFactory.INSTANCE.div().id(forceGraphContainerId));
Tag scriptTag = TagName.script.create(
System.lineSeparator(),
"import { CSS2DRenderer, CSS2DObject } from 'https://esm.sh/three/examples/jsm/renderers/CSS2DRenderer.js';",
System.lineSeparator(),
forceGraph3D).attribute("type", "module");
page.body(scriptTag);
page.head(TagName.style.create(
"""
.node-label {
font-size: 12px;
padding: 1px 4px;
border-radius: 4px;
background-color: rgba(0,0,0,0.5);
user-select: none;
}
"""));
Files.writeString(new File("docs/force-graph-3d-util.html").toPath(), page.toString());
Handling double-clicks:
(node, event) => {
if (node.lastClick) {
if (event.timeStamp - node.lastClick < 500) {
if (Array.isArray(node.value) && node.value.length > 0) {
if (node.value[0].link) {
window.open(node.value[0].link, "_self");
} else if (node.value[0].externalLink) {
window.open(node.value[0].externalLink);
}
}
delete node.lastClick;
} else {
node.lastClick = event.timeStamp;
}
} else {
node.lastClick = event.timeStamp;
}
}