Generating multiple maps from 2d array from netcdf - arrays

I need to generate multiple temperature plots for every month of every year, spanning from 2002 to 2018.
I have managed to develop one plot for all of the data in 2002 (about 6 months). I had import my netcdf files into an array and slice the 3d array to a 2d array of lat lon lst.
Using pcolormesh I plotted one singular lon, lat, data_2d plot but can't figure out how to define the months so I can plot them separately.
The data is not available online but I am looking for a general function or command that can iterate through the months and plot them separately onto a map.
import netCDF4
import numpy as np
import xarray as xr
import pandas as pd
import os
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import cartopy.crs as ccrs
import cartopy.feature as cfeature
print ('All packages imported')
# make an empty np array
data_grid = []
# list of files
days = pd.date_range (start='4/7/2002', end='31/7/2002')
print ('Initiate for loop')
# for loop iterating through %date range to find path files
for day in days:
# defining what paths should look like
# %i = integer, %02d = integer with two characters, for year month and day
path = "/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/%i/%02d/%02d/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY-%i%02d%02d000000-fv2.00.nc" % (day.year, day.month, day.day, day.year, day.month, day.day)
print(path)
# os fetches contents
if os.path.isfile(path):
# open file and define lst daily mean
f = Dataset(path)
lst = f['lst'][:]
# populate numpy array, lst slice ignoring first index and accounting for NaN
data_grid.append(f['lst'][0,:,:].filled(np.nan))
f.close()
else: print ("%s not found" % path)
# stack array into three dimensions (lat, lon, time)
data_3d = np.dstack(data_grid)
# calculate mean of each grid point pixel and ignore NaN values
# make it 2d
data_2d = np.nanmean(data_3d, axis = 2)
# define lat lon from last file
f = netCDF4.Dataset ('/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/2018/12/31/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY-20181231000000-fv2.00.nc')
print (f)
# define lat and lons including all indicies
lat = f['lat'][:]
lon = f['lon'][:]
# plot data
# set up a map and size
fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
# define the coordinate system that the grid lons and grid lats are on
mapped_array = ax.pcolormesh(lon, lat, data_2d)
# add title
plt.title ('Aqua Modis London', fontsize=12)
# set axis labels
plt.xlabel('Latitude', fontsize=10)
plt.ylabel('Longitude',fontsize=10)
# add features
ax.coastlines()
ax.add_feature(cfeature.BORDERS, edgecolor='black')
# add colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.1, axes_class=plt.Axes)
fig.add_axes(ax_cb)
plt.colorbar(mapped_array, cax=ax_cb)
#add lat lon grids and ticks
gl = ax.gridlines(draw_labels=True, alpha=0.1)
gl.top_labels = False
gl.right_labels = False
# show and save
plt.show()
plt.close()

I would recommend using the xarray package for collecting all of your data into a single array. First put all of the netCDFs in the same directory, then merge with:
import xarray as xr
f = xr.open_mfdataset('/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY*.nc')
From there it looks like you want the monthly mean:
f_monthly = f.resample(time = 'MS', skipna = True).mean()
Then it is pretty straightforward to loop through each of the months/years and plot.
f_monthly.sel(time = '2018-12').plot()

Related

2D Array for Heatmap

