Skip to content
  1. Examples

CSV

This example shows how to import a CSV file into an Ogma visualisation. The dataset contains edges network data for character relationships within George R. R. Martin's A Storm of Swords, the third novel in his series A Song of Ice and Fire (also known as the HBO television adaptation Game of Thrones). This data was originally compiled by A. Beveridge and J. Shan, "Network of Thrones," Math Horizons Magazine , Vol. 23, No. 4 (2016), pp. 18-22..

The Papa Parse library is used to correctly parse the CSV file.

ts
import Ogma, { RawEdge, RawNode } from '@linkurious/ogma';
import { getIconCode } from './utils';

const ogma = new Ogma({
  container: 'graph-container'
});

// some UI logic

const layout = () =>
  ogma.layouts.force({
    locate: { padding: 80 }
  });

// Add some edge styling rules
ogma.styles.addEdgeRule({
  color: e => '#80ce87',
  shape: 'arrow',
  width: e => Math.log(e.getData('weight'))
});

// Add some node styling rules
ogma.styles.addNodeRule({
  text: {
    content: n => n.getData('label'),
    minVisibleSize: 2
  },
  radius: n => n.getDegree(),
  icon: {
    content: () => getIconCode('icon-circle-user-round'),
    font: 'Lucide',
    color: 'rgb(61,139,223)',
    minVisibleSize: 0
  },
  outerStroke: {
    color: 'rgb(61,139,223)',
    width: 2
  },
  color: 'white'
});

interface Results {
  data: { source: string; target: string; weight: string }[];
}

const toRawGraph = (results: Results) => {
  // format the incoming JSON to the RawGraph format

  // use a Set here to avoid duplicates
  const nodesSet = new Set<string>();
  const edges: RawEdge[] = [];
  results.data.forEach((item, i) => {
    nodesSet.add(item.source);
    nodesSet.add(item.target);
    // mind parallel edges
    const edgeId = item.source + '-' + item.target + '-' + i;
    // cast to Number numeric values as in CSV everything is a string!
    edges.push({
      id: edgeId,
      source: item.source,
      target: item.target,
      data: { weight: Number(item.weight) }
    });
  });

  // now iterate on the nodesSet to create the node list
  const nodes: RawNode[] = [];
  nodesSet.forEach(id => {
    nodes.push({ id, data: { label: id } });
  });

  return ogma.setGraph({ nodes, edges }).then(layout);
};

document.querySelector('#load-csv')!.addEventListener('click', evt => {
  // load the csv from the URL and parse it returning a JSON
  // @ts-expect-error we are using a global variable here
  return Papa.parse('files/got-edges.csv', {
    download: true,
    header: true,
    complete: toRawGraph
  });
});
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />

    <link
      href="https://cdn.jsdelivr.net/npm/lucide-static@0.483.0/font/lucide.css"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
      rel="stylesheet"
    />
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js"
      integrity="sha256-Fh801SO9gqegfUdkDxyzXzIUPWzO/Vatqj8uN+5xcL4="
      crossorigin="anonymous"
    ></script>
    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>
    <div class="toolbar">
      <div class="controls">
        <button id="load-csv" class="btn menu">Click to load CSV</button>
      </div>
    </div>
    <script type="module" src="index.ts"></script>
  </body>
</html>
css
#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

.toolbar {
  display: block;
  position: absolute;
  top: 20px;
  right: 20px;
  padding: 10px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
  border-radius: 4px;
  background: #ffffff;
  color: #222222;
  font-weight: 300;
  max-width: 350px;
}

.controls {
  text-align: center;
  margin-top: 5px;
}

.btn {
  padding: 6px 8px;
  background-color: white;
  cursor: pointer;
  font-size: 18px;
  border: none;
  border-radius: 5px;
  outline: none;
}

.btn:hover {
  color: #333;
  background-color: #e6e6e6;
}

.menu {
  border: 1px solid #ddd;
  width: 80%;
  font-size: 14px;
  margin-top: 10px;
}
ts
// dummy icon element to retrieve the HEX code, it should be hidden
const placeholder = document.createElement('span');
document.body.appendChild(placeholder);
placeholder.style.visibility = 'hidden';

// helper routine to get the icon HEX code
export function getIconCode(className: string) {
  placeholder.className = className;
  const code = getComputedStyle(placeholder, ':before').content;
  return code[1];
}