How to use fitBounds in react-google-map once you have bounds

Viewed 17215

Where exactly do I call fitBounds or map.fitbounds. I am confused where to put it once I have the bounds or how to use it. I have set the bounds to a local state of bounds.

I have looked at this post here react-google-maps: how to use fitBounds, panBy, panTo, panToBounds public APIs? and either I just do it different or something because it does not make much since to me.

I have created a bounds const and then each time I make a marker I have added each of those to the bounds doing bounds.extends(Marker in here)

The code is a little messy so I will point out where I do this in it. Inside the filterItemsByRadius is where I create and set the bounds. At the end of the map function I think set the state of bounds to the bounds.

/* global google */
import { default as React, Component } from 'react';
import raf from 'raf';
import canUseDOM from 'can-use-dom';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import {
  withGoogleMap,
  GoogleMap,
  Circle,
  InfoWindow,
  Marker,
  withScriptjs,
} from 'react-google-maps';
import geolib from 'geolib';
import { geolocated } from 'react-geolocated';
import ItemList from './ItemList';
import { Col } from 'react-bootstrap';

import Paper from 'material-ui/Paper';
import Img from 'react-image';
import RaisedButton from 'material-ui/RaisedButton';
import FontIcon from 'material-ui/FontIcon';
import CreateRadius from './CreateRadius';
import offerActions from '../../redux/actions/offerActions';

const googleMapURL =
  'https://maps.googleapis.com/maps/api/js?libraries=places,geometry&key=AIzaSyA7XEFRxE4Lm28tAh44M_568fCLOP_On3k';

const isJson = str => {
  try {
    JSON.parse(str);
  } catch (e) {
    return false;
  }
  return true;
};

const GeolocationGoogleMap = withScriptjs(
  withGoogleMap(props => (
    <GoogleMap defaultZoom={6} zoom={props.zoom} center={props.center} onClick={props.onMapClick}>
      {props.center && (
        <Marker
          position={props.center}
          title={"User's Location"}
          options={{ icon: require('./assets/blueDot.png') }}
        >
          {props.showCenterInfo && (
            <InfoWindow>
              <div>User's Location</div>
            </InfoWindow>
          )}
        </Marker>
      )}
      {/* <Circle
          center={props.center}
          radius={props.zoom}
          options={{
            fillColor: 'red',
            fillOpacity: 0.2,
            strokeColor: 'red',
            strokeOpacity: 1,
            strokeWeight: 1,
          }}
        /> */}
      {props.markers.map((marker, index) => {
        const onClick = () => props.onMarkerClick(marker);
        const onCloseClick = () => props.onCloseClick(marker);

        return (
          <Marker
            key={index}
            position={marker.position}
            title={marker.number.toString()}
            onClick={onClick}
            options={{ icon: 'https://image.ibb.co/evMHxF/shopping_zone_marker_1.png' }}
          >
            {marker.showInfo && (
              <InfoWindow onCloseClick={onCloseClick}>
                <div>
                  <ItemList marker={marker} />
                </div>
              </InfoWindow>
            )}
          </Marker>
        );
      })}
    </GoogleMap>
  )),
);

