I'm trying to reproject GOES16 Full Disk imagery using cartopy or pyproj. I'd like to get it into a different projection. For this example, I try to reproject it to Mercator. However, when I run the code I get a full globe image of the data not in Mercator projection and without any cartopy features. I feel like I'm missing a simple step but can't figure out just what it is. Below is a reproducible example - I'm using Python 3.5.
import matplotlib
import matplotlib.pyplot as plt
from siphon.catalog import TDSCatalog, get_latest_access_url
import numpy as np
from datetime import datetime, timedelta
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# query data
nowdate = datetime.utcnow()
cat = TDSCatalog('http://thredds-jumbo.unidata.ucar.edu/thredds/catalog/satellite/goes16/GOES16/Products/SeaSurfaceTemperature/FullDisk/' + \
str(nowdate.year) + str("%02d"%nowdate.month) + str("%02d"%nowdate.day) + '/catalog.xml')
dataset_name = sorted(cat.datasets.keys())[-1]
dataset = cat.datasets[dataset_name]
# load netcdf and read variables
nc = dataset.remote_access()
sst = np.array(nc.variables['SST'][:,:])
sst[np.isnan(sst)] = -1
sst = np.ma.array(sst)
sst[sst < 0] = np.ma.masked
X = nc.variables['x'][:]
Y = nc.variables['y'][:]
# define projections
proj_var = nc.variables['goes_imager_projection']
globe = ccrs.Globe(ellipse='sphere', semimajor_axis=proj_var.semi_major_axis,
semiminor_axis=proj_var.semi_minor_axis)
# define reprojection target
proj = ccrs.Mercator(central_longitude=proj_var.longitude_of_projection_origin,
latitude_true_scale=proj_var.latitude_of_projection_origin,
globe=globe)
# Plot
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1, projection=proj)
ax.coastlines(resolution='50m', color='black')
ax.add_feature(cfeature.STATES, linestyle=':', edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=2, edgecolor='black')
im = ax.imshow(sst, extent=(X.min(), X.max(), Y.min(), Y.max()), origin='upper')
# try again, this time with pyproj
from pyproj import Proj
p = Proj(proj='geos', h=proj_var.perspective_point_height, lon_0=proj_var.longitude_of_projection_origin, sweep=proj_var.sweep_angle_axis)
X = nc.variables['x'][:] * proj_var.perspective_point_height
Y = nc.variables['y'][:] * proj_var.perspective_point_height
XX, YY = np.meshgrid(X,Y)
lons, lats = p(XX, YY, inverse=True)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1, projection=proj)
ax.coastlines(resolution='50m', color='black')
ax.add_feature(cfeature.STATES, linestyle=':', edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=2, edgecolor='black')
im = ax.imshow(sst, extent=(lons.min(), lons.max(), lats.min(), lats.max()), origin='upper')
Your methodology was correct, but you have to use pcolormesh instead of imshow.
This should work:
from datetime import datetime
import cartopy.feature as cfeature
from siphon.catalog import TDSCatalog
import matplotlib.pyplot as plt
from matplotlib import patheffects
import metpy
from metpy.plots import colortables
import xarray as xr
from xarray.backends import NetCDF4DataStore
%matplotlib inline
nowdate = datetime.utcnow()
cat = TDSCatalog('http://thredds-jumbo.unidata.ucar.edu/thredds/catalog/satellite/goes16/GOES16/Products/SeaSurfaceTemperature/FullDisk/' + \
str(nowdate.year) + str("%02d"%nowdate.month) + str("%02d"%nowdate.day) + '/catalog.xml')
dataset_name = sorted(cat.datasets.keys())[-1]
dataset = cat.datasets[dataset_name]
ds = dataset.remote_access(service='OPENDAP')
ds = NetCDF4DataStore(ds)
ds = xr.open_dataset(ds)
dqf = ds.metpy.parse_cf('DQF')
dat = ds.metpy.parse_cf('SST')
proj = dat.metpy.cartopy_crs
dat = dat.where(dqf == 0)
dat = dat.where(dat.variable > 274)
dat = dat.where(dat.variable < 310)
dat = dat - 273.15
# Plot in Mercator
import cartopy.crs as ccrs
newproj = ccrs.Mercator()
fig = plt.figure(figsize=[12, 12], dpi=100)
ax = fig.add_subplot(1,1,1, projection=newproj)
im = ax.pcolormesh(dat['x'], dat['y'], dat, cmap='jet', transform=proj, vmin=-2, vmax=38)
ax.set_extent((dat['x'].min() + 4000000, dat['x'].max()- 3200000, dat['y'].min()+ 5500000, dat['y'].max()- 650000), crs=proj)
Related
How to rotate matplotlib.patches.Polygon? The output from the code below shows nothing.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig, ax = plt.subplots(figsize=(10,3))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
trapezoid = patches.Polygon(xy=list(zip(x,y)), fill=False)
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t_start + t
trapezoid.set_transform(t_end)
print(repr(t_start))
print(repr(t_end))
ax.add_patch(trapezoid)
plt.show()
When composing the tranformations, you must use t_end = t + t_start instead of t_start + t. The + operator is overloaded: a + b means to first apply a and then apply b. For affine transformations this means the matrix product B#A where A and B are the transformation matrices of a and b respectively. The matrix product is not commutative.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
import copy
fig, ax = plt.subplots(figsize=(10,3))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
trapezoid = patches.Polygon(xy=list(zip(x,y)), fill=False)
ax.add_patch(copy.copy(trapezoid))
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t + t_start
trapezoid.set_transform(t_end)
ax.add_patch(trapezoid)
plt.show()
I am trying to do a basic plot of the world map using Python and the Matplotlib library. However, when I plot the polygons the plot shows many straight lines that do not seem to be part of the polygon. I am relatively new at working with shapefiles but the code I'm using has worked for a previous shapefile I used, so I'm confused and wondering what might be missing in the code.
The code I'm using is:
import numpy as np
import pandas as pd
import shapefile as shp
import matplotlib.pyplot as plt
import seaborn as sns
import os
sns.set(style='whitegrid', palette='ocean', color_codes=True)
sns.mpl.rc('figure', figsize=(10,6))
sf = shp.Reader(shp_path)
def plot_map(sf, x_lim = None, y_lim = None, figsize = (11,9)):
'''
Plot map with lim coordinates
'''
plt.figure(figsize = figsize)
id=0
for shape in sf.shapeRecords():
x = [i[0] for i in shape.shape.points[:]]
y = [i[1] for i in shape.shape.points[:]]
plt.plot(x, y, 'k')
if (x_lim == None) & (y_lim == None):
x0 = np.mean(x)
y0 = np.mean(y)
plt.text(x0, y0, id, fontsize=10)
id = id+1
if (x_lim != None) & (y_lim != None):
plt.xlim(x_lim)
plt.ylim(y_lim)
plot_map(sf)
plt.show()
The following link shows resulting graph (I'm not allowed to post pictures yet?):
Any help is appreciated, thank you all!
pls use 'k.', or use scatter instead of plot
import numpy as np
import pandas as pd
import shapefile as shp
import matplotlib.pyplot as plt
import seaborn as sns
import os
sns.set(style='whitegrid', palette='ocean', color_codes=True)
sns.mpl.rc('figure', figsize=(10,6))
sf = shp.Reader(shp_path)
def plot_map(sf, x_lim = None, y_lim = None, figsize = (11,9)):
'''
Plot map with lim coordinates
'''
plt.figure(figsize = figsize)
id=0
for shape in sf.shapeRecords():
x = [i[0] for i in shape.shape.points[:]]
y = [i[1] for i in shape.shape.points[:]]
## change here
plt.plot(x, y, 'k.')
if (x_lim == None) & (y_lim == None):
x0 = np.mean(x)
y0 = np.mean(y)
plt.text(x0, y0, id, fontsize=10)
id = id+1
if (x_lim != None) & (y_lim != None):
plt.xlim(x_lim)
plt.ylim(y_lim)
plot_map(sf)
plt.show()
Why I am getting a runtime error while running this code?? I wanted to show two SST data in a single plot using for loop. while I am running the code it shows your system has crashed. I am sharing the screenshot of the system log.
Screenshot of the error
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from netCDF4 import Dataset
from mpl_toolkits.axes_grid1 import ImageGrid
def read_sst (x):
ncfile=x
fh=Dataset(ncfile,mode='r')
sst=fh.variables['sst'][:]
sst_anom=sst[0,:,:]
return sst_anom
sst_anom_drought=read_sst("Drought_SST_Mean_Anomaly_Composite_1901-2014.nc")
sst_anom_flood=read_sst("Flood_SST_Mean_Anomaly_Composite_1901-2014.nc")
SST_ncfile='Flood_SST_Mean_Anomaly_Composite_1901-2014.nc'
fh1=Dataset(SST_ncfile,mode='r')
lons = fh1.variables['longitude'][:]
lats = fh1.variables['latitude'][:]
sst_data=[sst_anom_drought,sst_anom_flood]
fig = plt.figure(1,(15.,5.))
grid_top = ImageGrid(fig, 111, nrows_ncols = (2, 1),axes_pad=0.5)
cbar_ax = fig.add_axes([0.25,0.15, 0.55, 0.015])
for g, s in zip(grid_top,sst_data):
plt.sca(g)
m = Basemap(resolution='l',projection='merc',llcrnrlon=30, llcrnrlat=-31,
urcrnrlon=360, urcrnrlat=65)
clevs = np.linspace(-0.8, 0.8, 15)
lons, lats = np.meshgrid(lons, lats)
xi, yi = m(lons, lats)
color_map = plt.cm.RdBu_r
#reversed_color_map = color_map.reversed()
cs = m.contourf(xi,yi,s,clevs,cmap=color_map,extend='both')
cb=fig.colorbar(cs,orientation="horizontal",cax=cbar_ax)
m.drawparallels(np.arange(-30., 65., 15.), labels=[1,0,0,0], fontsize=12,linewidth=0.1)
m.drawmeridians(np.arange(-180., 180., 40.), labels=[0,0,0,1], fontsize=12,linewidth=0.1)
m.fillcontinents(color='whitesmoke',lake_color='whitesmoke')
m.drawcoastlines()
I want to fill the gridded map with colors based on the value of interest. A sample data is here:
import pandas as pd
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7]
Specifically, is it possible to do this with Cartopy? I found a similar question here:https://stackoverflow.com/questions/53412785/plotting-pandas-csv-data-onto-cartopy-map. But that post was to plot scattered points, I need to fill the grids with colors.
I myself tried:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
lon, lat = np.meshgrid(df['lon'], df['lat'])
fig = plt.figure(figsize=[15,15])
ax = plt.axes(projection=ccrs.PlateCarree())
ax.pcolormesh(lon,lat,df['variable'],latlon=True,cmap='jet')
plt.show()
The error is at ax.pcolormesh(...), it says "not enough values to unpack (expected 2, got 1)"
Many thanks for your help.
For discrete data you can create rectangular patches for each point. Here is a possible solution for your sample data set. Each row of data (lat, long, value) is used to create a rectangular patch. The value is normalized by dividing with max(value) to enable using colormap for coloring the patches.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
def make_rect(clon, clat, dlon, dlat):
lon_min = clon - dlon/2.
lat_min = clat - dlat/2.
lon_max = clon + dlon/2.
lat_max = clat + dlat/2.
# clockwise from LL
#lons = [lon_min, lon_min, lon_max, lon_max, lon_min]
#lats = [lat_min, lat_max, lat_max, lat_min, lat_min]
ll = [lon_min,lat_min]
ul = [lon_min,lat_max]
ur = [lon_max,lat_max]
lr = [lon_max,lat_min]
return [ll, ul, ur, lr, ll]
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7] # not suffice for meshgrid plot
# The colormap to use.
cm = plt.cm.get_cmap('jet')
fig = plt.figure(figsize=[8,6])
ax = plt.axes(projection=ccrs.PlateCarree(), extent=[95, 134, 35, 52])
# plot the red dots using the available data
# comment out if not needed
ax.plot(df['lon'], df['lat'], 'ro')
# plot rectangular patches at the data points
dlon, dlat = 5, 2 #spacings between data points
for lon1, lat1, val1 in zip(df['lon'], df['lat'], df['value']):
pcorners = make_rect(lon1, lat1, dlon, dlat)
poly = mpatches.Polygon(pcorners, ec='gray', fill=True, lw=0.25, \
fc=cm(val1 / max(df['value'])), transform=ccrs.PlateCarree())
ax.add_patch(poly)
ax.gridlines(draw_labels=True)
plt.show()
The output plot:
I have an hourly plot generated with matplotlib. I need to plot an event which goes for example, from 09:00 to 10:45. When I try to do it, using axvspan I obtain a bar from 9:00 to 10:00. How could I obtain the longer one?
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import datetime as dt
import pandas as pd
now_date = dt.datetime(2018,10,1,9)
d_tw_ini = now_date - dt.timedelta(hours = 1)
d_tw_fin = now_date + dt.timedelta(hours = 3)
dts = pd.date_range(start=d_tw_ini, end=d_tw_fin, freq='1H', name='ini', closed='left')
data=pd.DataFrame({'val':[0.5,0.4,0.7,0.9]})
ev1=[dt.datetime(2018,10,1,9,5),dt.datetime(2018,10,1,10,50)]
data['t']=dts.values
data.set_index('t',inplace=True)
fig = plt.figure()
gs = GridSpec(1, 1)
ax_1 = fig.add_subplot(gs[0, 0])
data.plot(ax=ax_1, y='val')
ax_1.axvspan(ev1[0],ev1[1], alpha=0.3, color= 'red')
Result
Juan, it looks when you used pandas to plot, the hourly indexing seems to cause issues with how axvspan gets plotted.
I replaced
data.plot(ax=ax_1, y='val')
with
ax_1.plot(data.index, data['val'])
which generates the image below, but unfortunately you lose the automated x-axis formatting.
Adding the two lines below will result in the same date formatting as your example.
ax_1.set_xticks([x for x in data.index])
ax_1.set_xticklabels([str(x)[11:16] for x in data.index])
Below is the full code to produce the above plot.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import datetime as dt
import pandas as pd
now_date = dt.datetime(2018,10,1,9)
d_tw_ini = now_date - dt.timedelta(hours = 1)
d_tw_fin = now_date + dt.timedelta(hours = 3)
dts = pd.date_range(start=d_tw_ini, end=d_tw_fin, freq='1h', name='ini',
closed='left')
data=pd.DataFrame({'val':[0.5,0.4,0.7,0.9]})
ev1=[dt.datetime(2018,10,1,9,5,0),dt.datetime(2018,10,1,10,50,0)]
data['t']=dts.values
data.set_index('t',inplace=True)
fig = plt.figure()
gs = GridSpec(1, 1)
ax_1 = fig.add_subplot(gs[0, 0])
# modified section below
ax_1.plot(data.index, data['val'])
ax_1.axvspan(ev1[0],ev1[1], alpha=0.3, color= 'red')
ax_1.set_xticks([x for x in data.index])
ax_1.set_xticklabels([str(x)[11:16] for x in data.index])
plt.show()