Matplotlib basemap: Popup box
Asked Answered
A

1

7

I want to know how to create a popup box in a basemap plot. When I hover my mouse over a location , it should trigger the popup box.

Is this possible?

Astri answered 18/7, 2012 at 8:38 Comment(0)
T
32

Yes it is possible thanks to matplotlib's event handling framework. I couldn't find an already written example which does what you are particularly interested in so I wrote one (which I will put forward for inclusion in the matplotlib source).

I would read http://matplotlib.sourceforge.net/users/event_handling.html thoroughly to best understand what is going on. Please note that although it sounds like the perfect solution "pick_event" is for mouse clicks -not for mouse over- events and doesn't work in this case.

My code, which could be objectified very nicely should one want, looks like:

import matplotlib.pyplot as plt

fig = plt.figure()
ax = plt.axes()


points_with_annotation = []
for i in range(10):
    point, = plt.plot(i, i, 'o', markersize=10)

    annotation = ax.annotate("Mouseover point %s" % i,
        xy=(i, i), xycoords='data',
        xytext=(i + 1, i), textcoords='data',
        horizontalalignment="left",
        arrowprops=dict(arrowstyle="simple",
                        connectionstyle="arc3,rad=-0.2"),
        bbox=dict(boxstyle="round", facecolor="w", 
                  edgecolor="0.5", alpha=0.9)
        )
    # by default, disable the annotation visibility
    annotation.set_visible(False)

    points_with_annotation.append([point, annotation])


def on_move(event):
    visibility_changed = False
    for point, annotation in points_with_annotation:
        should_be_visible = (point.contains(event)[0] == True)

        if should_be_visible != annotation.get_visible():
            visibility_changed = True
            annotation.set_visible(should_be_visible)

    if visibility_changed:        
        plt.draw()

on_move_id = fig.canvas.mpl_connect('motion_notify_event', on_move)

plt.show()

Hopefully everything should be fairly readable. A high level overview of the code goes:

  • Create a list of [point, annotation] pairs, where by default the annotation is not visible
  • Register a function, "on_move", to be called every time there is mouse motion detected
  • The on_move function iterates through each point and annotation, if the mouse is now over one of the points, make its associated annotation visible, if it is not, make it invisible. (the contains method is documented here)

Screenshot of the result

Tooley answered 19/7, 2012 at 7:46 Comment(5)
Ah lovely, thank you @Tooley for the code and explanation. So this means that there is no direct event handler for hovering over a location on the plot. It needs to be done indirectly.Astri
No. You can access the x and y coordinates of the mouse event in the on_move function. From that you can do anything. In my case I have looked through all of the artists and identified if any contain the mouse position, but you could equally update a single annotation instance's position given the x and y.Tooley
Yeah you are right on that point, but I was looking for something like registering a callback to the a particular artist with an event like "hover_event" That is what I mean by a direct eventAstri
Right. No, not that I am aware of. Would be a nice feature to have to be able to turn on (but may have performance impacts if it were always checking...). Hope thats been helpful.Tooley
Thanks @Tooley for the provided solution. I was just curious if you know why would annotations behave strangely if the ax is a polar graph. I am using your exact solution to add annotations on my polar graph, however, only a subset of the annotations actually show up (even though they all are created and are reacting to the onmove event). I posted a question here - #15650387 (simplified out the event handlers). Thanks in advance for any help that you can provide!Isahella

© 2022 - 2024 — McMap. All rights reserved.