I am trying to create a 2D array that I will use to plot a heatmap.
The array needs to be n by n and have the highest value be at its epicenter with diminishing values further away like in the diagram below.
How could I do that?
You can use numpy for the array and matplotlib for creating a heatmap respectively. Something like this:
import numpy as np
import matplotlib.pyplot as plt
# creating array using numpy
array=np.ones((9,9),dtype=int)
array[1:8,1:8]=2
array[2:7,2:7]=3
array[3:6,3:6]=4
array[4,4]=5
print(array)
fig, ax = plt.subplots()
im = ax.imshow(array,cmap="PuBuGn") # cmap can be Greys, YlGnBu, PuBuGn, BuPu etc
# Create colorbar
cbar = ax.figure.colorbar(im, ax=ax,ticks=[1,2,3,4,5])
cbar.ax.set_ylabel("My bar [1-5]", rotation=-90, va="bottom")
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_title("My heatmap")
fig.tight_layout()
plt.show()
For automatically create an array, use a loop.
import numpy as np
import matplotlib.pyplot as plt
lim=100
arr=np.ones((lim,lim),dtype=int)
for i in range(1,lim):
arr[i:len(arr)-i,i:len(arr)-i]=i+1
fig, ax = plt.subplots()
im = ax.imshow(arr,cmap="Purples") # cmap can be Greys, YlGnBu, PuBuGn, BuPu etc
# Create colorbar
cbar = ax.figure.colorbar(im, ax=ax,ticks=list(range(1,lim,5)))
cbar.ax.set_ylabel("My bar [1-50]", rotation=-90, va="bottom")
ax.set_xticklabels([])
ax.set_yticklabels([])
# Show all ticks and label them with the respective list entries
ax.set_title("My heatmap")
fig.tight_layout()
plt.show()

only size-1 arrays can be converted to Python scalars / Rasterio

I have this code and my aim to calculate the sin of my raster in the power of 0.8.
import os
os.chdir('D:/NOA/Soil_Erosion/test_Project/Workspace/Input_Data_LS_Factor')
import rasterio
import math
data = rasterio.open('Slope_degrees_clipped.tif')
band = data.read(1) # array of float32 with size (3297,2537)
w = band.shape[0]
print(w)
h = band.shape[1]
print(h)
dtypes =data.dtypes[0]
band_calc = math.sin(band)**0.8 # the formula I would like to calculate
However, the following error pops up:
only size-1 arrays can be converted to Python scalars / Rasterio
May you know how I should fix this?
P.S. I tried to vectorize it (np.vectorize()) but it does not work as it needs a real number.
When I use the np.ndarray.flatten(band) the same error occurs.
I found the solution on Geographic Information Systems:
import os
os.chdir('D:/NOA/Soil_Erosion/test_Project/Workspace/Input_Data_LS_Factor')
import rasterio
import math
data = rasterio.open('Slope_degrees_clipped.tif')
from rasterio.plot import show
show(data)
band = data.read(1) # array of float32 with size (3297,2537)
w = band.shape[0]
print(w)
h = band.shape[1]
print(h)
dtypes =data.dtypes[0]
Calculate the sine of the raster in the power of 0.8
import numpy as np
band_calc2 = np.sin(band)**0.8 # the formula I would like to calculate
"""
another way to do it
band_calc = [ [] for i in range(len(band)) ]
for i,row in enumerate(band):
for element in row:
band_calc[i].append(math.sin(element*math.pi/180)**0.8)
"""

Updating matplotlib figures within a for loop in python

