Skip to content
  1. Examples

Custom JSON

This example shows how you can pre-process the imported JSON in case when you for some reason cannot do it at an earlier stage. This is especially convenient for prototyping or static graphs. Click on any node to see its raw JSON shape

ts
import Ogma, { RawEdge, RawGraph } from '@linkurious/ogma';

type LineCode = keyof typeof colors;

interface EdgeData {
  code: LineCode | null;
}

interface NodeData {}

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

// data from https://en.wikipedia.org/wiki/Tokyo_subway
const colors = {
  G: '#F9A328',
  M: '#EE2628',
  H: '#C7BEB3',
  T: '#00AFEF',
  C: '#1BB267',
  Y: '#D1A662',
  Z: '#8C7DBA',
  N: '#02B69B',
  F: '#A95E33',
  A: '#EF463C',
  I: '#0072BC',
  S: '#ABBA41',
  E: '#CE1C64'
};

ogma.styles.addRule({
  edgeAttributes: {
    color: edge => colors[edge.getData('code')!],
    width: 12
    //outline: true
  },
  nodeAttributes: {
    radius: 12,
    color: '#ffffff',
    innerStroke: {
      width: 7,
      color: '#505050',
      scalingMethod: 'scaled',
      minVisibleSize: 0
    },
    draggable: false,
    text: {
      minVisibleSize: 0,
      size: 8
    }
  }
});

const customJsonToRawGraph = (input: any): RawGraph<NodeData, EdgeData> => {
  const edges: RawEdge<EdgeData>[] = [];

  // keys are stations
  const edgesVisited = new Set<string>();
  const nodes = Object.keys(input).map(name => {
    const node = input[name];
    // @ts-expect-error
    node.connections.forEach(connection => {
      const source = name;
      const target = connection.target_id;

      // read line codes
      const sourceLine = source.substring(0, 1);
      const targetLine = target.substring(0, 1);

      // make edges bi-directional
      if (
        !edgesVisited.has(source + ':' + target) &&
        !edgesVisited.has(target + ':' + source)
      ) {
        edgesVisited.add(source + ':' + target);
        // identify the line by first symbol
        const code =
          sourceLine === targetLine ? (sourceLine as LineCode) : null;
        edges.push({
          source: name,
          target: connection.target_id,
          data: { code }
        });
      }
    });
    return { id: name, data: node };
  });

  return { nodes, edges };
};

ogma.tools.tooltip.onNodeClick(
  node => {
    return (
      '<div class="arrow"></div>' +
      '<div class="ogma-tooltip-header">' +
      node.getId() +
      '</div>' +
      '<div class="ogma-tooltip-body">' +
      '<code><pre>' +
      JSON.stringify(node.getData(), undefined, 2) +
      '</pre></code>' +
      '</div>'
    );
  },
  { className: 'ogma-tooltip' }
);

Ogma.parse
  .jsonFromUrl('tokyo-subway.json', customJsonToRawGraph)
  .then(graph => ogma.setGraph(graph))
  .then(() => {
    return ogma.layouts.force({
      gravity: 0,
      edgeStrength: 1,
      locate: true
    });
  })
  .then(() => {
    console.log('import done');
    const indicator = document.getElementById('info')!;
    indicator.parentElement!.removeChild(indicator);
  });

ogma.tools.brand.set(
  '<div class="attribution">' +
    '<img src="img/tokyo-metro.png" class="logo">' +
    '<h1>Tokyo Subway</h1>' +
    '<p>Sources: <a href="https://github.com/Jugendhackt/tokyo-metro-data/" target="_blank">graph</a>, <a href="https://en.wikipedia.org/wiki/Tokyo_subway" target="_blank">colors</a></p>' +
    '</div>',
  {
    position: 'bottom-right',
    className: 'myCssClass'
  }
);
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />

    <link
      href="https://fonts.googleapis.com/css?family=Roboto:400,700"
      rel="stylesheet"
      type="text/css"
    />
    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>
    <div id="info">loading...</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;
}

#info {
  position: absolute;
  color: #fff;
  background: #141229;
  font-size: 12px;
  font-family: monospace;
  padding: 5px;
  top: 0;
  left: 0;
}

