Search code examples
matplotlibcartopy

Get projected coordinates from geometric coordinates


I have a map figure rendered with Cartopy and Matplotlib. I have a specific geometric coordinate (in lat/lon) and I would like to know the pixel coordinate closest to this geometric coordinate's projection (if it is visible), for instance to draw a graphic over the coordinate on the map.

(Note I don't want to draw with Matplotlib; I'm exporting the figure as a bitmap image and drawing in a different part of the pipeline.)

This documentation suggests it might be something like this:

import cartopy, matplotlib.pyplot

fig = matplotlib.pyplot.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=cartopy.crs.Orthographic())
ax.add_feature(cartopy.feature.LAND, facecolor='black')

# Print the location of New York City in display coordinates
lon, lat = -74.0060, 40.7128
trans = cartopy.crs.Geodetic()._as_mpl_transform(ax)
x, y = trans.transform((lon, lat))
print(x, y)

# Or this way
projx, projy = ax.projection.transform_point(lon, lat, cartopy.crs.Geodetic())
x, y = ax.transData.transform((projx, projy))
print(x, y)

Though interestingly, if I plot this point, the figure centers on and zooms into Manhattan, and then the output display coordinates are indeed in the center of the figure at (640, 480).

matplotlib.pyplot.plot(lon, lat, marker='o', color='red', markersize=12,
    alpha=0.7, transform=cartopy.crs.Geodetic())

Solution

  • I just found that the transforms are not properly set until the figure is in its final state. So the key is to first draw the figure,

    fig.canvas.draw()
    

    or at least apply the aspect properly.

    ax.apply_aspect()
    

    Then you will get the correct pixel coordinates out,

    import matplotlib.pyplot as plt
    import cartopy
    import cartopy.crs as ccrs
    
    fig = plt.figure()
    ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree())
    ax.add_feature(cartopy.feature.LAND, facecolor='black')
    ax.set_global()
    
    # before being able to call any of the transforms, the figure needs to be drawn
    fig.canvas.draw()
    # or
    # ax.apply_aspect()
    
    # Print the location of New York City in display coordinates
    lon, lat = -74.0060, 40.7128
    trans = ccrs.PlateCarree()._as_mpl_transform(ax)
    x, y = trans.transform_point((lon, lat))
    print(x,y)
    
    plt.show()
    

    This prints:

    188.43377777777778 312.3783111111111
    

    Note that those coordinates refer to the pixels from the lower left corner.