I am trying to move a magnetic object and update its magnetic field which is then plotted in a for loop. I want to update the matplotlib figures such that the last figure is deleted before the latest one is shown (with delay as can be seen). I want this to happen in one window (I might be using the wrong technical term) only. Currently it creates a new figure every time it updates the magnetic field. I tried using plt.cla(), plt.close(), and plt.clf() without success. The code is given below. Any help will be much appreciated
import matplotlib.pyplot as plt
import numpy as np
from magpylib.source.magnet import Box,Cylinder
from magpylib import Collection, displaySystem
# create magnets
s1 = Box(mag=(0,0,600), dim=(3,3,3), pos=(-4,0,20))
# calculate the grid
xs = np.linspace(-15,10,33)
zs = np.linspace(-5,25,44)
POS = np.array([(x,0,z) for z in zs for x in xs])
X,Z = np.meshgrid(xs,zs)
for i in range(20):
Bs = s1.getB(POS).reshape(44,33,3) #B-field
s1.move((0,0,-1))
# create figure
fig = plt.figure(figsize=(5,9))
# display field in xz-plane using matplotlib
U,V = Bs[:,:,0], Bs[:,:,2]
plt.streamplot(X, Z, U, V, color=np.log(U**2+V**2))
plt.show()
sleep(0.2)```
You want to make use of matplotlib's interactive mode by invoking plt.ion() and clear the axes after every frame in the loop using plt.cla():
import matplotlib.pyplot as plt
import numpy as np
from magpylib.source.magnet import Box,Cylinder
from magpylib import Collection, displaySystem
fig, ax = plt.subplots()
# create magnets
s1 = Box(mag=(0,0,600), dim=(3,3,3), pos=(-4,0,20))
# calculate the grid
xs = np.linspace(-15,10,33)
zs = np.linspace(-5,25,44)
POS = np.array([(x,0,z) for z in zs for x in xs])
X,Z = np.meshgrid(xs,zs)
plt.ion()
plt.show()
img=0
for i in range(20):
Bs = s1.getB(POS).reshape(44,33,3) #B-field
s1.move((0,0,-1))
U,V = Bs[:,:,0], Bs[:,:,2]
ax.streamplot(X, Z, U, V, color=np.log(U**2+V**2))
plt.gcf().canvas.draw()
plt.savefig('{}'.format(img))
plt.pause(0.01)
plt.clf()
img=img+1

How to find the maximum element value across multiple arrays?

I am trying to manually convert a BGR image to HSV. I need to find the maximum pixel value each of 3 image channels (numPy arrays) and create a new array which contains the maximum of the 3 channels.
def convertBGRtoHSV(image):
# normalize image
scaledImage = image // 256
# split image into 3 channels
B, G, R = cv2.split(scaledImage)
# find the shape of each array
heightB, widthB = B.shape
V = []
for h_i in range(0, height):
for w_i in range(0, width):
V[h_i][w_i] = max(B[h_i][w_i], G[h_i][w_i], R[h_i][w_i])
I am getting this error: IndexError: list index out of range
I know this loop is incorrect. I know to access the value of a pixel in an array you must say the location as such as x[:,:] but I am not sure how to loop over all the pixels of each image and make a new array with the max value of each array element.
If possible I would like to know how to use a numPy "Vectorized Operation" to accomplish this as well as the for loop.
There is a builtin function for element-wise maximum:
V = np.maximum(np.maximum(R, G), B)
... and you are done
Following up on my comment:
import cv2
import numpy as np
image = cv2.imread(image)
height, width, _ = image.shape
# initialize your output array 'v'
v = np.zeros((height, width))
# loop over each index in ranges dictated by the image shape
for row in range(height):
for col in range(width):
# assign the maximum value across the 3rd dimension (color channel)
# from the original image to your output array
v[row, col] = max(image[row, col, :])

Accessing properties of objects in a numpy array

I've got a numpy array of custom objects. How can I get a new array containing the values of specific attributes of those objects?
Example:
import numpy as np
class Pos():
def __init__(self, x, y):
self.x = x
self.y = y
arr = np.array( [ Pos(0,1), Pos(2,3), Pos(4,5) ] )
# Magic line
xy_arr = .... # arr[ [arr.x,arr.y] ]
print xy_arr
# array([[0,1],
[2,3],
[4,5]])
I should add that my motives for such an operation is to calculate the centre of mass of the objects in the array.
Usually, when I have multiple quantities that belong together and I want to benefit from numpys indexing power I use record arrays. Beware, if you do a lot of append/remove operations, numpy might be rather ineffective in terms of speed.
If I understood your comment correctly, this is an example where two values are selected by a third:
import numpy as np
# create a table for your data
dt = np.dtype([('A', np.double), ('x', np.double), ('y', np.double)])
table = np.array([(1,1,1), (2,2,2), (3,3,3)], dtype=dt)
# define a selection mask
selection = table['A'] > 1.5
columns = ['x', 'y']
print table[selection][columns]
A nice side effect is that saving this table using h5py is very simple and convenient as your data is already labeled.

Resources