plotly / plotly.js

Open-source JavaScript charting library behind Plotly and Dash
https://plotly.com/javascript/
MIT License
17.05k stars 1.86k forks source link

contour labels #1395

Closed geocosmite closed 7 years ago

geocosmite commented 7 years ago

The 'contour' plot type is very useful but would be even more powerful if it included an automated facility for creating labels. Mapbox seems to incorporate a particularly nice implementation of contour labeling as illustrated here: https://www.mapbox.com/blog/satellite-map-with-contours/

Given that the two systems already apparently have some connections perhaps something could be worked out to incorporate Mapbox's approach for contour labeling into Plotly for cases where the data are not defined in terms of lat / long but instead by x and y?

etpinard commented 7 years ago

Some label work is being done at the moment for the upcoming carpet plots in https://github.com/plotly/plotly.js/pull/1239

geocosmite commented 7 years ago

The labels in the examples look fabulous.

rreusser commented 7 years ago

Thanks for the suggestion, @geocosmite! I've been debating how elaborate to get with this since a major feature of carpet plots is inequalities which require at least some sort of label. For a start, I've been considering just labeling the inequality constraints at the edge of the plot, though that leaves a bit to be desired. It might be the best option to get the feature out the door though.

As for labels that follow the contours, I spent a bit of time analyzing examples on google images trying to reverse engineer the rules by which they're placed. The carpet plots mostly just reuse the regular contour plot code so it's certainly not inconceivable to hit two birds with one contour label placement stone.

plot_contour_ex_1

A few various features/rules that are pretty easy to notice:

Of course could dig up the matplotlib code and analyze how they're doing it, but I think most of the challenge is in the details of the particular implementation rather than figuring out the logic/heuristics which can always be incrementally improved.

geocosmite commented 7 years ago

Thanks very much @rreusser for sharing your ideas about approaches for labeling. From what you have written I can see that it is difficult to come up with an implementation that will satisfy all situations. Nonetheless what you've laid out sounds like a great place to start.

Over the past couple of weeks as I have been learning to use Plotly I have grown increasingly impressed by its capabilities, stability, and API design. The careful thought that shines through your comments and those of your colleagues in the forums show me why the system is as good as it is.

Something to consider in terms of future plot types for your labeling scheme is that it would be extremely useful to have a 'contour3d' plot type that supports labels.

We are interested in this plot type given that it will allow us to view a third data attribute on a 3D surface when used in conjunction with the 'z' and 'surfacecolor' attributes supported by the 'surface' plot type. I hope to get a mockup of such a plot together in the next day or two and would be glad to share it with you if you like.

alexcjohnson commented 7 years ago

Some more observations as I start to work on this:

rreusser commented 7 years ago

There's the possibility of using an optimizer. You could write a simple cost function like proximity to other labels and curvature of the path, then optimize. It's not too difficult to dig up a nonlinear unconstrained minimizer (I made a super simple but one here. Has been working well, but pretty swappable for a better one).

(Undocumented, but the syntax for mine is just minimize(function (a) { return cost; }, a0) where a is some state vector.)

geocosmite commented 7 years ago

I like your way of thinking Alex! I agree that having the text along a straight line path where the angle is tangent to the contour line should lead to better readability for cases where there is significant curvature over the text's length. And I'm on board with your assessment that readability will be improved by keeping text close to horizontal while also dispersing the labels over a broader area rather than clustering them. I'm also with you on the lack of a need to include units in the labels provided that there is a legend entry where the units can be shown. @rreusser's clip idea sounds great as well.

Regarding the label frequency for a given contour, I wonder if it would make sense to base this on the ratio between the line length for the contour to the x-axis or y-axis distance (whichever is longer)? Here are some examples of poorly thought through rules that you might use for inspiration:

Using an optimizer to determine the label locations as suggested by @rreusser would be terrific. If this is too labor intensive to implement a simpler approach to consider might be (1) find reference points by determining the positions where the labels will be evenly spaced along the contour's length, and (2) for each reference point find the position that comes closest to a horizontal text orientation within some tolerance of the reference point location (say 0.1 times the contour line length). You'll also want to check to ensure that the labels don't intersect an axis boundary.

alexcjohnson commented 7 years ago

@geocosmite interesting idea to bring the axis length (or perhaps the diagonal) into the calculation, I like it. I still think the distance to the next contour will need to play a role - imagine doubling the number of contours you show: that will make it harder to follow the contours that were already present, even though those contours didn't change. But there may well be a place for both, something like the max or quadrature combination of the two results.

geocosmite commented 7 years ago

Glad you like the contour / length ratio idea Alex. Here's another couple of options to consider that might help with clutter and readability: (1) define a label interval or (2) introduce the concept of major and minor contours. For the former idea a value of 2 would mean that labels would be defined for every other contour value. For the latter idea you could place labels only on the major contours. This would also make it possible to define different line properties and intervals for the major vs. minor contours.

Here's an example I made that shows major and minor contours draped over a Plotly 3D surface plot. The labels (white text) aren't oriented very well but I hope it at least gives an idea of the concept. image

alexcjohnson commented 7 years ago

Thanks @geocosmite - I've thought about major/minor contours, I agree it would be a nice feature, and coupled somewhat to the use of labels. But to keep the feature focused I'm planning to leave that out of the first stage.

Then as long as major/minor contours are in the roadmap, I'd rather not include a label interval. It would be fairly easy to add, but major/minor is more flexible, doesn't miss any capabilities of interval (that I can think of anyway - can you?) and there's at least one complication to the interval approach that major/minor avoids: choosing the right starting point. For example, if your contour interval is 10, your first contour is at 30, and you set a label interval of 5: naturally you're asking to label contours 50, 100, and 150, not 30, 80, and 130. Major/minor would automatically default to round numbers for the major values while still allowing unusual starting points, without adding any additional attributes.

geocosmite commented 7 years ago

Sounds like a good plan Alex. The easy work around in the interim is to create separate traces for the major and minor contours (that's what I did in the example above) and only use the new labelling option on the major contour. Regarding the labeling issue when using an interval perhaps you could get around the problem using something analogous to the tick0 attribute for defining the first tick mark?