Skip to content

Add pole_of_inaccessibility() function in Lua for polygons#1822

Merged
lonvia merged 1 commit intoosm2pgsql-dev:masterfrom
joto:pole-of-inaccessibility
Jan 18, 2023
Merged

Add pole_of_inaccessibility() function in Lua for polygons#1822
lonvia merged 1 commit intoosm2pgsql-dev:masterfrom
joto:pole-of-inaccessibility

Conversation

@joto
Copy link
Collaborator

@joto joto commented Nov 10, 2022

Implementation of the "polylabel" algorithm for finding the "pole of inaccessibility" or center of the maximum inscribed circle of a polygon. This is accessible from Lua as the pole_of_inaccessibility() function.

The current implementation is only defined for polygons, because it is unclear how it should behave for other types of geometries. We can extend this later if needed.

For the moment this PR is just to get some feedback. Do not merge yet.

See also https://blog.jochentopf.com/2022-11-10-finding-representative-points-for-polygons.html

@tordans
Copy link

tordans commented Nov 15, 2022

Thanks for sharing those details in the blog post!

Today I read that Mapbox also has an implementation of the "pole of inaccessibility" at https://github.com/mapbox/polylabel. And they also list other language implementations https://github.com/mapbox/polylabel#ports-to-other-languages.

A fast algorithm for finding the pole of inaccessibility of a polygon (in JavaScript and C++)

