Reputation: 154
I have plotted a series of latitude-longitude pairs using Basemap in Python. A sample image is:
I need to display the names of the places when the mouse is clicked (or hovered) on the points. I have the station names with the file that contains the latitude-longitude pairs.
Firstly, how do I make the hover functionality in Basemap (or something better)? Secondly, how do I add the text as labels when the point is hovered over?
Here's what I have so far:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
lattd, lngtd = [], []
# latitude, longitude and station names are as headers rows(rows starting with "#", in plot_file.txt.
# read all the lines and make lists for latitude and longitude
inputfile = open('data-por-IN/plot_file.txt', 'r')
for i, line in enumerate(inputfile):
if line.startswith('#'):
lattd.append(int(line[56:62])/10000)
lngtd.append(int(line[65:71])/10000)
m = Basemap(width=4000000,height=4000000,projection='lcc',
resolution='c',lat_1=45.,lat_2=55,lat_0=20,lon_0=80.)
m.drawmapboundary(fill_color='aqua')
m.drawcountries()
m.drawcoastlines(linewidth=0.50)
m.fillcontinents(color='green', alpha = 0.6, lake_color='aqua')
for i in range(len(lngtd)):
lon = lngtd[i] #77.580643
lat = lattd[i] #12.972442
xpt,ypt = m(lon,lat)
lonpt, latpt = m(xpt,ypt,inverse=True)
m.plot(xpt,ypt,'yo')
ax.bluemarble()
plt.show()
Upvotes: 0
Views: 1319
Reputation: 9810
The problem at hand can be solved with matplotlib event handling, annotations, and (for figure size independent marker picking) transformations. Below is an example that shows a label whenever the mouse pointer is moved on top of one of the blue markers. As the marker size is given in points (one point is 1/72 inches), I transform the data coordinates into figure coordinates, following the shadow effect transformation in the matplotlib transformation tutorial. Hope this helps.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import math
# latitude, longitude and station names are as headers rows(rows starting with "#", in plot_file.txt.
# read all the lines and make lists for latitude and longitude
##inputfile = open('data-por-IN/plot_file.txt', 'r')
##for i, line in enumerate(inputfile):
## if line.startswith('#'):
## lattd.append(int(line[56:62])/10000)
## lngtd.append(int(line[65:71])/10000)
##fake coordinates and labels
lattd, lngtd, labels = zip(*[
(20.6, 79.0, 'point 1'),
(21.3, 77.5, 'point 2'),
(13.0, 77.6, 'point 3'),
])
##a list for keeping track of all annotations
annotations = [None for label in labels]
##defining size of markers:
markersize = 5
markersize_inches = markersize/72.
##setting up figure
fig, ax = plt.subplots()
m = Basemap(
width=4000000,height=4000000,projection='lcc',
resolution='c',lat_1=45.,lat_2=55,lat_0=20,lon_0=80.,
ax = ax,
)
m.drawcountries()
m.drawcoastlines(linewidth=0.50)
m.bluemarble()
##data coordinates
xdata, ydata = zip(*[m(lon,lat) for lon,lat in zip(lngtd,lattd)])
ax.plot(xdata,ydata,'bo', mec='k', ms = markersize)
##figure coordinates in inches
trans = ax.transData+fig.dpi_scale_trans.inverted()
##function for checking mouse coordinates and annotating
def on_move(event):
if event.inaxes:
x0, y0 = trans.transform((event.xdata, event.ydata))
xfig, yfig = zip(*[trans.transform((x,y)) for x,y in zip(xdata,ydata)])
dists = [math.sqrt((x-x0)**2+(y-y0)**2) for x,y in zip(xfig, yfig)]
for n,(x,y,dist,label) in enumerate(zip(xdata,ydata,dists, labels)):
if dist < markersize_inches and annotations[n] is None:
annotations[n]=ax.annotate(
label,
[x,y], xycoords='data',
xytext = (10,10), textcoords='offset points',
ha='left', va='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round'),
zorder=10,
)
fig.canvas.draw()
elif dist > markersize_inches and annotations[n] is not None:
annotations[n].remove()
annotations[n] = None
fig.canvas.draw()
##connecting the event handler
cid = fig.canvas.mpl_connect('motion_notify_event', on_move)
plt.show()
Upvotes: 2