Skip to content
  1. Examples

Vector layers and GeoJSON

This example shows how to add custom layers in Geo mode and to make them interract with the nodes. While dragging a node, the map will update the shapes to show the countries the node is in.
GeoJSON Europe shape source: Github repo.
Note: Linkurious SAS makes no warranty, expressed or implied, as to the shapes borders obtained from the use of the GeoJSON on the website.

js
import Ogma from '@linkurious/ogma';

// This demo uses 2 external dependencies in addition to Leaflet and Ogma for the example code:
// * leaflet-pip: a leaflet plugin to check if a point is within a GeoJSON shape
// * lodash.debounce: a debounce function implementation
// feel free to replace them with your custom ones if you need to

Ogma.libraries['leaflet'] = L;

let graph = {
  nodes: [
    {
      id: 'Station A',
      data: { latitude: 48.858838, longitude: 2.343436 },
      attributes: { radius: 10, text: 'Drag me!', x: 0, y: 0 }
    },
    {
      id: 'Station B',
      data: { latitude: 51.509615, longitude: -0.134514 },
      attributes: { radius: 10, text: 'Drag me!', x: 100, y: 0 }
    }
  ],
  edges: [
    {
      id: 'Eurostar',
      source: 'Station A',
      target: 'Station B',
      attributes: { width: 5, text: 'Eurostar' }
    }
  ]
};

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

// this is the reference to the layer we're going to add to the leaflet map
let countriesShapesLayer;

// fetch the GeoJSON data
const getGeoJSONData = () =>
  fetch('files/eu-shapes.geojson').then(response => response.json());

const updateShapesVisibility = nodeList => {
  // for each node check if it's inside a country shape and return the country name
  let countries = nodeList.getGeoCoordinates().map(coords => {
    // list of country polygons that contain the provided node point
    let polygons = leafletPip.pointInLayer(
      L.latLng([coords.latitude, coords.longitude]),
      countriesShapesLayer,
      true
    );
    // just return the first one: countries do not overlap, the array is made of a single element.
    return polygons[0];
  });
  // now iterate through all country shapes in the layer and set the opacity
  countriesShapesLayer.getLayers().forEach(country => {
    let isInside = countries.indexOf(country) > -1;
    // play with
    country.setStyle({
      opacity: isInside ? 1 : 0,
      fillOpacity: isInside ? 0.2 : 0
    });
  });
};
// Enable Geo mode making the nodes draggable
ogma.geo
  .enable({ disableNodeDragging: false })
  .then(() => getGeoJSONData())
  .then(shapesData => {
    // ask the map from Ogma
    let map = ogma.geo.getMap();
    // add a custom layer to the map
    countriesShapesLayer = L.geoJSON(shapesData, {
      style: {
        color: '#ff7800',
        weight: 5
      }
    }).addTo(map);
    // now apply a filter based on the graph
    let nodeList = ogma.getNodes();
    updateShapesVisibility(nodeList);

    // When a node is dragged update the map
    // debounce the update for performance reason: ogma is pretty fast at notify but the
    // leafletPip.pointInLayer method is calculationally heavy
    ogma.events.on('dragProgress', () => {
      requestAnimationFrame(() => updateShapesVisibility(nodeList));
    });

    // set the center in europe
    return ogma.geo.setView(49.429053, 15.140625, 4);
  });
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>
    <script src="https://cdn.jsdelivr.net/npm/unfetch@4.2.0/dist/unfetch.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@mapbox/leaflet-pip@1.1.0/leaflet-pip.min.js"></script>
    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>
    <script src="index.js"></script>
  </body>
</html>
css
html,
body {
  margin: 0;
}

#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
}