(Via: facebook/Rapid#534 (comment))

Since you referenced other implementation but not this one, I though I would share it.

@joto
Copy link
Collaborator Author

joto commented Nov 15, 2022

@tordans The Mapbox implementation is the original one from Vladimir that I cite and link to in my blog post.

-- The second parameter is a "stretch" factor for the Y axis. Make
-- this larger than 1 (the default) to use an ellipse instead of a
-- circle when calculating the point. This makes it more suitable
-- for labels which usually have larger width that height.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- for labels which usually have larger width that height.
-- for labels which usually have larger width than height.

@JakobMiksch
Copy link
Contributor

The computation worked well for my test area (Heidelberg city).

I like the approach with the ellipse a lot. In many cases it gives better results for labeling than the circle. Like in these examples:

Screenshot from 2022-11-18 19-01-51

Screenshot from 2022-11-18 19-02-21

Screenshot from 2022-11-18 19-05-03

However, in the following example I was surprised about the location of poi2. I would have expected it somewhere in the right-top of poi1 (blue)

Screenshot from 2022-11-18 19-03-44

*
* \param polygon The input polygon
* \param precision Used as cutoff in the recursive algorithm. A minimum cutoff
* is also set at max(width, height) of the polygo envelope / 1000.0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* is also set at max(width, height) of the polygo envelope / 1000.0.
* is also set at max(width, height) of the polygon envelope / 1000.0.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the typos found. They are fixed now.

@joto
Copy link
Collaborator Author

joto commented Nov 18, 2022

However, in the following example I was surprised about the location of poi2. I would have expected it somewhere in the right-top of poi1 (blue)

If the polygon has several areas that are quite good it seems that it can take the algorithm a while to find the best one, it will be stuck on some local maximum somewhere and not consider that there is something better somewhere else. If you set the precision parameter smaller it should find the best position eventually but it will run much longer. You can try whether that makes a difference in this case.

@joto joto force-pushed the pole-of-inaccessibility branch from 214128a to 7663725 Compare November 19, 2022 11:07
@Komzpa
Copy link
Contributor

Komzpa commented Nov 30, 2022

This is available in PostGIS since 3.1.0: https://postgis.net/docs/ST_MaximumInscribedCircle.html

@joto joto force-pushed the pole-of-inaccessibility branch from 7663725 to 353df4a Compare December 22, 2022 15:50
@joto
Copy link
Collaborator Author

joto commented Dec 23, 2022

@pnorman had the suggestion in chat to scale by aspect ration of the bounding box (ST_YMax(g)-ST_YMin(g))/(ST_XMax(g)-ST_XMin(g)) instead of with a fixed scale.

Code here: https://gist.github.com/pnorman/58a58346915eb5c7369d8ff80bfac849

@pnorman
Copy link
Collaborator

pnorman commented Dec 23, 2022

I've been playing with PIA via PostGIS in my latest style, and on the whole, it's better than other placement methods but does have some weaknesses.

My theory is that a better label placement algorithm is to take the inscribed ellipse with a maximum area. This can be done by rotating and scaling the polygon by different values, computing the maximum inscribed circle, undoing the rotation and scaling to compute the area, and then returning the point from the ellipse with the maximum area.

For practical reasons, it's not possible to try this for every possible scaling and rotation, so I've done it by scaling by 1.0, some hand-selected values, and the aspect ratio of the bounding box of the polygon. Assuming my theory above holds, this will return either the PIA or a point better than the PIA. I haven't tackled rotation, but my idea is to do a couple of rotations.

This is not perfect, but in my tests, does deal with the weaknesses of PIA while not causing any problems.

I'm not sure how we would handle the compute multiple points and select one based on properties of the computation in osm2pgsql.

@joto
Copy link
Collaborator Author

joto commented Jan 2, 2023

I tried scaling with the the aspect ratio of the bounding box of the polygon and the result was sometimes better, sometimes worse. So for itself I don't think that's a win.

Calculating the PIA is expensive enough that I am sceptical about doing this many times for different aspect rations and/or rotations, so I don't think that's a good approach. I am certain there are better algorithms, but its unlikely we'll come up with something quickly. The main question for me is: Is the PIA we have here (with the scaling modification) a good enough solution for now that it makes sense baking it into osm2pgsql?

@joto joto force-pushed the pole-of-inaccessibility branch from 353df4a to 8f9731b Compare January 2, 2023 15:51
@pnorman
Copy link
Collaborator

pnorman commented Jan 3, 2023

Calculating the PIA is expensive enough that I am sceptical about doing this many times for different aspect rations and/or rotations, so I don't think that's a good approach. I am certain there are better algorithms, but its unlikely we'll come up with something quickly. The main question for me is: Is the PIA we have here (with the scaling modification) a good enough solution for now that it makes sense baking it into osm2pgsql?

Yes. I'm not sure the scaling modification gains anything, but it's already coded.

@joto joto force-pushed the pole-of-inaccessibility branch from 8f9731b to 6cdd260 Compare January 9, 2023 09:51
@joto joto changed the title WORK IN PROGRESS: Add pole_of_inaccessibility() function in Lua for polygons Add pole_of_inaccessibility() function in Lua for polygons Jan 9, 2023
@joto
Copy link
Collaborator Author

joto commented Jan 9, 2023

I think this has been tested enough so I am removing the "work in progress" label. I just rebased this to the current master.

We can declare this feature as "experimental" in the docs, that gives us some room for changes in the future.

@joto joto force-pushed the pole-of-inaccessibility branch 2 times, most recently from bbd8631 to 1a5a005 Compare January 16, 2023 15:48
@joto
Copy link
Collaborator Author

joto commented Jan 17, 2023

I have changed the Lua function so that it takes a Lua table with options. Currently only option is "stretch" (the second parameter from before). This allows us more flexibility when we want to change things later. The "precision" parameter is not exposed to the Lua code, because it is unclear how useful it is. Its really hard to explain what it does and users will probably just leave it at the default anyway. The now fixed (formally default) value is the same as with the PostGIS function.

I updated the docs in the example config.

Implementation of the "polylabel" algorithm for finding the "pole of
inaccessibility" or center of the maximum inscribed circle of a polygon.
This is accessible from Lua as the pole_of_inaccessibility() function.

The current implementation is only defined for polygons, because it is
unclear how it should behave for other types of geometries. We can
extend this later if needed.
@joto joto force-pushed the pole-of-inaccessibility branch from 1a5a005 to 38e4efc Compare January 17, 2023 14:33
@lonvia lonvia merged commit 563111c into osm2pgsql-dev:master Jan 18, 2023
@joto joto deleted the pole-of-inaccessibility branch January 19, 2023 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants