How to plot the name of the states in a cartopy map? - python

I have a shapefile with the information of the names of the states of Perú country, and I want to plot them on the map according to their state.
So i did this code:
import cartopy
%matplotlib inline
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
import cartopy.io.shapereader as shpreader
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
from cartopy.feature import NaturalEarthFeature
reader = shpreader.Reader('C:/my/route/STATES_PERU.shp')
counties = list(reader.geometries())
states = reader.records()
state = next(states)
states_att = lambda state: state.attributes['DEPARTAMEN']
states_names = sorted(reader.records(), key=states_att)
COUNTIES = cfeature.ShapelyFeature(counties, ccrs.PlateCarree())
STATES = cfeature.ShapelyFeature(states_names, ccrs.PlateCarree())
fig = plt.figure('map', figsize=(7,7), dpi=200)
ax = fig.add_axes([0.1, 0.12, 0.80, 0.75], projection=ccrs.PlateCarree())
l1 = NaturalEarthFeature(category='cultural', name='admin_0_countries', scale='50m', facecolor='none')
ax.add_feature(l1, edgecolor='black', linewidth=0.1)
ax.set_extent([-83.0, -66.0, -19.0, 1.0], crs=ccrs.PlateCarree())
ax.add_feature(COUNTIES, facecolor='none', edgecolor='black')
ax.add_feature(STATES)
When i run this code i got this error:
AttributeError: 'FionaRecord' object has no attribute '_geom'
The variable COUNTIES have Polygon elements and the variable STATES have FionaRecord elements.
How can i plot the names of the states in the cartopy map, that are inside the variable STATES?
Thanks in advance.

It's probably easiest to loop over the records that the shpreader returns. That allows you to for example get the coordinates of the centroid of the polygon, and use that to plot the name from the attributes.
Using NE sample data:
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
# get the data
fn = shpreader.natural_earth(
resolution='10m', category='cultural',
name='admin_1_states_provinces',
)
reader = shpreader.Reader(fn)
states = [x for x in reader.records() if x.attributes["admin"] == "Peru"]
states_geom = cfeature.ShapelyFeature([x.geometry for x in states], ccrs.PlateCarree())
data_proj = ccrs.PlateCarree()
# create the plot
fig, ax = plt.subplots(
figsize=(7,7), dpi=130, facecolor="w",
subplot_kw=dict(projection=data_proj),
)
ax.add_feature(cfeature.BORDERS, color="k", lw=0.1)
ax.set_extent([-83.0, -66.0, -19.0, 1.0], crs=data_proj)
ax.add_feature(states_geom, facecolor="none", edgecolor="k")
# add the names
for state in states:
lon = state.geometry.centroid.x
lat = state.geometry.centroid.y
name = state.attributes["name"]
ax.text(
lon, lat, name, size=7, transform=data_proj, ha="center", va="center",
path_effects=[PathEffects.withStroke(linewidth=5, foreground="w")]
)
This method probably will cause some overlap between the labels. I'm not sure if there are more advanced label placement algorithms available that work well with Matplotlib.

Related

GOES-East Full Disk domain realtime imagery does not fit actual data

I'm trying to plot GOES-East full disk data using metpy, and Siphon to download the latest data from the THREDDS data server. However, after comparing my plots with the realtime imagery, ther seems to be a large difference.
Below is my code:
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import metpy.calc as mpcalc
from metpy.plots.ctables import registry
from metpy.plots import add_timestamp
from metpy.units import units
from siphon.catalog import TDSCatalog
import xarray as xr
import numpy as np
from xarray.backends import NetCDF4DataStore
from datetime import datetime, timedelta
dt = datetime.utcnow().date()
data = TDSCatalog(f'http://thredds.ucar.edu/thredds/catalog/satellite/goes/east/products/'
f'CloudAndMoistureImagery/FullDisk/Channel09/{dt:%Y%m%d}/catalog.xml')
sat_dataset = data.datasets[0].remote_access(use_xarray = True)
cmi = sat_dataset.metpy.parse_cf('Sectorized_CMI')
x = cmi.coords['x'][:]
y = cmi.coords['y'][:]
timestamp = datetime.strptime(str(cmi.time.values.astype('datetime64[s]')), '%Y-%m-%dT%H:%M:%S')
print(timestamp)
vtime = timestamp.strftime('%Y-%m-%d %H%M%S')
# Create the figure
fig = plt.figure(figsize = [16, 10])
ax = fig.add_subplot(1, 1, 1, projection = cmi.metpy.cartopy_crs)
ax.set_extent([-80, -45, -50, -15], crs = ccrs.PlateCarree())
ax.add_feature(cfeature.BORDERS.with_scale('50m'), edgecolor = 'black', linewidth = 1)
ax.add_feature(cfeature.COASTLINE.with_scale('50m'), edgecolor = 'black', linewidth = 1)
ax.add_feature(cfeature.STATES.with_scale('50m'), edgecolor = 'white', linewidth = 1)
# Add mapping information
ax.add_feature(cfeature.STATES)
ax.add_feature(cfeature.BORDERS, linewidth=2)
# Plot the image with our colormapping choices
wv_norm, wv_cmap = registry.get_with_range('WVCIMSS_r', 193, 283)
im = ax.imshow(cmi, extent=(x[0], x[-1], y[0], y[-1]), origin='upper',
cmap = wv_cmap, norm = wv_norm, transform = cmi.metpy.cartopy_crs)
plt.colorbar(im, ticks = np.arange(193, 293, 10), ax = ax)
plt.title(f'Vapor da Água em Níveis Médios [$K$] \nValid: {vtime} UTC', loc = 'left')
plt.savefig(f'/mnt/c/Users/vitor/Desktop/WV_{vtime}.jpg', bbox_inches = 'tight')
Also below, is a comparison between the output from my code and the actual water vapor imagery from the CODNEXLAB website. I also looked at the metadata of the downloaded files and everything seems to be fine. Not sure if I'm doing something wrong here.
What you're seeing is that your image is flipped (it's easier to identify if you look at the global plot of that data). What's happening is the origin you specified ('upper'/'lower') disagree with what you passed as extent. So either tweak your origin parameter:
im = ax.imshow(cmi, extent=(x[0], x[-1], y[0], y[-1]),
origin='lower', cmap=wv_cmap, norm=wv_norm,
transform=cmi.metpy.cartopy_crs)
or flip the order of your y extents:
im = ax.imshow(cmi, extent=(x[0], x[-1], y[-1], y[0]),
origin='upper', cmap=wv_cmap, norm=wv_norm,
transform=cmi.metpy.cartopy_crs)

Generating a plot for all the time steps of netcdf file into a map

