Newer
Older
The viz-layout is a hierarchical layout to draw metabolic pathways. To use the layout, a network with nodes and directed links is required. User can provide an object describing the network style. If defined in the network style, height and width of nodes can be taken into account. The layout change the position of nodes, and can add some classes for nodes and links.
The layout is designed to be applied on directed bipartite graphs, where metabolite nodes are only connected to reaction nodes. Reactions can be reversible, but for each reversible reaction, all links must be declared for one direction. To reverse the direction of a reaction, the source and target of the links are swapped. Additionally, metabolite nodes can be marked as side compounds, with common examples being water and ATP. A step of the layout can duplicate them and place them after the other nodes.
The y-axis is inverted compared to the usual Cartesian convention (where smaller y values are lower on the graph). It was designed for systems like SVG where smaller y values are positioned at the top of the screen. Therefore, the initial reactants, placed at the start of the reaction sequence, will have smaller y values, positioning them at the top of the diagram. The products of the reaction will have larger y values, placing them lower.
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
</svg> The layout doesn’t work for large graphs. The exact size limit is currently unknown, but it will be determined as development progresses.
---
## Table of Contents
- [Getting Started](#getting-started)
- [Create a Typescript Project](#create-a-typescript-project)
- [Install via npm](#install-via-npm)
- [Typescript Configuration](#typescript-configuration)
- [Usage](#usage)
- [Types](#types)
- [Types for Network](#types-for-network)
- [Network](#network)
- [Node](#node)
- [Link](#link)
- [Types for Style](#types-for-style)
- [GraphStyleProperties](#graphstyleproperties)
- [NodeStyle](#nodestyle)
- [Types for Parameters](#types-for-parameters)
- [getDefaultParam()](#getdefaultparam)
- [layoutOnNetwork()](#layoutonnetwork)
- [Step of the Layout](#step-of-the-layout)
- [Base Layout](#base-layout)
- [Management of Side Compounds](#management-of-side-compounds)
- [Management of Reversible Reaction](#management-of-reversible-reaction)
- [Management of Directed Cycles](#management-of-directed-cycles)
- [Management of Main Chains](#management-of-main-chains)
- [Shifting Coordinates](#shifting-coordinates)
---
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
## Getting started
#### Create a typescript project
1. **Initialize a new project**
First, create a new directory for your project and initialize it with `npm`:
```bash
mkdir my-project
cd my-project
npm init -y
```
2. **Install TypeScript**
You'll need to install TypeScript as a development dependency in your project:
```bash
npm install --save-dev typescript
```
3. **Set up the TypeScript configuration**
TypeScript requires a `tsconfig.json` file (at the root of the project) to specify how your TypeScript code should be compiled. You can generate a default configuration file by running:
```bash
npx tsc --init
```
4. **Create your source files**
Inside your project, create a `src` directory and a `index.ts` file for your TypeScript code:
```
#### Install via npm
The viz-layout package is currently only available on the MetaboHUB forge. To install it, you need to configure an `.npmrc` file (at the root of the project) to specify the retrieval path.
```.npmrc
@metabohub:registry=https://forgemia.inra.fr/api/v4/packages/npm/
```
Then you can install the package:
```
npm install @metabohub/viz-layout
```
#### Typescript configuration
Once the installation step is completed, you need to declare the module. To do this, add the following line in the `env.d.ts` file (at the root of the project):
```ts
declare module "@metabohub/viz-layout";
```
import type { UserParameters, Network , Node, Link, GraphStyleProperties, NodeStyle} from "@metabohub/viz-layout";
import { layoutOnNetwork, getDefaultParam, PathType } from "@metabohub/viz-layout";
// Creation of network
const nodes : {[key:string]:Node} = {
x: 50,
y: 50,
classes: ["metabolite"],
metadata :{
isSideCompound : false
}
},
x: 100,
y: 100,
classes: ["reaction"],
metadata : {
isReversible : true
}
classes: ["metabolite"],
metadata : {
isSideCompound : false
}
source: nodes.MetaboliteA,
target: nodes.Reaction1,
id: 'MetaboliteA->Reaction1'
},
{
source: nodes.Reaction1,
target: nodes.MetaboliteB,
id: 'Reaction1->MetaboliteB'
}
]
const network:Network = { id: 'network', nodes: nodes, links: links };
// Creation of network styles
}
};
const networkStyle :GraphStyleProperties ={ nodeStyles: nodeStyle };
// Choosing parameters
// get default parameters and modify some of them
// estimate parameters of spacing depending on network and network style (not necessary)
const defaultSpacing= getDefaultSpacingAttributesPixel(network,styleNetwork,2);
parameters.spacePixelHorizontal=defaultSpacing.spacePixelHorizontal;
parameters.spacePixelVertical=defaultSpacing.spacePixelVertical;
const newNetwork = await layoutOnNetwork(network, networkStyle, parameters);
Another example of a more complex network can be found in the `public` folder. It includes variables for the nodes, the links, network, and a svg of the drawing with default parameters.
<img src="public/HistidineMetabolism.png" height="500px">
## Types
### Types for network
##### Network
| Attribut | Type | Description |
| -------- | ---- | ----------- |
| id | `string` | Network's id |
| nodes | `{[key: string] Node}` | Object that contains nodes |
| links | `Array<Link>` | List that contains links |
##### Node
| Attribut | Type | Description |
| -------- | ---- | ----------- |
| id | `string` | Node's id |
| x | `number` | X node's position |
| y | `number` | Y node's position |
| classes | `Array<string>` | Node's classes to manage style |
| metadata | `{[key: string]: string \| number \| {[key: string]: string \| number} \| Array<string> \| boolean}` | Node's metadata |
To have a bipartite metabolic network, the classes of a node can contain at least either `metabolite` or `reaction`.
Metadata can contains those elements :
| Key | Type | Description |
| -------- | ---- | ----------- |
| isSideCompound | `boolean` | Node is declared as a side compound |
| duplicate | `boolean` | Node is declared as a duplicated node |
| isReversible | `boolean` | Node is declared as a reversible |
If `isSideCompound` or `duplicate` is not set, the step to handle side compounds will not perform any actions, as no nodes are marked as side compounds or duplicated node. A `duplicate` class will be added to side compound nodes that are duplicated.
If `isReversible` is not set, and the reaction node does not have a `reaction` class, the step to handle reversible reactions will not perform any actions, as no reactions are marked as reversible.
##### Link
| Attribut | Type | Description |
| -------- | ---- | ----------- |
| id | string | Link's id |
| source | Node | Source node of the link |
| target | Node | Target node of the link |
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
</svg> Both the source and target must be references to nodes that are present in the network (see <a href="#usage">usage</a> for details)!
In order to have a bipartite graph, links should associate a metabolite node with a reaction node.
A class `reversible` will be added by the layout for links associated with a reversible reaction if the step to manage reversible reaction is done.
### Types for style
#### GraphStyleProperties
| Attribut | Type | Description |
| -------- | ---- | ----------- |
| nodeStyles | { [key: string]: NodeStyle } | Object that contains nodes classes name associated to their style |
The keys of nodeStyles must match the node classes to correctly associate styles with the corresponding nodes.
#### NodeStyle
| Attribut | Type | Description |
| -------- | ---- | ----------- |
| height | `number` | Node's height (in pixels)|
| width | `number` | Node's width (in pixels) |
If not defined for a node, the default value used is 25 pixels.
#### UserParameters
To initialize the parameters, use the `getDefaultParam()` function from the package, it return a type `UserParameters`. You can then modify the desired parameters as needed (see [usage](#usage)). Refer to the [Step of the layout](#step-of-the-layout) section for a better understanding of certain parameters.
| Parameter | Type | Default | Description |
|--------------------------|-------------|---------------|--------------------------------------------------------------------------------------------------|
| spacePixelHorizontal | `number` | 100 | Minimum horizontal spacing (in pixels) for hierarchical layout |
| spacePixelVertical | `number`|100 | Minimum vertical spacing (in pixels) for hierarchical layout |
|doDuplicateSideCompounds | `boolean` | true | Whether to duplicate side compounds |
| doPutAsideSideCompounds | `boolean` | true | Whether to remove (temporarily) side compounds |
|doInsertionSideCompounds | `boolean` | true | Whether to reinsert side compounds |
| doMainChain | `boolean` | true | Whether to find and clusterize main chain |
| pathType | `PathType` | ALL_LONGEST | Defines the path type for the main chain step: LONGEST, ALL_LONGEST, or ALL. |
| merge | `boolean` | true| Whether to merge path with common nodes |
| doCycle | `boolean` | true | Whether to process directed cycles |
| shiftCoord | `boolean` | false | Whether to get top left corner coordinates of nodes (if false, center of nodes is returned) |
Get default parameters for the layout.
`getDefaultParam()`
##### Output
Return default parameters.
| Type | Description |
| ----------- | --|
`UserParameters` | default user parameters |
## getDefaultSpacingAttributesPixel
Estimate minimal spacing parameters (`spacePixelHorizontal`, `spacePixelVertical`) depending on the size of nodes in the network.
`getDefaultSpacingAttributesPixel(network:Network,styleNetwork?:GraphStyleProperties,factor?:number)`
##### Input
| Arguments | Type | default | Description | Optional |
| ----- | ---- | ------- | ----------- | -------- |
| network | `Network` | - | Network object that contains nodes and links of the network | No |
| networkStyle | `GraphStyleProperties` | *undefined* | Network style object that contains classes and style associated with nodes | Yes |
| factor | `number` | 3 | A factor to increase the minimal spacing | yes |
The network style input is not mandatory but the function isn't usefull if not provided as all nodes will be considered of size 25 pixels.
Return an object with spacing parameters.
|Key| Type | Description |
---- | ----------- | --|
| spacePixelHorizontal | `number` | Mean of width of all nodes in the network multiply by the factor |
| spacePixelVertical | `number` | Mean of height of all nodes in the network multiply by the factor |
Apply the layout on a network.
`layoutOnNetwork(network, networkStyle?, parameters?)` is a asynchronous function.
##### Input
| Arguments | Type | default | Description | Optional |
| ----- | ---- | ------- | ----------- | -------- |
| network | `Network` | - | Network object that contains nodes and links of the network | No |
| networkStyle | `GraphStyleProperties` | { } | Network style object that contains classes and style associated with nodes | Yes |
| parameters | `Parameters` | *undefined* | Parameters of the layout | Yes |
If parameters is undefined, default parameters defined above are used (see ([defaultParameters](#types-for-parameters))) and the values for `spacePixelHorizontal` and `spacePixelVertical` are estimated with `getDefaultSpacingAttributesPixel`.
##### Output
Type | Description
---- | -----------
`Promise<Network>` | network with modified node positions, and some added classes
The base layout used in the algorithm is a Sugiyama layout. It is implemented by the viz.js library (https://github.com/mdaines/viz-js), a JS wrapper of the dot layout of GraphViz (https://graphviz.org/documentation/). The separation between nodes in the dot layout is define with `spacePixelVertical` (graphviz attribut `ranksep`) and by `spacePixelHorizontal` (graphviz attribut `nodesep`).
<small>E. R. GANSNER, E. KOUTSOFIOS, S. C. NORTH et K.-P. VO, “A technique for drawing directed graphs”, IEEE Transactions on Software Engineering, t. 19, no 3, p. 214-230, 1993.</small>
Nodes declared as side compounds are duplicated if `doDuplicateSideCompounds = true`. It will add a `duplicate` attribut and class on those nodes. If side compounds nodes are already duplicated and `doDuplicateSideCompounds = false`, they need to be declared as duplicated nodes (see [type node](#node)).
If `doPutAsideSideCompounds = true`, duplicated nodes are temporarily removed from the network object.
They will be reinserted based on the locations of other nodes during the layout process if `doInsertionSideCompounds = true`.
If side compounds are temporarily removed and then reinserted without being duplicated, there is currently no method to reintegrate them and that can create an error in the algorithm.
For this step to take effect, reaction nodes representing a reversible reaction must have the class `reaction` in their classes and `metadata.isReversible = true`.
In this step, a direction is choosen to each reversible reaction not yet determined. The method promotes the continuity of the reaction sequence in the drawing. Links corresponding to these reactions are assigned the class `reversible`.
The step is done if `doCycle = true`.
Directed cycles with more than three nodes are identified and arranged in a circle as much as possible. When multiple directed cycles share common nodes, not all nodes may be positioned in a circle; those nodes will be placed using a force layout (utilizing the D3 library: https://d3js.org/).
It is important to note that not all cycles may be detected. A timer is implemented to limit the duration of the cycle search, ensuring the process does not take too long. As a result, the outcomes are not deterministic, and the set of cycles found may vary between runs.
For the cycle search, the algorithm considers both directions of reversible reactions, enabling the identification of different directed cycles. The direction of a reversible reaction that allows for cycle formation is retained. This selection is made prior to the direction choice in the reversible reaction management step.
The step is done if `doMainChain = true`.
Some nodes representing the "main chain" are grouped into a cluster subgraph in the viz.js library (Sugiyama layout), which may enhance or negatively impact the resulting drawing. You can test the effect of this step by enabling or disabling it.
Main chains are defined as merges of paths in the network. Start nodes are determined by the algorithm, after which the network is converted into a Directed Acyclic Graph (DAG) with no directed cycles. The longest path(s) is identified from each start nodes. If multiple paths share common nodes, they can be merged (`merge = true`), with each merged path representing a main chain. If `merge = false` and paths have common nodes, only the longest of them is defined as a main chain.
Several versions of the path search are implemented :
- `LONGEST` : when longest paths are searched from a start node, if several are found, only one will be keeped
- `ALL_LONGEST` : when longest paths are searched from a start node, all the longest are keeped
- `ALL` : when longest paths are searched from a start node, all paths between start node and terminal node of longest paths are keeped
`ALL_LONGEST` is the default version, to change it you need to change the parameter pathType with the defined type `PathType` (ex : `parameters.pathType = PathType.ALL`).
If `shiftCoord = true`, the node's coordinates represent its top-left corner; otherwise, they represent the node's center (default). When shifting the coordinates, the adjustment is made so that positioning the node by its top-left corner keeps it centered in its original location. Specifically, the coordinates are shifted by half the node's height and width. To define a node's `height` and `width`, refer to [type for style](#types-for-style). This is useful when rendering networks in SVG format.