31

I'd like to automatically place 100-200 bubble labels so that the following requirements are met:

  • Labels should not overlap
  • Labels should preferably not overlap bubbles
  • Label should be close to bubble
  • Preferred label position (top left, top, bottom, right etc.) should be respected to some extent
  • Font-size can vary

Are there any helpful libraries / algorithms for this? (preferably JavaScript or PHP)

(The label placement in the image does not meet these requirements)

enter image description here

dani
  • 4,614
  • 6
  • 47
  • 91
  • 5
    Excellent question. I'd offer a bouty, but reading "Murders per 1,000 population" in the chart makes me shut up and forget about the word "bounty". – András Szepesházi Apr 29 '11 at 22:20
  • András, the image merely serves as an illustration of a scatter gram with labels. I took it from here: http://flowingdata.com/2010/11/23/how-to-make-bubble-charts/. I'm sorry if it was not a suitable example image - I didn't pay much attention to the actual indicators plotted. – dani Apr 30 '11 at 10:02
  • I think the scope of this question is overly broad. – Lightness Races in Orbit May 02 '11 at 17:19
  • A starting point built with d3.js and https://github.com/d3fc/d3fc-label-layout, using the same dataset: http://codepen.io/Sphinxxxx/pen/ygGKbO – Sphinxxx Feb 13 '17 at 04:17

9 Answers9

9

How about this?

An Efficient Algorithm for Scatter Chart Labeling

Skippy le Grand Gourou
  • 4,607
  • 2
  • 38
  • 60
Jason Moore
  • 3,204
  • 13
  • 18
  • Thanks for the link to the excellent paper. I had a read-through but it might be hard to adapt the algorithm to JavaScript and the criteria listed above. If you have more "applicable" resources to share, they'd be much appreciated. – dani May 01 '11 at 09:51
  • @dani I agree that this algorithm will not be easily implementable in JavaScript. But that shows how hard the problem is. The algorithm allows to place labels at a distance to their bubble, as long as the connector line can be drawn without intersection. If you do not allow this, the problem becomes easier again. – Sebastian Nov 15 '13 at 11:42
  • FYI: The algorithm is currently implemented in C++ & is running in the background on all available cores. The results are excellent though, better than manually labeled charts, our clients find. – Sebastian Nov 15 '13 at 11:43
  • 1
    Here is the document: https://www.aaai.org/Papers/AAAI/2006/AAAI06-167.pdf – Rick Morgan Jan 12 '20 at 22:53
5

I think if you are using D3 inside your tools, you can use "force-based" label placement algorithm. The solution originally belongs to Max Planck Research Networks but there is already a ready-made Javascript example, here: Force-based label placement in D3

FidEliO
  • 867
  • 2
  • 14
  • 24
5

I imagine you're working with javascript, html and css? Well in any case two approaches come to mind.

First is to formulate it as an optimisation problem. You need to compute the ideal position for each label. This will be based on the size of the bubble, the desired location (i.e. top, down, left, right), and the size of the label (both font and length). Then you need to parameterise your coordinates, for example into a list of 2N elements where N is the number of labels. Then you need to initialise the labels in some position (or a population if using a genetic algorithm) and apply an optimisation algorithm which will require a cost function. This would be based on how far a set of label positions are from the ideal, as well as anything that violates your rules, such as overlapping.

Or, make it a physics problem. 'Attach' each label to its bubble with some rigid link. Give every label and every bubble a repelling force and also add a global and stronger graviational force (in the preferred top/left/right/down direction). Have a short physical simulation until you reach an equilibrium. The maths shouldn't be too hard.

zenna
  • 8,540
  • 10
  • 67
  • 100
4

This can be formulated as a Linear Programming problem. You want to maximize or minimize a function (representing the "weight" or "goodness" of the solution) subject to certain constraints (your requirements). You'll need to formalize your requirements as values. Something like this:

Variables:
x1 = 1 if Labels overlap, 0 otherwise
x2 = some measure of "how much" a label overlaps a bubble
x3 = distance from label to bubble //Label should be close to bubble
x4 = distance between ideal and actual label position
x5 = difference between actual and ideal font size


minimize x1 + 10*x2 + x3 + 20*x4 + 5*x5

subject to constraints:
    x1   = 0  // labels can never overlap
    x2   < /* maximum amount a label is allowed to overlap a bubble */
    ...
    x5   < 6 // font size does not vary by more than +/- 6 pts.

I made up the coefficients and the constraints, you can use them to tune the result based on which features are most important to you. Coefficients increase the value of a requirement (in this case, f4 is weighted most so it 'most important'. The constraints are hard limits that cannot be violated.

Since this is a well-known problem, you can now solve it using a number of methods. The Simplex algorithm is popular. There is a whole chapter in Cormen et. al about these problems.

Gabe Moothart
  • 28,997
  • 13
  • 73
  • 98
  • Assigning weights seems very reasonable. I however wonder how the simplex algorithm described can be implemented in JavaScript. Thanks. – dani May 04 '11 at 08:02
  • 4
    Downvoted, because the problem cannot be formulated as a Linear Programming Problem. Your constraint x1 is not linear, it is binary making this a decision problem. These are hard. If you write down the equations for intersection/non-intersection instead you will notice, that the constraints are not linear. – Sebastian Nov 15 '13 at 11:38
3

Maybe play with some helper toolkits like the following:

Bashwork
  • 1,599
  • 7
  • 14
1

I found an approach in Java actually for this problem that actually works and is called Force Directed Map Labeling and is an open-source academy experience.

Here is the documentation + project source code: Force Directed Map Labeling

ambodi
  • 5,445
  • 2
  • 30
  • 21
1

Unfortunately, I think it is going to be hard to find this solution readily available in Javascript or PHP. But, I do think your problem can be broken down into small sub-problems (based on your rules) to help you design your solution.

I would identify which of your rules are most important. From the looks of the graph you provided, I'd say that rule #1 and #2 would provide the greatest improvement in readability.

To determine placement according to those rules, I would calculate the boundary containers of the text and bubbles and test for intersection. Upon intersection, move to a location with no intersection. If one cannot be found, use a space with a minimum overlap.

This would allow you to also create a weighted placement heuristic for top-left, bottom-right, etc, to help place the labels in "preferred" locations.

I'd try writing out a small piece of the placement algorithm using two bubbles with two labels that are generally close and would potentially overlap. If you can generalize your placement algorithm to work for this small subset, you should be good moving forward with more bubbles.

Also, perhaps you could use something on the order of a kd-tree or another space partitioning datastructure to locate nearest neighbors to avoid.

jsherer
  • 880
  • 6
  • 8
0

This thread is very good in covering some approaches. I am using the force layout with multiple foci, as it seems like the best overall method.

Community
  • 1
  • 1
Union find
  • 6,247
  • 12
  • 46
  • 89
0

How about

label.substring(0, Math.sqrt(bubbleValueOrRadius))

I found this in one of protovis.js examples.

Yesh
  • 23
  • 3