deck.gl GeoJsonLayer (Path) Example

Demo

Source

<html>
  <head>
    <title>deck.gl GeoJsonLayer (Path) Example</title>

    <script src="https://unpkg.com/deck.gl@^9.0.0/dist.min.js"></script>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src='https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.js'></script>

    <link href='https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.css' rel='stylesheet' />
    <style type="text/css">
      body {
        width: 100vw;
        height: 100vh;
        margin: 0;
        overflow: hidden;
      }
      .deck-tooltip {
        font-family: Helvetica, Arial, sans-serif;
        padding: 6px !important;
        margin: 8px;
        max-width: 300px;
        font-size: 10px;
      }
      #options {
        position: fixed;
        z-index: 1;
        background: #fff;
        top: 0;
        left: 0;
        margin: 20px;
        padding: 12px;
        font-size: 11px;
        box-shadow: 0 0 8px rgba(0,0,0,0.3);
      }
    </style>
  </head>

  <body>
    <div id="options"></div>
    <div id="tooltip"></div>
  </body>

  <script type="text/javascript">

  const {DeckGL, GeoJsonLayer} = deck;

  const COLOR_SCALE = d3.scaleThreshold()
    .domain([0, 4, 8, 12, 20, 32, 52, 84, 136, 220])
    .range([
      [26, 152, 80],
      [102, 189, 99],
      [166, 217, 106],
      [217, 239, 139],
      [255, 255, 191],
      [254, 224, 139],
      [253, 174, 97],
      [244, 109, 67],
      [215, 48, 39],
      [168, 0, 0]
    ]);

  const WIDTH_SCALE = d3.scaleLinear()
    .clamp(true)
    .domain([0, 200])
    .range([10, 2000]);

  let accidents = {};
  let fatalities = {};
  let year = -1;

  const deckgl = new deck.DeckGL({
    initialViewState: {
      longitude: -100,
      latitude: 38,
      zoom: 4,
      pitch: 0,
      minZoom: 2,
      maxZoom: 8
    },
    controller: true,
    mapStyle: 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json',
    pickingRadius: 5,
    layers: [],
    getTooltip
  });

  d3.csv('https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/highway/accidents.csv')
    .then(accidents => {

    // sort accident data
    incidents = {};
    fatalities = {};

    accidents.forEach(a => {
      const r = (incidents[a.year] = incidents[a.year] || {});
      const f = (fatalities[a.year] = fatalities[a.year] || {});
      const key = getKey(a);
      r[key] = a.incidents;
      f[key] = a.fatalities;
    });

    year = accidents[0].year;
    
    initInputs(d3.select('#options'));
    redraw();
  });

  // Update deck.gl layers
  function redraw() {

    const layers = [
      new GeoJsonLayer({
        id: 'geojson',
        data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/highway/roads.json',
        stroked: false,
        filled: false,
        lineWidthMinPixels: 1,
        parameters: {
          depthTest: false
        },

        getLineColor: getLineColor,
        getLineWidth: getLineWidth,

        pickable: true,

        updateTriggers: {
          getLineColor: {year},
          getLineWidth: {year}
        },

        transitions: {
          getLineColor: 1000,
          getLineWidth: 1000
        }
      })
    ];

    deckgl.setProps({layers});
  }

  function getKey({state, type, id}) {
    return `${state}-${type}-${id}`;
  }

  function getLineColor(f) {
    if (!fatalities[year]) {
      return [200, 200, 200];
    }
    const key = getKey(f.properties);
    const fatalitiesPer1KMile = ((fatalities[year][key] || 0) / f.properties.length) * 1000;
    return COLOR_SCALE(fatalitiesPer1KMile);
  }

  function getLineWidth(f) {
    if (!incidents[year]) {
      return 10;
    }
    const key = getKey(f.properties);
    const incidentsPer1KMile = ((incidents[year][key] || 0) / f.properties.length) * 1000;
    return WIDTH_SCALE(incidentsPer1KMile);
  }

  /* UI */

  function initInputs(container) {
    const inputValue = container.append('div').text(`YEAR: ${year}`);

    const yearRange = Object.keys(incidents).map(Number);

    const input = container.append('input').attr('type', 'range')
      .attr('min', d3.min(yearRange))
      .attr('max', d3.max(yearRange))
      .attr('step', 5)
      .attr('value', year)
      .on('input', () => {
        year = input.property('value');
        inputValue.text(`YEAR: ${year}`);
        redraw();
      });
  }

  function getTooltip(info) {
    const props = info.object ? info.object.properties : null;
    let content = '';

    if (props) {
      const key = getKey(props);
      const r = incidents[year][key];
      const f = fatalities[year][key];

      let content = `<big>${props.name} (${props.state})</big>`;
      if (r) {
        content += `
  <div>
    <b>${f}</b> people died in
    <b>${r}</b> crashes
    on ${props.type === 'SR' ? props.state : props.type}-${props.id}
    in <b>${year}</b>
  </div>`;
      } else {
        content += `<div>no accidents recorded in <b>${year}</b></div>`;
      }
      return {html: content};
    } else {
      return null;
    }
  }

  </script>
</html>