Coloring points based on a variable value - python

I have a dataset of temperature values at a network of weather stations. I am looking to plot the data on a map, with the points colored based on the temperature at that station. Is there such capability found in Python/Matplotlib to do this?
For reference: My dataset is in a netCDF file and contains lat/lon values of each station. The temperature variable is a function of station and time. So far, I have plotted the points on a map using Cartopy, but with one color for each point.

You're looking for the scatter plotting command, using the c argument:
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import numpy as np
lon = np.random.rand(50) * 50 - 120
lat = np.random.rand(50) * 25 + 25
temps = np.random.randn(50) * 10 + 25
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.LambertConformal())
ax.scatter(lon, lat, c=temps, transform=ccrs.PlateCarree())
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS)

Related

Set plt.colorbar range with python/cartopy

Hello I have this code here, that I use to plot climate maps of aerosols.
import matplotlib.pyplot as plt
from netCDF4 import Dataset as netcdf_dataset
import numpy as np
from cartopy import config
import cartopy.crs as ccrs
import cartopy.feature as cfeature
AODs_Season = netcdf_dataset('/home/vescovint/Documents/AOD_0219_DJF.nc')
lats = AODs_Season.variables['lat'][:]
lons = AODs_Season.variables['lon'][:]
AODS = AODs_Season.variables['AOD_550_Dark_Target_Deep_Blue_Combined_Mean_Mean'][0, :, :]
ax = plt.axes(projection=ccrs.PlateCarree())
plt.contourf(lons, lats, AODS, 60, transform=ccrs.PlateCarree(), cmap='YlOrRd')
plt.colorbar(shrink=0.55)
plt.gcf().set_size_inches(15, 11)
ax.coastlines()
plt.savefig('/home/vescovint/Documents/AOD_Maps/DJF2002')
My problem is that, all my maps avec different range of value on the colorbar from 0 to 5 and i would to set it to min = 0 and max = 3.
I can't figure it out, if anyone can help me, I would be grateful.
Thomas
You can use clim() method from pyplot. For example:
plt.clim(0,3)
to set the color range of your plot (see doc)

Why can't I use cartopy to plot certain time averages of the same dataset?

I have a 3-dimensional xarray DataArray of changes in surface temperature with coordinates of time, lat and lon. I am visualizing the data using Cartopy. You can find the 125 MB file here.
While producing plots of time-averages over different periods, I've found that I'm unable to produce orthographic projections when including certain time steps, such as the 132nd (index 131) time. Here is a plot of the time average from 0 to 130:
But this happens when I instead perform the time average from 0 to 131:
Here is the code I used to produce the plots:
# import statements
import cartopy.crs as ccrs
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
from cartopy.util import add_cyclic_point
%matplotlib inline
%config InlineBackend.figure_format = "jpg"
# read in data
ens_mean = xr.open_dataarray('temp_changes_ens_mean.nc')
# time average subset of data
to_plot = ens_mean.isel(time=slice(None,131)).mean(dim='time') # change 130 to 131 to break cartopy
# add cyclic point to avoid white lines
data = to_plot
lon = to_plot.coords['lon']
lon_idx = data.dims.index('lon')
wrap_data, wrap_lon = add_cyclic_point(data.values, coord=lon, axis=lon_idx)
# make an orthographic plot centered on north pole
fig = plt.figure(figsize=(4.5,3.5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Orthographic(0, 90))
ax.coastlines()
im = ax.contourf(wrap_lon, to_plot.lat, wrap_data,
transform=ccrs.PlateCarree())
# add colorbar
cb = fig.colorbar(im,orientation='horizontal',shrink=0.5,pad=0.05)
cb.ax.tick_params(labelsize=8)
cb.set_label('ΔSAT (K)',fontsize=8)
plt.tight_layout(w_pad=0.05)
plt.show()
This occurs whether I add a cyclic point or not. I am able to make quick plots of the data using matplotlib or xarray's built-in plotting without error. I've already checked for NaN values in the data. Lastly, if I remove the transform argument in the contourf line, it is able to produce a coherent plot, which leads me to think it is the transformation step that produces this odd plot.
Thanks for the help!
You can use ax.set_global() method to reset the coordinate limits:
#!/usr/bin/env ipython
# --------------------------------------------
import cartopy.crs as ccrs
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
from cartopy.util import add_cyclic_point
# --------------------------------------------------------------------------------------
#%matplotlib inline
#%config InlineBackend.figure_format = "jpg"
# read in data
ens_mean = xr.open_dataarray('temp_changes_ens_mean.nc')
# time average subset of data
to_plot = ens_mean.isel(time=slice(None,131)).mean(dim='time') # change 130 to 131 to break cartopy
# add cyclic point to avoid white lines
data = to_plot
lon = to_plot.coords['lon']
lon_idx = data.dims.index('lon')
wrap_data, wrap_lon = add_cyclic_point(data.values, coord=lon, axis=lon_idx)
# ------------------------------------------------------------------
# this is not working:
xlims = (np.min(ens_mean['lon']),np.max(ens_mean['lon']));
ylims = (np.min(ens_mean['lat']),np.max(ens_mean['lat']));
# ------------------------------------------------------------------
lon = to_plot.coords['lon']
# ====================================================================================
# make an orthographic plot centered on north pole
# Let us make a working/satisfying plot:
fig = plt.figure(figsize=(4.5,3.5))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Orthographic(0, 90))
ax.coastlines()
im = ax.contourf(wrap_lon, to_plot.lat, wrap_data,
transform=ccrs.PlateCarree())
# -----------------------------------------------------------
# add colorbar
cb = fig.colorbar(im,orientation='horizontal',shrink=0.5,pad=0.05)
cb.ax.tick_params(labelsize=8)
cb.set_label('ΔSAT (K)',fontsize=8)
plt.tight_layout(w_pad=0.05)
ax.set_global();
#ax.set_xlim(xlims);
#ax.set_ylim(ylims);
plt.show()

