Browse a graph with G6
kgdiff shows what changed between two versions of a graph. It has no mode for looking at one graph on its own, by design: its colors are reserved for change, so a single graph would be drawn all in gray, and exploring a graph (layouts, filters, styles, expanding from a node) is what graph visualization libraries already do well.
kgdiff's parsers are still useful there. This page reads a file with @kgdiff/formats, in any format kgdiff reads, and draws it with AntV G6 v5.
Install
npm install @antv/g6 @kgdiff/core @kgdiff/formatsDraw a graph
<div id="graph" style="height: 640px"></div>import { Graph } from '@antv/g6';
import { normalize } from '@kgdiff/core';
import { compactIri, parseGraph } from '@kgdiff/formats';
const file = 'ontology.ttl';
const parsed = parseGraph(await (await fetch(file)).text(), { filename: file });
const graph = normalize(parsed.graph);
const { prefixes, predicateLabels } = parsed;
const g6 = new Graph({
container: 'graph',
autoFit: 'view',
data: {
nodes: [...graph.nodes.values()].map((node) => ({
id: node.id,
data: { type: node.types[0] ?? '' },
style: { labelText: node.label === node.id ? compactIri(node.id, prefixes) : node.label },
})),
edges: [...graph.edges.values()].map((edge) => ({
id: edge.key, // unique, also for two relations between the same nodes
source: edge.source,
target: edge.target,
style: {
labelText: Object.hasOwn(predicateLabels, edge.predicate)
? predicateLabels[edge.predicate]
: edge.label,
},
})),
},
node: { palette: { type: 'group', field: 'type' } }, // a color per type
edge: { style: { endArrow: true } },
layout: {
type: 'd3-force',
animation: false, // compute the final positions at once
link: { distance: 100 },
manyBody: { strength: -300 },
collide: { radius: 40 },
x: { strength: 0.05 }, // keep unconnected nodes close
y: { strength: 0.05 },
},
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
});
await g6.render();G6 draws into the container, which needs a size. Drag the canvas to pan, scroll to zoom and drag a node to move it.
What the code takes care of
- Any format.
parseGraphdetects the format and reads it the way kgdiff does: Turtle, N-Triples, N-Quads, TriG, RDF/XML, JSON-LD, OBO and the common JSON graph shapes, with labels in your preferred language, OWL existential restrictions as edges and stable ids for blank nodes (Formats).rdf: { languages: ['zh', 'en'] }prefers Chinese labels. - A node at both ends of every edge. Files often point at IRIs they do not describe (
rdfs:subClassOf owl:Thing). Parsing keeps such edges, but G6 refuses an edge to a missing node (Node not found).normalizefrom@kgdiff/coreadds those ends as nodes, merges nodes declared twice and gives every node a label. - Unique edge ids. Without an id, G6 names an edge
source-target, so two relations between the same nodes clash (Edge already exists).edge.keyis unique within the graph. - Readable names. A node without a label is labelled with its IRI, shortened with the prefixes the file declares. A relation shows the name the file gives it (an RDF property's own
rdfs:label, an OBO[Typedef]name), or else a readable form of its predicate, such assubClassOforpart of.Object.hasOwnkeeps a relation calledconstructorfrom reading a built-in property. - A color per type. The palette colors nodes by their first type. kgdiff itself never does that: in its visual language, color means change.
- A quick layout.
animation: falsecomputes the final positions in one go instead of animating all 300 iterations of the layout, and thex/yforces keep nodes without relations from drifting away and shrinking the view.
Going further
Everything else is G6: other layouts (hierarchical, radial, circular…), tooltips, filters, styles and interactions are in the G6 documentation. G6 draws every element it is given, so for an ontology with tens of thousands of terms, draw a part of it, such as a branch or the neighbourhood of a term: graph.outgoing.get(id) and graph.incoming.get(id) give the keys of a node's edges, and graph.edges.get(key) the edge.
When the graph changes
Reviewing a change between two versions is kgdiff's job:
- the CLI, for the terminal, CI and self-contained HTML reports;
- the
<kgdiff-viewer>web component, to show diffs in your own pages; @kgdiff/g6, to draw a diff in a G6 graph, in kgdiff's visual language, next to the G6 views you already have.