Can you change iris cube projections in cartopy - python

I really like the idea that cartopy can automatically plot in different map projections. However, I couldn't figure out how to do with the Iris cubes. As its a sister project, I expected that I might be able to. Is it possible to do something like this?
import iris as I
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
someCube = I.load('someCube.pp')
ax = plt.axes(projection=ccrs.Robinson())
I.plot.contourf(someCube, transform=ccrs.Robinson())
plt.show()
thanks

I took your pseudo code and made it runnable with Iris' sample data:
import iris
import iris.plot as iplt
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fname = iris.sample_data_path('air_temp.pp')
air_temp = iris.load_cube(fname)
ax = plt.axes(projection=ccrs.Robinson())
iplt.contourf(air_temp, transform=ccrs.Robinson(central_longitude=180))
ax.coastlines()
plt.show()
If you run this code, you will get an exception along the lines of:
Traceback (most recent call last):
File "using_custom_projections.py", line 11, in <module>
iris.plot.contourf(air_temp, transform=ccrs.Robinson())
File "lib/iris/plot.py", line 452, in contourf
result = _draw_2d_from_points('contourf', None, cube, *args, **kwargs)
File "lib/iris/plot.py", line 263, in _draw_2d_from_points
result = _map_common(draw_method_name, arg_func, iris.coords.POINT_MODE, cube, data, *args, **kwargs)
File "lib/iris/plot.py", line 406, in _map_common
assert 'transform' not in kwargs, 'Transform keyword is not allowed.'
AssertionError: Transform keyword is not allowed.
Which is trying to tell you that you do not need to tell it which "transform" (or coordinate system) the cube is in. The reason for that is that an Iris cube should contain full metadata about the underlying data: the coordinate systems is part of that metadata.
So, to get the example to work, you can simply remove the transform keyword argument in your contourf call:
import iris
import iris.plot as iplt
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fname = iris.sample_data_path('air_temp.pp')
air_temp = iris.load_cube(fname)
ax = plt.axes(projection=ccrs.Robinson(central_longitude=180))
iplt.contourf(air_temp)
ax.coastlines()
plt.show()
There is a similar example in the iris gallery, specifically http://scitools.org.uk/iris/docs/latest/examples/graphics/rotated_pole_mapping.html#rotated-pole-mapping-03 (the very last plot in the example).
HTH,

Related

Inscribing a smaller domain onto a cartopy map in Python

