Skip to content
  1. Examples

Show Overlaping Nodes

This example shows how to display nodes in geo mode when they have the same coordinates. Hover a cluster to show the underlying nodes. It uses two instances of Ogma: one in geo mode, the other one in regular mode.

ts
import Ogma from '@linkurious/ogma';

// @ts-expect-error
Ogma.libraries['leaflet'] = L;

const graph = {
  nodes: new Array(10)
    .fill(0)
    .map((_, i) => ({
      id: i,
      data: { latitude: 48.858838, longitude: 2.343436 },
      attributes: { radius: 10, text: 'Paris', x: 0, y: 0 }
    }))
    .concat(
      new Array(10).fill(0).map((_, i) => ({
        id: i + 10,
        data: { latitude: 51.509615, longitude: -0.134514 },
        attributes: { radius: 10, text: 'London', x: 0, y: 0 }
      }))
    ),
  edges: new Array(20).fill(0).map((_, i) => ({
    id: i,
    source: Math.floor(Math.random() * 20),
    target: Math.floor(Math.random() * 20)
  }))
};

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

ogma.styles.addNodeRule(n => n.isVirtual(), {
  radius: 10,
  badges: {
    topRight: {
      scale: 0.6,
      stroke: {
        width: 0.5
      },
      text: n => `${n.getSubNodes()?.size}`
    }
  }
});
ogma.transformations.addNodeGrouping({
  selector: node => node.getData('latitude'),
  groupIdFunction: node => {
    const { longitude, latitude } = node.getData();
    return `${latitude}-${longitude}`;
  },
  nodeGenerator: (nodes, id) => {
    const [latitude, longitude] = id.split('-').map(e => +e);
    return {
      data: {
        latitude,
        longitude
      }
    };
  }
});
const hoverOgmaContainer = document.querySelector(
  '.hoverogma'
) as HTMLDivElement;
const hoverOgma = new Ogma({
  container: hoverOgmaContainer,
  options: {
    width: 150,
    height: 150,
    backgroundColor: 'rgba(250,250,250,0.75)'
  }
});

ogma.events.on('mouseover', ({ target }) => {
  if (!target || !target.isNode || !target.isVirtual()) return;
  const subNodes = target.getSubNodes()!;
  const subEdges = subNodes.getAdjacentEdges({
    filter: 'all',
    bothExtremities: true
  });
  hoverOgmaContainer.classList.remove('hidden');
  hoverOgma
    .setGraph({ nodes: subNodes.toJSON(), edges: subEdges.toJSON() })
    .then(() => hoverOgma.layouts.force({ locate: true }));
  const { x, y } = target.getPositionOnScreen();
  hoverOgmaContainer.style.left = `${x + 50}px`;
  hoverOgmaContainer.style.top = `${y}px`;
});

ogma.events.on(['click', 'dragStart', 'viewChanged'], () => {
  hoverOgmaContainer.classList.add('hidden');
});

ogma.geo
  .toggle({
    duration: 0
  })
  .then(() => ogma.geo.setView(50, 1, 6))
  .then(() => {
    ogma.geo.getMap()!.getContainer().appendChild(hoverOgmaContainer);
  });
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.3/leaflet.css"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.3/leaflet.js"></script>
    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>
    <div class="hoverogma hidden"></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;
}

.hoverogma{
  border-radius: 50%;
  position: absolute;
  z-index: 10000;
  transform: translate(0,-50%);
  overflow: hidden;
  border: 1px dashed black;
}
.hidden{
  display: none;
}