Dynamic Visualisation of Global Plots

I have produced 17 global plots that show the decadal averages in maximum surface ozone from 1850-2015. Rather than plotting them individually, I wish to create an animation that cycles through them (almost like a gif), i.e. have the same coastlines, axes and colour bar throughout but change what is being plotted as the contour.
Any help on how to adapt my code to do this would be greatly appreciated - thank you in advance!!
import numpy as np
import netCDF4 as n4
import matplotlib.pyplot as plt
from matplotlib import colorbar, colors
import matplotlib.cm as cm
import cartopy as cart
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.feature as cfeature
nc = n4.Dataset('datafile.nc','r')
# daily maximum O3 VMR (units: mol mol-1)
sfo3max = nc.variables['sfo3max']
lon = nc.variables['lon'] # longitude
lat = nc.variables['lat'] # latitude
# (I manipulate the data to produce 17 arrays containing the decadal average O3 VMR which are
# listed below in sfo3max_avg)
sfo3max_avg = [sfo3max_1850_1860_avg, sfo3max_1860_1870_avg, sfo3max_1870_1880_avg,
sfo3max_1880_1890_avg, sfo3max_1890_1900_avg, sfo3max_1900_1910_avg,
sfo3max_1910_1920_avg, sfo3max_1920_1930_avg, sfo3max_1930_1940_avg,
sfo3max_1940_1950_avg, sfo3max_1950_1960_avg, sfo3max_1960_1970_avg,
sfo3max_1970_1980_avg, sfo3max_1980_1990_avg, sfo3max_1990_2000_avg,
sfo3max_2000_2010_avg, sfo3max_2010_2015_avg]
# find overall min & max values for colour bar in plots
min_sfo3max_avg = np.array([])
for i in sfo3max_avg:
sfo3max_avg_min = np.amin(i)
min_sfo3max_avg = np.append(min_sfo3max_avg, sfo3max_avg_min)
overall_min_sfo3max_avg = np.amin(min_sfo3max_avg)
max_sfo3max_avg = np.array([])
for i in sfo3max_avg:
sfo3max_avg_max = np.amax(i)
max_sfo3max_avg = np.append(max_sfo3max_avg, sfo3max_avg_max)
overall_max_sfo3max_avg = np.amax(max_sfo3max_avg)
# finally plot the 17 global plots of sfo3max_avg
for k in sfo3max_avg:
fig = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines() # Adding coastlines
cs = ax.contourf(lon[:], lat[:], k[:], cmap='magma')
ax.set_title('Decadal Average of Maximum O3 Volume Mixing Ratio')
m = plt.cm.ScalarMappable(cmap=cm.magma)
m.set_array(i[:])
m.set_clim(overall_min_sfo3max_avg, overall_max_sfo3max_avg)
# Additional necessary information
cbar = plt.colorbar(m, boundaries=np.arange(overall_min_sfo3max_avg, overall_max_sfo3max_avg
+ 0.5e-08, 0.5e-08))
cbar.set_label('mol mol-1')
# Adding axis labels - latitude & longitude
gridl = ax.gridlines(color="black", linestyle="dotted", draw_labels=True)
gridl.xformatter=LONGITUDE_FORMATTER
gridl.yformatter=LATITUDE_FORMATTER
gridl.xlabels_top = False
gridl.ylabels_right = False
fig.set_size_inches(w=20,h=10)
plt.show() # show global plot
Several elements in your plotting can be kept out of the loop because they only need to be set up once. After you set up the plot elements you can update the plot and animate by looping over the list. This can be achieved by making use of matplotlib's interactive mode as shown in the code below:
import numpy as np
import netCDF4 as n4
import matplotlib
matplotlib.use("nbagg")
import matplotlib.pyplot as plt
from matplotlib import colorbar, colors
import matplotlib.cm as cm
import cartopy as cart
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.feature as cfeature
nc = n4.Dataset('datafile.nc','r')
# daily maximum O3 VMR (units: mol mol-1)
sfo3max = nc.variables['sfo3max']
lon = nc.variables['lon'] # longitude
lat = nc.variables['lat'] # latitude
# (I manipulate the data to produce 17 arrays containing the decadal average O3 VMR which are
# listed below in sfo3max_avg)
sfo3max_avg = [sfo3max_1850_1860_avg, sfo3max_1860_1870_avg, sfo3max_1870_1880_avg,
sfo3max_1880_1890_avg, sfo3max_1890_1900_avg, sfo3max_1900_1910_avg,
sfo3max_1910_1920_avg, sfo3max_1920_1930_avg, sfo3max_1930_1940_avg,
sfo3max_1940_1950_avg, sfo3max_1950_1960_avg, sfo3max_1960_1970_avg,
sfo3max_1970_1980_avg, sfo3max_1980_1990_avg, sfo3max_1990_2000_avg,
sfo3max_2000_2010_avg, sfo3max_2010_2015_avg]
# find overall min & max values for colour bar in plots
min_sfo3max_avg = np.array([])
for i in sfo3max_avg:
sfo3max_avg_min = np.amin(i)
min_sfo3max_avg = np.append(min_sfo3max_avg, sfo3max_avg_min)
overall_min_sfo3max_avg = np.amin(min_sfo3max_avg)
max_sfo3max_avg = np.array([])
for i in sfo3max_avg:
sfo3max_avg_max = np.amax(i)
max_sfo3max_avg = np.append(max_sfo3max_avg, sfo3max_avg_max)
overall_max_sfo3max_avg = np.amax(max_sfo3max_avg)
#setup the plot elements
fig = plt.figure()
fig.set_size_inches(w=20,h=10)
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines() # Adding coastlines
ax.set_title('Decadal Average of Maximum O3 Volume Mixing Ratio')
m = plt.cm.ScalarMappable(cmap=cm.magma)
m.set_array(i[:])
m.set_clim(overall_min_sfo3max_avg, overall_max_sfo3max_avg)
# Additional necessary information
cbar = plt.colorbar(m, boundaries=np.arange(overall_min_sfo3max_avg, overall_max_sfo3max_avg
+ 0.5e-08, 0.5e-08))
cbar.set_label('mol mol-1')
# plot here only the 1st item in your sfo3max_avg list.
cs = ax.contourf(lon[:], lat[:], sfo3max_avg[0][:], cmap='magma')
# Adding axis labels - latitude & longitude
gridl = ax.gridlines(color="black", linestyle="dotted", draw_labels=True)
gridl.xformatter=LONGITUDE_FORMATTER
gridl.yformatter=LATITUDE_FORMATTER
gridl.xlabels_top = False
gridl.ylabels_right = False
plt.ion() # set interactive mode
plt.show()
# finally plot the 17 global plots of sfo3max_avg
for k in sfo3max_avg:
cs = ax.contourf(lon[:], lat[:], k[:], cmap='magma')
plt.gcf().canvas.draw()
plt.pause(1) #control the interval between successive displays, currently set to 1 sec.

