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())
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.