Finding the center of Leaflet polygon?

后端 未结 4 479
逝去的感伤
逝去的感伤 2020-12-24 13:00

I have a bunch of leaflet polygons on a map I created. Each polygon represents something different. A specific set of information is displayed in a popup depending on the pa

相关标签:
4条回答
  • 2020-12-24 13:41

    Since some time Leaflet has built-in getCenter() method:

    polygon.getBounds().getCenter();
    
    0 讨论(0)
  • 2020-12-24 13:46

    There are a few ways to approximate the centroid of a polygon.

    The easiest (but least accurate method) is to get the center of the bounding box that contains the polygon, as yarl suggested, using polygon.getBounds().getCenter();

    I originally answered the question with the formula for finding the centroid of the points, which can be found by averaging the coordinates of its vertices.

    var getCentroid = function (arr) { 
        return arr.reduce(function (x,y) {
            return [x[0] + y[0]/arr.length, x[1] + y[1]/arr.length] 
        }, [0,0]) 
    }
    
    centerL20 = getCentroid(L20);
    

    While the centroid of the points is a close enough approximation to trick me, a commenter pointed out that it is not the centroid of the polygon.

    An implementation based on the formula for a centroid of a non-self-intersecting closed polygon gives the correct result:

    var getCentroid2 = function (arr) {
        var twoTimesSignedArea = 0;
        var cxTimes6SignedArea = 0;
        var cyTimes6SignedArea = 0;
    
        var length = arr.length
    
        var x = function (i) { return arr[i % length][0] };
        var y = function (i) { return arr[i % length][1] };
    
        for ( var i = 0; i < arr.length; i++) {
            var twoSA = x(i)*y(i+1) - x(i+1)*y(i);
            twoTimesSignedArea += twoSA;
            cxTimes6SignedArea += (x(i) + x(i+1)) * twoSA;
            cyTimes6SignedArea += (y(i) + y(i+1)) * twoSA;
        }
        var sixSignedArea = 3 * twoTimesSignedArea;
        return [ cxTimes6SignedArea / sixSignedArea, cyTimes6SignedArea / sixSignedArea];        
    }
    
    0 讨论(0)
  • 2020-12-24 13:47

    assuming each polygon has only 4 sides it is simple

    var L20 = [
    [74.0995, -99.92615],
    [74.14008, -99.4043],
    [74.07691, -99.33838],
    [74.03617, -99.86023]
    ];
    

    using this example get max and min lat: 74.03617 and 74.14008 respectively so same for long: -99.92615 and 99.33838 respectively

    Then get the middle value for each: (max - min) / 2 = 0.051955 and -0.293885 then add them to the minimum amount

    gives you a centre of 74.088125, -99.632265

    0 讨论(0)
  • 2020-12-24 13:49

    The problem you are trying to solve is called the pole of inaccessibility problem. Finding the best place to put a label in a polygon isn't completely solved by finding the center of the bounding box. Consider a polygon in the shape of the letter U. The center of the bounding box puts the label outside of the polygon. It took me forever to find this outstanding library: https://github.com/mapbox/polylabel

    From the README.MD:

    A fast algorithm for finding polygon pole of inaccessibility, the most distant internal point from the polygon outline (not to be confused with centroid), implemented as a JavaScript library. Useful for optimal placement of a text label on a polygon.

    It's an iterative grid algorithm, inspired by paper by Garcia-Castellanos & Lombardo, 2007. Unlike the one in the paper, this algorithm:

    • guarantees finding global optimum within the given precision
    • is many times faster (10-40x)

    Usage:

    Given polygon coordinates in GeoJSON-like format and precision (1.0 by default), Polylabel returns the pole of inaccessibility coordinate in [x, y] format.

    var p = polylabel(polygon, 1.0);

    How the algorithm works:

    This is an iterative grid-based algorithm, which starts by covering the polygon with big square cells and then iteratively splitting them in the order of the most promising ones, while aggressively pruning uninteresting cells.

    1. Generate initial square cells that fully cover the polygon (with cell size equal to either width or height, whichever is lower). Calculate distance from the center of each cell to the outer polygon, using negative value if the point is outside the polygon (detected by ray-casting).
    2. Put the cells into a priority queue sorted by the maximum potential distance from a point inside a cell, defined as a sum of the distance from the center and the cell radius (equal to cell_size * sqrt(2) / 2).
    3. Calculate the distance from the centroid of the polygon and pick it as the first "best so far".
    4. Pull out cells from the priority queue one by one. If a cell's distance is better than the current best, save it as such. Then, if the cell potentially contains a better solution that the current best (cell_max - best_dist > precision), split it into 4 children cells and put them in the queue.
    5. Stop the algorithm when we have exhausted the queue and return the best cell's center as the pole of inaccessibility. It will be guaranteed to be a global optimum within the given precision.

    0 讨论(0)
提交回复
热议问题