문제

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 page the user is on. I need to find a way to make the "popup" bubble open in the center of the polygon it represents.

Each polygon is drawn using the following code:

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



var L19 = [
    [74.02559, -99.84924],
    [74.06636, -99.32739],
    [74.0029, -99.26147],
    [73.96197, -99.77783]
];

var L18 = [
    [73.95142, -99.76684],
    [73.99235, -99.25048],
    [73.92889, -99.18456],
    [73.8878, -99.69543]
];

var set1 = L.polygon([L20, L19, L18], {
    color: "#fff",
    weight: 1,
    stroke: true,
    opacity: 0.05,
    fillColor: "#346B1F",

}).addTo(map);

The popup is drawn using the following code:

var popup = L.popup({})
    .setLatLng([73.64017, -100.32715])
    .setContent(content).openOn(map);
    var popup = L.popup();

So I need to find a way for .setLatLang to determin or be given the center of the polygon.

I came up with 3 solutions that may work, not sure how to go about it.

  1. find a way to use the coordinates of a polygon to determine the center of the polygon where the popup will open.

  2. call one point of the polygon, then offset the position of the popup.

  3. Use an id for each polygon, so each popup knows the box area (polygon) it can be opened in.

Can someone help me please?

도움이 되었습니까?

해결책 2

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];        
}

다른 팁

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

polygon.getBounds().getCenter();

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);

Pole of Inaccessibility

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.

The most distant internal point from the polygon outline

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

To move a polygon into view and center it use:

    map.fitBounds(poly.getBounds())

This will also set the zoom level correctly.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top