I've been working to make a visual for a poster regarding the physical domain that I am studying. I'm working with a nested domain, so I have 1 smaller domain inside a larger outer domain. I'm trying to create a cartopy plot that shows both the outer domain and inner domain. Ideally, the result would look something like this:
I'm really struggling with trying to get my smaller domain inscribed onto my map. I've attempted to make a Shapely LinearRing to show the inner domain, but it is not working. Here's the code I have created so far:
# Imports
import numpy as np
import sys, os
import matplotlib.pyplot as plt
%matplotlib inline
import netCDF4
from netCDF4 import Dataset
from matplotlib.cm import get_cmap
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.feature import NaturalEarthFeature, COLORS
import metpy as mp
import metpy.calc as mpcalc
from metpy.calc import divergence, smooth_gaussian
from metpy.units import units
import xarray as xr
from wrf import getvar, interplevel, to_np, latlon_coords, get_cartopy, cartopy_xlim, cartopy_ylim, ALL_TIMES
from shapely.geometry.polygon import LinearRing
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
from shapely import geometry
# Get 1km lats/lons
lats1km = getvar(ds1, 'lat')
lons1km = getvar(ds1, 'lon')
lat1km_max = to_np(np.max(lats1km))
lat1km_min = to_np(np.min(lats1km))
lon1km_max = to_np(np.max(lons1km))
lon1km_min = to_np(np.min(lons1km))
# Get 3km lats/lons
lats3km = getvar(ds3, 'lat')
lons3km = getvar(ds3, 'lon')
lat3km_max = to_np(np.max(lats3km))
lat3km_min = to_np(np.min(lats3km))
lon3km_max = to_np(np.max(lons3km))
lon3km_min = to_np(np.min(lons3km))
domain = [lon3km_min, lat3km_min, lon3km_max, lat3km_max]
lons = [lat1km_min, lat1km_min, lat1km_max, lat1km_max]
lats = [lon1km_min, lon1km_max, lon1km_max, lon1km_min]
ring = LinearRing(list(zip(lons, lats)))
geom = geometry.box(minx=lon1km_min, miny=lat1km_min, maxx=lon1km_max, maxy=lat1km_max)
# Grab CRS
crs = get_cartopy(wrfin=ds1)
# Create figure and axes
fig = plt.figure(figsize=(20,10))
ax0 = fig.add_subplot(1, 1, 1, projection=crs)
ax0.set_extent([lon3km_min, lon3km_max, lat3km_min, lat3km_max])
ax0.add_geometries([ring], crs=crs, facecolor='blue', edgecolor='black')
ax0.add_geometries([geom], crs=crs, alpha=0.3)
plot_background(ax0)
This yields my outer domain, but not my inner domain:
What am I doing wrong, and what can I do to get my inner domain shown on the map? Thank you for the help! I really appreciate it!
NOTE: I have already attempted the solution in this link. I am still unable to visualize my polygon.
Have a look at EOmaps ! (I'm the dev) it provides simple functions to add static (or interactive) indicators such as projected rectangles or ellipses to cartopy plots in 1 line!
from eomaps import Maps
m = Maps()
m.add_coastlines()
props = dict(xy=(10, 45), xy_crs=4326, radius_crs=4326, shape="rectangles")
m.add_marker(**props, radius=3, fc=(0,1,0,.5), ec="r", lw=2)
m.add_marker(**props, radius=5, fc="none", ec="k")
m.add_marker(**props, radius=(15, 10), fc="none", ec="m", ls="--", lw=2)
m.figure.ax.set_extent((-15., 65., -5., 75.))

import image-plot the SentinelHub-py utils issue

I am trying to plot an image using SentinelHub-py. This is part of my the code:
from sentinelhub import SHConfig
config = SHConfig()
config.sh_client_id = "76186bb6-a02e-4457-9a9d-126e4fffaed4"
config.sh_client_secret = "aTlX[s:39vzA8p}HA{k*Zp!fJNF~(c7e.u7r21V!"
config.save()
%reload_ext autoreload
%autoreload 2
%matplotlib inline
#Import them
import os
import datetime
import numpy as np
import matplotlib.pyplot as plt
from sentinelhub import SHConfig
from sentinelhub import MimeType, CRS, BBox, SentinelHubRequest, SentinelHubDownloadClient, DataCollection, bbox_to_dimensions, DownloadRequest
from utils import plot_image
betsiboka_coords_wgs84 = [46.16, -16.15, 46.51, -15.58]
resolution = 60
betsiboka_bbox = BBox(bbox=betsiboka_coords_wgs84, crs=CRS.WGS84)
betsiboka_size = bbox_to_dimensions(betsiboka_bbox, resolution=resolution)
print(f'Image shape at {resolution} m resolution: {betsiboka_size} pixels')
"""
Utilities used by example notebooks
"""
import matplotlib.pyplot as plt
import numpy as np
def plot_image(image, factor=3.5/255, clip_range=(0,1)):
"""
Utility function for plotting RGB images.
"""
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(15, 15))
if clip_range is not None:
ax.imshow(np.clip(image * factor, *clip_range), **kwargs)
else:
ax.imshow(image * factor, **kwargs)
ax.set_xticks([])
ax.set_yticks([])
which give me the following error:
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_6632/1226496077.py in <module>
9 from sentinelhub import MimeType, CRS, BBox, SentinelHubRequest, SentinelHubDownloadClient, DataCollection, bbox_to_dimensions, DownloadRequest
10
---> 11 from utils import plot_image
12
13 CLIENT_ID='76186bb6-a02e-4457-9a9d-126e4fffaed4'
ImportError: cannot import name 'plot_image' from 'utils' (c:\xxxxxxxxxxxx.py)
someone made a comment on this issue by saying:
"It seems that the utils package that you are calling is not the correct one. Try loading the utils.py from the examples folder, or that you can find [here][1] (i.e. copy the file to your working directory with your notebook)."
[1]: https://github.com/sentinel-hub/sentinelhub-py
I change my code to this:
"""
Utilities used by example notebooks
"""
import matplotlib.pyplot as plt
import numpy as np
def plot_image(image, factor=3.5/255, clip_range=(0,1)):
"""
Utility function for plotting RGB images.
"""
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(15, 15))
if clip_range is not None:
ax.imshow(np.clip(image * factor, *clip_range), **kwargs)
else:
ax.imshow(image * factor, **kwargs)
ax.set_xticks([])
ax.set_yticks([])
still didn't plot the image.
Please, any suggestions?
You have to navigate to your utils.py. In my computer is:
(/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/utils).
Later, you have to copy and paste the next script from:
https://github.com/sentinel-hub/sentinelhub-py/blob/master/examples/utils.py
Finally, you will be able to type in your python script: "from utils import plot_image"
Best,
Jose

Python netcdf cartopy - Plotting a selection of data

