graph-explorer
Version:
Graph Explorer can be used to explore and RDF graphs in SPARQL endpoints or on the web.
137 lines (119 loc) • 3.49 kB
text/typescript
import { keyBy } from "lodash";
import { GenerateID } from "../schema";
import {
LayoutElement,
LayoutLink,
SerializedDiagram,
makeSerializedDiagram,
} from "../../editor/serializedDiagram";
import { uniformGrid } from "../../viewUtils/layout";
import {
Dictionary,
ElementModel,
LinkModel,
ElementIri,
LinkTypeIri,
} from "../model";
import { DataProvider } from "../provider";
import { Triple } from "./sparqlModels";
import { parseTurtleText } from "./turtle";
const GREED_STEP = 150;
export class GraphBuilder {
constructor(public dataProvider: DataProvider) {}
createGraph(graph: {
elementIds: ElementIri[];
links: LinkModel[];
}): Promise<{
preloadedElements: Dictionary<ElementModel>;
diagram: SerializedDiagram;
}> {
return this.dataProvider
.elementInfo({ elementIds: graph.elementIds })
.then((elementsInfo) => ({
preloadedElements: elementsInfo,
diagram: makeLayout(graph.elementIds, graph.links),
}));
}
getGraphFromRDFGraph(graph: Triple[]): Promise<{
preloadedElements: Dictionary<ElementModel>;
diagram: SerializedDiagram;
}> {
const { elementIds, links } = makeGraphItems(graph);
return this.createGraph({ elementIds, links });
}
getGraphFromTurtleGraph(graph: string): Promise<{
preloadedElements: Dictionary<ElementModel>;
diagram: SerializedDiagram;
}> {
return parseTurtleText(graph).then((triples) =>
this.getGraphFromRDFGraph(triples)
);
}
}
export function makeGraphItems(response: readonly Triple[]): {
elementIds: ElementIri[];
links: LinkModel[];
} {
const elements: Dictionary<boolean> = {};
const links: LinkModel[] = [];
for (const { subject, predicate, object } of response) {
if (subject.type === "uri" && !elements[subject.value]) {
elements[subject.value] = true;
}
if (object.type === "uri" && !elements[object.value]) {
elements[object.value] = true;
}
if (subject.type === "uri" && object.type === "uri") {
links.push({
linkTypeId: predicate.value as LinkTypeIri,
sourceId: subject.value as ElementIri,
targetId: object.value as ElementIri,
});
}
}
return { elementIds: Object.keys(elements) as ElementIri[], links };
}
export function makeLayout(
elementsIds: readonly ElementIri[],
linksInfo: readonly LinkModel[]
): SerializedDiagram {
const rows = Math.ceil(Math.sqrt(elementsIds.length));
const grid = uniformGrid({
rows,
cellSize: { x: GREED_STEP, y: GREED_STEP },
});
const elements: LayoutElement[] = elementsIds.map<LayoutElement>(
(id, index) => {
const { x, y } = grid(index);
return {
"@type": "Element",
"@id": GenerateID.forElement(),
iri: id,
position: { x, y },
};
}
);
const layoutElementsMap: Record<string, LayoutElement> = keyBy(
elements,
"iri"
);
const links: LayoutLink[] = [];
linksInfo.forEach((link, _index) => {
const source = layoutElementsMap[link.sourceId];
const target = layoutElementsMap[link.targetId];
if (!source || !target) {
return;
}
links.push({
"@type": "Link",
"@id": GenerateID.forLink(),
property: link.linkTypeId,
source: { "@id": source["@id"] },
target: { "@id": target["@id"] },
});
});
return makeSerializedDiagram({
layoutData: { "@type": "Layout", elements, links },
linkTypeOptions: [],
});
}