13

I'm using the centroid of polygons to attach a marker in a map application. This works definitely fine for convex polygons and quite good for many concave polygons.

However, some polygons (banana, donut) obviously don't produce the desired result: The centroid is in these cases outside the polygons area.

Does anybody know a better approach to find a suitable point within any polygons area (which may contain holes!) to attach a marker?

enter image description here

user2033412
  • 1,950
  • 2
  • 27
  • 47
  • 1
    some applications do a point-in-polygon after the centroid is found. If it is outside, either a new x or y is calculated based on the halfway point of the ray intersecting the polygon. – NaN May 29 '18 at 15:34
  • I think you can do it by triangulation. For example at the first step, triangulate the polygon and then try to find a triangle which seems to be the center triangle in a desired criteria and return it's centroid. – vahidreza May 29 '18 at 15:35
  • 1
    I would split a concave polygon into convex parts (maybe it is already represented so), took the "plumpiest" of them (with large absolute diameter and a ratio of max diameter / min diameter closer to 1), and put the point in the center of that part. It may take a noticeable amount of computation, though, so I would consider caching the results, or storing them along with the polygons. – 9000 May 29 '18 at 15:41
  • 4
    You seem to be looking for the point furthest from any border. Ask this on a math forum. – Christophe Roussy May 29 '18 at 15:43
  • @ChristopheRoussy: Exactly! – user2033412 May 29 '18 at 15:46
  • [gis.se] may have similar questions, so search there, too. – Toby Speight May 29 '18 at 16:28
  • I have created [a similar question](https://stackoverflow.com/questions/58424042/fast-dirty-approximation-of-center-of-list-of-3d-vertex-that-forms-a-very-shal) about hull. If you happen to know any approach, you may kindly answer it. Thank. – javaLover Oct 17 '19 at 03:21

6 Answers6

5

One approach would be to generate and refine a skeleton of the polygon, then use the midpoint of the skeleton to place your marker (and if it's text, to orient the text correctly). This works well for most shapes, including ones with holes, and banana-shaped or tadpole-shaped crescents.

The CGAL library has a 2D Straight Skeleton and Polygon Offsetting module, or you could use PostGIS, for example.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • so, if each contour in the continuous shrinking process is given an increasing height, take the highest point. – Will Ness May 29 '18 at 19:11
  • @Will - that's one approach (and will give you the point furthest from any edge). For a dumbbell-shaped polygon, you might want the algorithm to choose a point on the 'bar' - in which case, you should prefer a (possibly weighted) mean position along the spine of the skeleton. – Toby Speight May 30 '18 at 07:53
3

To rephrase comment of ChristopheRoussy we may look for the largest circle inside of the polygon.

The largest circle is the one which cannot grow anymore because it touches three vertices or edges (if it touches only two, it can become bigger or just moved until it touches third).

So if you have few vertices, you can just enumerate all possible triples of vertices/edges, find for each one a circle and then select the largest one.

But it will require creating four functions:

  1. Circle(vertex,vertex,vertex)
  2. Circle(vertex,vertex,edge)
  3. Circle(vertex,edge,edge)
  4. Circle(edge,edge,edge)

All of them are possible, but may require some effort.

maxim1000
  • 6,297
  • 1
  • 23
  • 19
3

Find the extreme ordinates and draw an horizontal line in the middle. It is guaranteed to cross the polygon.

Find the intersection with the sides and sort them by increasing abscissa. Pick a point in the middle of two intersections.

This is an O(N + K Log K) process where K is the number of intersections (usually a very small even number). Pretty straightforward to write.

To increase the chances of a nice placement, you can try three horizontals instead of one an pick the longest intersection segment.

enter image description here

  • This method may fail if the interior of the polygon is not connected and the intersection of the horizontal line and polygon lies on a self-intersection of the polygon. For example, the polygon could be a figure 8 and the horizontal line could cross the waist of the 8. – Adam Gawne-Cain May 25 '19 at 09:02
  • @AdamGawne-Cain: no, this works. You will get two overlapping intersection points, defining a degenerate segment. –  May 25 '19 at 10:15
  • I agree you would get a point, but it would not be strictly *within* the polygon as OP requested. Actually, I think your method is suitable to get a point for a marker, as the marker is probably bigger than a point and may be bigger than all the space within the polygon. – Adam Gawne-Cain May 25 '19 at 15:28
2

I have no idea how to solve this for any possible shape (and not doing heavy computation), but maybe for simpler shapes like the ones you have shown:

https://en.wikipedia.org/wiki/Force-directed_graph_drawing

Heuristic: This could converge to a reasonable approximation after a while

  • transform shape border into many points (more = more precise)
  • start out with many random points inside the polygon
  • push them until they are furthest away from border points, or just compute distance ... (can be done in parallel)
  • take best point

Another way could be to use multiple algorithms depending on the nature of the shape (like another one for donuts ...). Also perhaps relying on measuring 'fattest' sections first ?

IMHO would ask this on a math forum.

Similar: Calculate Centroid WITHIN / INSIDE a SpatialPolygon

Similar: How to find two most distant points?

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
1

To get a point for a marker I would use Yves Daoust's method.

To get a point that is reliably "within any polygon with holes" I would split polygon into triangles with a reliable library (e.g. OpenGL's GLUtessellator), and then get centroid of triangle with largest area.

If I had time for developing and testing, and I wanted good performance, then I would use a hybrid method: First use Yves Daoust's method to get some candidate points and then test candidates to see if they are within polygon. If all candidates fail, then fall back to slower reliable method (e.g. GLUtesselator).

Adam Gawne-Cain
  • 1,347
  • 14
  • 14
0
for (int i = 0; i < n; /*++i*/) {
    p = RandomPointInsideConvexHull();
    if (IsInsidePolygon(p)) {
        ++i;
        d = DistanceToClosestEdge(p);
        if (d > bestD) {
            bestP = p;
        }
    }
}

After running this loop you will approximate solution by bestP. n is parameter to choose. If you want more accurate result you can restart search, but now instead of picking a point inside polygon's convex hull you can pick one in the neighborhood of bestP, say not farther than bestD / 5 (this time you don't need to check if random point is inside polygon).

Yola
  • 18,496
  • 11
  • 65
  • 106
  • This algorithm will not work for polygons with many vertices but small area, such as the outline of a thin track/road that winds through mountains. The problem is that a randomly chosen point inside the convex hull is likely to be outside the polygon. In some cases *all* points may be outside the polygon. – Adam Gawne-Cain May 25 '19 at 08:56
  • @AdamGawne-Cain please check the condition on which `i` get incremented. Though this is not the most efficient algorithm for the kind of polygons you mentioned. – Yola May 25 '19 at 11:58
  • The good thing about your method is that it will find a nice point in many polygons. But OP wanted method for "any polygon". Your code will run forever if the input polygon is degenerate (e.g. L shape with area=0 such as {0 0, 1 0, 1 1, 1 0, 0 0}). – Adam Gawne-Cain May 25 '19 at 15:38
  • @AdamGawne-Cain Hm... right, but polygon with zero area doesn't have points which are strictly *within* the polygon;) – Yola May 25 '19 at 17:39