class OfferMap extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentPosition: null,
      center: null,
      content: null,
      radius: 15, // ACZ --> put this const in config_env.
      showCenterInfo: true,
      markers: [],
      zoom: 6,
      bounds: null,
    };

    this.handleMarkerClick = this.handleMarkerClick.bind(this);
    this.handleCloseClick = this.handleCloseClick.bind(this);
    this.handleMapClick = this.handleMapClick.bind(this);
    this.filterItemsByRadius = this.filterItemsByRadius.bind(this);
    this.radiusChange = this.radiusChange.bind(this);
    this.zoomChange = this.zoomChange.bind(this);
  }

  componentWillReceiveProps(props) {
    if (props.coords && !props.coords.positionError) {
      this.setState({ center: { lat: props.coords.latitude, lng: props.coords.longitude } });
    } else {
      fetch('http://ip-api.com/json')
        .then(res => res.json())
        .then(data => {
          this.setState({ center: { lat: data.lat, lng: data.lon } });
        })
        .catch(error => {
          this.setState({ content: `Error: The Geolocation service failed (${error.message}).` });
        });
    }
    this.setState({ markers: props.items });
  }

  handleMapClick() {
    this.setState({ showCenterInfo: false });
  }

  handleMarkerClick(targetMarker) {
    this.setState({
      markers: this.state.markers.map(marker => {
        if (marker._id === targetMarker._id) {
          return {
            ...marker,
            showInfo: true,
          };
        }
        return marker;
      }),
    });
  }

  handleCloseClick(targetMarker) {
    this.setState({
      markers: this.state.markers.map(marker => {
        if (marker._id === targetMarker._id) {
          return {
            ...marker,
            showInfo: false,
          };
        }
        return marker;
      }),
    });
  }

  filterItemsByRadius(userRadius) {
    const items = this.state.markers;
    const markers = [];
    const bounds = new google.maps.LatLngBounds();

    items.map((item, i) => {
      let itemGeolocation;
      let itemDescription = 'NO DESCRIPTION';
      let itemThumb;
      // Should be ready - tbaustinedit
      if (item) {
        itemDescription = item.description;
        itemThumb = item.media.mediaVault[item.media.defaultIdx] || {
          mediaType: 'txt',
        };
      }
      if (item.geolocation) {
        itemGeolocation = item.geolocation.coords;
      }
      if (this.state.center) {
        const currentLocation = {
          latitude: this.state.center.lat,
          longitude: this.state.center.lng,
        };
        const distanceArr = geolib.orderByDistance(currentLocation, [itemGeolocation]);
        const miles = (distanceArr[0].distance / 1609.34).toFixed(2);
        if (miles <= userRadius) {
          const loc = new google.maps.LatLng(itemGeolocation.lat, itemGeolocation.lng);
          bounds.extends(loc);
          markers.push({
            _id: item._id,
            position: itemGeolocation,
            number: i,
            content: itemDescription,
            price: item.price,
            quantity: item.quantity,
            currency: item.currency,
            category: item.category,
            title: item.title,
            offer: item.offer,
            thumbnail: itemThumb,
            showInfo: item.showInfo || false,
          });
        }
      }
    });
    this.setState({
      bounds,
    });
    return markers;
  }

  radiusChange(event) {
    console.log(event.target.value);
    this.setState({
      radius: event.target.value,
    });

    const { filter } = this.props.offers;
    filter.radius = event.target.value;
    this.props.sortOffersNew(filter);
  }

  zoomChange(e) {
    console.log('value', e.target.value);
    this.setState({ zoom: Number(e.target.value) });
  }

  render() {
    const markers = this.filterItemsByRadius(this.state.radius);
    return (
      <Col xs={12} smOffset={0} mdOffset={0}>
        <div>
          <div style={{ fontFamily: 'Roboto', fontStyle: 'normal' }}>
            Offers within radius of: {' '}
            <input type="text" defaultValue={this.state.radius} onChange={this.radiusChange} /> {' '}
            miles
            <br />
          </div>
          {/* <CreateRadius
            radiusChange={this.radiusChange}
            numOffers={markers.length}
            initRadius={this.state.zoom}
          /> */}
        </div>
        <br />

        <div
          style={{
            width: '100%',
            height: '500px',
          }}
        >
          <GeolocationGoogleMap
            googleMapURL={googleMapURL}
            loadingElement={<div style={{ height: '100%' }} />}
            containerElement={<div style={{ height: '100%' }} />}
            mapElement={<div style={{ height: '100%' }} />}
            center={this.state.center}
            showCenterInfo={this.state.showCenterInfo}
            content={this.state.content}
            radius={this.state.radius}
            onMapClick={this.handleMapClick}
            onMarkerClick={this.handleMarkerClick}
            onCloseClick={this.handleCloseClick}
            markers={markers}
            zoom={this.state.zoom}
            bounds={this.state.bounds}
          />
        </div>
      </Col>
    );
  }
}

function mapStateToProps({ browser, offers }) {
  return { browser, offers };
}

const dispatchToProps = dispatch => ({
  sortOffersNew: filter => dispatch(offerActions.sortOffers(filter)),
});

export default connect(mapStateToProps, dispatchToProps)(
  geolocated({
    positionOptions: {
      enableHighAccuracy: false,
    },
    userDecisionTimeout: 5000,
  })(OfferMap),
);
5 Answers

