237

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
  • 14,320
  • 6
  • 42
  • 61
MultiformeIngegno
  • 6,545
  • 14
  • 54
  • 107
  • 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

8 Answers8

473

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,191
  • 1
  • 16
  • 22
  • Thanks, it works! The only thing that stops me from accepting the answer is that zoom level is no longer respected. Do you know how to have it work again? :) – MultiformeIngegno Mar 30 '13 at 15:23
  • @MultiformeIngegno should work fine if you use `setZoom` AFTER `fitBounds` – metadept Mar 30 '13 at 15:25
  • @MultiformeIngegno Calling it right after `fitBounds` worked for me in a brief test; let me know if you're still having trouble. – metadept Mar 30 '13 at 15:35
  • @MultiformeIngegno Ahhh, that's the problem, it can't set the zoom while the map is still scaling (solution was in http://stackoverflow.com/questions/2437683/google-maps-api-v3-can-i-setzoom-after-fitbounds). Should work as updated; see my jsFiddle. – metadept Mar 30 '13 at 15:44
  • Thanks!! :D Works like a charm! :) – MultiformeIngegno Mar 30 '13 at 15:49
  • 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
  • Thanks! you save my day :) – Winn Minn Soe Feb 21 '14 at 03:19
  • 2
    @metadept how can I set a marker on this center position? Its possible to get latLng of bounds? – Vitor Guerreiro 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
30

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
  • 32,975
  • 19
  • 110
  • 159
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
  • 2,855
  • 1
  • 19
  • 33
1

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

1

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

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
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
  • 2,976
  • 4
  • 25
  • 52