I have a netcdf file ('test.nc'). The variables of the netcdf file are the following:
variables(dimensions): float64 lon(lon), float64 lat(lat), int32 crs(), int16 Band1(lat,lon)
I am interested in the ´Band1´ variable.
Using cartopy, I could plot the data using the following code:
import numpy as np
import pandas as pd
import gzip
from netCDF4 import Dataset,num2date
import time
import matplotlib.pyplot as plt
import os
import matplotlib as mplt
#mplt.use('Agg')
import cartopy.crs as ccrs
import cartopy.feature as cfea
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
projection=ccrs.PlateCarree()
bbox=[-180,180,-60,85];creg='glob'
mplt.rc('xtick', labelsize=9)
mplt.rc('ytick', labelsize=9)
nc = Dataset('test.nc','r')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
kopi= (nc.variables['Band1'][:,:])
nc.close()
fig=plt.figure(figsize=(11,5))
ax=fig.add_subplot(1,1,1,projection=projection)
ax.set_extent(bbox,projection)
ax.add_feature(cfea.COASTLINE,lw=.5)
ax.add_feature(cfea.RIVERS,lw=.5)
ax.add_feature(cfea.BORDERS, linewidth=0.6, edgecolor='dimgray')
ax.background_patch.set_facecolor('.9')
levels=[1,4,8,11,14,17,21,25,29]
cmap=plt.cm.BrBG
norm=mplt.colors.BoundaryNorm(levels,cmap.N)
ddlalo=.25
pc=ax.contourf(lon,lat,kopi,levels=levels,transform=projection,cmap=cmap,norm=norm,extend='both')
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="3%", pad=0.1, axes_class=plt.Axes)
fig.colorbar(pc,extend='both', cax=ax_cb)
fig.add_axes(ax_cb)
fig.colorbar(pc,extend='both', cax=ax_cb)
ttitle='Jony'
ax.set_title(ttitle,loc='left',fontsize=9)
plt.show()
However, I would like just to plot a selection of values inside the variable ´Band1´. I thought I could use the following code:
kopi= (nc.variables['Band1'][:,:])<=3
However it does not work and instead of plotting the area corresponding to the value selection it selected the all map.
How could I select and plot a desired range of values inside the variables ´Band1´?
Just mask the values with np.nan
kopi[kopi <=3] = np.nan
This should yield to white pixels in your plot.
Please provide test data in the future.

Extracting data from cartopy.feature

how can I extract contour lines from data imported through cartopy's feature interface? If the solution involves geoviews.feature or another wrapper, that is OK, of course.
For instance, how would I extract the data plotted as cfeature.COASTLINE in the following example?
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
ax = plt.axes(projection=ccrs.PlateCarree())
ax.add_feature(cfeature.COASTLINE)
plt.show()
I'm grateful for any hints you might have!
FWIW, in basemap, I would do it like this:
import mpl_toolkits.basemap as bm
import matplotlib.pyplot as plt
m = bm.Basemap(width=2000e3,height=2000e3,
resolution='l',projection='stere',
lat_ts=70,lat_0=70,lon_0=-60.)
fig,ax=plt.subplots()
coastlines = m.drawcoastlines().get_segments()
You can get the coordinates for the plotted lines directly from the feature, which contains a set of shapely.MultiLineStrings. As a proof of concept, check out this code:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
fig, (ax1,ax2) = plt.subplots(nrows=2, subplot_kw = dict(projection=ccrs.PlateCarree()))
ax1.add_feature(cfeature.COASTLINE)
for geom in cfeature.COASTLINE.geometries():
for g in geom.geoms:
print(list(g.coords))
ax2.plot(*zip(*list(g.coords)))
plt.show()
which gives this picture:
In other words, you can iterate over the MultiLineStrings of the feature by accessing its geometries(). Each of these MultiLineStrings then contains one or more LineStrings, which have a coords attribute that can be converted into a list. Hope this helps.
For future reference: Some time later, I also came across this (more general?) method to access any feature:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
shpfilename = shpreader.natural_earth(resolution='110m',
category='physical',
name='coastline')
coastlines = shpreader.Reader(shpfilename).records()
fig, ax = plt.subplots(subplot_kw = dict(projection=ccrs.PlateCarree()))
for c in coastlines:
for g in c.geometry:
ax.plot(*zip(*list(g.coords)))
yielding the same plot as above.

how to modify the autocorrelation default plot style and write the output of a acorr function to a dat/txt file?

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab`
mu = np.loadtxt('my_data/corr.txt')
d = mu[:,2]
y=[]
tot=0
min=999
for i in d:
y.append(float(i))
tot=tot+float(i)
if (min>float(i)):
min=float(i)
av=tot/len(y)
z=[]
m=[]
for i in y:
z.append(i-av)
m.append(i-min)
plt.acorr(z,usevlines=True,maxlags=None,normed=True)
plt.show()
WIth this code I have the output showing a bar chart.
Now,
1) How do I change this plot style to give just the trend line? I cant modify the line properties by any means.
2) How do I write this output data to a dat or txt file?
this should be a working minimal example:
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import normal
data = normal(0, 1, 1000)
# return values are lags, correlation vector and the drawn line
lags, corr, line, rest = plt.acorr(data, marker=None, linestyle='-', color='red', usevlines=False)
plt.show()
np.savetxt("correlations.txt", np.transpose((lags, corr)), header='Lags\tCorrelation')
But i would recommand not to connect the points.

Categories