2
$\begingroup$

Callout is a relatively recent functionality that is pretty useful to add custom labels to points and plots. A minimal example of it being used in ListPlot is

ListPlot[{{0, 0}, Callout[{1, 1}, "Cool label"], {2, 2}}]

What I am looking for is the same exact functionality, but for points in Graphics objects. I'm looking for a good way to do the above with points drawn with something like

Graphics[{Point@{{0,0},{1,1},{2,2}}}]

Unfortunately, naive guesses such as Point@Callout[{0,0},"ASD"], Callout[Point@{0,0}] don't seem to work. The documentation of Callout also doesn't mention any example using Graphics object, which makes me thing the functionality simply isn't implemented for Graphics.

Is there a good way to still do the above? Of course, I could "hack it" by having the callout drawn in a ListPlot, and then put in the embedding Graphics via Show. Something like:

Show[
  Graphics[
    Point@{0, 0},
    PlotRange -> ConstantArray[{-1, 1}, 2], Frame -> True
  ],
  ListPlot[
    {Callout[{0, 0}, "Cool label"]},
    Axes -> False, PlotStyle -> Directive[Opacity@0]
  ]
]

does what it should, but isn't exactly "nice". Is there a better way to achieve this? It doesn't have to be a solution with Callout; similar functionality using something else would also be fine. As long as it doesn't require manually drawing and positioning label and connecting line.

$\endgroup$
4
  • $\begingroup$ Perhaps Text is what you are looking for? $\endgroup$ Oct 26 '21 at 13:33
  • $\begingroup$ @UlrichNeumann is there a way to have it generate the line connecting the text to a given point? $\endgroup$
    – glS
    Oct 26 '21 at 13:42
  • $\begingroup$ @gIS Probably not I think. You have to define it. $\endgroup$ Oct 26 '21 at 13:49
  • 1
    $\begingroup$ Related: community.wolfram.com/groups/-/m/t/2357939 $\endgroup$ Oct 26 '21 at 13:51
2
$\begingroup$

Please note that with Show you don't embed ListPlot graphics into a Graphics object, but combine both of them into a single Graphics object.

Positioning of the callouts by ListPlot depends on AspectRatio, PlotRange, PlotRangePadding and the style settings for the plot marker. By setting the target values of these options explicitly, you get the desired "nice" appearance:

Show[ListPlot[{Callout[{0, 0}, "Cool label"]},
   AspectRatio -> Automatic,
   PlotRange -> ConstantArray[{-1, 1}, 2], PlotRangePadding -> None,
   PlotStyle -> AbsolutePointSize[2]] /. _Point -> Nothing,
 Graphics[Point@{0, 0},
  PlotRange -> ConstantArray[{-1, 1}, 2]],
 Frame -> True, Axes -> False]

output

In principle, you could write your own "callout primitive" using Inset as follows:

Clear[callout]
callout[point_, label_, markerSize_ : 2] := 
 Inset[ListPlot[{Callout[{0, 0}, label]}, ImageSize -> All, 
    Axes -> False, AlignmentPoint -> {0, 0}, 
    PlotStyle -> AbsolutePointSize[markerSize]] /. _Point -> Nothing, 
  point]

Examples of use:

Graphics[{Point[{{1, 1}}], callout[{1, 1}, "Cool label", 1]}, 
 Frame -> True]

Graphics[{Point[{{1, 1}}]}, Frame -> True, 
 Epilog -> callout[{1, 1}, "Cool label", 1]]

output1

output2

$\endgroup$
2
  • $\begingroup$ so the solution is to trick Mathematica into drawing the callouts in a ListPlot and then put the result in the desired Graphics anyway? Ideally, I was hoping for some way to directly access whatever algorithm Mathematica is using under the hood to draw and position lines and labels of Callouts, but I suppose there might be no easy way to access that $\endgroup$
    – glS
    Oct 27 '21 at 8:05
  • $\begingroup$ @glS Yes, I think ListPlot is the easiest way to access the label positioning algorithm. $\endgroup$ Oct 27 '21 at 8:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.