Avoid interpolation of data when plotting map using latitude and longitude instead of provided x-y coordinates

I have a 2-d numpy array with with some data, in x and y coordinates. With each x and y point is also associated a latitude and a longitude (also 2-d numpy arrays). There are some values in the oceans, but not over land.
When I plot the data in x and y coordinates, I get the following:
plt.imshow(data)
Then, I want to plot this data on a map using lon-lat coordinates. If I do so as following, I get some plotted data on land, since some interpolation is operated.
import cartopy.crs as ccrs
ax = plt.axes(projection=ccrs.Robinson())
plt.pcolor(lon, lat, data, transform=ccrs.PlateCarree())
ax.coastlines()
I obtain the same for any type of plot that I can use on a map (pcolormesh, contour, contourf).
How can this be avoided since no data would show on land, where values should be Nans?
Thank you!
The data is part of the CMIP6 project. It can be accessed through Pangeo by accessing ocean.pangeo.io and running the following code:
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr
import zarr
import gcsfs
import intake
from dask.distributed import Client
from dask_kubernetes import KubeCluster
cluster = KubeCluster()
cluster.adapt(minimum=1, maximum=20, interval='2s', wait_count=10)
client = Client(cluster)
client
url = 'https://raw.githubusercontent.com/NCAR/intake-esm-datastore/master/catalogs/pangeo-cmip6.json'
col = intake.open_esm_datastore(url)
col_hist = col.search(variable_id='o2', table_id='Omon', experiment_id='historical')
dict_hist = col_hist.to_dataset_dict(zarr_kwargs={'consolidated': True})
import cartopy.crs as ccrs
import cartopy.feature as cfeature
models = list(dict_hist.keys())
model = models[5]
ds = dict_hist[model]
sub1 = ds.o2.sel(member_id=ds.member_id.values[0], time=ds.time.values[0])
sub2 = sub1.sel(lev=100, method='nearest')
ax = plt.axes(projection=ccrs.Robinson())
q = sub2.plot(ax=ax, transform=ccrs.PlateCarree(), x='lon', y='lat', vmin=0, vmax=0.4, cbar_kwargs={'shrink': 0.5})
ax.set_global(); ax.coastlines();
Edited to account for comments on data.
The issue with the filled areas is one of wrapping. What you see is data plotting from e.g. +179.8 (right side of plot) to -178.4 (left side of plot)--CartoPy is not correctly clipping, so you have long streaks of data plotting across the map. In areas with other data plotted, these streaks are covered. Where you have no data plotted (i.e. on land) the streaks show through.
One work-around is to adjust the origin of the projection, which can eliminate some of those lines. I've had some success with small adjustments (~1 degree), but the best would be to make it so that the left and right edges of the original domain of the data are the left and right edges of the plot. For this dataset the left edge is +73.5 longitude, so we want the origin to be that +180, which is -107.5 longitude:
import intake
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
url = ('https://raw.githubusercontent.com/NCAR/'
'intake-esm-datastore/master/catalogs/pangeo-cmip6.json')
col = intake.open_esm_datastore(url)
col_hist = col.search(variable_id='o2', table_id='Omon', experiment_id='historical')
dict_hist = col_hist.to_dataset_dict(zarr_kwargs={'consolidated': True})
models = list(dict_hist.keys())
model = models[5]
ds = dict_hist[model]
sub1 = ds.o2.sel(member_id=ds.member_id.values[0], time=ds.time.values[0])
sub2 = sub1.sel(lev=100, method='nearest')
ax = plt.axes(projection=ccrs.Robinson(central_longitude=-107.5))
q = sub2.plot(ax=ax, transform=ccrs.PlateCarree(), x='longitude', y='latitude',
vmin=0, vmax=0.4, cbar_kwargs={'shrink': 0.5})
ax.coastlines()
ax.set_global()
which gives me this image:
Ideally, CartoPy would handle this correctly, but right now there are lots of issues with this (like this one for example).

Python: Basemap precipitation data not showing up

I am using python 3.6 to plot precipitation data from CMIP5, the file I have downloaded is a netCDF4 file. I have used this code on another similar file and it worked out fine so I am not sure what the problem is. I am not receiving any error message with this code, it just displays a world map that is all one color when it should be a variety of colors. The variables found in this file are time, time_bnds, lat, lat_bnds, lon, lon_bnds, and prc. prc is the precipitation variable and the one I an interested in plotting. Any ideas would be helpful, Thank you!
Here is my code
from mpl_toolkits.basemap import Basemap, cm
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
nc = NetCDFFile('filename.nc','r')
p = nc.variables['prc']
data = p[:,:,0]
fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(projection='cyl',lon_0=180,lat_0=0,resolution='l')
m.drawcoastlines()
m.drawstates()
m.drawcountries()
ny = data.shape[0]; nx = data.shape[1]
lons, lats = m.makegrid(nx,ny)
x,y = m(lons, lats) # compute map proj coordinates.
cs=plt.contourf(x,-y,data,range(0,1000,10),cmap=cm.s3pcpn,latlon=True)
cbar = m.colorbar(cs,location='bottom',pad="5%")
cbar.set_label('mm')
plt.show()

Categories

Resources