Skip to content
  1. Examples
  2. Transformations

Group edges

This example shows how to group parrallel edges by using the edgeGrouping transformation.

ts
import Ogma, { Color, EdgeGrouping } from '@linkurious/ogma';

const NB_EDGES = 10;
const COUNTRY = ['France', 'Russia'] as const;
type Country = (typeof COUNTRY)[number];

type NodeData = {};
type EdgeData = { country: Country };

const ogma = new Ogma<NodeData, EdgeData>({
  container: 'graph-container'
});

const COLORS: Record<Country, Color> = {
  France: '#de1d4d',
  Russia: '#1d60de'
};

ogma.addNodes([
  { id: 'n0', attributes: { x: -50, y: -50 } },
  { id: 'n1', attributes: { x: 50, y: 50 } }
]);

ogma.styles.addEdgeRule({
  color: edge => COLORS[edge.getData('country')] || 'grey'
});

ogma.view.locateGraph();

for (let i = 0; i < NB_EDGES; i++) {
  const country = COUNTRY[(Math.random() * COUNTRY.length) | 0];
  ogma.addEdge({
    id: 'e' + i,
    source: 'n0',
    target: 'n1',
    attributes: {
      text: country,
      width: 2
    },
    data: { country }
  });
}

const input = document.getElementById('animation') as HTMLInputElement;
const button = document.getElementById('group-btn') as HTMLButtonElement;


// Groups all parallel edges that have the same data.country property
// into meta-edges which the size is the sum of every edge and
// the text is the content of data.country:
const transformation: EdgeGrouping<EdgeData, NodeData> = ogma.transformations.addEdgeGrouping({
  groupIdFunction: edge => edge.getData('country'),
  generator: (edges, groupId) => ({
    data: {
      subEdges: edges.getId(),
      country: edges.getData('country')[0]
    },
    attributes: {
      width: edges.reduce((width, edge) => {
        return Number(edge.getAttribute('width')) + width;
      }, 0),
      text: groupId
    }
  }),
  enabled: false
});

button.addEventListener('click', () => {
  const duration = input.checked ? 500 : 0;
  // Disable the button to prevent multiple clicks during the transformation
  button.disabled = true;
  transformation.toggle(duration).then(() => {
    const buttonText = transformation?.isEnabled()
      ? 'Ungroup edges'
      : 'Group edges';
    button.textContent = buttonText;

    // Re-enable the button after the transformation is done
    button.disabled = false;
  });
});

input.addEventListener('change', () => {
  const duration = input.checked ? 500 : 0;
  transformation.getAnimation().duration = duration;
});

ogma.events.on('click', evt => {
  if (evt.target && !evt.target.isNode) {
    const subEdges = evt.target.getData('subEdges');
    if (subEdges) console.log('sub edges:', subEdges);
  }
});
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />

    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div id="graph-container"></div>
    <div class="controls">
      <button id="group-btn">Group edges</button>
      <label for="animation"
        >Animated: <input id="animation" type="checkbox" checked
      /></label>
    </div>
    <script src="./index.ts"></script>
  </body>
</html>
css
#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
}
.controls {
  position: absolute;
  top: 10px;
  left: 10px;
  background: #fff;
  padding: 10px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
  border-radius: 5px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.controls label {
  display: block;
  margin-top: 10px;
}