dictionaryvuejs3leafletfitbounds

why bounds in fitbounds of leaflet are not valid?


I have this map:

<l-map
    ref="mapRef"
    :use-global-leaflet="false"
>

and wanted to fit the map based on bounds which calculated in this way:

bounds.extend([lat, lng]);

now when I use this code:

mapRef.value.leafletObject.fitBounds(bounds, { padding: [40, 40] });

I get this error:

Error fitting map bounds: Error: Bounds are not valid.

and when I use this code

zoom = mapRef.value.leafletObject.getBoundsZoom(bounds);

i get this error:

Error fitting map bounds: TypeError: can't access property "lat", this._northEast is undefined

I dont have any idea why this happens?

this is the log of bounds just before the line of using it:

Calculated bounds: 
Object { _southWest: {…}, _northEast: {…} }
​
_northEast: Object { lat: 36.376582309526, lng: 59.671965269915 }
​
_southWest: Object { lat: 36.2296652, lng: 57.6810134 }

Solution

  • You should pass a simple bounds literal. Leaflet accepts it and creates an internal bounds.

    // compute min/max lat/lng from your points
    const sw = [minLat, minLng];
    const ne = [maxLat, maxLng];
    mapRef.value.leafletObject.fitBounds([sw, ne], { padding: [40, 40] });
    

    or you can consider taking it from array of points

    const points = [[36.2296652, 57.6810134], [36.376582309526, 59.671965269915]];
    mapRef.value.leafletObject.fitBounds(points, { padding: [40, 40] }); // also valid
    

    or you can create the bounds without importing L, using the map’s own instance methods:

    const b = mapRef.value.leafletObject.getBounds(); 
    const b = mapRef.value.leafletObject.getBounds().constructor
    // Not recommended
    

    You can go use a single global Leaflet instance If you want to use L.latLngBounds() and bounds.extend() explicitly. Kindly ensure the same L is used everywhere (attach your imported Leaflet to window.L before the map mounts):

    <l-map ref="mapRef" :use-global-leaflet="true">
    
    import * as L from 'leaflet';
    window.L = L;
    // now both your code and Vue-Leaflet use the same L
    

    Build bounds accordingly:

    const bounds = L.latLngBounds([]);
    points.forEach(([lat, lng]) => bounds.extend(L.latLng(lat, lng))); 
    mapRef.value.leafletObject.fitBounds(bounds, { padding: [40, 40] });
    

    fitBounds expects at least 2 points or a valid rectangle. If you only extended once, fallback to setView:

    if (points.length === 1) {
      mapRef.value.leafletObject.setView(points[0], desiredZoom);
    } else {
      mapRef.value.leafletObject.fitBounds(points, { padding: [40, 40] });
    }
    

    This is more safe:

    // Given an array of [lat, lng] points
    const points = [
      [36.2296652, 57.6810134],
      [36.376582309526, 59.671965269915]
    ];
    mapRef.value.leafletObject.fitBounds(points, { padding: [40, 40] });
    const lats = points.map(p => p[0]);
    const lngs = points.map(p => p[1]);
    const sw = [Math.min(...lats), Math.min(...lngs)];
    const ne = [Math.max(...lats), Math.max(...lngs)];
    mapRef.value.leafletObject.fitBounds([sw, ne], { padding: [40, 40] });
    

    and use global Leaflet and class-based bounds

    <l-map ref="mapRef" :use-global-leaflet="true">
    
    import * as L from 'leaflet';
    window.L = L;
    
    const bounds = L.latLngBounds([]);
    for (const [lat, lng] of points) bounds.extend(L.latLng(lat, lng));
    
    if (bounds.isValid()) {
      mapRef.value.leafletObject.fitBounds(bounds, { padding: [40, 40] });
    } else {
      console.warn('Bounds not valid, falling back to setView');
    }
    

    Hope it gives you some clarity.