Search code examples
reactjsmapbox-gl-jsturfjs

How to select and highlight a cell from a square grid using turfjs and mapbox js?


I'm trying to select a cell from a square grid layer that I've added to my mapbox js map.

Here it's my code:

import React, { useRef, useEffect, useState } from 'react';
import './App.css';
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
import * as turf from '@turf/turf'

mapboxgl.accessToken = 'MY_TOKEN';


function App() {
  const mapContainer = useRef(null);
  const map = useRef(null);
  const [lng, setLng] = useState(-82.659521999370077); // eslint-disable-line no-unused-vars
  const [lat, setLat] = useState(9.628698847753714); // eslint-disable-line no-unused-vars
  const [zoom, setZoom] = useState(17); // eslint-disable-line no-unused-vars

  useEffect(() => {
    if (map.current) return;
    map.current = new mapboxgl.Map({
      container: mapContainer.current,
      style: 'mapbox://styles/USER/MAP_ID',
      center: [lng, lat],
      zoom: zoom,
      scrollZoom: true
    });

    const bounds = map.current.getBounds();

    console.log(`bounds:`, bounds);

    const NE = bounds.getNorthEast();
    const SW = bounds.getSouthWest();

    var cellSide = 0.02;
    var grid = turf.squareGrid([SW.lng, SW.lat, NE.lng, NE.lat], cellSide, 'kilometers');

    console.log(`squareGrid - before:`, grid);

    // Set all features to highlighted == 'No'
    for (let i = 0; i < grid.features.length; i++) {
      grid.features[i].properties.highlighted = 'No';
    }

    console.log(`squareGrid - after:`, grid);

    map.current.on('load', () => {
      console.log(`-- Loaded --`);
      map.current.addSource('grid-source', {
        'type': "geojson",
        'data': grid,
        'generateId': true
      });
      map.current.addLayer(
        {
          'id': 'grid-layer',
          'type': 'fill',
          'source': 'grid-source',
          'paint': {
            'fill-outline-color': 'rgba(0,0,0,0.1)',
            'fill-color': 'rgba(0,0,0,0.1)'
          }
        }
      );
      map.current.addLayer(
        {
          'id': 'grid-layer-highlighted',
          'type': 'fill',
          'source': 'grid-source',
          'paint': {
            'fill-outline-color': '#484896',
            'fill-color': '#6e599f',
            'fill-opacity': 0.75
          },
          'filter': ['==', ['get', 'highlighted'], 'Yes']
        }
      );

      map.current.on('click', 'grid-layer', function (e) {

        e.features[0].properties.highlighted = 'Yes';
        console.log(`feature:`, e.features[0]);

        const filter = ['match', ['get', 'highlighted'], ['Yes', 'No'], true, false];
        map.current.setFilter('grid-layer-highlighted', filter);
      });
    });

    // Clean up on unmount
    return () => map.current.remove();
  });

  return (
    <div>
      <div className="sidebar">
        Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
      </div>
      <div ref={mapContainer} className="map-container" />
    </div>
  );
}

export default App;

Currently every time I click on a cell I get all the layer 'grid-layer-highlighted' to be visible while I just want to highlight the cell I have clicked.

Please, any help?


Solution

  • Try this (I use index):

    import React, { useRef, useEffect, useState } from 'react';
    import mapboxgl from 'mapbox-gl'; 
    import * as turf from '@turf/turf'
    
    mapboxgl.accessToken = 'TOKEN!';
    
    
    function Mapbox() {
      const mapContainer = useRef(null);
      const map = useRef(null);
      const [lng, setLng] = useState(-82.659521999370077); // eslint-disable-line no-unused-vars
      const [lat, setLat] = useState(9.628698847753714); // eslint-disable-line no-unused-vars
      const [zoom, setZoom] = useState(17); // eslint-disable-line no-unused-vars
      const [flag, setFlag] = useState(false); // eslint-disable-line no-unused-vars
    
      useEffect(() => {
        if (map.current) return;
        map.current = new mapboxgl.Map({
          container: mapContainer.current,
          style: 'mapbox://styles/mapbox/outdoors-v11',
          center: [lng, lat],
          zoom: zoom,
          scrollZoom: true
        });
    
        const bounds = map.current.getBounds();
    
        console.log(`bounds:`, bounds);
    
        const NE = bounds.getNorthEast();
        const SW = bounds.getSouthWest();
    
        var cellSide = 0.01;
        var grid = turf.squareGrid([SW.lng, SW.lat, NE.lng, NE.lat], cellSide, 'kilometers');
    
        console.log(`squareGrid - before:`, grid);
    
        // Set all features to highlighted == 'No'
        for (let i = 0; i < grid.features.length; i++) {
          grid.features[i].properties.highlighted = 'No';
          grid.features[i].properties.id = i;
        }
    
        console.log(`squareGrid - after:`, grid);
    
        map.current.on('load', () => {
          console.log(`-- Loaded --`);
          map.current.addSource('grid-source', {
            'type': "geojson",
            'data': grid,
            'generateId': true
          });
          map.current.addLayer(
            {
              'id': 'grid-layer',
              'type': 'fill',
              'source': 'grid-source',
              'paint': {
                'fill-outline-color': 'rgba(0,0,0,0.1)',
                'fill-color': 'rgba(0,0,0,0.1)'
              }
            }
          );
          map.current.addLayer(
            {
              'id': 'grid-layer-highlighted',
              'type': 'fill',
              'source': 'grid-source',
              'paint': {
                'fill-outline-color': '#484896',
                'fill-color': '#6e599f',
                'fill-opacity': 0.75
              },
              //'filter': ['==', ['get', 'highlighted'], 'Yes']
              'filter': ['==', ['get', 'id'], -1]
            }
          );
    
          
          //click action
          map.current.on('click', 'grid-layer', function (e) {
              var selectIndex = e.features[0].id;
              grid.features[e.features[0].id].properties.highlighted = 'Yes';
              console.log(`highlighted before:`, e.features[0].properties.highlighted);
              e.features[0].properties.highlighted = 'Yes';
              console.log(`feature:`, e.features[0]);
              console.log(`selectIndex:`, selectIndex);
              console.log(`highlighted after:`, e.features[0].properties.highlighted);
    
              //this! 무야호!
              const filter = ['==', ['number', ['get', 'id']], selectIndex];
      
              map.current.setFilter('grid-layer-highlighted', filter);
           
            
          });
        });
    
        // Clean up on unmount
        return () => map.current.remove();
      });
    
      return (
        <div>
          <div className="sidebar">
            Longitude: {lng} | Latitude: {lat} | Zoom: {zoom} | click : {flag == true}
          </div>
          <div ref={mapContainer} className="map-container" />
        </div>
      );
    }
    
    export default Mapbox;
    

    Result:

    Result