Make rectangle with OverlayView in Google Maps no matter which direction drawn

Solution for Make rectangle with OverlayView in Google Maps no matter which direction drawn
is Given Below:

I’m trying to create an overlay view that you can create by dragging to resize. Specifically need it so it goes on top of markers so google.maps.Rectangle will not work.

The issue is it only work if you drag and drop from bottom left to top right of the map. I.e. click and hold near the bottom left of the map and, while holding down, drag to the top right and release. You’ll get a red rectangle. Good!

However, all other directions don’t work. I can’t figure out how to make sure a rectangle always appears no matter which direction you draw.

I’m using the example code right from Google here but deleted parts I don’t need and added the mousedown and up handler: https://developers.google.com/maps/documentation/javascript/examples/overlay-simple

I’ve simplified the code for an example to this https://jsfiddle.net/jd2ox7u4/

The markers are just a visual/proof the lat/lng is where you click and release.

Code from JS Fiddle

function initMap() {
  const myLatLng = { lat: -25.363, lng: 131.044 };
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: myLatLng,
    draggable: false
  });
  new google.maps.Marker({
    position: myLatLng,
    map,
    title: "Hello World!",
  });
  
  
  class USGSOverlay extends google.maps.OverlayView {
    bounds;
    div;

    constructor(bounds, image) {
      super();
      this.bounds = bounds;
    }

    onAdd() {
      this.div = document.createElement("div");
      this.div.style.position = "absolute";
      this.div.style.background = 'red';
      const panes = this.getPanes();
      panes.overlayImage.appendChild(this.div);
    }

    draw() {
      // We use the south-west and north-east
      // coordinates of the overlay to peg it to the correct position and size.
      // To do this, we need to retrieve the projection from the overlay.
      const overlayProjection = this.getProjection();
      // Retrieve the south-west and north-east coordinates of this overlay
      // in LatLngs and convert them to pixel coordinates.
      // We'll use these coordinates to resize the div.
      const sw = overlayProjection.fromLatLngToDivPixel(
        this.bounds.getSouthWest()
      );
      const ne = overlayProjection.fromLatLngToDivPixel(
        this.bounds.getNorthEast()
      );

      // Resize the image's div to fit the indicated dimensions.
      if (this.div) {
        this.div.style.left = sw.x + "px";
        this.div.style.top = ne.y + "px";
        this.div.style.width = ne.x - sw.x + "px";
        this.div.style.height = sw.y - ne.y + "px";
      }
    }

    onRemove() {
      if (this.div) {
        this.div.parentNode.removeChild(this.div);
        delete this.div;
      }
    }
  }

  let isDragging = false;
  let startPosition = null;
  let stopPosition = null;

  google.maps.event.addListener(map, 'mousedown', function (event) {
    console.log('Mouse Down At', event.latLng.lat(), event.latLng.lng())
    isDragging = true;
    startPosition = event.latLng;
    new google.maps.Marker({
      map,
      position: {lat: startPosition.lat(), lng: startPosition.lng()}
    })
  })

  google.maps.event.addListener(map, 'mouseup', function (event) {
    if (isDragging) {
      console.log('Mouse Up At', event.latLng.lat(), event.latLng.lng())

      isDragging = false;
      stopPosition = event.latLng;
      new google.maps.Marker({
        map,
        position: {lat: stopPosition.lat(), lng: stopPosition.lng()}
      })

      var overlay = new USGSOverlay(new google.maps.LatLngBounds(
        new google.maps.LatLng(startPosition.lat(), startPosition.lng()),
        new google.maps.LatLng(stopPosition.lat(), stopPosition.lng())
      ), 'https://developers.google.com/maps/documentation/javascript/examples/full/images/talkeetna.png')
      overlay.setMap(map);
    }

  })
  
  
}

A google.maps.LatLngBounds object requires the arguments to its constructor be in the order (southwest, northeast):

Constructor
LatLngBounds LatLngBounds([sw, ne])

The simplest way to fix this in your code is to use the extend method (which doesn’t have that restriction, it allows you to add single LatLng objects to a LatLngBounds object):

var overlayBounds = new google.maps.LatLngBounds();
overlayBounds.extend(new google.maps.LatLng(startPosition.lat(), startPosition.lng()));
overlayBounds.extend(new google.maps.LatLng(stopPosition.lat(), stopPosition.lng()));
var overlay = new USGSOverlay(overlayBounds, 'https://developers.google.com/maps/documentation/javascript/examples/full/images/talkeetna.png')
overlay.setMap(map);

proof of concept fiddle

screenshot of resulting map

code snippet:

function initMap() {
  const myLatLng = {
    lat: -25.363,
    lng: 131.044
  };
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: myLatLng,
    draggable: false
  });
  new google.maps.Marker({
    position: myLatLng,
    map,
    title: "Hello World!",
  });




  class USGSOverlay extends google.maps.OverlayView {
    bounds;
    div;

    constructor(bounds, image) {
      super();
      this.bounds = bounds;
    }

    onAdd() {
      this.div = document.createElement("div");
      this.div.style.position = "absolute";
      this.div.style.background = 'red';
      const panes = this.getPanes();
      panes.overlayImage.appendChild(this.div);
    }

    draw() {
      // We use the south-west and north-east
      // coordinates of the overlay to peg it to the correct position and size.
      // To do this, we need to retrieve the projection from the overlay.
      const overlayProjection = this.getProjection();
      // Retrieve the south-west and north-east coordinates of this overlay
      // in LatLngs and convert them to pixel coordinates.
      // We'll use these coordinates to resize the div.
      const sw = overlayProjection.fromLatLngToDivPixel(
        this.bounds.getSouthWest()
      );
      const ne = overlayProjection.fromLatLngToDivPixel(
        this.bounds.getNorthEast()
      );

      // Resize the image's div to fit the indicated dimensions.
      if (this.div) {
        this.div.style.left = sw.x + "px";
        this.div.style.top = ne.y + "px";
        this.div.style.width = ne.x - sw.x + "px";
        this.div.style.height = sw.y - ne.y + "px";
      }
    }

    onRemove() {
      if (this.div) {
        this.div.parentNode.removeChild(this.div);
        delete this.div;
      }
    }
  }

  let isDragging = false;
  let startPosition = null;
  let stopPosition = null;

  google.maps.event.addListener(map, 'mousedown', function(event) {
    console.log('Mouse Down At', event.latLng.lat(), event.latLng.lng())
    isDragging = true;
    startPosition = event.latLng;
    new google.maps.Marker({
      map,
      position: {
        lat: startPosition.lat(),
        lng: startPosition.lng()
      }
    })
  })

  google.maps.event.addListener(map, 'mouseup', function(event) {
    if (isDragging) {
      console.log('Mouse Up At', event.latLng.lat(), event.latLng.lng())

      isDragging = false;
      stopPosition = event.latLng;
      new google.maps.Marker({
        map,
        position: {
          lat: stopPosition.lat(),
          lng: stopPosition.lng()
        }
      })

      var overlayBounds = new google.maps.LatLngBounds();
      overlayBounds.extend(new google.maps.LatLng(startPosition.lat(), startPosition.lng()));
      overlayBounds.extend(new google.maps.LatLng(stopPosition.lat(), stopPosition.lng()));
      var overlay = new USGSOverlay(overlayBounds, 'https://developers.google.com/maps/documentation/javascript/examples/full/images/talkeetna.png')
      overlay.setMap(map);
    }

  })


}
/* Always set the map height explicitly to define the size of the div
       * element that contains the map. */

#map {
  height: 100%;
}


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

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

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

<body>
  <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=&v=weekly&channel=2" async></script>
</body>

</html>