How to control the axis units in a map made with astropy and matplotlib? - astropy

When using astropy and matplotlib to create a map, the units in the right ascension axis are deg/min/sec, instead of h/m/s. I do not find an easy way in astropy to select the units h/m/s.
For example, if I try to reproduce the map of the Horsehead nebula as in the documentation of astropy.wcs, I get a R.A. axis in deg/min/sec.
The code is simply:
from matplotlib import pyplot as plt
from astropy.io import fits
from astropy.wcs import WCS
from astropy.utils.data import get_pkg_data_filename
filename = get_pkg_data_filename('tutorials/FITS-images/HorseHead.fits')
hdu = fits.open(filename)[0]
wcs = WCS(hdu.header)
fig = plt.figure()
fig.add_subplot(111, projection=wcs)
plt.imshow(hdu.data, origin='lower', cmap=plt.cm.viridis)
plt.xlabel('RA')
plt.ylabel('Dec')
plt.show()
It is supposed to produce this:
correct units
but I get that:
wrong units

You can use:
ax = fig.gca()
ra = ax.coords[0]
ra.set_format_unit('hour')
e.g. as specified here: http://docs.astropy.org/en/stable/visualization/wcsaxes/controlling_axes.html
However, when I ran the same example, it defaulted to hours, so I'm not sure what configuration you have set that defaulted to degrees instead.

Related

Cartopy: coastlines() and contourf() interfering

I'm trying to migrate from Basemap to Cartopy looking demo examples. I have a simple code using both coastlines() and contourf(). I can get both separately but not simultaneously. The data set is a netcdf file containing the sea surface temperature data of the west Med. The code is:
import numpy as np
from netCDF4 import Dataset
import cartopy
import matplotlib.pyplot as plt
# DATA
data = Dataset('20190715.0504.n19.nc','r')
lon = data.variables['lon'][:]
lat = data.variables['lat'][:]
sst = data.variables['mcsst'][0,:,:].squeeze()
xxT,yyT = np.meshgrid(lon,lat)
# PLOT
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_axes([0.01,0.01,0.98,0.98],projection=cartopy.crs.Mercator())
ax1.coastlines()
#ax1.contourf(xxT,yyT,sst)
ax1.set_extent([16.5, -15.0, 35.0, 46.5])
plt.show()
With this code I get:
If I use:
#ax1.coastlines()
ax1.contourf(xxT,yyT,sst)
ax1.set_extent([16.5, -15.0, 35.0, 46.5])
I get a white rectangle.
If I use:
#ax1.coastlines()
ax1.contourf(xxT,yyT,sst)
ax1.set_extent([16.5,-15.0,35.0,46.5],crs=cartopy.crs.Mercator())
I get the contoured data.
But with both:
ax1.coastlines()
ax1.contourf(xxT,yyT,sst)
ax1.set_extent([16.5,-15.0,35.0,46.5],crs=cartopy.crs.Mercator())
the contour is ok ! but without coastlines. And if finally
ax1.coastlines()
ax1.contourf(xxT,yyT,sst)
ax1.set_extent([16.5,-15.0,35.0,46.5])
only coastlines are shown, not contour !. I try to understand how I have to proceed because problems arose when trying to include this into a GUI with options show/hide for coatlines, features, etc. Just in case I'm using Python 3.7.4, Cartopy 0.17, proj4 5.2, matplotlib 3.1.1. Thanks !
Thanks to swatchai suggestion, although, I still don't understand why I need to use the transform keyword with the specific PlateCarree projection keyword, the code works fine if:
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_axes([0.01, 0.01, 0.98, 0.98],projection=cartopy.crs.Mercator())
ax1.coastlines('10m')
ax1.set_extent([16.5, -15.0, 35.0, 46.5])
ax1.contourf(xxT,yyT,sst,transform=cartopy.crs.PlateCarree())
Here the result:

Matplotlib: Discrete colorbar fails for custom labels

