0

I am loading a map from react-google-maps-api, and then based on the current zoom & center point, we calculate the radius of the map to retrieve items within the "viewable" area of the map. However, the radius being calculated seems to be larger than the visible area.

I have referenced some existing SO threads including this, this, this.

Our map is initialized like:

<GoogleMap
            id="live-view-map"
            mapContainerStyle={{ width: 'inherit', height: 'inherit' }}
            center={userLocation} // denver, co for reference
            options={{
              disableDefaultUI: true,
              clickableIcons: false,
              fullscreenControl: false,
              zoomControl: true,
            }}
            zoom={13}
            onLoad={onLoad}
            onTilesLoaded={() => {
              dispatch({ type: ACTIONS.SET_MAP_LOADED, payload: true });
            }}
            onDragEnd={handleDrag}
            onZoomChanged={handleZoomChanged}
            onUnmount={() => {
              dispatch({ type: ACTIONS.SET_LATLNG, payload: null });
              dispatch({ type: ACTIONS.SET_MAP, payload: null });
              dispatch({ type: ACTIONS.SET_MAP_LOADED, payload: false });
            }}
          >
           // items from endpoint get rendered as markers here
</GoogleMap>

My current get radius is comparing the NE corner & SW corner to determine the smaller distance from center:

const getRadius = (): number | null => {
    const bounds = state.mapInstance?.getBounds();
    if (!bounds) return null;

    // computeDistanceBetween returns meters
    const neRadius = google.maps.geometry.spherical.computeDistanceBetween(
      bounds.getCenter(),
      bounds.getNorthEast()
    );
    const swRadius = google.maps.geometry.spherical.computeDistanceBetween(
      bounds.getCenter(),
      bounds.getSouthWest()
    );

    const radius = neRadius <= swRadius ? neRadius : swRadius;
    return Number((radius * 0.000621371).toFixed(2));
  };

The radius is returning as 6.39 miles. However, the map visible region is for sure around 3.5miles. I thought the radius formula was perhaps incorrect, but I feel confident it's OK. So I am wondering if there's something wrong with the map visible region?

enter image description here

The black circle is the “radius” calculated from the bounds of google. This function is the same whether we use a custom function to calc radius or the google API maps.geometry.spherical.computeDistanceBetween. The red circle is the same radius value above, but divided by 2 (i had a theory that it’s like diameter instead?). The red circle just about fits the initial map zoom level. (screenshot is zoomed out to capture both circles)

What am I doing wrong?

Phil Lucks
  • 2,704
  • 6
  • 31
  • 57

1 Answers1

0

A circle with a radius equal to the corners of the bounding box will extend outside the viewable area.

If you want a circle that is completely visible, use the shorter of the distances from the center of the map to the center of each side of the bonding box.

const getRadius = function() {
console.log("getRadius")
    const bounds = map.getBounds();
    if (!bounds) return null;
console.log("getRadius bounds="+bounds.toUrlValue(6))
    // computeDistanceBetween returns meters
    const nsRadius = google.maps.geometry.spherical.computeDistanceBetween(
      bounds.getCenter(),
      new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getCenter().lng())
    );
    const ewRadius = google.maps.geometry.spherical.computeDistanceBetween(
      bounds.getCenter(),
      new google.maps.LatLng(bounds.getCenter().lat(),bounds.getNorthEast().lng())
    );

    const radius = nsRadius <= ewRadius ? nsRadius : ewRadius;
    return Number((radius).toFixed(2));
  }

proof of concept fiddle

screenshot of resulting map

code snippet:

let map;

function initMap() {
  // Create the map.
  map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: {
      lat: 37.09,
      lng: -95.712
    },
    mapTypeId: "terrain",
  });
  google.maps.event.addListenerOnce(map, 'bounds_changed', function() {
    var bounds = map.getBounds();
    console.log(bounds.toUrlValue(6));
    const circle = new google.maps.Circle({
      strokeColor: "#FF0000",
      strokeOpacity: 0.8,
      strokeWeight: 2,
      fillColor: "#FF0000",
      fillOpacity: 0.35,
      map: map,
      center: bounds.getCenter(),
      radius: getRadius(),
    });
  });
  google.maps.event.addDomListener(document.getElementById('btn'), 'click', function() {
    var bounds = map.getBounds();
    console.log(bounds.toUrlValue(6));
    const circle = new google.maps.Circle({
      strokeColor: "#0000FF",
      strokeOpacity: 0.8,
      strokeWeight: 2,
      fillColor: "#0000FF",
      fillOpacity: 0.35,
      map: map,
      center: bounds.getCenter(),
      radius: getRadius(),
    });
  })
}
const getRadius = function() {
  console.log("getRadius")
  const bounds = map.getBounds();
  if (!bounds) return null;
  console.log("getRadius bounds=" + bounds.toUrlValue(6))
  // computeDistanceBetween returns meters
  const nsRadius = google.maps.geometry.spherical.computeDistanceBetween(
    bounds.getCenter(),
    new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getCenter().lng())
  );
  const ewRadius = google.maps.geometry.spherical.computeDistanceBetween(
    bounds.getCenter(),
    new google.maps.LatLng(bounds.getCenter().lat(), bounds.getNorthEast().lng())
  );

  const radius = nsRadius <= ewRadius ? nsRadius : ewRadius;
  return Number((radius).toFixed(2));
}
/* Always set the map height explicitly to define the size of the div
       * element that contains the map. */

#map {
  height: 90%;
}


/* Optional: Makes the sample page fill the window. */

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<!DOCTYPE html>
<html>

<head>
  <title>Circles</title>
  <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
  <!-- jsFiddle will insert css and js -->
</head>

<body>
  <input id="btn" type="button" value="computeBounds" />
  <div id="map"></div>

  <!-- Async script executes immediately and must be after any DOM elements used in callback. -->
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=geometry&v=weekly&channel=2" async></script>
</body>

</html>
geocodezip
  • 158,664
  • 13
  • 220
  • 245