zoom in dynamically to fit all the marker React-leaflet - reactjs

I'm using react-leaflet. to show the map in my react app. I'm also showing marker on the map too. The problem is the zoom level is not appropriate because sometimes the marker might be quite near to each other and sometimes they will be very far apart. Is there any way to set the zoom level depending on the distance of the markers so that the user can see all the markers at once?
Here is my code
<Map center={center} maxZoom={9} zoom={5}>
<MarkerClusterGroup showCoverageOnHover={false}>
{
markers.map(({fillColor, position, id}) =>
<CircleMarker fillColor={fillColor} color={darken(0.1, fillColor)} radius={10} fillOpacity={1} key={id} center={position} onClick={this.onClick} />
}
</MarkerClusterGroup>
</Map>
P.S: My react-leaflet version is 2.4.0

Assuming MarkerClusterGroup is a component from react-leaflet-markercluster package, the following example demonstartes how to auto-zoom to cover visible markers:
function CustomLayer(props) {
const groupRef = useRef(null);
const { markers } = props;
const mapContext = useLeaflet();
const { map} = mapContext; //get map instance
useEffect(() => {
const group = groupRef.current.leafletElement; //get leaflet.markercluster instance
map.fitBounds(group.getBounds()); //zoom to cover visible markers
}, []);
return (
<MarkerClusterGroup ref={groupRef} showCoverageOnHover={false}>
{markers.map(({ fillColor, position, id }) => (
<CircleMarker
fillColor={fillColor}
radius={10}
fillOpacity={1}
key={id}
center={position}
/>
))}
</MarkerClusterGroup>
);
}
Usage
function MapExample(props) {
const { markers, center } = props;
return (
<Map center={center} maxZoom={9} zoom={5}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
<CustomLayer markers={markers} />
</Map>
);
}

Related

Next js with react-leaflet window is not defined when refreshing page

im using react-leaflet in Next js but when reloading page shows "window is not defined" even i am using dynamic import with ssr:false,
i saw this question made by others here and tried answers that they offered but didn't work, also tried to make the map mounted after component but again no result,
my code:
function ContactPage() {
const MapWithNoSSR = dynamic(() => import('../Map/Map'), {
ssr: false,
})
return (
<div className={styles.map}>
<MapWithNoSSR/>
</div>
)
}
function Map(){
const [markers, setMarkers]= useState([
{cord:[12.3774729,20.446257]},
{cord:[45.3774729,10.45224757]},
{cord:[40.3774729,15.4364757]},
])
<MapContainer center={[12.374729,22.4464757]} zoom={13} scrollWheelZoom={true} style={{height: "100%", width: "100%",zIndex:'1'}}>
<TileLayer
url={`example.com`}
attribution='Map data © <a>Mapbox</a>'
/>
{markers?.map((element,idx)=>{
return <Marker
position={element?.cord}
draggable={true}
animate={true}
key={idx}
>
<Popup>
Test PopUP
</Popup>
</Marker>
})}
</MapContainer>
}}
}
as you were told in the comment, dynamic () has to go outside of the component or screen you are going to return, e. g.
import dynamic from "next/dynamic"
const MyAwesomeMap = dynamic(() => import("../components/Map/index"), { ssr:false })
export default function inicio() {
return(
<>
<p>Example Map</p>
<MyAwesomeMap />
</>
)
}
Your Map component doesn't return anything

React-Leaflet: Polyline does not change colour despite colour value in Redux store updating

I have setup a Redux store which contains a hex colour code. I plan to implement a function where users can select a colour which the line will appear as. However, when the I used the selector to update the color prop of the polyline, the line on the map itself does not change colour. I have already verified the redux store is working fine.
const DashboardLineAnimation: React.FC<Props> = (): ReactElement => {
const [start, setStart] = useState([0, 0]);
const [end, setEnd] = useState([45, 45]);
return (
<div>
<input onChange={() => {setStart([start[0] + 10, start[1] + 10])}}></input>
<Polyline color={useSelector((state: RootState): string => state.attackMap.options.color.hex)} positions={[start as LatLngTuple, end as LatLngTuple]} />
</div>
);
};
You have to use the documented prop pathOptions which is an object the property color on that object and change the value of that property. color prop is available but normally it should be immutable as it is not documented on the official docs while pathOptions is documented as mutable, thus can be changed.
Also make sure the Polyline is a child of MapContainer when you use it. Here is a demo without using redux with a local state variable
function App() {
...
const [color, setColor] = useState("blue");
const handleClick = () => setColor("red");
return (
<>
<button onClick={handleClick}>Change polyline color</button>
<MapContainer center={position} zoom={13} style={{ height: "100vh" }}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Polyline pathOptions={{ color }} positions={polyline} />
</MapContainer>
</>
);
}

How pass props to React Leaflet.markercluster options since v2.0.0?

