deck.gl GeoJsonLayer: update feature color on click
Asked Answered
L

1

5

I'm using the deck.gl GeoJsonLayer to display regions on a map. through an event, I was able to make these regions selectable. what I am struggling with, is to change the color of a feature in the feature selection after clicking on it.

this is the render layer function (adapted from here)

_renderLayers(props) {
  const { geofeats } = props;

return [
  new GeoJsonLayer({
    id: 'geojson',
    data: geofeats,
    opacity: 0.8,
    stroked: true,
    filled: true,
    extruded: true,
    wireframe: true,
    getElevation: f => 10000, 
    getFillColor: f =>
    {
        if(f.properties.selected)
        {
            return  [200, 200, 100];
        } else return  [200, 100, 150];

    },
    getLineColor: [255, 255, 255],
    pickable: true,
    onHover: this._onHover, 
    onClick: this._onClick
  })
];
}

the problem is, when I update the selection state of a feature in the feature collection via setState(), the rendering is not updated even though the state change is represented in the data..

this is how I relay the 'geofeats' object:

  render() {
  const {features} = this.state;

  const {mapStyle = 'mapbox://styles/mapbox/light-v9'} = this.props;

return (
  <DeckGL
    layers={this._renderLayers({geofeats: features})}
    effects={this._effects}
    initialViewState={INITIAL_VIEW_STATE}
    controller={true}
  >
    <StaticMap
      reuseMaps
      mapStyle={mapStyle}
      preventStyleDiffing={true}
      mapboxApiAccessToken={MAPBOX_TOKEN}
    />

    {this._renderTooltip}
  </DeckGL>
);
}

I tried it via setState instead of via props - but the result is the same. the feature collection is handed over to the GeoJsonLayer but never updated.

could someone tell me, what I'm doing wrong?

Update: gist example with error reproduction: https://gist.github.com/jaronimoe/efdbb58b3f52c2aac63362a921802cfe

Lenorelenox answered 8/9, 2019 at 18:33 Comment(10)
when you way the selection state is updated in the feature collection, are you meaning you update the properties of your geojson with different color properties for the selected item?Rhomboid
A guess as to why its not updating is maybe the id field of your geojson isn't changing, or the signature of your data object isn't changing maybe (location in memory). You could try updating those to see if it helpsRhomboid
@JohnRuddell I update the "selected" property of the respective geojson feature: getFillColor: f => { if(f.properties.selected) { return [200, 200, 100]; } else return [200, 100, 150]; }, but it seems getFillColor is not re-triggered.. would you know of a code sample where a redraw is triggered successfully?Lenorelenox
I think you need to change the geojson. update the properties for it.Rhomboid
that's what I'm doing - the geojson property is changed but it is not reflected in the color / the color does not update.Lenorelenox
Can you post an example where I can debug? Doubt I can help without actually testing the setupRhomboid
I will do it - but will not be able to get to it until next week. if you know of a working sample code where such a render update is achieved, I could check that in the meanwhile.Lenorelenox
@JohnRuddell I created a gist with the code: gist.github.com/jaronimoe/efdbb58b3f52c2aac63362a921802cfe my geojson object is updated on hover (setting the properties.selected state to true) but the rendering does not update..Lenorelenox
it seem an update trigger has to be specified for deep data changes. deck.gl/#/documentation/deckgl-api-reference/layers/… however, I'm unsure of how to supply the data attribute to the update trigger.Lenorelenox
ok great, let me take a look then!Rhomboid
R
6

Your issue is two fold.

First you never assign a unique ID to each feature, which ends up meaning that every feature would be assigned to selected when you hover one item. this is because in setFeatureSelected you are comparing undefined == undefined aka (currentID == selectedId). Just as a side note you should default to triple equal

Second, you need to tell your GeoJsonLayer how to update the colors. These are the changes you'll need to make:

function setFeatureSelected(features, selfeat) {
  let selectedID = selfeat.properties.id;
  for (let i = 0; i < features.features.length; i++) {
    let currentID = features.features[i].properties.id;

    if (selectedID === currentID) {
      features.features[i].properties.selected = true;
    } else {
      // Make sure to update the others to be false, so that way only one is ever selected
      features.features[i].properties.selected = false;
    }
  }

  return features;
}

Then you want to update initFeatureSelected to actually inject ID's. For now I'm just using the index in the array, but you can use something more explicit if you'd like

function initFeatureSelected(features) {
  for (let i = 0; i < features.features.length; i++) {
    features.features[i].properties.selected = false;

    // Track each feature individually with a unique ID.
    features.features[i].properties.id = i;
  }

  return features;
}

The last part that you need is to tell the GeoJsonLayer that you have new values to re-calculate the getFillColor function. This is done with updateTriggers where you pass the value of the thing that determines if the color should change. Which, in your case is the selected id of your feature

new GeoJsonLayer({
  id: 'geojson',
  data,
  opacity: 0.8,
  stroked: false,
  filled: true,
  extruded: true,
  wireframe: true,
  getElevation: 50, //Math.sqrt(f.properties.valuePerSqm) * 10,
  getFillColor: d => d.properties.selected ? [100, 105, 155] : [55, 205, 155], //COLOR_SCALE(f.properties.growth),
  getLineColor: [255, 255, 255],
  updateTriggers: {
    getFillColor: [
      this.state.hoveredObject
        ? this.state.hoveredObject.properties.id
        : null
    ]
  },
  pickable: true,
  onHover: this._onHover
})
Rhomboid answered 12/9, 2019 at 18:6 Comment(2)
thank you, now I finally got it to work! btw it also works by just pointing to this.state.hoveredObject.properties apparently - but I would have never figured to point to the actual hovered object instead of the entire feature collection. (and sorry for the missing IDs - they are actually in my data but I switched to the demo data to avoid having to upload my geojson file... should have checkd for that.)Lenorelenox
No worries! There's stuff that's lost in translation between a project and the demo. Glad I could help you figure it out! :) But I would recommend that you point to the ID in updateTriggers. That way you have an explicit value for deck.gl to compare. Rather than an object and or it's location in memory.Rhomboid

© 2022 - 2024 — McMap. All rights reserved.