[GIS] highlight feature with click in mapbox gl

javascriptmapboxmapbox-glmapbox-gl-js

I have a geojson layer of streets that are highlighted when moused-over.

My objective is now to highlight individual streets red with a click event. Only one street should be able to be highlighted at a time, and should remain highlighted until a different street is clicked.

All of my attempts so far have failed. Any idea as to what needs to be added to the following code?

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>HTML markers from geoJSON url</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.15.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.15.0/mapbox-gl.css' rel='stylesheet'/>
<style>
  body { margin:0; padding:0; }
  #map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>

<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiaXNrYW5kYXJibHVlIiwiYSI6ImNpbHIxMXA3ejAwNWl2Zmx5aXl2MzRhbG4ifQ.qsQjbbm1A71QzVg8OcR7rQ';


var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/dark-v8',
    center: [37.625224, 55.744537,],
    zoom: 13
});

map.on('style.load', function () {
    map.addSource('streets', {
        "type": "geojson",
        "data": "https://iskandarblue.github.io/mapbox/data/simplify_prototype.geojson"
    });




    map.addLayer({
        "id": "m_streets",
        "type": "line",
        "source": "streets",
        "interactive": true,
        "layout": {},
        "paint": {
            "line-color": "#627BC1",
            "line-opacity": 0.0,
            "line-width": 2.5
        }
    });

    map.addLayer({
        "id": "route-hover",
        "type": "line",
        "source": "streets",
        "layout": {},
        "paint": {
            "line-color": "#f48024",
            "line-opacity": 0.9,
            "line-width": 2.5
        },
        "filter": ["==", "rd_name", ""]
    });

    map.addLayer({
    "id" : "street_toggle",
    "source": "streets",
    "type": "line",
    "layout": {"line-join": "round",
              "line-cap": "round"},
    "paint": {
        "line-color": "#FF0000",
        "line-opacity": 0.9,
        "line-width:": 3.5
      }
     });

    map.on('mousemove', function(e) {
        map.featuresAt(e.point, {
            radius: 5,
            layer: ["m_streets"]
        }, function (err, features) {
            if (!err && features.length) {
                map.setFilter('route-hover', ['==', 'rd_name', features[0].properties.rd_name]);
            } else {
                map.setFilter('route-hover', ['==', 'rd_name', '']);
            }
        });
    });

    map.on('click', function(e) {
        map.featuresAt(e.point, {
            radius: 5,
            layer: ["street_toggle"]
        }, function (err, features) {
            if (!err && features.length) {
                map.setFilter('street_toggle', ['==', 'rd_name', features[0].properties.rd_name]);
            } else {
                map.setFilter('street_toggle', ['==', 'rd_name', '']);
            }
        });
    });


});


   //.addTo(map);


</script>
</body>
</html>

Best Answer

You can query the feature using queryRenderedFeature() (you need to upgrade from v0.15 to v0.16) then send the results back into a new layer... This is how I "select" data in most of my apps.

I removed your hover code and added colored streets/selected features for ease of understanding.

I think you could avoid adding geojson and add the resultant vectortilefeature... but geojson is what i know best.

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>HTML markers from geoJSON url</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.16.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.16.0/mapbox-gl.css' rel='stylesheet'/>
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>

<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiaXNrYW5kYXJibHVlIiwiYSI6ImNpbHIxMXA3ejAwNWl2Zmx5aXl2MzRhbG4ifQ.qsQjbbm1A71QzVg8OcR7rQ';


var map = new mapboxgl.Map({
     container: 'map',
     style: 'mapbox://styles/mapbox/dark-v8',
     center: [37.625224, 55.744537,],
     zoom: 13
});

map.on('style.load', function () {
    map.addSource('streets', {
    "type": "geojson",
    "data": "https://iskandarblue.github.io/mapbox/data/simplify_prototype.geojson"
    });

map.addLayer({
    "id": "m_streets",
    "type": "line",
    "source": "streets",
    "interactive": true,
    "layout": {},
    "paint": {
        "line-color": "red",
        // "line-opacity": 0.0,
       "line-width": 2.5
    }
});

map.on('click', function(e) {
    var features = map.queryRenderedFeatures(e.point, { layers: ['m_streets'] });
    if (!features.length) {
        return;
    }
    if (typeof map.getLayer('selectedRoad') !== "undefined" ){         
        map.removeLayer('selectedRoad')
        map.removeSource('selectedRoad');   
    }
    var feature = features[0];
    //I think you could add the vector tile feature to the map, but I'm more familiar with JSON
    console.log(feature.toJSON());
    map.addSource('selectedRoad', {
        "type":"geojson",
        "data": feature.toJSON()
    });
    map.addLayer({
        "id": "selectedRoad",
        "type": "line",
        "source": "selectedRoad",
        "layout": {
            "line-join": "round",
            "line-cap": "round"
        },
        "paint": {
            "line-color": "yellow",
            "line-width": 8
        }
    });
});
});

</script>
</body>
</html>

enter image description here