I faced a serious problem when I was trying to add colorbar to scatter plot which indicates in which classes individual sample belongs to. The code works perfectly when classes are [0,1,2] but when the classes are for example [4,5,6] chooses colorbar automatically color values in the end of colormap and colorbar looks blue solid color. I'm missing something obvious but I just can't figure out what it is.
Here is the example code about the problem:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1 , figsize=(6, 6))
plt.scatter(datapoints[:,0], datapoints[:,1], s=20, c=labels, cmap='jet', alpha=1.0)
plt.setp(ax, xticks=[], yticks=[])
cbar = plt.colorbar(boundaries=np.arange(len(classes)+1)-0.5)
cbar.set_ticks(np.arange(len(classes)))
cbar.set_ticklabels(classes)
plt.show()
Variables can be for example
datapoints = np.array([[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7]])
labels = np.array([4,5,6,4,5,6,4])
classes = np.array([4,5,6])
Correct result is got when
labels = np.array([0,1,2,0,1,2,0])
In my case I want it to work also for classes [4,5,6]
The buoundaries need to be in data units. Meaning, if your classes are 4,5,6, you probably want to use boundaries of 3.5, 4.5, 5.5, 6.5.
import matplotlib.pyplot as plt
import numpy as np
datapoints = np.array([[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7]])
labels = np.array([4,5,6,4,5,6,4])
classes = np.array([4,5,6])
fig, ax = plt.subplots(1 , figsize=(6, 6))
sc = ax.scatter(datapoints[:,0], datapoints[:,1], s=20, c=labels, cmap='jet', alpha=1.0)
ax.set(xticks=[], yticks=[])
cbar = plt.colorbar(sc, ticks=classes, boundaries=np.arange(4,8)-0.5)
plt.show()
If you wanted to have the boundaries determined automatically from the classes, some assumption must me made. E.g. if all classes are subsequent integers,
boundaries=np.arange(classes.min(), classes.max()+2)-0.5
In general, an alternative would be to use a BoundaryNorm, as shown e.g. in Create a discrete colorbar in matplotlib
or How to specify different color for a specific year value range in a single figure? (Python) or python colormap quantisation (matplotlib)

Cartopy AzimuthalEquidistant projection: zooming into a region and coastlines

I am trying to plot some data on an AzimuthalEquidistant projection using cartopy. However, it gives me a couple of problems. First the coastlines no longer show for this type of projection. Not sure if this is my code or a Cartopy problem. I also notice that if I use a ccrs.PlateCarree() transform in the pcolormesh command the coastlines do show but then, presumably, my data is on the wrong type of prejection?
Second I would prefer if the axis boarder was circular after plotting the data, is it possible to use set_extent or some similar function to do this?
The code below should reproduce the problems, the circle shows how I would like the boarder to look.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
clat = 55.0
clon = -8.0
lons = np.arange(clon-15,clon+16,0.5)
lats = np.arange(clat-15,clat+16,0.5)
d = np.random.rand(lons.shape[0],lats.shape[0])
trans = ccrs.AzimuthalEquidistant(central_latitude=clat, central_longitude=clon)
ax = plt.axes(projection=trans)
ax.coastlines(resolution='10m')
CB=ax.pcolormesh(lons-0.25, lats-0.25, d.T,
cmap=plt.cm.viridis, alpha=0.5,
transform=trans)#ccrs.PlateCarree())
p1 = mpatches.Circle((clon,clat), radius=15, color='k', lw=5, fill=False,
transform=trans)
ax.add_patch(p1)
If the data you are plotting is in latitude/longitude coordinates then the correct value for the transform keyword is indeed ccrs.PlateCarree(). This is common gotcha for new users. The transform argument tells cartopy what coordinates your data are in, and is completely independent of the projection you want to plot onto.
To make the plot circular you'll need to set the boundary yourself. The Cartopy documentation have a couple of examples of this: http://scitools.org.uk/cartopy/docs/latest/examples/always_circular_stereo.html and http://scitools.org.uk/cartopy/docs/latest/examples/star_shaped_boundary.html.

How to add a point-feature shapefile to map using cartopy

