Search code examples
javascriptreactjsleafletreact-leaflet

React and leaflet-velocity


I'm learning maps and using Leaflet which is great, and now I'm digging into some extensions. I'm trying to get leaflet-velocity to work as a React Component, but I seem to be missing something. Running it as a standard index.html with the associated files works fine, but that doesn't integrate into an existing project. Ideally I would like to be able to add in Velocity as an Overlay layer with dynamic markers.

Here is code I have

           <LayerGroup>
                {datas.length > 0 && datas.map((data, index) => {
                    return(
                        <div key={index}>
                        <Marker position={[data.lat, data.long]} icon={color}>
                        </Marker>
                        </div>
                    )})}
                  
                  </LayerGroup>
          </LayersControl.Overlay>
          <LayersControl.Overlay>

              # I was thinking it would be something like this...but nope
              <L.velocityLayer data={windGBR} displayValues={true}></L.velocityLayer>

          </LayersControl.Overlay>

Error:

Objects are not valid as a React child (found: object with keys {options, _initHooksCalled}). If you meant to render a collection of children, use an array instead

So my question is how can I take demo.js and integrate it into React as if it was a regular component? Thanks in advance

P.S. Let me know if I should post the full demo file here or if providing the github link is sufficient...


Solution

  • You cannot include react-leaflet code in combination with vanilla js leaflet code because simply it is not supported

    You need to do the following steps:

    1. replace jquery with fetch API to fetch data or any other of your preference in combination with useEffect
    2. use a ref to track the reference of layersControl.
    3. this ref you are going to pass it to the LeafletVelocity component the one which will include the code to load the plugin. You are going to need to use forwardRef to forward the layersControl reference from the map component to the plugin component.
    4. Last but not least you are going to need to cleanup adding the layer in the useEffect

    Map component:

    export default function Map() {
       const layerControlRef = useRef();
    
      return (
    
    <MapContainer center={position} zoom={5} style={{ height: "100vh" }}>
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <LayersControl position="topright" ref={layerControlRef}>
        <LayersControl.Overlay name="Satellite">
          <TileLayer
            attribution="Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS
        AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
          />
    ...
    }
    

    Leaflet Velocity component:

    const LeafletVelocity = forwardRef((props, ref) => {
      const map = useMap();
    
      useEffect(() => {
        if (!map) return;
    
        let mounted = true;
        let windGbrLayer;
        let waterGbrLayer;
        let windGlobalLayer;
    
        fetch("https://onaci.github.io/leaflet-velocity/wind-global.json")
          .then((response) => response.json())
          .then((data) => {
            if (!mounted) return;
    
            windGlobalLayer = L.velocityLayer({
              displayValues: true,
              displayOptions: {
                velocityType: "GBR Water",
                position: "bottomleft",
                emptyString: "No water data"
              },
              data: data,
              maxVelocity: 0.6,
              velocityScale: 0.1 // arbitrary default 0.005
            });
    
            if (ref.current && windGlobalLayer)
              ref.current.addOverlay(windGlobalLayer, "Wind - Global");
          })
          .catch((err) => console.log(err));
    
     return () => {
          mounted = false;
          if (ref.current) {
            ref.current.removeOverlay(windGbrLayer);
          }
        };
      }, [map]);
    
      return null;
    });
    
    export default LeafletVelocity;
    

    Similar approach for the other two api calls.

    For more check the demo