Skip to content
  1. Examples

Density contours

See how you can render the density controus using d3-contours in a background layer. This example also shows how you can pre-calculate and cache the contours to improve performance and appearance.

ts
import Ogma, { Point } from '@linkurious/ogma';
import * as d3 from 'd3';

const ogma = new Ogma({
  container: 'graph-container',
  options: {
    interactions: {
      zoom: { maxValue: () => Infinity },
      drag: { enabled: false }
    }
  }
});

ogma.styles.addRule({
  edgeAttributes: {
    width: 0.5,
    color: '#aaa'
  },
  nodeAttributes: {
    color: '#333'
  }
});

const clamp = (value: number, min: number, max: number) =>
  Math.max(min, Math.min(max, value));

const graph = await Ogma.parse.jsonFromUrl('files/codeminer.json');
await ogma.setGraph(graph);
await ogma.layouts.force({ gpu: true, steps: 80, locate: true });

// shift the graph to the center, d3-contour doesn't work with negative values
const { width, height } = ogma.getNodes().getBoundingBox();
await ogma.getNodes().setAttributes(
  ogma
    .getNodes()
    .getPosition()
    .map(p => ({ x: p.x + width / 2, y: p.y + height / 2 }))
);
await ogma.view.locateGraph();

const positions = ogma.getNodes().getPosition();
const maxLod = 4;
const minLod = 0;

const lods = Array.from({ length: maxLod - minLod + 1 }, (_, i) => i + minLod);

// pre-calculate lods
const contours = lods.map(zoom =>
  d3
    .contourDensity<Point>()
    .x(d => d.x)
    .y(d => d.y)
    .size([width, height])
    .bandwidth(120)
    .thresholds(zoom * 10)(positions)
);

const render = (ctx: CanvasRenderingContext2D) => {
  const renderContour = d3.geoPath().context(ctx);
  const zoom = ogma.view.getZoom();
  ctx.beginPath();
  ctx.strokeStyle = 'red';
  const lod = clamp(Math.round(zoom * 10), minLod, maxLod);
  const LODcontour = contours[lod];
  ctx.lineWidth = 1 / ogma.view.getZoom();

  LODcontour.forEach(contour => renderContour(contour));
  ctx.closePath();
  ctx.stroke();
};

const layer = ogma.layers.addCanvasLayer(ctx => render(ctx));
layer.moveToBottom();
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>
    <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;
}
json
{
  "name": "test",
  "version": "0.0.0",
  "description": "Ogma, large-scale graph visualization library",
  "main": "index.ts",
  "module": "index.ts",
  "scripts": {
    "start": "vite dev --open",
    "build": "vite build"
  },
  "dependencies": {
    "@linkurious/ogma": "https://get.linkurio.us/api/get/npm/5.2.0/?secret=YOUR_API_KEY",
    "d3": "7.9.0"
  },
  "devDependencies": {
    "vite": "latest"
  }
}