Skip to content
  1. Examples
  2. Geo Mode

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;
}