Find centerpoint of polygon in JavaScript
Asked Answered
A

6

21

I have a "place" object from Google Maps which has a set of coordinates that represent a bounding box for a given location, say London. Each set of coordinates has a latitude and longitude.

I have written the below code to find the centerpoint, but I am not sure if it does actually produce the centerpoint. What if the polygon has 5 points instead of 4? Also, can this be done in a more efficient way, with less operations?

function average(array) {
  // Add together and then divide by the length
  return _.reduce(array, function (sum, num) {
    return sum + num;
  }, 0) / array.length;
}

// I have a two-dimensional array that I want to get the average of

var coords = [
  [ -1.2, 5.1 ],
  [ -1.3, 5.2 ],
  [ -1.8, 5.9 ],
  [ -1.9, 5.8 ]
]

// So I get the first column

var lats = coords.map(function (coord) {
  return coord[0];
})

// Then the second

var longs = coords.map(function (coord) {
  return coord[1];
})

// And average each column out

console.log([average(lats), average(longs)])

Example.

Azimuth answered 29/4, 2013 at 15:18 Comment(3)
Are you using the Google Maps Javascript API v3?Passive
I would suggest getting the bounds of the polygon, then using the center of the bounds.Passive
@Passive That's what I have, I believe? The "bounding box".Azimuth
C
32

This should get the centroid of the area of any polygon

/*jslint sub: true, maxerr: 50, indent: 4, browser: true */
/*global console */

(function () {
    "use strict";

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }

    function Region(points) {
        this.points = points || [];
        this.length = points.length;
    }

    Region.prototype.area = function () {
        var area = 0,
            i,
            j,
            point1,
            point2;

        for (i = 0, j = this.length - 1; i < this.length; j=i,i++) {
            point1 = this.points[i];
            point2 = this.points[j];
            area += point1.x * point2.y;
            area -= point1.y * point2.x;
        }
        area /= 2;

        return area;
    };

    Region.prototype.centroid = function () {
        var x = 0,
            y = 0,
            i,
            j,
            f,
            point1,
            point2;

        for (i = 0, j = this.length - 1; i < this.length; j=i,i++) {
            point1 = this.points[i];
            point2 = this.points[j];
            f = point1.x * point2.y - point2.x * point1.y;
            x += (point1.x + point2.x) * f;
            y += (point1.y + point2.y) * f;
        }

        f = this.area() * 6;

        return new Point(x / f, y / f);
    };

    var polygon = [
            {"x": -1.2, "y": 5.1},
            {"x": -1.3, "y": 5.2},
            {"x": -1.8, "y": 5.9},
            {"x": -1.9, "y": 5.8}
        ],
        region = new Region(polygon);

    console.log(region.centroid());
}());

On jsfiddle

Current answered 29/4, 2013 at 16:12 Comment(2)
I'm accepting this answer because it answers my question explicitly, although I'm using the Google Maps API answer by @razzak in my app.Azimuth
Unless you find some information that says it's not.Current
V
16

This will get the centerpoint of any shape as an array [centerX, centerY]:

var center = function (arr)
{
    var minX, maxX, minY, maxY;
    for (var i = 0; i < arr.length; i++)
    {
        minX = (arr[i][0] < minX || minX == null) ? arr[i][0] : minX;
        maxX = (arr[i][0] > maxX || maxX == null) ? arr[i][0] : maxX;
        minY = (arr[i][1] < minY || minY == null) ? arr[i][1] : minY;
        maxY = (arr[i][1] > maxY || maxY == null) ? arr[i][1] : maxY;
    }
    return [(minX + maxX) / 2, (minY + maxY) / 2];
}

Another way:

var center = function (arr)
{
    var x = arr.map (function (a){ return a[0] });
    var y = arr.map (function (a){ return a[1] });
    var minX = Math.min.apply (null, x);
    var maxX = Math.max.apply (null, x);
    var minY = Math.min.apply (null, y);
    var maxY = Math.max.apply (null, y);
    return [(minX + maxX) / 2, (minY + maxY) / 2];
}
getCenter (coords);

Alternatively if your browser supports ECMAScript 6, then you can use Arrow functions and Spread syntax as follows:

var center = function (arr)
{
    var x = arr.map (xy => xy[0]);
    var y = arr.map (xy => xy[1]);
    var cx = (Math.min (...x) + Math.max (...x)) / 2;
    var cy = (Math.min (...y) + Math.max (...y)) / 2;
    return [cx, cy];
}

jsfiddle

