76

It doesn't need to be 100% correct, it can be the center of the bounding rectangle.

Brian Webster
  • 30,033
  • 48
  • 152
  • 225
ANd
  • 1,155
  • 2
  • 10
  • 8

8 Answers8

235

Matthew's answer is a good solution. However, when using the Google Maps API v3, you might want to pass each point of the polygon to a LatLngBounds object through the extend() method, and then finally call the getCenter() method on the LatLngBounds object. Consider the following example:

var bounds = new google.maps.LatLngBounds();
var i;

// The Bermuda Triangle
var polygonCoords = [
  new google.maps.LatLng(25.774252, -80.190262),
  new google.maps.LatLng(18.466465, -66.118292),
  new google.maps.LatLng(32.321384, -64.757370),
  new google.maps.LatLng(25.774252, -80.190262)
];

for (i = 0; i < polygonCoords.length; i++) {
  bounds.extend(polygonCoords[i]);
}

// The Center of the Bermuda Triangle - (25.3939245, -72.473816)
console.log(bounds.getCenter());
Community
  • 1
  • 1
Daniel Vassallo
  • 337,827
  • 72
  • 505
  • 443
  • 3
    Wrapped up nicely here: http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js – Steve Feb 19 '11 at 23:01
  • 4
    Why is this the better solution? To me it seems that both solutions should always get to the same answer. Is this faster? – Nicolas Jul 05 '13 at 20:55
  • 5
    @Nicolas: Both will get to the same solution, but this is simpler to implement, relies on Googles API's and is more obvious what it does at a glance thanks to being shorter in code (4 lines at worst). Either method should have negligible runtime, so speed isn't an issue. – Matthew Scharley Nov 03 '13 at 08:06
  • I getting the following Error with the above code. TypeError: a is undefined – ArunRaj Jan 17 '14 at 16:15
  • 6
    Also, this is a better solution as it will take into account the curvature of the Earth. The trivial algorithm I posted will be a reasonable approximation for small areas, but if you are trying to work out the geographical centre of a region for instance, then this will give a better answer. The world isn't rectangular, only maps are. – Matthew Scharley Jul 24 '14 at 09:23
  • @Nicolas - this answer is better because it deals with polygons that cross the international date line (e.g. Russia's international border). If you just take the min/max lon, as in the accepted answer, you'll get bizarre results sometimes. – Canuck Jul 12 '16 at 20:02
  • 3
    This is good, unless your Polygon is of a "L" shape for example, in which case, the `bounds.getCenter()` will be off the Polygon. – MrUpsidown Feb 01 '19 at 12:56
  • Also worth the note that Bounds get's the top left and bottom right and then returns the center point of those. It does not always give you a visual center of a more complex polygon. – Mark Tomlin Nov 26 '19 at 20:06
82

Algorithm:

Run through all the points in the polygon. For all the points find;

  • x1, the lowest x coordinate
  • y1, the lowest y coordinate
  • x2, the highest x coordinate
  • y2, the highest y coordinate

You now have the bounding rectangle, and can work out the center using:

center.x = x1 + ((x2 - x1) / 2);
center.y = y1 + ((y2 - y1) / 2);
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • 8
    Well, i've found that in v2, I was able to do it just by calling polygon.getBounds().getCenter(), but I guess i'll have to do it your way. Thanks – ANd Jun 20 '10 at 22:41
  • I have no idea, that may work in v3 too. I havn't looked too closely at the Google Maps API. But this way does essentially that anyway. – Matthew Scharley Jun 20 '10 at 23:24
  • It looks like Google forgot the `Polygon.getBounds()` method. It is available for `Circle` and `Rectangle`, but not for the `Polygon` class. – Daniel Vassallo Jun 21 '10 at 05:29
  • 3
    This will fail horrifically if the polygon crosses the international date line (example: Russia's international boundary). – Canuck Jul 12 '16 at 20:00
  • As @Canuk stated, this will fail in certain circumstances. Ideally you want to use the mean of circular quantities, and not a arithmetic mean. https://en.wikipedia.org/wiki/Mean_of_circular_quantities – martin Sep 18 '16 at 22:09
58

you can extend the Polygon class with your own version of the missing function, let's call it my_getBounds() :

google.maps.Polygon.prototype.my_getBounds=function(){
    var bounds = new google.maps.LatLngBounds()
    this.getPath().forEach(function(element,index){bounds.extend(element)})
    return bounds
}

and than use it in code like this :

myPolygon.my_getBounds().getCenter()

... etc, it should be equivalent to the v2 behavior

furiozo
  • 581
  • 4
  • 2
  • 2
    Found this posting when doing a google search for the same issue.. This suggestion worked out perfectly for me! thanks.. – Dennis Jan 09 '13 at 11:57
  • For some reason, this doesn't work for me. the `my_getBounds()` function is undefined when I try to call it on Polygon objects. – coredumperror Feb 02 '15 at 23:08
  • Upon further experimentation, this appears to be out-of-date compared to the newest iteration of Google Maps Javascript API v3. The Polygon class is now `google.maps.Data.Polygon`, and there's no `getPath()` method on the class. – coredumperror Feb 02 '15 at 23:20
  • This answer worked verbatim for me today (August 21, 2015) on google.maps.Polygon objects with no changes using the Google Maps API v3. – Daniel Nalbach Aug 21 '15 at 18:31
  • A smaller version of this on es6 `polygon.getPath().getArray().reduce((prev, curr) => prev.extend(curr), new google.maps.LatLngBounds());` – Guillaume Dec 30 '20 at 10:45
8

Here is a custom function I wrote. Feel free to use it.

function polygonCenter(poly) {
    const vertices = poly.getPath();

    // put all latitudes and longitudes in arrays
    const longitudes = new Array(vertices.length).map((_, i) => vertices.getAt(i).lng());
    const latitudes = new Array(vertices.length).map((_, i) => vertices.getAt(i).lat());

    // sort the arrays low to high
    latitudes.sort();
    longitudes.sort();

    // get the min and max of each
    const lowX = latitudes[0];
    const highX = latitudes[latitudes.length - 1];
    const lowy = longitudes[0];
    const highy = longitudes[latitudes.length - 1];

    // center of the polygon is the starting point plus the midpoint
    const centerX = lowX + ((highX - lowX) / 2);
    const centerY = lowy + ((highy - lowy) / 2);

    return (new google.maps.LatLng(centerX, centerY));
}
Jared Beach
  • 2,635
  • 34
  • 38
6

For somone seaching for an answer in dart/flutter you can achieve the same by using the code below.

 calculateCenter(List<LatLng> points) {
    var longitudes = points.map((i) => i.longitude).toList();
    var latitudes = points.map((i) => i.latitude).toList();

    latitudes.sort();
    longitudes.sort();

    var lowX = latitudes.first;
    var highX = latitudes.last;
    var lowy = longitudes.first;
    var highy = longitudes.last;

    var centerX = lowX + ((highX - lowX) / 2);
    var centerY = lowy + ((highy - lowy) / 2);

    return LatLng(centerX, centerY);
  }
Wisely D Cruizer
  • 916
  • 9
  • 23
4

Note that in the case of a concave polygon the center of the bounding rectangle might be completely outside the polygon. If your polygons might be concaved, I'd recommend using the center of the biggest inscribed circle as the "center" of the polygon. You can see a simple enough algorithm here (p. 4). If your task is to place a label on the polygon, this will also give the most aesthetically pleasing results (in which case I'd recommend using this method even if your polygons might not be concave).

gkdm
  • 2,375
  • 4
  • 21
  • 27
1

Getting the center of a polygon with multiple points and setting it on a map

=== Start of Coordinates ===

const polyCoords = [
{lat:7.47107077, lng:8.63921547},
{lat:7.46994686, lng:8.63547325},
{lat:7.46309805, lng:8.62589836},
{lat:7.46286821, lng:8.62590313},
{lat:7.45313215, lng:8.60899639},
{lat:7.43740988, lng:8.59334755},
{lat:7.42844009, lng:8.58104324},
{lat:7.41945982, lng:8.56827736},
{lat:7.40859699, lng:8.55323505},
{lat:7.40289497, lng:8.54318237},
{lat:7.392138, lng:8.53482914},
{lat:7.37328196, lng:8.52523136},
{lat:7.35658979, lng:8.52090073},
{lat:7.34258223, lng:8.51121902},
{lat:7.33046389, lng:8.50427532},
{lat:7.31578684, lng:8.49598789},
{lat:7.3011179, lng:8.48839378},
{lat:7.28741598, lng:8.4833231},
{lat:7.27968311, lng:8.4763031},
{lat:7.26344395, lng:8.47150517},
{lat:7.23888779, lng:8.46638584},
{lat:7.21805, lng:8.46258926},
{lat:7.20483398, lng:8.45912266},
{lat:7.18925285, lng:8.45223522},
{lat:7.17993593, lng:8.4470892},
{lat:7.15788603, lng:8.43984985},
{lat:7.13150883, lng:8.43637943},
{lat:7.11252594, lng:8.43324089},
{lat:7.09124422, lng:8.43060589},
{lat:7.06365919, lng:8.42369366},
{lat:7.0361681, lng:8.42277718},
{lat:7.02137899, lng:8.42187691},
{lat:7.00718498, lng:8.41496658},
{lat:6.98336077, lng:8.41237545},
{lat:6.97815323, lng:8.41181469},
{lat:6.96920681, lng:8.41085052},
{lat:6.96485901, lng:8.41038322},
{lat:6.95552588, lng:8.40431404},
{lat:6.94989014, lng:8.40449047},
{lat:6.93937397, lng:8.40481758},
{lat:6.92389822, lng:8.40438938},
{lat:6.90868998, lng:8.40626526},
{lat:6.89626408, lng:8.40878296},
{lat:6.88465405, lng:8.40741062},
{lat:6.88261604, lng:8.4071703},
{lat:6.87496519, lng:8.40522289},
{lat:6.87435913, lng:8.40444374},
{lat:6.874259, lng:8.40431404},
{lat:6.86711407, lng:8.40512753},
{lat:6.85861206, lng:8.40488625},
{lat:6.85695124, lng:8.40484142},
{lat:6.84681988, lng:8.4066267},
{lat:6.83782721, lng:8.40724277},
{lat:6.83494997, lng:8.40681171},
{lat:6.82532978, lng:8.40537834},
{lat:6.81447697, lng:8.40533257},
{lat:6.80816412, lng:8.40398979},
{lat:6.80544186, lng:8.40341187},
{lat:6.80309677, lng:8.40415192},
{lat:6.79915714, lng:8.4053936},
{lat:6.79694414, lng:8.40609169},
{lat:6.79487896, lng:8.40666771},
{lat:6.79399681, lng:8.40691471},
{lat:6.79165602, lng:8.40756893},
{lat:6.79155779, lng:8.40760326},
{lat:6.78247595, lng:8.41095257},
{lat:6.77746391, lng:8.42351627},
{lat:6.76626587, lng:8.45157433},
{lat:6.75096416, lng:8.48992825},
{lat:6.75101185, lng:8.49293423},
{lat:6.75103521, lng:8.49443054},
{lat:6.75106716, lng:8.49638653},
{lat:6.7513752, lng:8.49816227},
{lat:6.75229788, lng:8.5035038},
{lat:6.75233889, lng:8.50374794},
{lat:6.75729084, lng:8.51012325},
{lat:6.75758314, lng:8.51057243},
{lat:6.75847387, lng:8.51194859},
{lat:6.75807714, lng:8.5156126},
{lat:6.7578721, lng:8.51749611},
{lat:6.75769711, lng:8.51795578},
{lat:6.75742817, lng:8.51865578},
{lat:6.75132704, lng:8.97139263},
{lat:6.7482481, lng:8.98090267},
{lat:6.74862814, lng:8.98276329},
{lat:6.74871206, lng:8.9831686},
{lat:6.74890804, lng:8.98458385},
{lat:6.74876881, lng:8.98458672},
{lat:6.74830914, lng:8.98459339},
{lat:6.74822187, lng:8.98898792},
{lat:6.74818087, lng:8.9910574},
{lat:6.74633312, lng:8.99746799},
{lat:6.74763012, lng:8.99746132},
{lat:6.74740887, lng:9.00071144},
{lat:6.74718285, lng:9.00401211},
{lat:6.7468462, lng:9.00896358},
{lat:6.74784899, lng:9.03189945},
{lat:6.74912691, lng:9.04191589},
{lat:6.749506, lng:9.04487896},
{lat:6.7501688, lng:9.06040001},
{lat:6.74998713, lng:9.06665802},
{lat:6.7495532, lng:9.08146954},
{lat:6.75030279, lng:9.10532761},
{lat:6.75147676, lng:9.11876583},
{lat:6.75115299, lng:9.12830734},
{lat:6.751019, lng:9.13230419},
{lat:6.75097609, lng:9.13358688},
{lat:6.75030613, lng:9.15766907},
{lat:6.74956512, lng:9.18568802},
{lat:6.74977493, lng:9.20027924},
{lat:6.75178003, lng:9.20677471},
{lat:6.75207376, lng:9.21673679},
{lat:6.75762796, lng:9.22278786},
{lat:6.76703405, lng:9.22469616},
{lat:6.77835321, lng:9.22522545},
{lat:6.78895092, lng:9.22644424},
{lat:6.80095482, lng:9.22813511},
{lat:6.80803204, lng:9.22840595},
{lat:6.81769705, lng:9.2289257},
{lat:6.8349371, lng:9.2283287},
{lat:6.85686684, lng:9.22915077},
{lat:6.86674404, lng:9.23106098},
{lat:6.87596607, lng:9.23018837},
{lat:6.88755703, lng:9.22863388},
{lat:6.90605688, lng:9.22916317},
{lat:6.92060709, lng:9.22969246},
{lat:6.93092918, lng:9.23265743},
{lat:6.94083691, lng:9.23125458},
{lat:6.96318817, lng:9.23240376},
{lat:6.97901201, lng:9.23571301},
{lat:6.99151802, lng:9.23115253},
{lat:7.00751305, lng:9.22559357},
{lat:7.01615286, lng:9.21457386},
{lat:7.03671312, lng:9.19904041},
{lat:7.04957199, lng:9.18851852},
{lat:7.06313276, lng:9.17939568},
{lat:7.07376099, lng:9.16631126},
{lat:7.09011078, lng:9.15050983},
{lat:7.10480881, lng:9.13908195},
{lat:7.11774111, lng:9.12994957},
{lat:7.12020206, lng:9.12689114},
{lat:7.12678719, lng:9.1187048},
{lat:7.13768578, lng:9.10678005},
{lat:7.14586782, lng:9.09899521},
{lat:7.15596008, lng:9.09561729},
{lat:7.16383696, lng:9.0976162},
{lat:7.16397095, lng:9.09764957},
{lat:7.16446781, lng:9.09777832},
{lat:7.16467285, lng:9.09782982},
{lat:7.16668797, lng:9.0983429},
{lat:7.17027283, lng:9.09925175},
{lat:7.17104816, lng:9.0994482},
{lat:7.17748404, lng:9.11013699},
{lat:7.1852951, lng:9.12178135},
{lat:7.18590498, lng:9.12269306},
{lat:7.19736004, lng:9.14105415},
{lat:7.20605278, lng:9.1508379},
{lat:7.21073914, lng:9.15619564},
{lat:7.22404385, lng:9.17387962},
{lat:7.23505497, lng:9.19177437},
{lat:7.24421883, lng:9.20248795},
{lat:7.25214815, lng:9.2136507},
{lat:7.26130819, lng:9.22829533},
{lat:7.27181292, lng:9.24456787},
{lat:7.27600622, lng:9.25246239},
{lat:7.28409719, lng:9.25877571},
{lat:7.2944541, lng:9.2762022},
{lat:7.29864502, lng:9.28409672},
{lat:7.30395985, lng:9.29338932},
{lat:7.30883694, lng:9.29736137},
{lat:7.31085396, lng:9.29749584},
{lat:7.31294203, lng:9.29763317},
{lat:7.32206106, lng:9.29725742},
{lat:7.32526493, lng:9.30005836},
{lat:7.3387351, lng:9.30434513},
{lat:7.36335707, lng:9.30342197},
{lat:7.3788681, lng:9.30495262},
{lat:7.39461279, lng:9.30810261},
{lat:7.40965986, lng:9.30755138},
{lat:7.43268299, lng:9.30545902},
{lat:7.45160484, lng:9.30448055},
{lat:7.46140003, lng:9.30249405},
{lat:7.47527122, lng:9.291996},
{lat:7.48597622, lng:9.28886032},
{lat:7.49556684, lng:9.29241562},
{lat:7.50426912, lng:9.30243492},
{lat:7.51206589, lng:9.31382847},
{lat:7.51597023, lng:9.32126045},
{lat:7.52583599, lng:9.33729362},
{lat:7.53341389, lng:9.35122871},
{lat:7.54164696, lng:9.35685062},
{lat:7.55055094, lng:9.35993958},
{lat:7.55715799, lng:9.35861778},
{lat:7.56785202, lng:9.35293961},
{lat:7.57442522, lng:9.34260464},
{lat:7.57712793, lng:9.33361816},
{lat:7.58118677, lng:9.32210445},
{lat:7.58320713, lng:9.31334305},
{lat:7.58979607, lng:9.30693436},
{lat:7.5916028, lng:9.30302334},
{lat:7.60486794, lng:9.29872894},
{lat:7.60572386, lng:9.29845142},
{lat:7.60686207, lng:9.29808426},
{lat:7.61690998, lng:9.30256939},
{lat:7.62142801, lng:9.30766106},
{lat:7.62240505, lng:9.30839729},
{lat:7.6258359, lng:9.31074047},
{lat:7.62646914, lng:9.29734325},
{lat:7.62800121, lng:9.29264832},
{lat:7.62849808, lng:9.29112339},
{lat:7.62477303, lng:9.27168179},
{lat:7.61906385, lng:9.26338196},
{lat:7.61789513, lng:9.26168156},
{lat:7.60666084, lng:9.260952},
{lat:7.60124397, lng:9.26060104},
{lat:7.60034084, lng:9.25955677},
{lat:7.59277201, lng:9.25081539},
{lat:7.58999205, lng:9.24243736},
{lat:7.58469677, lng:9.22647762},
{lat:7.57617283, lng:9.20398521},
{lat:7.57207108, lng:9.18593216},
{lat:7.57107019, lng:9.18152714},
{lat:7.56439877, lng:9.16621494},
{lat:7.55862188, lng:9.14605904},
{lat:7.55898905, lng:9.13870907},
{lat:7.55946493, lng:9.12918186},
{lat:7.55989313, lng:9.12065315},
{lat:7.55908394, lng:9.11564445},
{lat:7.55753183, lng:9.10603333},
{lat:7.55615616, lng:9.0975132},
{lat:7.55674982, lng:9.09182739},
{lat:7.55695581, lng:9.08985138},
{lat:7.55813408, lng:9.07858467},
{lat:7.55744982, lng:9.05847168},
{lat:7.55736303, lng:9.05593491},
{lat:7.55961514, lng:9.04537582},
{lat:7.56139517, lng:9.03702545},
{lat:7.56247616, lng:9.03288078},
{lat:7.5634079, lng:9.02930832},
{lat:7.56566381, lng:9.02066135},
{lat:7.56515789, lng:9.00748539},
{lat:7.5639658, lng:9.00404358},
{lat:7.56356478, lng:9.00404358},
{lat:7.56289005, lng:9.00404358},
{lat:7.56242323, lng:9.00015068},
{lat:7.56232119, lng:8.99929619},
{lat:7.56194496, lng:8.99821568},
{lat:7.56198788, lng:8.99778271},
{lat:7.56215, lng:8.99696159},
{lat:7.56281996, lng:8.96886539},
{lat:7.55899286, lng:8.95738316},
{lat:7.55724001, lng:8.95211983},
{lat:7.55969477, lng:8.93246746},
{lat:7.55676603, lng:8.90829468},
{lat:7.55125523, lng:8.89593029},
{lat:7.54641199, lng:8.88194084},
{lat:7.54488802, lng:8.87319756},
{lat:7.54288197, lng:8.86331367},
{lat:7.53791094, lng:8.85578632},
{lat:7.53442907, lng:8.84015846},
{lat:7.52946901, lng:8.83331966},
{lat:7.5268302, lng:8.82713699},
{lat:7.52469683, lng:8.8237114},
{lat:7.52377796, lng:8.80965614},
{lat:7.522964, lng:8.80752277},
{lat:7.51968002, lng:8.79888344},
{lat:7.51792288, lng:8.79014683},
{lat:7.51585579, lng:8.77657032},
{lat:7.5152092, lng:8.76504707},
{lat:7.51339722, lng:8.75284863},
{lat:7.50989103, lng:8.73583889},
{lat:7.50696516, lng:8.72619915},
{lat:7.50269794, lng:8.71934986},
{lat:7.50024414, lng:8.69586086},
{lat:7.48745108, lng:8.69007874},
{lat:7.48527384, lng:8.68388939},
{lat:7.48022795, lng:8.67174816},
{lat:7.475317, lng:8.65337563},
{lat:7.47107077, lng:8.63921547}];

=== End of Coordinates ===

// Construct the polygon.
  const polygong= new google.maps.Polygon({
    paths: polyCoords,
    strokeColor: "#FF0000",
    strokeOpacity: 0.8,
    strokeWeight: 3,
    fillColor: "#FF0000",
    fillOpacity: 0.35,
  });
  
  var bounds = new google.maps.LatLngBounds();
  polyCoords.forEach(function(coord, index)
  {
     bounds.extend(coord);
  });

  //Note: "map" object already set and initialized elsewhere but not shown in code snippet to reduce verbosity 
  map.setCenter(bounds.getCenter());
Dev
  • 411
  • 3
  • 6
0

kotlin version of getting center of polygon. mPoints is array of latitude and longitude.

val bounds = LatLngBounds.Builder()

for(i in 0 until mPoints.size) {
    val point = LatLng(mPoints[i].latitude, mPoints[i].longitude)

    bounds.include(point)
}
mMap.moveCamera(CameraUpdateFactory.newLatLng(bounds.build().center))
Abduhafiz
  • 3,318
  • 5
  • 38
  • 48