Search code examples
javascriptreactjsdeck.gl

change layer property based on slider input with deck.gl


I am following an example provided on the deck.gl github repository that displays polygons from a geojson.

I've since changed the initial focus of the map and provided my own geojson to visualise, the data I've replaced the examples with has a temporal component that I'd like to visualise via the manipulation of a range input.


Example GeoJSON Structure

{
"type": "FeatureCollection",
"name": "RandomData",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature",
    "properties": { "id": 1,"hr00": 10000, "hr01": 12000, "hr02": 12000, "hr03": 30000, "hr04": 40000, "hr05": 10500, "hr06": 50000}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 103.73992, 1.15903 ], [ 103.74048, 1.15935 ], [ 103.74104, 1.15903 ], [ 103.74104, 1.15837 ], [ 103.74048, 1.15805 ], [ 103.73992, 1.15837 ], [ 103.73992, 1.15903 ] ] ] } } ] }

Instead of repeating each geometry for every timepoint I've shifted the temporal aspect of the data to the properties. This makes the file size manageable on the complete dataset (~50mb versus ~500mb).


For visualising a single time point I know that I can provide the property to getElevation and getFillColor.

_renderLayers() {
    const {data = DATA_URL} = this.props;

    return [
      new GeoJsonLayer({
        id: 'geojson',
        data,
        opacity: 0.8,
        stroked: false,
        filled: true,
        extruded: true,
        wireframe: true,
        fp64: true,
        getElevation: f => f.properties.hr00,
        getFillColor: f => COLOR_SCALE(f.properties.hr00),
        getLineColor: [255, 255, 255],
        lightSettings: LIGHT_SETTINGS,
        pickable: true,
        onHover: this._onHover,
        transitions: {
          duration: 300
        }
      })
    ];
  }

So I went ahead and used range.slider, adding code to my app.js, this following snippet was added. I believe I also may be placing this in the wrong location, should this exist in render()?

import ionRangeSlider from 'ion-rangeslider';

// Code for slider input
$("#slider").ionRangeSlider({
      min: 0,
      max: 24,
      from: 12,
      step: 1,
      grid: true,
      grid_num: 1,
      grid_snap: true
  });
$(".js-range-slider").ionRangeSlider();

added to my index.html

<input type="text" id="slider" class="js-range-slider" name="my_range" value=""/>

So how can I have the slider change which property of my geojson is being supplied to getElevation and getFillColor?

My JavaScript/JQuery is lacking and I have been unable to find any clear examples of how to change the data property based on the input, any help is greatly appreciated.

Here is a codesandbox link - doesn't seem to like it there however. Locally with npm install and npm start should have it behave as intended.


Solution

  • At first you'll need to tell your dependent accessors about the value that is going to be changed by the slider. This can be done by using updateTriggers:

    _renderLayers() {
      const { data = DATA_URL } = this.props;
    
      return [
        new GeoJsonLayer({
          // ...
          getElevation: f => f.properties[this.state.geoJsonValue],
          getFillColor: f => COLOR_SCALE(f.properties[this.state.geoJsonValue]),
    
          updateTriggers: {
            getElevation: [this.state.geoJsonValue],
            getFillColor: [this.state.geoJsonValue]
          }
          // ...
        })
      ];
    }
    

    And to actually change this value using range-slider you need to add onChange callback during the initialization:

    constructor(props) {
        super(props);
        this.state = { hoveredObject: null, geoJsonValue: "hr01" };
        this.sliderRef = React.createRef();
        this._handleChange = this._handleChange.bind(this);
        // ...
    }
    
    componentDidMount() {
      // Code for slider input
      $(this.sliderRef.current).ionRangeSlider({
        // ...
        onChange: this._handleChange
      });
    }
    
    _handleChange(data) {
      this.setState({
        geoJsonValue: `hr0${data.from}`
      });
    }
    
    render() {
      ...
      <DeckGL ...>
        ...
      </DeckGL>
    
      <div id="sliderstyle">
        <input
          ref={this.sliderRef}
          id="slider"
          className="js-range-slider"
          name="my_range"
        />
      </div>
    
      ...
    }
    

    And this is basically it. And here is the full code