You can access fitBounds by adding a ref to the DOM element that google-maps-react generates.

first, add a ref: <GoogleMap ref={(ref) => { this.map = ref; }}>...</GoogleMap>

Once the component is mounted, you will be able to call fitBounds using the ref. this.map.fitBounds(bounds)

You can use fitbounds in componentDidMount or render function

add ref to GoogleMap and apply fitbounds <GoogleMap ref={map => map && map.fitBounds(bounds)}> .... </GoogleMap>

You can assign a ref to the GoogleMap Component, then call the fitBounds() function using a lifecycle hook.

import React, { useRef, useEffect } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import { GOOGLE_API_KEY } from '../../config/config';


// =======================================================================
//  GOOGLE MAPS
// =======================================================================
const RegularMap = withScriptjs(
  withGoogleMap(
    ({ defaultCenter, markers }) => {
      const mapRef = useRef(null);

      // Fit bounds function
      const fitBounds = () => {
        const bounds = new window.google.maps.LatLngBounds();
        markers.map(item => {
          bounds.extend(item.position);
          return item.id
        });
        mapRef.current.fitBounds(bounds);
      };

      // Fit bounds on mount, and when the markers change
      useEffect(() => {
        fitBounds();
      }, [markers]);


      return (
        <GoogleMap ref={mapRef} defaultCenter={defaultCenter}>
          {markers.map(
            ({ position }, index) => <Marker key={`marker_${index}`} position={position} />
          )}
        </GoogleMap>
      );
    })
);


// =======================================================================
//  THE MAIN COMPONENT
// =======================================================================

const MapView = () => {
  const markers = [
    { position: { lat: 37.778519, lng: -122.405640 } },
    { position: { lat: 6.4454594, lng: 3.449074 } }
  ];

  return (
      <RegularMap
        defaultCenter={{ lat: 6.4454594, lng: 3.449074 }}
        markers={markers}
        googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}`}
        loadingElement={<div className="loader" />}
        containerElement={<div className="mapContainer" style={{ height: "400px" }} />}
        mapElement={<div className="map" style={{ height: '100%' }} />}
      />
  );
}

export default MapView;

The example demonstrates how to center viewport for the given markers:

const MapWithAMarkers = compose(
    withProps({
        googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg",
        loadingElement: <div style={{ height: `100%` }} />,
        containerElement: <div style={{ height: `400px` }} />,
        mapElement: <div style={{ height: `100%` }} />,
    }),
    lifecycle({
        componentWillMount() {

            this.setState({

                zoomToMarkers: map => {
                    //console.log("Zoom to markers");
                    const bounds = new window.google.maps.LatLngBounds();
                    map.props.children.forEach((child) => {
                        if (child.type === Marker) {
                            bounds.extend(new window.google.maps.LatLng(child.props.position.lat, child.props.position.lng));
                        }
                    })
                    map.fitBounds(bounds);
                }
            })
        },
    }),
    withScriptjs,
    withGoogleMap
)(props =>
    <GoogleMap ref={props.zoomToMarkers} defaultZoom={5} defaultCenter={{ lat: 25.0391667, lng: 121.525 }}>
        {props.markers.map(marker => (
            <Marker
                key={marker.id}
                position={{ lat: marker.lat, lng: marker.lng }}
            />
        ))}
    </GoogleMap>
    );

Demo

I wanted to restrict my map (https://developers.google.com/maps/documentation/javascript/examples/control-bounds-restriction).

I used the options property with GoogleMap component with https://www.npmjs.com/package/@react-google-maps/api library. The library is complete re-write of the react-google-maps library

CODE:

<GoogleMap
        mapContainerStyle={containerStyle}
        center={center}
        zoom={10}
        ref={(ref) => {this.state.map = ref}}
        onLoad={this.onGoogleMapLoad}
        options={{
                          restriction: {
                               latLngBounds: {
                                   north: 19.137384, // Mumbai
                                   south: 18.510866, // Pune
                                   west: 72.874748,  // Mumbai
                                   east: 73.879864,  // Pune
                                },
                                strictBounds: false,
               }
         }}
>
Related