Search code examples
reactjsdeck.gl

Hover in deck.gl


I've run into an impasse in deck.gl and would hope you can help me out guys. I haven´t been able to understand how you display the properties of the bars in the hexagonLayer example that is displayed in the deck.gl official webpage: https://uber.github.io/deck.gl/#/examples/core-layers/hexagon-layer. The example that is available in GitHub doesn't have this functionality and the browser layer is a too intricate to for me. I tried incorporating the layer-info component that is embedded in the layer-browser example, but I don´t understand what is (info), and I keep getting this error :

"Module parse failed: You may need an appropriate loader to handle this file type.

Thank you very much for your time! I'm a newbie in react! I'm sorry if it's trivial.

App.js

    import React, {Component} from 'react';
    import {render} from 'react-dom';
    import MapGL from 'react-map-gl';
    import DeckGLOverlay from './deckgl-overlay.js';
    import Controls from './components/controls';
    import {csv as requestCsv} from 'd3-request';
    import LayerInfo from './components/layer-info';
    // Set your mapbox token here
    const MAPBOX_TOKEN =MBToken

    // Source data CSV
    const DATA_URL =
      'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv'; // eslint-disable-line

    class Root extends Component {
      constructor(props) {
        super(props);
        this.state = {
          datad: {
            package: 'react-dat-gui',
            radius: 1100,
            isAwesome: true,
            feelsLike: '#2FA1D6',
          },
          viewport: {
            ...DeckGLOverlay.defaultViewport,
            width: 500,
            height: 500
          },
          data: null,
          hoveredItem: null,
          clickedItem: null,
          queriedItems: null
        };



      requestCsv(DATA_URL, (error, response) => {
          if (!error) {
            const data = response.map(d => [Number(d.lng), Number(d.lat)]);
            this.setState({data});
          }
        });
      }

      componentDidMount() {
        window.addEventListener('resize', this._resize.bind(this));
        this._resize();
      }


      _resize() {
        this._onViewportChange({
          width: window.innerWidth,
          height: window.innerHeight
        });
      }

      _onViewportChange(viewport) {
        this.setState({
          viewport: {...this.state.viewport, ...viewport}
        });
      }

      onParentUpdate(datad) {
        this.setState({ datad });
      }

      onHover(info) {

  this.setState({hoveredItem: info});
}


  render() {
    const {viewport, datad, data} = this.state;

    return (
      <div>
          <MapGL
            {...viewport}
            mapStyle="mapbox://styles/mapbox/dark-v9"
            onViewportChange={this._onViewportChange.bind(this)}
            mapboxApiAccessToken={MAPBOX_TOKEN}
          >
            <DeckGLOverlay viewport={viewport}
            data={data || [] }
            datad = {this.state.datad }
            onLayerHover= {this.onHover}
          />
          </MapGL>

          <Controls
          onUpdate = {(datad) => this.onParentUpdate(datad)}
          datad = {this.state.datad}
          />

          <LayerInfo
            hovered={hoveredItem}
          />

         </div>

        );
      }
    }

    render(<Root />, document.body.appendChild(document.createElement('div')));

layer-info.js

import React, {PureComponent} from 'react';

export default class LayerInfo extends PureComponent {
  _infoToString(info) {
    const object = info.feature || info.object;
    if (!object) {
      return 'None';
    }
    const props = object.properties || object;
    return JSON.stringify(props);
  }

  render() {
    const {hovered, clicked, queried} = this.props;

    return (
      <div>
        {hovered && (
          <div>
            <h4>Hover</h4>
            <span>
              Layer: {hovered.layer.id} Object: {this._infoToString(hovered)}
            </span>
          </div>
        )}
      </div>
    );
  }
}

deckgl-overlay.js

import React, {Component} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import DeckGLOverlay from './deckgl-overlay.js';
import Controls from './components/controls';
import {csv as requestCsv} from 'd3-request';
import LayerInfo from './components/layer-info';
// Set your mapbox token here
const MAPBOX_TOKEN = MBToken; // eslint-disable-line

// Source data CSV
const DATA_URL =
  'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv'; // eslint-disable-line

class Root extends Component {
  constructor(props) {
    super(props);
    this.state = {
      datad: {
        package: 'react-dat-gui',
        radius: 1100,
        isAwesome: true,
        feelsLike: '#2FA1D6',
      },
      viewport: {
        ...DeckGLOverlay.defaultViewport,
        width: 500,
        height: 500
      },
      data: null,
      hoveredItem: null,
      clickedItem: null,
      queriedItems: null
    };

    requestCsv(DATA_URL, (error, response) => {
      if (!error) {
        const data = response.map(d => [Number(d.lng), Number(d.lat)]);
        this.setState({data});
      }
    });
  }

  componentDidMount() {
    window.addEventListener('resize', this._resize.bind(this));
    this._resize();
  }


  _resize() {
    this._onViewportChange({
      width: window.innerWidth,
      height: window.innerHeight
    });
  }

  _onViewportChange(viewport) {
    this.setState({
      viewport: {...this.state.viewport, ...viewport}
    });
  }

  onParentUpdate(datad) {
    this.setState({ datad });
  }

  onHover(info) {
  this.setState({hoveredItem: info});
}


  render() {
    const {viewport, datad, data} = this.state;

    return (
      <div>
          <MapGL
            {...viewport}
            mapStyle="mapbox://styles/mapbox/dark-v9"
            onViewportChange={this._onViewportChange.bind(this)}
            mapboxApiAccessToken={MAPBOX_TOKEN}
          >
            <DeckGLOverlay viewport={viewport}
            data={data || [] }
            datad = {this.state.datad }
            onLayerHover= {this.onHover}
          />
          </MapGL>

          <Controls
          onUpdate = {(datad) => this.onParentUpdate(datad)}
          datad = {this.state.datad}
          />

          <LayerInfo
            hovered={hoveredItem}
          />

     </div>

    );
  }
}

render(<Root />, document.body.appendChild(document.createElement('div')));

update: I now get another error "Uncaught ReferenceError: hoveredItem is not defined"


Solution

  • In deckgl-overlay.js in the render method, you pass hoveredItem to the LayerInfo but there is no definition for it thus the Uncaught ReferenceError.

    As it seems to be stored in the component state, you should define it at the top of the render method like this:

    const {viewport, datad, data, hoveredItem} = this.state;