Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fit map to feature layer bounds in react-leaflet

What I want to achieve:

Have a <Map><FeatureGroup><Circle />[1 or more]...</FeatureGroup></Map> hierarchy and fit the map bounds to the feature group so that all the circles are in the viewport.

If there is only one circle, it should fit the bounds (ie: zoom in on) to that circle.

What I've tried:

  1. giving FeatureGroup a ref and calling getBounds on it to pass onto Map. Because of the lifecycle FeatureGroup doesn't exist at the time componentDidMount is called - it gets rendered later (https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-161594328).
  2. Storing Circle in state and calling getBounds on that (assuming, in this case, that there is only one circle. That didn't work either.

I think I might need to do something with the React Context but I'm not sure that I fully understand it right now, so I need some help.

Other information

I'm using [email protected]

Thanks for any help offered!

like image 744
ndtreviv Avatar asked Oct 23 '25 06:10

ndtreviv


1 Answers

Because the contents of the Map are unavailable at componentDidMount-time (https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-161594328) you cannot get the bounds of the FeatureGroup at that point, and out of all the refs you assign, only the Map ref will be available in this.refs.

However, as per this GitHub comment: https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-366263225 you can give a FeatureGroup an onAdd handler function:

<FeatureGroup ref="features" onAdd={this.onFeatureGroupAdd}>...

and you can then use the Map refs to access the leafletElement and call fitBounds with the bounds of the incoming event target, which will be the FeatureGroup:

  onFeatureGroupAdd = (e) => {
    this.refs.map.leafletElement.fitBounds(e.target.getBounds());
  }

This will then "zoom" the map into the bounds of your FeatureGroup, as desired.

Update

I modified my React component so that zoom and centre are controlled by query parameters. The problem with the above solution was that if you zoomed in on a MarkerClusterGroup by clicking on it, for example, it would update the zoom in the url, re-render the map and re-call onFeatureGroupAdd, thus undoing all the marker cluster goodness.

What I needed was to access the zoom level required to keep the newly drawn circle nicely in bounds, then update the url with the correct zoom level and center.

  onDrawCircle = (e) => {
    ...
    var targetZoom = this.refs.map.leafletElement.getBoundsZoom(e.layer.getBounds());
        // Call function to update url here:
        functionToUpdateUrl(targetZoom, e.layer.getBounds().getCenter());
    }
  }

In order to be able to control the whole map I also call functionToUpdateUrl in onZoomEnd and onDragEnd event handlers, like so:

  onChangeView = (e) => {
      functionToUpdateUrl(e.target._zoom, this.refs.map.leafletElement.getCenter());
  }

and one for handling cluster clicks:

  onClusterClick = (e) => {
     // This time we want the center of the layer, not the map?
     functionToUpdateUrl(e.target._zoom, (e.layer ? e.layer.getBounds().getCenter() : e.target.getBounds().getCenter()));
  }

Then, when rendering the Map element, pass these properties:

  <Map
    center={center}
    ref='map'
    zoom={zoom}
    maxZoom={18}
    onZoomEnd={this.onChangeView}
    onDragEnd={this.onChangeView}
  >
  ....
  </Map>

And remember to give any MarkerClusterGroups their onClusterClick callback:

<MarkerClusterGroup onAdd={this.onMarkerGroupAdd} onClusterClick={this.onClusterClick}>
like image 65
ndtreviv Avatar answered Oct 25 '25 20:10

ndtreviv



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!