Situation: I am plotting a country and all its state boundaries on a map using the react-leaflet. I am plotting each state boundary as a polygon (definitions are passed in a JSON file.) using the function . All the state boundary definition is passed in a single JSON file as different JSON objects. Each object has a unique id.
My Code:
import React from 'react'
import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet'
import * as L from "leaflet";
const Map = (props) => {
let cordinates = [14.716, -14.467] // Dispaly coordinates
return (
// Cordinates of map
<MapContainer center={cordinates} zoom={7} scrollWheelZoom={false}>
{/* Display map */}
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{/* <GeoJSON id={Math.random()} data={country} /> */}
{/* Highlited area in map */}
<GeoJSON data={props.state} onEachFeature={props.handleEachFeature} />
</MapContainer>
)
}
export default Map
I am passing the JSON file and handleEachFeature function(returning the whole JSON file in the console) as props.
What I want to do: When a user clicks on the map, I want to clear up the entire map and only plot the state within which the click was. Basically, the state will be zoomed and I also want to plot its district boundaries (I have definitions for the district boundaries for each state as well).
Approach I am taking: I am trying to capture the id associated with the polygon (corresponding to the state) that was clicked inside the onClick event. I can then erase the existing map and using the captured id I can plot the state (and its districts) clicked. However, no matter which state is clicked, the onClick event is returning me the whole data of all the polygons. Following is my code:
On click handleEachFeature function:
function handleEachFeature(feature, layer) {
layer.on("click", L.bind(handleClick, null, layer));
}
// This is returning whole json file in console. But, I want only Polygon id on which user clicked.
function handleClick(e) {
console.log(e);
}
Things I already tried:
Please suggest any approach using react-leaflet or some other library.
You can do this by storing the unique identifier (cartodb_id
in the provided example) in a variable and then use it to change the style of the geojson and render the clicked district with a specific style.
Using onEachFeature
function you can derive the unique id and zoom to the clicked district. Once you store it in a var you can then filter the geojson by showing only this object that contains the unique id. Since react-leaflet's geojson comp data property is immutable you have to play with the reference (ref
). You can use leaflet's eachLayer
to attach specific style to all objects apart from the clicked. The latter will be achieved by setting the clicked layer style once you filter the geojson via a useeffect
(see code below). Then using leaflet's addData
you can readd the filtered geojson on the map.
export default function Map() {
const [map, setMap] = useState(null);
const geojsonRef = useRef();
const [featureId, setFeatureId] = useState(null);
const handleEachFeature = (feature, layer) => {
layer.on({
click: (e) => {
setFeatureId(feature.properties.cartodb_id);
map.fitBounds(e.target.getBounds());
}
});
};
useEffect(() => {
if (!featureId || !geojsonRef.current) return;
geojsonRef.current.eachLayer((layer) => {
layer.setStyle({
opacity: 0.5,
weight: 0
});
}); // inherited from LayerGroup
const newDistricts = districts.features.filter((district) => {
return district.properties.cartodb_id === featureId;
});
geojsonRef.current.addData(newDistricts);
}, [featureId]);
return (
<MapContainer
center={position}
zoom={9}
style={{ height: "100vh" }}
ref={setMap}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{map && (
<GeoJSON
ref={geojsonRef}
data={districts}
onEachFeature={handleEachFeature}
/>
)}
</MapContainer>
);
}
You could erase it entirely using clearLayers
method but that makes no sense for me because you will end up with showing only the clicked district once you click it. I tried another approach by changing the style of all other districts apart from the clicked one. This way you can click a new one and revert the style of the previous one.
A simple free geojson is used to present the result.