Search code examples
reactjsmapsreact-map-gl

Get clicked country name reactmapgl


I am using React Map GL and I want to display the name of clicked country (I just need name for other purposes). Currently, I have this code:

import React, { useCallback, useMemo, useState } from "react";
import Map, {
  Marker,
  NavigationControl,
  FullscreenControl,
  ScaleControl,
  GeolocateControl,
  Source,
  Layer,
} from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import Pin from "../../atoms/Pin/Pin";
import "./Map.scss";
import CITIES from "./cities";
import { useNavigate } from "react-router";

const MapNew = () => {
  const navigate = useNavigate();
  const [hoveredCountry, setHoveredCountry] = useState(null);
  const [cursor, setCursor] = useState<string>("auto");

  const onClick = useCallback((event) => {
    const feature = event.features && event.features[0];
    if (feature) {
      const countryName = feature.properties.name;
      window.alert(`Clicked on ${countryName}`);
    }
  }, []);

  const pins = useMemo(
    () =>
      CITIES.map((city, index) => (
        <Marker
          key={`marker-${index}`}
          longitude={city.longitude}
          latitude={city.latitude}
          anchor="bottom"
        >
          <Pin />
        </Marker>
      )),
    []
  );

  const geojsonUrl =
    "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_countries.geojson";

  const sourceId = "countries";
  const layerId = "country-fills";

  return (
    <div className="map-parent-wrapper">
      <Map
        initialViewState={{
          latitude: 51.1657,
          longitude: 10.4515,
          zoom: 3,
          bearing: 0,
          pitch: 0,
        }}
        mapStyle="mapbox://styles/mapbox/navigation-day-v1"
        mapboxAccessToken=""
        scrollZoom={false}
        onClick={onClick}
        cursor={cursor}
      >
        <GeolocateControl position="top-left" />
        <FullscreenControl position="top-left" />
        <NavigationControl position="top-left" />
        <ScaleControl />

        <Source id={sourceId} type="geojson" data={geojsonUrl}>
          <Layer
            id={layerId}
            type="fill"
            source={sourceId}
            paint={{
              "fill-color": [
                "case",
                ["==", ["get", "name"], "Austria"],
                "blue", // Color for Austria
                ["==", ["get", "name"], "Croatia"],
                "green", // Color for Croatia
                "gray", // Default color
              ],
              "fill-opacity": 0.5,
            }}
          />
        </Source>

        {pins}
      </Map>
    </div>
  );
};

export default MapNew;

Whatever I do, I cannot access the name onClick even though the logic for coloring works:

enter image description here

So, I want that when I click on a country that is colored, I get its name.


Solution

  • You can do it this way:

    1. Add a reference for your Map

    const mapRef = useRef(null);

    1. Add onClick function that uses geojson data (and returns clicked country name):
     const onClick = useCallback(async (event) => {
        console.log(mapRef);
        const map = mapRef.current.getMap();
        const features = await map.queryRenderedFeatures(event.point, {
          layers: [layerId],
        });
    
        if (features.length > 0) {
          const clickedCountryName = features[0].properties.name;
          console.log(`clicked: ${clickedCountryName}`);
          navigate(`/${clickedCountryName}`);
        }
      }, []);
    
    1. Add ref and onClick to Map:
    <Map
        initialViewState={{
            latitude: 51.1657,
            longitude: 10.4515,
            zoom: 3,
            bearing: 0,
            pitch: 0,
        }}
        mapStyle="mapbox://styles/mapbox/navigation-day-v1"
        mapboxAccessToken=""
        scrollZoom={false}
        onClick={onClick}
        cursor={cursor}
        ref={mapRef} // add this
    >