observablehq / plot

A concise API for exploratory data visualization implementing a layered grammar of graphics
https://observablehq.com/plot/
ISC License
4.16k stars 171 forks source link

Add default tips for the geo mark #2087

Closed Fil closed 1 month ago

Fil commented 1 month ago

If the geo mark has no x and y channels, it would be nice if the tip option worked. It could create x and y with a centroid initializer. However, a system that tracks the closest geometry, relying on distance to the shape rather than to its centroid, would arguably be better. The difference in strategy is striking if you consider France, whose centroid is in the Atlantic, because Guyane. But centroids show the small features better.

(Note: this is a duplicate of #1743)

jwoLondon commented 1 month ago

Don't know how expensive SDF or similar would be comparison to centroid calculations, but if it is too costly, in my experience taking the centroid of the largest polygon in a multi polygon almost always captures a representative centre point.

The more problematic cases can be heavily concave features (e.g. a 'C' shape mostly surrounding a different convex polygon). There a midpoint from some kind of skeletonisation might be more meaningful, but won't be cheap.

Fil commented 1 month ago

The best solution IMO would be to use the “pole of inaccessibility” transform (discussed in #1587 — which I had failed to mention yesterday), as implemented in mapbox/polylabel. This could be the default for #2088 — it is a pragmatic approach that would make interactive tips behave nicely in general (and could also result in better placement for labels).

More speculatively, I also want to suggest a new geo option value for Plot.pointer (besides x, y and xy), which would point to the closest geometry. The way I would do this:

  1. draw the shapes on a relatively coarse canvas and read the active pixels back (like https://observablehq.com/@fil/another-hex-map#geoContainsCache but for an array of geometries).
  2. add the poles of inaccessibility of each geometry, to make sure each one has at least 1 active point.
  3. on pointer move, do a closest point search in that point list (e.g. with a quadtree search) and return the corresponding index.