LineLayer with arrows

by felixp

HTML

<!DOCTYPE html>
<html>

  <head>
    <title>deck.gl+Google</title>
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.4.0/mapbox-gl.js"></script>
    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.4.0/mapbox-gl.css" rel="stylesheet" />

    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script src="https://unpkg.com/[email protected]/dist.min.js"></script>
    <script src="https://unpkg.com/@deck.gl/[email protected]/dist.min.js"></script>

    <!-- jsFiddle will insert css and js -->
  </head>

  <body>
    <div id="map"></div>
  </body>

</html>

CSS

/* Always set the map height explicitly to define the size of the div element that contains the map. */
#map {
  height: 100%;
}

/* Optional: Makes the sample page fill the window. */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

JavaScript

const data =
  'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson';
const dataTransform = d => d.features.filter(f => f.properties.scalerank < 3);

const origin = [14.42076, 50.08804]; // Prague

const deckgl = new deck.DeckGL({
  container: 'map',
  mapStyle: deck.carto.BASEMAP.DARK_MATTER,

  initialViewState: {
    latitude: 40,
    longitude: 0,
    zoom: 1
  },
  controller: true,

  onViewStateChange: update
});

function update({
  viewState
}) {
  const flightsLayer = new deck.LineLayer({
    id: 'flights',
    data,
    dataTransform,
    getSourcePosition: origin,
    getTargetPosition: f => f.geometry.coordinates,
    getColor: [0, 128, 200],
    getWidth: 2
  });

  // Project positions to get correct rotation in screen space
  const viewport = new deck.WebMercatorViewport(viewState);
  const p2 = viewport.project(origin);
  const arrowLayer = new deck.IconLayer({
    id: 'icon-layer',
    data,
    dataTransform,

    // iconAtlas and iconMapping are required
    // getIcon: return a string
    iconAtlas: 'https://unpkg.com/@mapbox/[email protected]/icons/triangle.svg',
    iconMapping: {
      triangle: {
        x: 0,
        y: 0,
        width: 15,
        height: 15,
        mask: true
      }
    },
    getIcon: d => 'triangle',

    getPosition: f => f.geometry.coordinates,
    getAngle: f => {
      const p1 = viewport.project(f.geometry.coordinates);
      const deltaLng = p1[0] - p2[0];
      const deltaLat = p1[1] - p2[1];
      return 180 * Math.atan2(deltaLng, deltaLat) / Math.PI + 180;
    },

    getSize: d => 1,
    getColor: [0, 128, 200],
    sizeScale: 10,

    updateTriggers: {
      getAngle: [viewport]
    }
  });

  deckgl.setProps({
    layers: [flightsLayer, arrowLayer]
  })
}