I tried to pass property with <MarkerClusterGroup {...markerclusterOptions}>
by let markerclusterOptions but i think it is not a good way, because it does not work!
in documentation (# React leaflet markercluster v2.0.0):
Since now you don't need to use options prop to pass Leaflet.markercluster option into <MarkerClusterGroup />.
I do not understand how do to that.
import React from 'react';
import MarkerClusterGroup from 'react-leaflet-markercluster';
let markerclusterOptions: {
maxClusterRadius: 10;
spiderfyDistanceMultiplier: 2;
spiderfyOnMaxZoom: true;
showCoverageOnHover: false;
zoomToBoundsOnClick: true;
};
// == Composant
const Map: React.FC<Props> = ({ lights }) => (
<div className="map">
<MapContainer
center={[46.362205, 1.523151]}
zoom={5}
minZoom={2}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png?api_key=******"
/>
<SetViewOnClick />
<MarkerClusterGroup {...markerclusterOptions}>{
lights.map((light) => (
<Marker
key={light.id}
position={[light.latitude, light.longitude]}
icon={iconPerson}
>
<Tooltip>
Name: {light.user_description} (I{light.index}.G{light.group})
<br />
Power: {light.power_level} %
<br />
Rf Quality: {light.rfquality}/5
</Tooltip>
</Marker>
))
}
</MarkerClusterGroup>
</MapContainer>
</div>
);
export default Map;
Most probably a typo:
let markerclusterOptions: { // with colon ":" you declare a type
maxClusterRadius: 10;
spiderfyDistanceMultiplier: 2;
spiderfyOnMaxZoom: true;
showCoverageOnHover: false;
zoomToBoundsOnClick: true;
}; // no assignment, value is undefined
Should have been:
let markerclusterOptions = { // with equal you assign a value
maxClusterRadius: 10,
spiderfyDistanceMultiplier: 2,
spiderfyOnMaxZoom: true,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
};

Using react-leaflet-geosearch in React Leaflet: cannot read property addControl

Trying to add react-leaflet-geosearch to my leaflet map and I get the error:
Cannot read property 'addControl' of undefined
I do not know what the problem seems to be:
My code:
render() {
const prov = OpenStreetMapProvider();
const GeoSearchControlElement = SearchControl;
return (
<>
<MapContainer
style={{ height: "100%", width: "100%" }}
center={position}
zoom="0"
scrollWheelZoom={true}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://api.mapbox.com/styles/v1/<style>/tiles/256/{z}/{x}/{y}#2x?access_token=<token>"
/>
<Marker position={position} ></Marker>
<ChangeMapView coords={position} />
<GeoSearchControlElement
provider={prov}
showMarker={true}
showPopup={false}
popupFormat={({ query, result }) => result.label}
maxMarkers={3}
retainZoomLevel={false}
animateZoom={true}
autoClose={false}
searchLabel={"Enter address, please"}
keepResult={true}
/>
</MapContainer>
</>
);
You seem to use React Leaflet version 3 since you have a <MapContainer> component.
Unfortunately, the react-leaflet-geosearch plugin you are trying to integrate was made for React Leaflet version 2: https://github.com/TA-Geoforce/react-leaflet-geosearch/blob/master/package.json#L108
Due to the big API changes between these major versions, it is unlikely it will be compatible.
react-leaflet has been re-designed and useLeaflet hook is no longer available in v3 API which makes SearchControl component not compatible with v3
Until react-leaflet-geosearch compatibility with react-leaflet v3 is implemented, the following SearchControl could be considered instead (compatible with react-leaflet v3)
const SearchControl = (props) => {
const map = useMap();
useEffect(() => {
const searchControl = new GeoSearchControl({
provider: props.provider,
...props,
});
map.addControl(searchControl);
return () => map.removeControl(searchControl);
}, [props]);
return null;
};
Usage
class MapComponent extends React.Component {
render() {
const { position, zoom } = this.props;
const prov = OpenStreetMapProvider();
return (
<MapContainer center={position} zoom={zoom}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
<SearchControl
provider={prov}
showMarker={true}
showPopup={false}
popupFormat={({ query, result }) => result.label}
maxMarkers={3}
retainZoomLevel={false}
animateZoom={true}
autoClose={false}
searchLabel={"Enter address, please"}
keepResult={true}
/>
</MapContainer>
);
}
}
Live demo

React leaflet How to have all markers with a specific icons but the one active with a different icon

So I am trying to have an active marker that is different from the rest of the non active marker.
So basically I want something like this.
To display all of the icon I am using the following.
that is displayed inside the map
{parkdata.features.map(park =>(
<Marker key = {park.properties.PARK_ID} position={[
park.geometry.coordinates[1],
park.geometry.coordinates[0]
]}
onClick={()=>{this.selected(park);
The selected function that was intended to be used to change the active icon. But it does not seem to be doing anything
selected(park){
return(
<Marker key = {park.properties.PARK_ID} position={[
park.geometry.coordinates[1],
park.geometry.coordinates[0]
]}
icon= {active}
/>
)
}
UPDATE:
This is what I have now.
SO when I click on one of the markers I want to change to a different icon. For now, it just does not do anything.
The following example demonstrates how to create a markers and update icon property on marker click:
function createIcon(url) {
return new L.Icon({
iconUrl: url,
});
}
function MapExample(props) {
const [selectedIndex, setSelectedIndex] = useState(-1);
function handleClick(e) {
setSelectedIndex(e.target.options.index) //get index via custom marker property
}
function getMarkerIcon(index) {
if(index === selectedIndex)
return createIcon("/bigcity_red.png");
return createIcon("/bigcity_green.png");
}
return (
<Map center={props.center} zoom={props.zoom}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
{props.data.map((item, index) => (
<Marker
key={index}
index={index}
position={item.position}
icon={getMarkerIcon(index)}
onclick={handleClick}
/>
))}
</Map>
);
}
Notes:
icon property for Marker component expects the value of L.Icon type
selectedIndex state is used to keep track of selected Marker (index)
Here is a demo for your reference

Resources