Skip to content

Commit

Permalink
Merge pull request #205 from amosproj/hierarchical-graph-layout
Browse files Browse the repository at this point in the history
Hierarchical graph layout
  • Loading branch information
derwehr authored Jun 1, 2021
2 parents 4808db1 + e685c54 commit 8e8dca2
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 46 deletions.
5 changes: 5 additions & 0 deletions frontend/src/routing/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ const routes: Record<string, RouteDefinition> = {
label: 'Schema',
content: () => <Schema />,
},
{
path: '/visualization/hierarchical',
label: 'Hierarchies',
content: () => <Graph layout="hierarchical" />,
},
],
},
Exploration: {
Expand Down
63 changes: 18 additions & 45 deletions frontend/src/visualization/Graph.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import React, { useState } from 'react';
import VisGraph, { GraphData } from 'react-graph-vis';
import * as vis from 'vis-network';
import { createStyles, makeStyles } from '@material-ui/core/styles';
import { uuid } from 'uuidv4';
import { tap } from 'rxjs/operators';
import useService from '../dependency-injection/useService';
import { EdgeDescriptor } from '../shared/entities';
import { NodeResultDescriptor, QueryResult } from '../shared/queries';
import { useSize } from '../utils/useSize';
import Filter from './filtering/Filter';
import useObservable from '../utils/useObservable';
import QueryResultStore from '../stores/QueryResultStore';
import convertQueryResult from './shared-ops/convertQueryResult';

const useStyles = makeStyles(() =>
createStyles({
Expand Down Expand Up @@ -45,54 +43,16 @@ const useStyles = makeStyles(() =>
})
);

function convertNode(node: NodeResultDescriptor): vis.Node {
const result: vis.Node = {
id: node.id,
label: node.id.toString(),
// Advanced stuff, like styling nodes with different types differently...
};

if (node.subsidiary) {
result.color = 'yellow';
}

return result;
}

function convertNodes(nodes: NodeResultDescriptor[]): vis.Node[] {
return nodes.map((node) => convertNode(node));
}

function convertEdge(edge: EdgeDescriptor): vis.Edge {
return {
id: edge.id,
from: edge.from,
to: edge.to,
// Advanced stuff, like styling edges with different types differently...
};
}

function convertEdges(edges: EdgeDescriptor[]): vis.Edge[] {
return edges.map((edge) => convertEdge(edge));
}

function convertQueryResult(queryResult: QueryResult): GraphData {
return {
nodes: convertNodes(queryResult.nodes),
edges: convertEdges(queryResult.edges),
};
}

/**
* Builds the graph options passed to react-graph-vis.
* @param width The width of the graph.
* @param height The height of the graph.
* @returns The react-graph-vis options.
*/
function buildOptions(width: number, height: number) {
function buildOptions(width: number, height: number, layout?: string) {
return {
layout: {
hierarchical: false,
hierarchical: layout === 'hierarchical',
},
edges: {
color: '#000000',
Expand All @@ -102,7 +62,12 @@ function buildOptions(width: number, height: number) {
};
}

function Graph(): JSX.Element {
type GraphProps = {
layout?: string;
};

function Graph(props: GraphProps): JSX.Element {
const { layout } = props;
const classes = useStyles();

// A React ref to the container that is used to measure the available space for the graph.
Expand Down Expand Up @@ -138,7 +103,11 @@ function Graph(): JSX.Element {
<div className={classes.graphContainer}>
<VisGraph
graph={graphData}
options={buildOptions(containerSize.width, containerSize.height)}
options={buildOptions(
containerSize.width,
containerSize.height,
layout
)}
key={uuid()}
/>
</div>
Expand All @@ -150,4 +119,8 @@ function Graph(): JSX.Element {
);
}

Graph.defaultProps = {
layout: undefined,
};

export default Graph;
2 changes: 1 addition & 1 deletion frontend/src/visualization/Visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function Visualization(): JSX.Element {

// TODO: remove placeholder grids and fill cards with existing tabs
const schemaCard = cards[cards.length - 1];
const placeholders = Array<CardDefinition>(4).fill(schemaCard);
const placeholders = Array<CardDefinition>(3).fill(schemaCard);

return (
<>
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/visualization/dashboard-card/cardContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ const cardContents: Array<CardPresentationContent> = [
description: 'Take a deeper look into the database structure',
icon: 'account_tree',
},
{
label: 'Hierarchies',
subLabel: 'Node relations',
description: 'Display queried data in a hierarchically structured graph.',
icon: 'device_hub',
},
];

/**
Expand Down
44 changes: 44 additions & 0 deletions frontend/src/visualization/shared-ops/convertQueryResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { GraphData } from 'react-graph-vis';
import * as vis from 'vis-network';
import { EdgeDescriptor } from '../../shared/entities';
import { NodeResultDescriptor, QueryResult } from '../../shared/queries';

function convertNode(node: NodeResultDescriptor): vis.Node {
const result: vis.Node = {
id: node.id,
label: node.id.toString(),
// Advanced stuff, like styling nodes with different types differently...
};

if (node.subsidiary) {
result.color = 'yellow';
}

return result;
}

function convertNodes(nodes: NodeResultDescriptor[]): vis.Node[] {
return nodes.map((node) => convertNode(node));
}

function convertEdge(edge: EdgeDescriptor): vis.Edge {
return {
id: edge.id,
from: edge.from,
to: edge.to,
// Advanced stuff, like styling edges with different types differently...
};
}

function convertEdges(edges: EdgeDescriptor[]): vis.Edge[] {
return edges.map((edge) => convertEdge(edge));
}

export default function convertQueryResult(
queryResult: QueryResult
): GraphData {
return {
nodes: convertNodes(queryResult.nodes),
edges: convertEdges(queryResult.edges),
};
}

0 comments on commit 8e8dca2

Please sign in to comment.