.attribution {
  font-family: "Roboto", Helvetica, sans-serif;
  padding: 10px;
  text-align: center;
  font-size: 0.75em;
}

.attribution h1 {
  font-size: 1.5em;
}

.attribution .logo {
  width: 80px;
  height: 80px;
}

.ogma-tooltip {
  max-width: 240px;
  max-height: 280px;
  background-color: #fff;
  border: 1px solid #999;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  border-radius: 6px;
  cursor: auto;
  font-family: Arial;
  font-size: 12px;
}

.ogma-tooltip-header {
  font-variant: small-caps;
  font-size: 120%;
  color: #000;
  border-bottom: 1px solid #999;
  padding: 10px;
}

.ogma-tooltip-body {
  padding: 10px;
  overflow-x: hidden;
  overflow-y: auto;
  max-width: inherit;
  max-height: 180px;
  background: #ddd;
}

.ogma-tooltip-footer {
  padding: 10px;
  border-top: 1px solid #999;
}

.ogma-tooltip > .arrow {
  border-width: 10px;
  position: absolute;
  display: block;
  width: 0;
  height: 0;
  border-color: transparent;
  border-style: solid;
}

.ogma-tooltip.top {
  margin-top: -12px;
}

.ogma-tooltip.top > .arrow {
  left: 50%;
  bottom: -10px;
  margin-left: -10px;
  border-top-color: #999;
  border-bottom-width: 0;
}

.ogma-tooltip.bottom {
  margin-top: 12px;
}

.ogma-tooltip.bottom > .arrow {
  left: 50%;
  top: -10px;
  margin-left: -10px;
  border-bottom-color: #999;
  border-top-width: 0;
}

.ogma-tooltip.left {
  margin-left: -12px;
}

.ogma-tooltip.left > .arrow {
  top: 50%;
  right: -10px;
  margin-top: -10px;
  border-left-color: #999;
  border-right-width: 0;
}

.ogma-tooltip.right {
  margin-left: 12px;
}

.ogma-tooltip.right > .arrow {
  top: 50%;
  left: -10px;
  margin-top: -10px;
  border-right-color: #999;
  border-left-width: 0;
}
json
{
  "A01": {
    "connections": [
      {
        "duration": 3,
        "target_id": "A02",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A02": {
    "connections": [
      {
        "duration": 3,
        "target_id": "A01",
        "type": "ride"
      },
      {
        "duration": 1,
        "target_id": "A03",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A03": {
    "connections": [
      {
        "duration": 1,
        "target_id": "A02",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A04",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A04": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A03",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A05",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A05": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A04",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A06",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A06": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A05",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A07",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A07": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A06",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "A08",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A08": {
    "connections": [
      {
        "duration": 3,
        "target_id": "A07",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A09",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A09": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A08",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A10",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "E20",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A10": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A09",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A11",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "G08",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A11": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A10",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A12",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "H09",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A12": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A11",
        "type": "ride"
      },
      {
        "duration": 1,
        "target_id": "A13",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A13": {
    "connections": [
      {
        "duration": 1,
        "target_id": "A12",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A14",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "T10",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A14": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A13",
        "type": "ride"
      },
      {
        "duration": 2,
        "target_id": "A15",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "H13",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A15": {
    "connections": [
      {
        "duration": 2,
        "target_id": "A14",
        "type": "ride"
      },
      {
        "duration": 1,
        "target_id": "A16",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "S09",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A16": {
    "connections": [
      {
        "duration": 1,
        "target_id": "A15",
        "type": "ride"
      },
      {
        "duration": 1,
        "target_id": "A17",
        "type": "ride"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A17": {
    "connections": [
      {
        "duration": 1,
        "target_id": "A16",
        "type": "ride"
      },
      {
        "duration": 1,
        "target_id": "A18",
        "type": "ride"
      },
      {
        "duration": 3,
        "target_id": "E11",
        "type": "walk"
      }
    ],
    "name_en": null,
    "name_jp": null
  },
  "A18": {
    "connections": [
      {
        "d

...