Search code examples
javascriptreactjsleafletgeojsonreact-leaflet

Adding a Legend and Info about the highlighted state


I have been trying to add a legend and or an info section on the top right to show description about my highlighted state but I have not been able to do so, therefore I have had to post for some guidance.

I am using React 16.13 with typescript, so using hooks at this stage and not class components (which can still be used but trying to avoid it). The library I am using is React-Leaflet. I have been reading online all day and I can not figure out how to add these two things to my project, I guess once I figure out how to do one the other one will be easily implemented.

This is my code below:

const App: React.FC = () => {
  const [mapCenter, setMapCenter] = useState<L.LatLngTuple>([
    36.778259,
    -119.417931
  ]);
  const [geoJson, setGeoJson] = useState<TopoJSON[]>([]);
  const geoJsonRef = useRef<GeoJSON>(null);

  useEffect(() => {
    if (data.type === "Topology") {
      for (let key in data.objects) {
        if (data.objects.hasOwnProperty(key)) {
          setGeoJson(topojson.feature(data, data.objects[key]));
        }
      }
    }
  }, []);

  const highlightFeature = (e: L.LeafletMouseEvent) => {
    let layer = e.target;
    layer.setStyle({
      weight: 5,
      color: "#666",
      dashArray: "",
      fillOpacity: 0.7
    });
    if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
      layer.bringToFront();
    }
  };

  const resetHighlight = (e: L.LeafletMouseEvent) => {
    geoJsonRef.current!.leafletElement.resetStyle(e.target);
  };

  const onEachFeature = (feature: GeoJSON.Feature, layer: L.Layer) => {
    layer.bindPopup(feature.properties!.NAME10);
    layer.on({
      mouseover: highlightFeature,
      mouseout: resetHighlight
    });
  };

  return (
    <div className="App">
      <Map center={mapCenter} zoom={4} maxZoom={5} minZoom={4}>
        <TileLayer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        <GeoJSON
          data={geoJson}
          style={GetStyle}
          onEachFeature={onEachFeature}
          ref={geoJsonRef}
        />
      </Map>
    </div>
  );
 };

export default App;

Sorry in advance for the blob of code. Just not sure how to implement the Legend or info big like the docs show. Any advice or examples would be appreciated, thanks for reading!


Solution

  • React-leaflet is just a wrapper. you just use fragments of native leaflet code to keep your chunks more organised and reusable.

    You can use the native leaflet's code. Only thing you need to do to implement the legend and the highlighted info box is to have a map reference which you can take using useLeaflet hook since you are using hooks.

    I have created for you two wrappers, one for the Legend and one to visualize the geojson and show the info box. Here is the latter:

    const HighlightedGeoJson = () => {
      const { map } = useLeaflet();
    
      useEffect(() => {
        // control that shows state info on hover
        const info = L.control();
    
        info.onAdd = () => {
          info._div = L.DomUtil.create("div", "info");
          info.update();
          return info._div;
        };
    
        info.update = props => {
          info._div.innerHTML =
            "<h4>US Population Density</h4>" +
            (props
              ? "<b>" +
                props.name +
                "</b><br />" +
                props.density +
                " people / mi<sup>2</sup>"
              : "Hover over a state");
        };
    
        info.addTo(map);
    
        const highlightFeature = e => {
          const layer = e.target;
    
          layer.setStyle({
            weight: 5,
            color: "#666",
            dashArray: "",
            fillOpacity: 0.7
          });
    
          if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
            layer.bringToFront();
          }
    
          info.update(layer.feature.properties);
        };
    
        let geojson;
    
        const resetHighlight = e => {
          geojson.resetStyle(e.target);
          info.update();
        };
    
        const zoomToFeature = e => {
          map.fitBounds(e.target.getBounds());
        };
    
        const onEachFeature = (feature, layer) => {
          layer.on({
            mouseover: highlightFeature,
            mouseout: resetHighlight,
            click: zoomToFeature
          });
        };
    
        geojson = L.geoJson(statesData, {
          style,
          onEachFeature: onEachFeature
        }).addTo(map);
      }, []);
    
      return null;
    };
    

    Demo