I have two shapefiles. One is a point feature shapefile, named "point.shp", the other is a polygon shapefile named "polygon.shp". Both I want to add to a map using cartopy.
I managed to add the "polygon.shp", but failed with the "point.shp".
Here's my code:
import matplotlib.pyplot as plt
from cartopy import crs
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature
ax = plt.axes(projection=crs.PlateCarree())
# add the polygon file, worked
ax.add_geometries(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='w')
# or(also worked):
ax.add_feature(ShapelyFeature(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='r'))
# but these two ways both failed with the "point.shp"
ax.add_geometries(Reader("point.shp").geometries(), crs.PlateCarree())
# or, this doesn't work neither:
ax.add_feature(ShapelyFeature(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='r'))
Does any one know how to do this, or why, without retrieving all the points' x, y coords and then plotting them?
And with coordinates(x, y values), ax.plot() works, but ax.scatter() fails, why?
Thanks
add_geometries currently turns a geometry into a polygon and then colours it appropriately, which of course means that when you pass points the add_geometries, the polygons are not visible. Potentially cartopy could do a better job of this in the future, but in the meantime, it sounds like you just want to use something like scatter to visualize your data.
You can achieve this by getting the x and y coordinate values out of the geometry and passing these straight on to scatter with the appropriate transform:
import cartopy.crs as ccrs
import cartopy.io
import matplotlib.pyplot as plt
fname = cartopy.io.shapereader.natural_earth(resolution='10m',
category='cultural',
name='populated_places_simple')
plt.figure(figsize=(12, 6))
ax = plt.axes(projection=ccrs.Robinson())
ax.set_title('Populated places of the world.')
ax.coastlines()
points = list(cartopy.io.shapereader.Reader(fname).geometries())
ax.scatter([point.x for point in points],
[point.y for point in points],
transform=ccrs.Geodetic())
plt.show()
HTH

Trying to add a 3d subplot to a matplotlib figure

So I'm trying to create a figure that presents a 3d plot from data points, along with the plots 3 projections in 3 other subplots. I can add the subplots for the projections with no problems, but when I try to place the 3 dimensional plot into the figure things backfire.
here's my code:
def plotAll(data):
fig = plt.figure()
plot_3d = fig.add_subplot(221)
ax = Axes3D(plot_3d)
for i,traj in enumerate(data.values()):
ax.plot3D([traj[0][-1]],[traj[1][-1]],[traj[2][-1]],".",color=[0.91,0.39,0.046])
#plot_12v13 = fig.add_subplot(222)
#plot_projections(data,0,1)
#plot_13v14 = fig.add_subplot(223)
#plot_projections(data,1,2)
#plot_12v14 = fig.add_subplot(224)
#plot_projections(data,0,2)
#plt.plot()
which throws back:
'AxesSubplot' object has no attribute 'transFigure'
I'm using matplotlib 0.99.3, any help would be greatly appreciated, thanks!
I was searching for a way to create my 3D-plots with the nice fig, axes = plt.subplots(...) shortcut, but since I just browsed Matplotlib's mplot3d tutorial, I want to share a quote from the top of this site.
New in version 1.0.0: This approach is the preferred method of creating a 3D axes.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Note
Prior to version 1.0.0, the method of creating a 3D axes was different. For those using older versions of matplotlib, change ax = fig.add_subplot(111, projection='3d') to ax = Axes3D(fig).
So if you have to use the <1.0.0 version of Matplotlib, this should be taken into account.
If you would like to use plt.subplots instead of plt.subplot (see the difference here), then you can do something like this one:
import matplotlib.pyplot as plt
from matplotlib import cm # for a scatter plot
from mpl_toolkits.mplot3d import Axes3D
fig, ax = plt.subplots(1,2,figsize=(10,10),subplot_kw=dict(projection='3d'))
sc1 = ax[0].scatter(x,y,z, c = true, cmap=cm.jet)
ax[0].set_title('True solution')
sc2 = ax[1].scatter(x,y,z c = y_pred, cmap=cm.jet)
ax[1].set_title('Predicted Solution')
Well, I don't know how to set individual axes as 3D using plt.subplots. It would be helpful if someone could comment down.
The preferred way of creating an 3D axis is giving the projection keyword:
def plotAll(data):
fig = plt.figure()
ax = fig.add_subplot(221, projection='3d')
for i,traj in enumerate(data.values()):
ax.plot3D([traj[0][-1]],[traj[1][-1]],[traj[2][-1]],".",color=[0.91,0.39,0.046])
plot_12v13 = fig.add_subplot(222)
plot_projections(data,0,1)
plot_13v14 = fig.add_subplot(223)
plot_projections(data,1,2)
plot_12v14 = fig.add_subplot(224)
plot_projections(data,0,2)
plt.plot()
Unfortunately, you didn't supply a working example with suitable data, so I couldn't test the code. Also, I would recommend updating to a newer version of matplotlib.