257

This is what I use to display a map with 3 pins/markers:

<script>
  function initialize() {
    var locations = [
      ['DESCRIPTION', 41.926979, 12.517385, 3],
      ['DESCRIPTION', 41.914873, 12.506486, 2],
      ['DESCRIPTION', 41.918574, 12.507201, 1]
    ];

    var map = new google.maps.Map(document.getElementById('map'), {
      zoom: 15,
      center: new google.maps.LatLng(41.923, 12.513),
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    var infowindow = new google.maps.InfoWindow();

    var marker, i;

    for (i = 0; i < locations.length; i++) {
      marker = new google.maps.Marker({
        position: new google.maps.LatLng(locations[i][1], locations[i][2]),
        map: map
      });

      google.maps.event.addListener(marker, 'click', (function(marker, i) {
        return function() {
          infowindow.setContent(locations[i][0]);
          infowindow.open(map, marker);
        }
      })(marker, i));
    }
  }

  function loadScript() {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&' + 'callback=initialize';
    document.body.appendChild(script);
  }

  window.onload = loadScript;
</script>

<div id="map" style="width: 900px; height: 700px;"></div>

What I’m looking for is a way to avoid having to “manually” find the center of the map with center: new google.maps.LatLng(41.923, 12.513). Is there a way to automatically have the map centered on the three coordinates?

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
MultiformeIngegno
  • 6,959
  • 15
  • 60
  • 119
  • Possible duplicate of [Google Maps API v3: Can I setZoom after fitBounds?](http://stackoverflow.com/questions/2437683/google-maps-api-v3-can-i-setzoom-after-fitbounds) – SphynxTech Apr 13 '17 at 17:27

9 Answers9

511

There's an easier way, by extending an empty LatLngBounds rather than creating one explicitly from two points. (See this question for more details)

Should look something like this, added to your code:

//create empty LatLngBounds object
var bounds = new google.maps.LatLngBounds();
var infowindow = new google.maps.InfoWindow();    

for (i = 0; i < locations.length; i++) {  
  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(locations[i][1], locations[i][2]),
    map: map
  });

  //extend the bounds to include each marker's position
  bounds.extend(marker.position);

  google.maps.event.addListener(marker, 'click', (function(marker, i) {
    return function() {
      infowindow.setContent(locations[i][0]);
      infowindow.open(map, marker);
    }
  })(marker, i));
}

//now fit the map to the newly inclusive bounds
map.fitBounds(bounds);

//(optional) restore the zoom level after the map is done scaling
var listener = google.maps.event.addListener(map, "idle", function () {
    map.setZoom(3);
    google.maps.event.removeListener(listener);
});

This way, you can use an arbitrary number of points, and don't need to know the order beforehand.

Demo jsFiddle here: http://jsfiddle.net/x5R63/

Community
  • 1
  • 1
metadept
  • 7,831
  • 2
  • 18
  • 25
  • This answer is great, but can someone explain this part to me after the marker event handler attachment? (marker, i) – Seano666 Oct 01 '13 at 18:49
  • 2
    @metadept how can I set a marker on this center position? Its possible to get latLng of bounds? – Vitor Oct 26 '15 at 13:41
  • @VitorGuerreiro - Maybe map.getBounds().getCenter() ? – Chris Wheeler Nov 30 '16 at 10:15
  • This works great, but is there a way to set a minimum zoom level if there is only one point? (so it's not at max zoom) And is there a way to set a maximum zoom level if the points are spread all over a country? UPDATE: see this: https://stackoverflow.com/questions/2989858/google-maps-v3-enforcing-min-zoom-level-when-using-fitbounds – Ben in CA Nov 25 '19 at 16:31
  • 2
    `map.panToBounds(bounds);` is for auto zoom. – namal Nov 28 '19 at 11:04
  • to make sure zoom is respected, make sure that you don't call fitbounds function again after it is already setted,, for example if you are using useEffect hook, just don't do: useEffect((fitBounds()) => [markers]) do useEffect((fitBounds()) => []) – EDMOND GIHOZO Sep 19 '22 at 23:47
35

I think you have to calculate latitudine min and longitude min: Here is an Example with the function to use to center your point:

//Example values of min & max latlng values
var lat_min = 1.3049337;
var lat_max = 1.3053515;
var lng_min = 103.2103116;
var lng_max = 103.8400188;

map.setCenter(new google.maps.LatLng(
  ((lat_max + lat_min) / 2.0),
  ((lng_max + lng_min) / 2.0)
));
map.fitBounds(new google.maps.LatLngBounds(
  //bottom left
  new google.maps.LatLng(lat_min, lng_min),
  //top right
  new google.maps.LatLng(lat_max, lng_max)
));
Alessandro Minoccheri
  • 35,521
  • 22
  • 122
  • 171
5

Another implementation, based on the previous answers, but more lean:

export class MapComponent {
    @ViewChild(GoogleMap) map: GoogleMap;

    markerList = [
        {
            lat: 41.926979,
            lng: 12.517385
        },
        {
            lat: 41.914873,
            lng: 12.506486
        },
        {
            lat: 41.918574, 
            lng: 12.507201
        }
    ]

    centralize() {
        const bounds = new google.maps.LatLngBounds();
        this.markerList.forEach((marker) => {
            bounds.extend(new google.maps.LatLng(marker.lat, marker.lng))
        })
        this.map.fitBounds(bounds)
    }
}

You don't need to create a "google.maps.Marker", you can just create an instance from LatLng and directly pass as parameter to the bounds extend function.

Txai Potier
  • 51
  • 1
  • 1
2

This work for me in Angular 9:

  import {GoogleMap, GoogleMapsModule} from "@angular/google-maps";
  @ViewChild('Map') Map: GoogleMap; /* Element Map */

  locations = [
   { lat: 7.423568, lng: 80.462287 },
   { lat: 7.532321, lng: 81.021187 },
   { lat: 6.117010, lng: 80.126269 }
  ];

  constructor() {
   var bounds = new google.maps.LatLngBounds();
    setTimeout(() => {
     for (let u in this.locations) {
      var marker = new google.maps.Marker({
       position: new google.maps.LatLng(this.locations[u].lat, 
       this.locations[u].lng),
      });
      bounds.extend(marker.getPosition());
     }

     this.Map.fitBounds(bounds)
    }, 200)
  }

And it automatically centers the map according to the indicated positions.

Result:

enter image description here

Santiago Vasquez
  • 149
  • 1
  • 10
2

I've tryied all answers of this topic, but just this below worked fine on my project.

Angular 7 and AGM Core 1.0.0-beta.7

<agm-map [latitude]="lat" [longitude]="long" [zoom]="zoom" [fitBounds]="true">
  <agm-marker latitude="{{localizacao.latitude}}" longitude="{{localizacao.longitude}}" [agmFitBounds]="true"
    *ngFor="let localizacao of localizacoesTec">
  </agm-marker>
</agm-map>

The properties [agmFitBounds]="true" at agm-marker and [fitBounds]="true" at agm-map does the job

1

To find the exact center of the map you'll need to translate the lat/lon coordinates into pixel coordinates and then find the pixel center and convert that back into lat/lon coordinates.

You might not notice or mind the drift depending how far north or south of the equator you are. You can see the drift by doing map.setCenter(map.getBounds().getCenter()) inside of a setInterval, the drift will slowly disappear as it approaches the equator.

You can use the following to translate between lat/lon and pixel coordinates. The pixel coordinates are based on a plane of the entire world fully zoomed in, but you can then find the center of that and switch it back into lat/lon.

   var HALF_WORLD_CIRCUMFERENCE = 268435456; // in pixels at zoom level 21
   var WORLD_RADIUS = HALF_WORLD_CIRCUMFERENCE / Math.PI;

   function _latToY ( lat ) {
      var sinLat = Math.sin( _toRadians( lat ) );
      return HALF_WORLD_CIRCUMFERENCE - WORLD_RADIUS * Math.log( ( 1 + sinLat ) / ( 1 - sinLat ) ) / 2;
   }

   function _lonToX ( lon ) {
      return HALF_WORLD_CIRCUMFERENCE + WORLD_RADIUS * _toRadians( lon );
   }

   function _xToLon ( x ) {
      return _toDegrees( ( x - HALF_WORLD_CIRCUMFERENCE ) / WORLD_RADIUS );
   }

   function _yToLat ( y ) {
      return _toDegrees( Math.PI / 2 - 2 * Math.atan( Math.exp( ( y - HALF_WORLD_CIRCUMFERENCE ) / WORLD_RADIUS ) ) );
   }

   function _toRadians ( degrees ) {
      return degrees * Math.PI / 180;
   }

   function _toDegrees ( radians ) {
      return radians * 180 / Math.PI;
   }
Shadow Man
  • 3,234
  • 1
  • 24
  • 35
0

I use the method above to set the map boundaries, then, instead of resetting the zoom level, I just calculate the average LAT and average LON and set the center point to that location. I add up all the lat values into latTotal and all the lon values into lontotal and then divide by the number of markers. I then set the map center point to those average values.

latCenter = latTotal / markercount; lonCenter = lontotal / markercount;

0

i had a situation where i can't change old code, so added this javascript function to calculate center point and zoom level:

//input
var tempdata = ["18.9400|72.8200-19.1717|72.9560-28.6139|77.2090"];

function getCenterPosition(tempdata){
 var tempLat = tempdata[0].split("-");
 var latitudearray = [];
 var longitudearray = [];
 var i;
 for(i=0; i<tempLat.length;i++){
  var coordinates = tempLat[i].split("|");
  latitudearray.push(coordinates[0]);
  longitudearray.push(coordinates[1]);
 }
 latitudearray.sort(function (a, b) { return a-b; });
 longitudearray.sort(function (a, b) { return a-b; });
 var latdifferenece = latitudearray[latitudearray.length-1] - latitudearray[0];
 var temp = (latdifferenece / 2).toFixed(4) ;
 var latitudeMid = parseFloat(latitudearray[0]) + parseFloat(temp);
 var longidifferenece = longitudearray[longitudearray.length-1] - longitudearray[0];
 temp = (longidifferenece / 2).toFixed(4) ;
 var longitudeMid = parseFloat(longitudearray[0]) + parseFloat(temp);
 var maxdifference = (latdifferenece > longidifferenece)? latdifferenece : longidifferenece;
 var zoomvalue; 
 if(maxdifference >= 0 && maxdifference <= 0.0037)  //zoom 17
  zoomvalue='17';
 else if(maxdifference > 0.0037 && maxdifference <= 0.0070)  //zoom 16
  zoomvalue='16';
 else if(maxdifference > 0.0070 && maxdifference <= 0.0130)  //zoom 15
  zoomvalue='15';
 else if(maxdifference > 0.0130 && maxdifference <= 0.0290)  //zoom 14
  zoomvalue='14';
 else if(maxdifference > 0.0290 && maxdifference <= 0.0550)  //zoom 13
  zoomvalue='13';
 else if(maxdifference > 0.0550 && maxdifference <= 0.1200)  //zoom 12
  zoomvalue='12';
 else if(maxdifference > 0.1200 && maxdifference <= 0.4640)  //zoom 10
  zoomvalue='10';
 else if(maxdifference > 0.4640 && maxdifference <= 1.8580)  //zoom 8
  zoomvalue='8';
 else if(maxdifference > 1.8580 && maxdifference <= 3.5310)  //zoom 7
  zoomvalue='7';
 else if(maxdifference > 3.5310 && maxdifference <= 7.3367)  //zoom 6
  zoomvalue='6';
 else if(maxdifference > 7.3367 && maxdifference <= 14.222)  //zoom 5
  zoomvalue='5';
 else if(maxdifference > 14.222 && maxdifference <= 28.000)  //zoom 4
  zoomvalue='4';
 else if(maxdifference > 28.000 && maxdifference <= 58.000)  //zoom 3
  zoomvalue='3';
 else
  zoomvalue='1';
 return latitudeMid+'|'+longitudeMid+'|'+zoomvalue;
}
NEQSH
  • 1
  • 1
0

Here's my take on this in case anyone comes across this thread:

This helps protect against non-numerical data destroying either of your final variables that determine lat and lng.

It works by taking in all of your coordinates, parsing them into separate lat and lng elements of an array, then determining the average of each. That average should be the center (and has proven true in my test cases.)

var coords = "50.0160001,3.2840073|50.014458,3.2778274|50.0169713,3.2750587|50.0180745,3.276742|50.0204038,3.2733474|50.0217796,3.2781737|50.0293064,3.2712542|50.0319918,3.2580816|50.0243287,3.2582281|50.0281447,3.2451177|50.0307925,3.2443178|50.0278165,3.2343882|50.0326574,3.2289809|50.0288569,3.2237612|50.0260081,3.2230589|50.0269495,3.2210104|50.0212645,3.2133541|50.0165868,3.1977592|50.0150515,3.1977341|50.0147901,3.1965286|50.0171915,3.1961636|50.0130074,3.1845098|50.0113267,3.1729483|50.0177206,3.1705726|50.0210692,3.1670394|50.0182166,3.158297|50.0207314,3.150927|50.0179787,3.1485753|50.0184944,3.1470782|50.0273077,3.149845|50.024227,3.1340514|50.0244172,3.1236235|50.0270676,3.1244474|50.0260853,3.1184879|50.0344525,3.113806";

var filteredtextCoordinatesArray = coords.split('|');    

    centerLatArray = [];
    centerLngArray = [];


    for (i=0 ; i < filteredtextCoordinatesArray.length ; i++) {

      var centerCoords = filteredtextCoordinatesArray[i]; 
      var centerCoordsArray = centerCoords.split(',');

      if (isNaN(Number(centerCoordsArray[0]))) {      
      } else {
        centerLatArray.push(Number(centerCoordsArray[0]));
      }

      if (isNaN(Number(centerCoordsArray[1]))) {
      } else {
        centerLngArray.push(Number(centerCoordsArray[1]));
      }                    

    }

    var centerLatSum = centerLatArray.reduce(function(a, b) { return a + b; });
    var centerLngSum = centerLngArray.reduce(function(a, b) { return a + b; });

    var centerLat = centerLatSum / filteredtextCoordinatesArray.length ; 
    var centerLng = centerLngSum / filteredtextCoordinatesArray.length ;                                    

    console.log(centerLat);
    console.log(centerLng);

    var mapOpt = {      
    zoom:8,
    center: {lat: centerLat, lng: centerLng}      
    };
Brian Powell
  • 3,336
  • 4
  • 34
  • 60