All versions of this documentation
X

Radial layout on a transport network

This is Paris metro map. Click on any station to position other stations according to the shortest path from the selected one. Use 'Push factor' control to adjust the density of the nodes on the layers, thus making them easier to read.

Open in a new window.
          <!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../build/ogma.min.js"></script>
  <style>
    #graph-container { top: 0; bottom: 0; left: 0; right: 0; position: absolute; margin: 0; overflow: hidden; }

    #options {
      position:   absolute;
      top:        20px;
      right:      20px;
      padding:    10px;
      background: white;
      z-index:    400;
    }

    #options label {
      display: block;
    }

    #options .controls {
      text-align: center;
      margin-top: 10px;
    }

    #options .content {
      line-height: 1.5em;
    }

    .control-bar {
      font-family:   Helvetica, Arial, sans-serif;
      box-shadow:    0 1px 5px rgba(0,0,0,0.65);
      border-radius: 4px;
    }

    .attribution {
      position: absolute;
      right: 0px;
      bottom: 0px;
      padding: 0px;
      z-index: 1000;
      font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
      font-size: 11px;
      padding: 1px 5px;
      background: rgba(255, 255, 255, 0.7);
    }
  </style>
</head>
<body>
  <div id="graph-container"></div>
    <div class="attribution">Paris Metro | Graph data © <a target="_blank" href="https://github.com/totetmatt/gexf/tree/master/metro/Paris">Matthieu Totet</a> CC-BY-SA
  </div>
  <form id="options" class="control-bar">
    <div class="content">
      <label><input type="range" id="push-ratio" value="1" min="0.1" max="2" step="0.1" /> Push factor</label>
      <label><input type="checkbox" id="overlap" /> allow nodes overlap</label>
      <label><input type="checkbox" id="randomize" /> randomize nodes</label>
    </div>
    <div class="controls">
      <button id="reset">Reset</button>
    </div>
  </form>

<script>
'use strict';

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

var overlapInput   = document.querySelector('#overlap');
var repulsionInput = document.querySelector('#push-ratio');
var randomizeInput = document.querySelector('#randomize');

function run (centralNodeId) {
  var overlap   = overlapInput.checked;
  var repulsion = repulsionInput.value;
  var randomize = randomizeInput.checked;

  // disable UI while layout is running
  overlapInput.disabled   =
  repulsionInput.disabled =
  randomizeInput.disabled = true;

  console.time('radial stress');
  ogma.layouts.radial({
    locate:       true, // locate nodes while calculating the layout
    centralNode:  centralNodeId,
    randomize:    randomize,
    allowOverlap: overlap,
    repulsion:    repulsion,
    radiusDelta:  130,
    duration:     300
  }).then(function() {
    console.timeEnd('radial stress');
    // enable UI
    overlapInput.disabled   =
    repulsionInput.disabled =
    randomizeInput.disabled = '';
  });
}


ogma.parse.jsonFromUrl('files/paris-metro.json').then(function (graph) {
  ogma.setGraph(graph);
  ogma.view.locateGraph();

  centralNode = ogma.getNode("ChâteletPARIS-01ER");
  centralNode.setSelected(true);

  ogma.events.onClick(function (evt) {
    if (evt.target && evt.target.isNode) {
      centralNode = evt.target.getId();
      run(evt.target.getId());
    }
  });

  ogma.events.onLayoutComplete(function (evt) {
    // store initial positions
    if (!initialPositions) {
      initialPositions = evt.positions.before;
      nodeIds = evt.ids;
    }
  });

  setTimeout(function() {
    run(centralNode);
  }, 1000);
});

// to store the positions before layout
var initialPositions, nodeIds;
var centralNode;

// reset to initial positions
function reset () {
  ogma.clearSelection();
  centralNode = null;

  if (nodeIds) ogma.getNodes().setPosition(initialPositions)
}

document.querySelector('#options').addEventListener('change', function () {
  if (centralNode) {
    Ogma.utils.requestAnimFrame(function() {
      run(centralNode);
    });
  }
});

document.querySelector('#reset').addEventListener('click', function (evt) {
  evt.preventDefault();
  reset();
});
</script>
</body>
</html>