emson
emson

Reputation: 10505

Mapbox mouseover circle to change color using feature-state

I have a problem making a marker change color during mouseover on a Mapbox map.
The 0.47 version of mapbox-gl.js allows you to use feature-state to change the feature for dynamic styling.

This works correctly for the Mapbox example: https://www.mapbox.com/mapbox-gl-js/example/hover-styles/

However with my example it seems that the styling never get's triggered when the feature marker's feature-state changes.

I have managed to get mouseover working using the map.setFilter function, however when I add 1000s of markers this becomes very inefficient and slow.

Any tips will be gratefully received, many thanks.

Please see my example code...

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8' />
    <title>Create a hover effect</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.css' rel='stylesheet' />
    <style>
        body { margin:0; padding:0; }
        /* #map { position:absolute; top:0; bottom:0; width:100%; } */
        #map { width:100%; height: 500px; }
    </style>
</head>
<body>

<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiY29kYm9kIiwiYSI6IjhjbFE1aUUifQ.Gimi98Oh3Uex9WQZlb5Wkw';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v9',
    center: [-0.1507524, 51.5140731],
    zoom: 9
});
var hoveredStateId =  null;

map.on('load', function () {
    map.addSource("sites", {
        "type": "geojson",
        "data": {
          "type": "FeatureCollection",
          "features": [
            {
              "type": "Feature",
              "geometry": {
                "type": "Point",
                "coordinates": [
                  -0.1407524,
                  51.5240731
                ]
              },
              "properties": {
                "id": 2,
                "name": "Oxford Street"
              }
            }
          ]
        }
    });

    // The feature-state dependent fill-opacity expression will render the hover effect
    // when a feature's hover state is set to true.
    map.addLayer({
        'id': 'sites-bold',
        'type': 'circle',
        'source': 'sites',
        'layout': {},
        'paint': {
          'circle-radius': 10,
          // 'circle-color': '#ff442b',
          'circle-color': ['case',
            ['boolean', ['feature-state', 'hover'], false],
              'red',
              'blue'
            ],
          'circle-opacity': 0.6,
          'circle-stroke-color': 'cyan',
          'circle-stroke-width': 1
        }
      });


    // When the user moves their mouse over the state-fill layer, we'll update the
    // feature state for the feature under the mouse.
    map.on("mousemove", "sites-bold", function(e) {
      // console.log('in', e.features)
      if (e.features.length > 0) {
          // if (this.hoveredStateId) {
          //     map.setFeatureState({source: 'sites', id: this.hoveredStateId}, { hover: false});
          // }
          this.hoveredStateId = e.features[0].properties.id;
          console.log('in-f-id', this.hoveredStateId)
          map.setFeatureState({source: 'sites', id: this.hoveredStateId}, { hover: true});
          console.log('in-f', e.features[0])
      }
      console.log('in', map.getFeatureState({source: 'sites', id: this.hoveredStateId}))
    });

    // When the mouse leaves the state-fill layer, update the feature state of the
    // previously hovered feature.
    map.on("mouseleave", "sites-bold", function() {
      console.log('out-id', this.hoveredStateId)
      // if (this.hoveredStateId) {
          map.setFeatureState({source: 'sites', id: this.hoveredStateId}, { hover: false});
      // }
      console.log('out', map.getFeatureState({source: 'sites', id: this.hoveredStateId}))
      hoveredStateId =  null;
    });
});
</script>

</body>
</html>

Upvotes: 5

Views: 5196

Answers (1)

Ryan Hamley
Ryan Hamley

Reputation: 2029

To use setFeatureState, you'll need to set a unique ID on each feature in the data source. So literally all you need to do is add an ID like so:

map.addSource("sites", {
    "type": "geojson",
    "data": {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "id": 2,
          "geometry": {
            "type": "Point",
            "coordinates": [
              -0.1407524,
              51.5240731
            ]
          },
          "properties": {
            "id": 2,
            "name": "Oxford Street"
          }
        }
      ]
    }
});

Note the id property on the feature. Nesting it inside properties isn't sufficient; it has to be at the top-level for the feature. The documentation for the method should be updated when the next release comes out this week.

Upvotes: 6

Related Questions