Search code examples
reactjsleafletreact-leafletpolylinepolyline-decorator

polylinedacorator with react leaflet 4


I am trying to include arrows to the Polyline in react-leaft. For that I am using polylinedecorator plugin. There is a similar post on this platform. However, it uses withLeaflet module which is not supported in react-leaflet 4.0. How can I make it run without using 'withLeaflet'. I have tried to implement it with the hooks. However, it does not work and need some assistance, how can I make it run.

export default function App(): JSX.Element {
   const polylineRef = useRef<any>(null);

  const arrow = [
{
  offset: "100%",
  repeat: 0,
  symbol: L.Symbol.arrowHead({
    pixelSize: 15,
    polygon: false,
    pathOptions: { stroke: true }
  })
}];


useEffect(()=>{
    L.polylineDecorator(polylineRef.current,{
      patterns: arrow
    })
}, [polylineRef]);

return (

  <MapContainer center={center} zoom={13} scrollWheelZoom={true} style={{height: 'calc(100% - 30px)'}}>
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
    />
    {currentData?.movingActors.map(line =>(<Polyline key={line.id} 
    positions={[line.startLocation, line.endLocation] } ref={polylineRef} 
    color={modeColor(line.mode)}
    />  
    ))}
  </MapContainer>
</>);}

CHANGES MADE TO THE ACCEPTED ANSWER TO MAKE IT RUN

function PolylineDecorator({ patterns, polyline,color }) {
  const map = useMap();
  useEffect(() => {
    if (!map) return;

   L.polyline(polyline, {color}).addTo(map);  // added color property
    L.polylineDecorator(polyline, {
      patterns,
      
    }).addTo(map);
  }, [map]);
 
  return null;
}


 {currentData?.movingActors.map(line =>(<PolylineDecorator key={line.id} patterns ={arrow} polyline={position}  color = {modeColor(line.mode)} />) ) }   //here I used color parameters to dynamically add colors

Solution

  • What you need is a custom react functional component that returns null and has a useEffect with the code to initialize the plugin:

    function PolylineDecorator({ patterns, polyline }) {
      const map = useMap();
    
      useEffect(() => {
        if (!map) return;
    
        L.polyline(polyline).addTo(map);
        L.polylineDecorator(polyline, {
          patterns
        }).addTo(map);
      }, [map]);
    
      return null;
    }
    

    and then use it like:

    <MapContainer...>
          <TileLayer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" />
          <PolylineDecorator patterns={arrow} polyline={polyline} />
     </MapContainer>
    

    Demo