Skip to content
  1. Examples

ESRI vector tiles new

This example shows the usage of Ogma geo mode with Esri/ArcGIS Vector tile services. This is done using the esri-leaflet adapter. Note the issues with importing the libraries - it's due to the fact that esri-leaflet doesn't have a ESM build yet. It will be solved by your build system of choice.

ts
import Ogma from '@linkurious/ogma';
// due to the fact that esri-leaflet-vector is not a module, we need
// to import it globally, but normally you would use the following lines:

// import L from 'leaflet';
// import { vectorTileLayer } from 'esri-leaflet-vector';

// Ogma.libraries['leaflet'] = L;

import { exportableEsriVectorTileLayer } from './ExportableEsriVectorLayer';

const graph = {
  nodes: [
    {
      id: 'Paris',
      data: { latitude: 48.858838, longitude: 2.343436 },
      attributes: { radius: 10, text: 'Paris', x: 0, y: 0 }
    },
    {
      id: 'London',
      data: { latitude: 51.509615, longitude: -0.134514 },
      attributes: { radius: 10, text: 'London', x: 100, y: 0 }
    },
    {
      // no geo coordinates in this one, it will be ignored
      id: 'Nowhere',
      attributes: { radius: 10, text: 'Nowhere', x: 100, y: 50 }
    }
  ],
  edges: [
    {
      id: 'Eurostar',
      source: 'Paris',
      target: 'London',
      attributes: { width: 5, text: 'Eurostar' }
    },
    {
      id: 'No road',
      source: 'Paris',
      target: 'Nowhere',
      attributes: { width: 5, text: 'No road' }
    }
  ]
};

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

const toggleGeo = () => {
  // @ts-expect-error
  const esriLayer = exportableEsriVectorTileLayer(
    'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer'
  );

  return ogma.geo.toggle({
    tiles: esriLayer,
    duration: 1000
  });
};

document
  .querySelector<HTMLInputElement>('#export')!
  .addEventListener('click', () => {
    ogma.export.png({
      filename: 'ogma-esri-vector-map.png',
      clip: true
    });
  });

toggleGeo();
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css"
    />
    <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>
    <!-- Load Esri Leaflet from CDN -->
    <script src="https://cdn.jsdelivr.net/npm/esri-leaflet/dist/esri-leaflet.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/esri-leaflet-vector/dist/esri-leaflet-vector.js"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
      rel="stylesheet"
    />

    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>
    <div class="control-bar" id="controls">
      <button id="export">Export as PNG</button>
    </div>
    <script src="index.ts"></script>
  </body>
</html>
css
:root {
  --font: 'IBM Plex Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-size: 12px;
}

html,
body {
  margin: 0;
  font-family: var(--font);
}

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

.attribution {
  font-family: var(--font);
  font-size: 11px;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.7);
}

.control-bar {
  font-family: var(--font);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
  border-radius: 4px;
  background: white;
  padding: 1em;
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 9999;
}

.control-bar button {
  line-height: 24px;
  font-family: var(--font);
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 0.5em 1em;
  transition:
    background-color 0.3s,
    border-color 0.3s,
    color 0.3s;
}

.control-bar button:hover {
  background-color: #fefefe;
  border-color: #0093ff;
  color: #0093ff;
}
.control-bar button:active {
  background-color: #e0e0e0;
  border-color: #666;
}
json
{
  "dependencies": {
    "leaflet": "1.5.0",
    "esri-leaflet": "3.0.0",
    "esri-leaflet-vector": "4.0.0"
  }
}
ts
export const ExportableEsriVectorTileLayer =
  L.esri.Vector.VectorTileLayer.extend({
    options: {
      // This is important for Ogma to be able to place the layer at the right
      // order in the exported image.
      pane: 'tilePane'
    },
    export(ctx: CanvasRenderingContext2D) {
      const mapLibre = this._maplibreGL;
      const glMap = mapLibre._glMap;
      const canvas = mapLibre.getCanvas() as HTMLCanvasElement;
      const size = mapLibre.getSize().round();
      const layerPosition = L.DomUtil.getPosition(mapLibre.getContainer());

      const { x, y } = layerPosition;

      return new Promise<void>(resolve => {
        glMap.on('idle', () => {
          ctx.drawImage(canvas as CanvasImageSource, x, y, size.x, size.y);
          resolve();
        });
        glMap.triggerRepaint();
      });
    }
  });

export const exportableEsriVectorTileLayer = (
  url: string,
  options?: L.Layer['options']
) => new ExportableEsriVectorTileLayer(url, options);