I was wondering if it's possible to plot all the steps from this single netcdf file into a separate plots.
Step 113 means that the current accessed data is for the date of October 22,2019. The Step 0 is July 1,2019. There are 135 time steps overall. Which means I need to produce 135 maps for each and single day.
#x,y,u,v for the maps
X=Data.longitude; Y=Data.latitude;
U=Data.u10[113]; V=Data.v10[113];
pm2p5=Data.pm2p5[113];
This is my code so far.
import xarray as xr
import cartopy.crs as ccrs
from cartopy import feature as cf
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
Data=xr.open_dataset('PMs ECMWF2.nc')
#x,y,u,v for the maps
X=Data.longitude; Y=Data.latitude;
U=Data.u10[113]; V=Data.v10[113];
pm2p5=Data.pm2p5[113];
nlon, nlat = np.meshgrid(X,Y)
fig, ax = plt.subplots(figsize=(12, 12), dpi=300)
# Add Plotting the plot
ax=plt.subplot(111,projection=ccrs.PlateCarree())
# Add Plot features
ax.add_feature(cf.BORDERS, linewidth=.5, edgecolor="yellow")
ax.coastlines('50m', linewidth=0.8)
ax.add_feature(cf.LAKES)
ax.add_feature(cf.OCEAN)
ax.add_feature(cf.BORDERS, edgecolor="yellow")
ax.add_feature(cf.COASTLINE, edgecolor="yellow")
ax.add_feature(cf.RIVERS)
ax.gridlines()
#changing the location of the map
ax.set_extent([90, 141, 24, -10])
# Add gridlines, and set their font size
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=1, color='black', alpha=0.05, linestyle='-')
gl.top_labels = False
gl.left_labels = True
gl.right_labels = False
gl.xlines = True
gl.ylines = True
#colorbar
cmap = cm.get_cmap('jet') # Colour map coolwarm,hsv,bwr, seismic
# plotting the variables
pm2p5.plot(transform=ccrs.PlateCarree(), cbar_kwargs={'shrink': 0.5}, cmap=cmap)
plt.contour(nlon, nlat, pm2p5, fontsize=10,cmap=cmap) #plotting the contours
#plotting the quiver
ax.quiver(X[::3],Y[::3],U[::3,::3],V[::3,::3], color='white')
#plot title
#plt.title('Carbon Monoxide on October 22, 2019')
plt.show()
As of right now this code only produce one image. I have to do this over and over again.
One example of making a loop over time variable and maps of variable inside the loop, is here:
#!/usr/bin/env ipython
# ---------------------------
import numpy as np
from netCDF4 import Dataset,num2date
# ---------------------------
# let us generate data, preferably to the netcdf:
nx=50;ny=50;ntime=10;
A=np.random.random((ntime,ny,nx));
lon = np.linspace(0,360,nx);
lat = np.linspace(-90,90,ny);
time = np.linspace(0,366,ntime);timeunit = 'days since 2020-01-01 00:00:00'
fout = 'test.nc'
with Dataset(fout,'w') as f:
f.createDimension('longitude',nx);
f.createDimension('latitude',ny);
f.createDimension('time',);
xv = f.createVariable('longitude','float32',('longitude'));xv[:]=lon
yv = f.createVariable('latitude','float32',('latitude'));yv[:]=lat;
tv = f.createVariable('time','float32',('time'));tv[:]=time;tv.setncattr('units',timeunit);
u10 = f.createVariable('u10','float32',('time','latitude','longitude'));u10[:]=A;
v10 = f.createVariable('v10','float32',('time','latitude','longitude'));v10[:]=-A;
pm2 = f.createVariable('pm2p5','float32',('time','latitude','longitude'));pm2[:]=A;
# -----------------------------------------------
# let us now make some maps:
import xarray as xr
import cartopy.crs as ccrs
from cartopy import feature as cf
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
Data=xr.open_dataset(fout)
#x,y,u,v for the maps
X=Data.longitude; Y=Data.latitude;
time = Data.time;
for itime in range(len(time)):
U=Data.u10[itime]; V=Data.v10[itime];
pm2p5=Data.pm2p5[itime];
nlon, nlat = np.meshgrid(X,Y)
fig, ax = plt.subplots(figsize=(12, 12), dpi=300)
# Add Plotting the plot
ax=plt.subplot(111,projection=ccrs.PlateCarree())
# Add Plot features
ax.add_feature(cf.BORDERS, linewidth=.5, edgecolor="yellow")
ax.coastlines('50m', linewidth=0.8)
ax.add_feature(cf.LAKES)
ax.add_feature(cf.OCEAN)
ax.add_feature(cf.BORDERS, edgecolor="yellow")
ax.add_feature(cf.COASTLINE, edgecolor="yellow")
ax.add_feature(cf.RIVERS)
ax.gridlines()
#changing the location of the map
ax.set_extent([90, 141, 24, -10])
# Add gridlines, and set their font size
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=1, color='black', alpha=0.05, linestyle='-')
gl.top_labels = False
gl.left_labels = True
gl.right_labels = False
gl.xlines = True
gl.ylines = True
#colorbar
cmap = cm.get_cmap('jet') # Colour map coolwarm,hsv,bwr, seismic
# plotting the variables
pm2p5.plot(transform=ccrs.PlateCarree(), cbar_kwargs={'shrink': 0.5}, cmap=cmap)
plt.contour(nlon, nlat, pm2p5, fontsize=10,cmap=cmap) #plotting the contours
#plotting the quiver
ax.quiver(X[::3],Y[::3],U[::3,::3],V[::3,::3], color='white')
#plot title
#plt.title('Carbon Monoxide on October 22, 2019')
plt.savefig('fig_'+str(itime).rjust(3,'0')+'.png',bbox_inches='tight');
plt.show();
# ----------------------------
I first generated a netCDF with random values and then plotted the variables for each time moment.

Plotting latitude and longitude from csv file

