0

I'd like to have a map using OpenStreetMap showing location of flowers. My point is to have LayersControl with colours and type and be able to check for example Orange and Tulip and see only orange tulips on the map but it seems hard from what i read on React LeafLet documentation.

To be easier to understand i will add some code :

Flower datas example:

const flowers = [
  {
    "type": "Tulip",
    "colour": "Orange",
    "latlng": [52.081222, 5.235965],
  },
  {
    "type": "Crocus",
    "colour": "Red",
    "latlng": [52.081421, 5.235534],
  },
]

LeafletMap.jsx (Partial):

const LeafletMap: React.FC = () => {

return (
     <MapContainer id="mapId"
          center={averagePos}
          zoom={zoom}>
            {flowerItems.map((flower, index) => (
              //Maybe do something here like sorting and creating 
              //multiple layers
              //or having already a layer for every type of search 
              //but if i want to add more colours ou type it can be very hard to update
            ))}
        </LayersControl>
     </MapContainer>
   )
}
Jeremy M.
  • 1,154
  • 1
  • 9
  • 29
  • Not clear what you want to achieve. Do you want to filter that array ? – Zied Hf May 19 '22 at 18:27
  • I'm looking for a proper way to have selectbox usable to filter trough flowers and show only the corresponding one. – Jeremy M. May 19 '22 at 18:32
  • Ok so if understand that correctly. Maybe a function could solve this problem. like a helper called searchFlowerByType = (flowers, type) => flowers.filter(flower => flower.type === type). First parameter is the array you pass for the flower list and the second one is the type you're looking for. Otherwise, LeafletMap could be a dump component which just receive that array as it is an display it. Then it's the parent responsability to do the filter and the sorting ... etc. – Zied Hf May 19 '22 at 18:48
  • I could not formulate what you say whit my english but now i can so i will take a look at how to handle this method with Leaflet ! – Jeremy M. May 19 '22 at 18:50
  • Basically, inside the map you don't want to change your data. Do not sort inside a map, do not filter inside it neither. Manupulate your data before that. That's just javascript try play with the map, filter and sort methods to understand better what you need to do. – Zied Hf May 19 '22 at 19:14
  • Otherwise I can speak in french too but it's not allowed in stackoverflow so feel free to reach me somehow if you want – Zied Hf May 19 '22 at 19:15

1 Answers1

0

You could maintain the selected layers using state variables - one array to track the type (Crocus or Tulip) and another to track the colour (red or orange). Initially, the states are setup to show everything:

const [type, setType] = useState(['Tulip', 'Crocus']);
const [colour, setColour] = useState(['Orange', 'Red']); 

For each type and colour you could create a LayersControl where you hook into the add and remove event handlers and update the corresponding state (type or colour) when the layer is checked or unchecked by the user:

<LayersControl.Overlay name="Crocus" checked="true">
  <LayerGroup
    eventHandlers={{
      add: (e) => {
        updateTypeState('Crocus', true);
      },
      remove: (e) => {
        updateTypeState('Crocus', false);
      },
    }}
  ></LayerGroup>
</LayersControl.Overlay>

The functions to update the state look like this (there is one function for updating type and one for updating colour):

  const updateTypeState = (key: string, value: boolean) => {
    setType((prevState) => {
      if (value) {
        prevState = [...prevState, key];
      } else {
        prevState = prevState.filter((e) => e !== key);
      }
      console.log(prevState);
      return prevState;
    });
  };

  const updateColourState = (key: string, value: boolean) => {
    setColour((prevState) => {
      if (value) {
        prevState = [...prevState, key];
      } else {
        prevState = prevState.filter((e) => e !== key);
      }
      return prevState;
    });
  };

You could then render the markers using the map function as you suggested:

{flowers
  .filter((flower) => {
    if (type.length == 0 && colour.length == 0) {
      // No layers selected, so show all
      return true;
    }
    if (type.length > 0 && colour.length > 0) {
      // Colours and types selected
      return (
        type.indexOf(flower.type) >= 0 &&
        colour.indexOf(flower.colour) >= 0
      );
    }
    if (type.length > 0) {
      // Type selected, no colour selected
      return type.indexOf(flower.type) >= 0;
    }
    // Colour selected, no type selected
    return colour.indexOf(flower.colour) >= 0;
  })
  .map((flower, index) => (
    <Marker position={flower.latlng}>
      <Popup>
        {flower.type}, {flower.colour}
      </Popup>
    </Marker>
  ))}

There's a working StackBlitz here, but please note the marker images do not display properly due to this issue, but you should be able to see a broken image icon and click on it to view details of the flower.

Ian A
  • 5,622
  • 2
  • 22
  • 31