0

Let me explain as short as I can: I have component written in next.js to fetch some data, and render it on MapBox map as heatmap.

Everything is working like a charm until I refresh page with rendered component:

  • heatmap pins disappearing
  • nothing in console

After I change whatever in component file (it can be string, whatever), and save that, on page heatmap pins appear.

Video can be viewed here video

I can paste code in next if someone is interested to help

  • changing ways of fetching data
  • static data loading from file
  • caching
  • updating and downgrading components
  • dynamic import component

Code of component:

import mapboxgl from 'mapbox-gl'
import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography'
import axios from 'axios'
import { useEffect, useState } from 'react'
import ReactMapboxGl from 'react-mapbox-gl'
import * as React from 'react'

const aqiColors = [
  '#00FF00', // Good (0 - 50)
  '#FFFF00', // Moderate (51 - 100)
  '#FFA500', // Unhealthy for Sensitive Groups (101 - 150)
  '#FF0000', // Unhealthy (151 - 200)
  '#800080', // Very Unhealthy (201 - 300)
  '#8B0000' // Hazardous (301 - 500)
]

const aqiRateWords = ['Dobar', 'Umjereno', 'Nezdrav za osjetljive skupine', 'Nezdrav', 'Veoma nezdrav', 'Opasan']

const AirQualityMap = () => {
  const [showLegend, setShowLegend] = useState(false)
  const [locations, setLocations] = useState([])

  useEffect(() => {
    mapboxgl.accessToken = 'pk.eyJ1IjoicmJlcmV0IiwiYSI6ImNsaXc2aG9lMzJkc2QzcW9jdTVqOWI5YXgifQ.ZOagw-O94DX7Dyxz9kuwGA'

    const fetchData = async () => {
      try {
        const response = await axios.get('http://localhost:3001/api/all-stations', { next: { revalidate: 3600 } })
        const data = response.data
        setLocations(data)
        localStorage.setItem('locations', JSON.stringify(data))
      } catch (error) {
        console.error('Error fetching data:', error)
      }
    }

    fetchData()

    const map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/mapbox/dark-v11',
      center: [18.434433, 43.89099],
      zoom: 11
    })

    map.on('load', () => {
      const heatmapData = {
        type: 'FeatureCollection',
        features: locations.map(location => ({
          type: 'Feature',
          properties: {
            name: location.naziv_stanice,
            aqi: parseFloat(location.aqi_izracun)
          },
          geometry: {
            type: 'Point',
            coordinates: location.koordinate.split(',').reverse()
          }
        }))
      }
      map.on('click', 'heatmapLayer', e => {
        const { name, aqi } = e.features[0].properties
        new mapboxgl.Popup().setLngLat(e.lngLat).setHTML(`<b>${name}</b><br>AQI: ${aqi}`).addTo(map)
      })

      map.on('mouseenter', 'heatmapLayer', () => {
        map.getCanvas().style.cursor = 'pointer'
      })

      map.on('mouseleave', 'heatmapLayer', () => {
        map.getCanvas().style.cursor = ''
      })

      // Add heatmap layer
      map.addLayer({
        id: 'heatmapLayer',
        type: 'heatmap',
        source: {
          type: 'geojson',
          data: heatmapData
        },
        paint: {
          // Increase the heatmap weight based on AQI value
          'heatmap-weight': ['interpolate', ['linear'], ['get', 'aqi'], 0, 0, 500, 1],

          // Increase the heatmap intensity based on zoom level
          'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 9, 3],

          // Color ramp for heatmap
          'heatmap-color': [
            'interpolate',
            ['linear'],
            ['heatmap-density'],
            0,
            'rgba(0, 0, 255, 0)',
            0.1,
            aqiColors[0],
            0.3,
            aqiColors[1],
            0.5,
            aqiColors[2],
            0.7,
            aqiColors[3],
            1,
            aqiColors[4]
          ],

          // Heatmap radius in pixels
          'heatmap-radius': 120
        }
      })
    })

    return () => map.remove()
  }, [])

  const handleLegendToggle = () => {
    setShowLegend(!showLegend)
  }

  return (
    <div>
      <div id='map' style={{ borderRadius: '6px', width: '100%', height: '80vh' }}>
        <Button onClick={handleLegendToggle} variant='contained' style={{ left: '20px', top: '20px', zIndex: '999' }}>
          <Typography variant='p' style={{ zIndex: '999', color: 'white' }}>
            {showLegend ? 'Sakrij AQI legendu' : 'Pokaži AQI legendu'}
          </Typography>
        </Button>
        {showLegend && (
          <div
            id='legend'
            style={{
              position: 'absolute',
              bottom: 30,
              left: 30,
              background: '#fff',
              padding: '10px',
              border: '1px solid #ccc',
              borderRadius: '4px',
              fontSize: '10px'
            }}
          >
            <div style={{ fontWeight: 'bold', marginBottom: '5px' }}>AQI legenda</div>
            {aqiColors.map((color, index) => (
              <div key={index}>
                <span
                  style={{
                    backgroundColor: color,
                    width: '20px',
                    height: '20px',
                    display: 'inline-block',
                    marginRight: '5px'
                  }}
                />
                {index * 50 + 1} - {(index + 1) * 50} ({aqiRateWords[index]})
              </div>
            ))}
          </div>
        )}
      </div>
      <noscript>
        <div>WebGL is not supported in your browser.</div>
      </noscript>
    </div>
  )
}

export default AirQualityMap

0 Answers0