Vervain answered 29/4, 2013 at 15:37 Comment(2)
At the risk of being a smart-ass, it's not the center of the points, it's the middle between the two farthest points. As if you drew a square around all the existing points and found the center of it. Notice that in the question he tries to average the latitudes and the longitudes. The center of a polygon would not be in the same spot as the middle of a square.Naturalism
Also if your polygon cross the 180/-180 longitude this will be really really weirdStemson
P
6

To get the bounds of a Polygon (with your data) in the Google Maps API v3 (not tested):

var coords = [
  [ -1.2, 5.1 ],
  [ -1.3, 5.2 ],
  [ -1.8, 5.9 ],
  [ -1.9, 5.8 ]
];

var bounds = new google.maps.LatLngBounds();
for (var i = 0; i<coords.length; i++) {
   bounds.extend(new google.maps.LatLng(coords[i][0], coords[i][1]));
}
var center = bounds.getCenter();

var latitude = center.lat();           // latitude=-1.5499999999999998
var longitude = center.lng();          // longitude=5.5
var coordinates = center.toUrlValue(); // coordinates=-1.55,5.5

var coords = [
  [-1.2, 5.1],
  [-1.3, 5.2],
  [-1.8, 5.9],
  [-1.9, 5.8]
];

var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < coords.length; i++) {
  bounds.extend(new google.maps.LatLng(coords[i][0], coords[i][1]));
}
var center = bounds.getCenter();

var latitude = center.lat();
console.log("latitude=" + latitude);
var longitude = center.lng();
console.log("longitude=" + longitude);
var coordinates = center.toUrlValue();
console.log("coordinates=" + coordinates);
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
Passive answered 29/4, 2013 at 15:45 Comment(0)
N
5

Here is my es6 solution to average an array of latitudes and longitudes. Average is not the exact center point but it gets the job done in my case.

getLatLonCenterFromGeom = (coords) => {
    const arrAvg = arr => arr.reduce((a,b) => a + b, 0) / arr.length;

    const centerLat = arrAvg(coords.map(c=>c.latitude));
    const centerLon = arrAvg(coords.map(c=>c.longitude));

    if (isNaN(centerLat)|| isNaN(centerLon))
        return null;
    else return {latitude: centerLat, longitude:centerLon};
}
Naturalism answered 6/9, 2018 at 15:23 Comment(0)
R
1

I realize this is not exactly what you are looking for, but in case no one else answers it may be of some help. This is a PHP function which I use to to find the center point of polygons for my map application. It should be fairly easily converted to javascript for your use.

function getCenter($coord_array){
    $i = 0;
    $center = $coord_array[0];
    unset($coord_array[0]);
    foreach($coord_array as $key => $coord){    
        $plat = $coord[0];
        $plng = $coord[1];
        $clat = $center[0];
        $clng = $center[1];
        $mlat = ($plat + ($clat * $i)) / ($i + 1);
        $mlng = ($plng + ($clng * $i)) / ($i + 1);
        $center = array($mlat, $mlng);
        $i++;
    }
    return array($mlat, $mlng);
}

Note that the polygon has to be closed, meaning the first point in the array and the last point in the array are the same.

The function that converts the coordinate string to the necessary array:

function coordStringToArray($coord_string)
{
    $coord_array = explode("\n",$coord_string);
    foreach($coord_array as $key => $coord){
        $coord_array[$key] = explode(', ',$coord);
    }
    return $coord_array;
}

A sample of the raw coordinate string:

42.390576, -71.074258
42.385822, -71.077091
42.382461, -71.079408
42.382018, -71.081468
42.380496, -71.080953
42.380433, -71.076576
42.373902, -71.073915
42.373078, -71.069967
42.369273, -71.064216
42.368892, -71.062328
42.369527, -71.056491
42.370288, -71.050741
42.371619, -71.047908
42.376185, -71.046278
42.383476, -71.045763
42.386139, -71.050483
42.386202, -71.057693
42.387597, -71.066534
42.390259, -71.072284
42.391210, -71.073658
Rogan answered 29/4, 2013 at 15:34 Comment(0)
H
0
//Pure JavaScript version

function getCenterPoint(polygonId){
  var p=document.getElementById(polygonId).points;
  
  //Set initial min and max values
  var minX=p[0].x, maxX=p[0].x, minY=p[0].y, maxY=p[0].y;

  for(var i=0;i<p.length; i++){
    if(p[i].x < minX){minX=p[i].x;}
    if(p[i].x > maxX){maxX=p[i].x;}
    if(p[i].y < minY){minY=p[i].y;}
    if(p[i].y > maxY){maxY=p[i].y;}
  }
  return [(maxX + minX)/2, (maxY + minY)/2];
}

centerPoint = getCenterPoint("MyPolygonId");

Hurt answered 23/5, 2021 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.