Hello been trying to do this all day!
I have created a map with Basemap and I am trying to plot all the latitude and longitude locations from the CSV file. Any ideas or tips?
Here is an image of the csv file
def map_test():
col_list = ["longitude", "latitude"]
dataset = pd.read_csv('charge_point_registry.csv', usecols=col_list)
latitudes = dataset.loc[:, 'latitude']
longitudes = dataset.loc[:, 'longitude']
# Creates a base map ready for attributes
plt.figure(figsize=(20, 15))
m = Basemap(projection='mill',
# coordinates of a box to contain map of UK
llcrnrlat=48.632909,
llcrnrlon=-14.452873,
urcrnrlon=3.136989,
urcrnrlat=61.648162,
# quality of map
resolution='l')
m.drawcoastlines()
m.drawcounties()
m.fillcontinents(color = "green")
geometry = [Point(xy) for xy in zip(longitudes, latitudes)]
gdf = GeoDataFrame(dataset, geometry=geometry)
gdf.plot(ax=m.plot(figsize=(20, 15)), marker='o', color='red', markersize=15)
# m.bluemarble()
plt.show()
Thanks :)
Matplotlib Basemap is deprecated in favor of Cartopy. Here is some code that will allow you to plot your UK Data.
import numpy as np
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import pandas as pd
long_list = np.arange(-179.5, 180, 1)
lat_list = np.arange(-89.5, 90, 1)
value_dict = {'latitude':[51.0, 51.2, 53.4, 54.5],
'longitude':[-1.1, -1.3, -0.2, -0.9]}
#replace this with your df
df = pd.DataFrame(value_dict)
proj = ccrs.PlateCarree(central_longitude=0)
fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(16,16))
ax.set_extent([-10, 3, 48, 61], crs=ccrs.PlateCarree())
fig.canvas.draw()
fig.tight_layout()
ax.add_feature(cfeature.LAND, facecolor='0.25')
ax.add_feature(cfeature.BORDERS, zorder=10)
ax.scatter(df['longitude'].values.tolist(), df['latitude'].values.tolist())
gl = ax.gridlines(crs=proj, draw_labels=False, alpha=1, linewidth=0.25)
gl.xlocator = mticker.FixedLocator(long_list)
gl.ylocator = mticker.FixedLocator(lat_list)
plt.show()

3D plot using geographic coordinates

I have a dataset looking like this:
1 38.7114 -7.92482 16.4375 0.2
...
I'd like to make a 3D scatter plot. I've done it using cartesian coordinates. How I can do it using geographic coordinates? Any hint?
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import sys
from mpl_toolkits.basemap import Basemap
ID=[]
Latitude=[]
Longitude=[]
Depth=[]
cluster1='data1'
with open(cluster1) as f:
lines = f.readlines()
for line in lines:
items = line.strip().split()
lat = float(items[1])
lon = float(items[2])
dep = float(items[3])
mag = float(items[4])
Latitude.append(lat)
Longitude.append(lon)
Depth.append(dep)
ID.append(mag)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
p = ax.scatter(Longitude, Latitude, Depth, c=ID, marker='o')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_zlabel('Depth (km)')
ax.invert_zaxis()
cb = fig.colorbar(p,label='Magnitude')
plt.savefig('plot1.png')

Matplotlib discretize colorbar between given values

I have the plot bellow and I would like to discretize the colormap between 0 and 20. Could anyone help with that?
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import MaxNLocator
epi='epi'
with open(epi, 'r') as f2:
lines = f2.readlines()
data = [line.split() for line in lines]
a = np.array(data)
print a.shape
lat = a[:,0]
lat1=list(lat)
lat2=np.asarray(lat1).astype(float)
lon = a[:,1]
lon1=list(lon)
lon2=np.asarray(lon).astype(float)
x_space = 60
y_space = x_space*1.7
gridx = np.linspace(-8.8, -7.0, x_space)
gridy = np.linspace(38, 39.5, y_space )
grid, _, _ = np.histogram2d(lat2, lon2, bins=[gridy, gridx])
cmap = plt.get_cmap('hot_r')
plt.figure()
plt.axis((-8.8,-7.0,38.2,39))
plt.pcolormesh(gridx, gridy, grid,cmap=cmap)
plt.colorbar()
plt.show()
If you want a coarsely discretized colormap, you can change your get_cmap call and include the number of different (discrete) colors you want:
import matplotlib.pylab as pl
import numpy as np
data = np.random.random([10,10]) * 40
hot2 = pl.cm.get_cmap('hot', 20)
pl.figure()
pl.subplot(121)
pl.pcolormesh(data, cmap=pl.cm.hot, vmin=0, vmax=20)
pl.colorbar()
pl.subplot(122)
pl.pcolormesh(data, cmap=hot2, vmin=0, vmax=20)
pl.colorbar()

Categories