Search code examples
pythonmatplotlibcartopycoordinate-transformationmap-projections

Transform event data to projection coordinates - Matplotlib Transformation


I have a plot that uses a Cartopy map for its coordinate system. I want to click anywhere on the plot, mark it, and return the location of the clicked point in terms of Latitude and Longitude. I am just not sure how to transform the event data back to map coordinates.

I have read through the Matplotlib Transformations Tutorial but it is still unclear to me. The Navigation Toolbar on the plot even displays the coordinates in Lat./Lon but I'm not sure how to access that.

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

fig = plt.figure()
ax = plt.axes(projection=ccrs.Sinusoidal())
ax.set_extent([0,10,0,10], crs=ccrs.PlateCarree())
ax.gridlines(draw_labels=True,dms=True,x_inline=False,y_inline=False)

def onclick(event): 
    ax.scatter(event.xdata, event.ydata)
    fig.canvas.draw()

    # I want to print xdata and ydata in terms of Latitude and Longitude
    print(event.xdata,event.ydata)

cid = fig.canvas.mpl_connect('button_press_event', onclick)

Example Plot Output


Solution

  • To convert the values back to latitude and longitude floats, simply use the transform_point method on the PlateCarree projection. Here I formatted the values using the formatters from the gridliner, but you could obviously format how you like.

    import matplotlib.pyplot as plt
    import cartopy.crs as ccrs
    from cartopy.mpl.ticker import LatitudeFormatter, LongitudeFormatter
    
    fig = plt.figure()
    plot_proj = ccrs.Sinusoidal()
    data_proj = ccrs.PlateCarree()
    ax = plt.axes(projection=plot_proj)
    ax.set_extent([0,10,0,10], crs=data_proj)
    gl = ax.gridlines(draw_labels=True,dms=True,x_inline=False,y_inline=False)
    
    def onclick(event): 
        ax.scatter(event.xdata, event.ydata)
        fig.canvas.draw()
    
        # Get hold of latitude and longitude as floats.
        lon, lat = data_proj.transform_point(event.xdata, event.ydata, plot_proj)
        # Format in degrees, minutes and seconds.
        print(gl.yformatter.format_data(lat), gl.xformatter.format_data(lon))
    
    cid = fig.canvas.mpl_connect('button_press_event', onclick)
    
    plt.show()
    

    enter image description here

    3°8′39.215″N 2°44′46.914″E
    6°41′19.267″N 6°5′21.873″E
    2°52′25.117″N 8°46′25.903″E