GEONETClass: Manipulating GOES-16 Data With Python – Part VIII

Python-NetCDF-8.png

Mesoscale - Hurricane Irma

All these Mesoscale and CONUS plots were created with a single script, shown in the end of this tutorial part

Conus - Hurricane Irma

This is the eighth part of the GOES-16 / Python tutorial series. You may find the other parts by clicking at the links below:

In this part, we’ll learn:

  • How to reproject CONUS, MESOSCALES and NetCDF’s provided by NOAA with any extent

First, let’s explain the need of discussing this subject.

-THE PROBLEM-

We received several e-mails from people reporting that the reprojection method provided on PART VI only works with FULL DISK NetCDF’s.

The following Blog comment illustrates the problem very well (Thanks Brian!):

The problem

Brian is right. Reprojecting a Full Disk file works well, like in the example below:

Full.png

However, if we use a CONUS NetCDF, it will be streched all over the globe:

Full_CONUS.png

Even if we select the CONUS extent within the code:

Min Lon: -140.61627197265625
Max Lon: -49.179290771484375
Min Lat: 14.000162124633789
Max Lat: 52.76770782470703

The plot will be wrong:

CONUS_Error_Lakes.png

This is happening because in the “remap.py” code provided in Part VI, the GOES16_EXTENT variable was fixed, and defined considering only the FULL DISK extent, as seen below:

GOES_16_Extent

-THE SOLUTION-

In order to solve this problem, we have to calculate the four values from GOES16_EXTENT according to the input file provided.

Let’s call these four values inside the GOES16_EXTENT, x1, y1, x2 and y2 for now on, like this:

GOES16_EXTENT = [x1, y1, x2, y2]

In order to calculate these numbers, we have to do this:

x1 = (GOES-16 fixed grid projection x-coordinate west extent) * (GOES-16 perspective point height)

y1 = (GOES-16 fixed grid projection y-coordinate south extent) * (GOES-16 perspective point height)

x2 = (GOES-16 fixed grid projection x-coordinate east extent) * (GOES-16 perspective point height)

y2 = (GOES-16 fixed grid projection y-coordinate north extent) * (GOES-16 perspective point height)

Thankfully, in the GOES-16 NetCDF header we have everything we need! In Part III we have shown how to get information from the NetCDF header.

To remember, this is how you do:

# Required libraries
from netCDF4 import Dataset

# Path to the GOES-16 image file
path = 'E:\\VLAB\\Python\\GOES-16 Samples\\OR_ABI-L2-CMIPC-M3C14_G16_s20172581602187_e20172581604559_c20172581605015.nc'

# Open the file using the NetCDF4 library
nc = Dataset(path)

# Calculate the image extent required for the reprojection
H = nc.variables['goes_imager_projection'].perspective_point_height
x1 = nc.variables['x_image_bounds'][0] * H
x2 = nc.variables['x_image_bounds'][1] * H
y1 = nc.variables['y_image_bounds'][1] * H
y2 = nc.variables['y_image_bounds'][0] * H 

# Print the results
print("x1 = " + str(x1))
print("y1 = " + str(y1))
print("x2 = " + str(x2))
print("y2 = " + str(y2))

The print output should be this, if you’re using a CONUS file:

Output

If you use a full disk file, you should see something like we had in the GOES16_EXTENT previously (yes, we had the wrong numbers after the decimal point):

Output2

And then, when calling the remap function, we have to pass x1, y1, x2 and y2, like this:

# Call the reprojection function
grid = remap(path, extent, resolution, x1, y1, x2, y2)

Then, in the new remap.py, we have the x1, y1, x2 and y2 variables used within the remap def:

def remap(path, extent, resolution, x1, y1, x2, y2):

        # GOES-16 Extent (satellite projection) [llx, lly, urx, ury]
        GOES16_EXTENT = [x1, y1, x2, y2]

Another nice change we did is to exclude the need to passing ‘HDF5’ or ‘NETCDF’ option to the remap function. Have you noticed that on the new remap call?

Previously, we had this:

# Call the reprojection function
grid = remap(path, extent, resolution, 'HDF5')

or this:

 # Call the reprojection function
grid = remap(path, extent, resolution, 'NETCDF')

Now we have this:

# Call the reprojection function
grid = remap(path, extent, resolution, x1, y1, x2, y2)

This is possible if we change the remap.py GDAL file open from this:

# Build connection info based on given driver name
 if(driver == 'NETCDF'):
 connectionInfo = 'NETCDF:\"' + path + '\":CMI'
 else: # HDF5
 connectionInfo = 'HDF5:\"' + path + '\"://CMI'

 # Open NetCDF file (GOES-16 data)
 raw = gdal.Open(connectionInfo)

to this:

try:
 connectionInfo = 'NETCDF:\"' + path + '\":CMI'
 # Open NetCDF file (GOES-16 data)
 raw = gdal.Open(connectionInfo)
 except:
 connectionInfo = 'HDF5:\"' + path + '\"://CMI'
 # Open NetCDF file (GOES-16 data)
 raw = gdal.Open(connectionInfo)

-TESTING THE SOLUTION-

1-) If you do not want to change the remap file for yourself, please download the new “remap.py” python file with the convert function at this link (you have to choose “Download” on the “…” in the upper part of the window). Put this file in the same directory where you have your scripts (in our case, “E:\VLAB\Python\Scripts”).

IMPORTANT NOTE: If you do not change the remap function call in all your scripts, we recommend using a new script folder if you use the new remap.py file, because the old scritps that we have the old remap call WILL NOT work with this new one. Also, do not forget to put the cpt_convert.py inside the new folder.

2-) Download a CONUS NetCDF file from the  NOAA’s Amazon Simple Storage Service (S3) GOES Archive. You may use the following web interface as shown on this blog post.

For this example, we downloaded this CONUS NetCDF sample.

4-) Use the following script example:

#======================================================================================================
# GNC-A Blog Python Tutorial: Part VIII
#======================================================================================================

# Required libraries ==================================================================================
import matplotlib.pyplot as plt # Import the Matplotlib package
from mpl_toolkits.basemap import Basemap # Import the Basemap toolkit

import numpy as np # Import the Numpy package

from remap import remap # Import the Remap function

from cpt_convert import loadCPT # Import the CPT convert function
from matplotlib.colors import LinearSegmentedColormap # Linear interpolation for color maps

import datetime # Library to convert julian day to dd-mm-yyyy

from matplotlib.patches import Rectangle # Library to draw rectangles on the plot

from netCDF4 import Dataset # Import the NetCDF Python interface
#======================================================================================================

# Load the Data =======================================================================================
# Path to the GOES-16 image file
path = 'E:\\VLAB\\Python\\GOES-16 Samples\\OR_ABI-L2-CMIPC-M3C13_G16_s20172910057131_e20172910059515_c20172910059556.nc'

# Getting information from the file name ==============================================================
# Search for the Scan start in the file name
Start = (path[path.find("_s")+2:path.find("_e")])
# Search for the GOES-16 channel in the file name
Band = int((path[path.find("M3C" or "M4C")+3:path.find("_G16")]))
# Create a GOES-16 Bands string array
Wavelenghts = ['[]','[0.47 μm]','[0.64 μm]','[0.865 μm]','[1.378 μm]','[1.61 μm]','[2.25 μm]','[3.90 μm]','[6.19 μm]','[6.95 μm]','[7.34 μm]','[8.50 μm]','[9.61 μm]','[10.35 μm]','[11.20 μm]','[12.30 μm]','[13.30 μm]']

# Converting from julian day to dd-mm-yyyy
year = int(Start[0:4])
dayjulian = int(Start[4:7]) - 1 # Subtract 1 because the year starts at "0"
dayconventional = datetime.datetime(year,1,1) + datetime.timedelta(dayjulian) # Convert from julian to conventional
date = dayconventional.strftime('%d-%b-%Y') # Format the date according to the strftime directives

time = Start [7:9] + ":" + Start [9:11] + ":" + Start [11:13] + " UTC" # Time of the Start of the Scan

# Get the unit based on the channel. If channels 1 trough 6 is Albedo. If channels 7 to 16 is BT.
if Band <= 6:
 Unit = "Reflectance"
else:
 Unit = "Brightness Temperature [°C]"

# Choose a title for the plot
Title = " GOES-16 ABI CMI Band " + str(Band) + " " + Wavelenghts[int(Band)] + " " + Unit + " " + date + " " + time
# Insert the institution name
Institution = "GNC-A Blog"
# =====================================================================================================

# Open the file using the NetCDF4 library
nc = Dataset(path)

# Get the latitude and longitude image bounds
geo_extent = nc.variables['geospatial_lat_lon_extent']
min_lon = float(geo_extent.geospatial_westbound_longitude)
max_lon = float(geo_extent.geospatial_eastbound_longitude)
min_lat = float(geo_extent.geospatial_southbound_latitude)
max_lat = float(geo_extent.geospatial_northbound_latitude)

# Choose the visualization extent (min lon, min lat, max lon, max lat)
#extent = [-85.0, -5.0, -60.0, 12.0]
extent = [min_lon, min_lat, max_lon, max_lat]

# Choose the image resolution (the higher the number the faster the processing is)
resolution = 2.0

# Calculate the image extent required for the reprojection
H = nc.variables['goes_imager_projection'].perspective_point_height
x1 = nc.variables['x_image_bounds'][0] * H
x2 = nc.variables['x_image_bounds'][1] * H
y1 = nc.variables['y_image_bounds'][1] * H
y2 = nc.variables['y_image_bounds'][0] * H

# Call the reprojection funcion
grid = remap(path, extent, resolution, x1, y1, x2, y2)

# Read the data returned by the function
if Band <= 6:
 data = grid.ReadAsArray()
else:
 # If it is an IR channel subtract 273.15 to convert to ° Celsius
 data = grid.ReadAsArray() - 273.15
#======================================================================================================

# Define the size of the saved picture=================================================================
DPI = 150
ax = plt.figure(figsize=(2000/float(DPI), 2000/float(DPI)), frameon=True, dpi=DPI)
#======================================================================================================

# Plot the Data =======================================================================================
# Create the basemap reference for the Rectangular Projection
bmap = Basemap(llcrnrlon=extent[0], llcrnrlat=extent[1], urcrnrlon=extent[2], urcrnrlat=extent[3], epsg=4326)

# Draw the countries and Brazilian states shapefiles
bmap.readshapefile('E:\\VLAB\\Python\\Shapefiles\\BRA_adm1','BRA_adm1',linewidth=0.50,color='cyan')
bmap.readshapefile('E:\\VLAB\Python\\Shapefiles\\ne_10m_admin_0_countries','ne_10m_admin_0_countries',linewidth=0.50,color='cyan')

# Draw parallels and meridians
bmap.drawparallels(np.arange(-90.0, 90.0, 5.0), linewidth=0.2, dashes=[4, 4], color='white', labels=[False,False,False,False], fmt='%g', labelstyle="+/-", xoffset=-0.80, yoffset=-1.00, size=7)
bmap.drawmeridians(np.arange(0.0, 360.0, 5.0), linewidth=0.2, dashes=[4, 4], color='white', labels=[False,False,False,False], fmt='%g', labelstyle="+/-", xoffset=-0.80, yoffset=-1.00, size=7)

if Band  7 and Band  10:
 # Converts a CPT file to be used in Python
 cpt = loadCPT('E:\\VLAB\\Python\\Colortables\\IR4AVHRR6.cpt')
 # Makes a linear interpolation
 cpt_convert = LinearSegmentedColormap('cpt', cpt)
 # Plot the GOES-16 channel with the converted CPT colors (you may alter the min and max to match your preference)
 bmap.imshow(data, origin='upper', cmap=cpt_convert, vmin=-103, vmax=84)

if Band <= 6:
 # Insert the colorbar at the bottom
 cb = bmap.colorbar(location='bottom', size = '2%', pad = '-4.2%', ticks=[0.2, 0.4, 0.6, 0.8])
 cb.ax.set_xticklabels(['0.2', '0.4', '0.8', '0.8'])
else:
 # Insert the colorbar at the bottom
 cb = bmap.colorbar(location='bottom', size = '2%', pad = '-4.2%')

cb.outline.set_visible(False) # Remove the colorbar outline
cb.ax.tick_params(width = 0) # Remove the colorbar ticks
cb.ax.xaxis.set_tick_params(pad=-9.5) # Put the colobar labels inside the colorbar
cb.ax.tick_params(axis='x', colors='yellow', labelsize=6) # Change the color and size of the colorbar labels

# Add a black rectangle in the bottom to insert the image description
lon_difference = (extent[2] - extent[0]) # Max Lon - Min Lon
currentAxis = plt.gca()
currentAxis.add_patch(Rectangle((extent[0], extent[1]), lon_difference, lon_difference * 0.010, alpha=1, zorder=3, facecolor='black'))

# Add the image description inside the black rectangle
lat_difference = (extent[3] - extent[1]) # Max lat - Min lat
plt.text(extent[0], extent[1] + lat_difference * 0.003,Title,horizontalalignment='left', color = 'white', size=7)
plt.text(extent[2], extent[1] + lat_difference * 0.003,Institution, horizontalalignment='right', color = 'yellow', size=7)

# Add logos / images to the plot
logo_INPE = plt.imread('E:\\VLAB\\Python\\Logos\\INPE Logo.png')
logo_NOAA = plt.imread('E:\\VLAB\\Python\\Logos\\NOAA Logo.png')
logo_GOES = plt.imread('E:\\VLAB\\Python\\Logos\\GOES Logo.png')
ax.figimage(logo_INPE, 10, 40, zorder=3, alpha = 1, origin = 'upper')
ax.figimage(logo_NOAA, 110, 40, zorder=3, alpha = 1, origin = 'upper')
ax.figimage(logo_GOES, 195, 40, zorder=3, alpha = 1, origin = 'upper')

date_save = dayconventional.strftime('%d%m%Y')
time_save = Start [7:9] + Start [9:11] + Start [11:13]

# Save the result
plt.savefig('E:\\VLAB\\Python\\Output\\G16_C' + str(Band) + '_' + date_save + '_' + time_save + '.png', dpi=DPI, bbox_inches='tight', pad_inches=0)
#======================================================================================================

Note: On this script the min_lon, max_lon, min_lat and max_lat variables are being read from the header (complete domain). You still may change the region according to your needs on the extent variable.

Note 2: This script will work with any of the 16 channels. You’ll need the colortables found on the colortables.zip file available at this link.

This should be the result:

G16_C13_18102017_005713.png

Let’s use a NetCDF with another domain. Please download this NetCDF sample from SMN Argentina. They receive it from PDA, and it is subsected on their region of interest.

Run the script with their file, changing the file name as appropriate. This should be the result:

G16_C13_15092017_131537.png

The new remap file also works for mesoscales. For the plot below, we have used a sample downloaded from Amazon at this link. Try it out! Do not forget to adjust the colorbar and logo position.

G16_C13_18102017_154720.png

GOES-16 data posted on this page are preliminary, non-operational and are undergoing testing

Products Generated by the Community [7]

The Meteorological Service of Chile sent us some nice examples of GOES-16 plots they are developing using the NetCDF’s provided by INPE via FTP. To produce these plots, they are using the free NCL tool, which supports NetCDF 3/4, GRIB 1/2, HDF 4/5, HDF-EOS 2/5, Shapefiles, ASCII and binary data formats. Thanks MeteoChile!

MeteoChile_Example_3.png

g16_41.png

g16_33.png

And below, an RGB produced by MARN – El Salvador, developed with the well known, freely available McIDAS-V. Plenty of free tools at the community’s disposal! Apart from GNC-A, MARN is downloading the NetCDF’s available at NOAA’s Amazon Simple Storage Service (S3) GOES Archive, as seen on this blog post.

RGB MARN.jpg

GOES-16 data posted on this page are preliminary, non-operational and are undergoing testing

Installing the GNC-A Ingestion software on Ubuntu 16.04: The Easy Way

KenCast and Ubuntu - Easy.png

We have seen on this previous Blog post how to install the GNC-A ingestion software (KENCAST FAZZT PROFESSIONAL CLIENT) on Ubuntu 16.04.

On this blog post, we’ll show you how to install it the easy way, using a single script. Let’s begin:

1 – It is mandatory to create an account called “fazzt”. This is required for the FAZZT database to work.

fazzt_user_ubuntu

2 – Download the installation script “install_FAZZT9_Ubuntu.sh” at this link. Put the installation script “install_FAZZT9_Ubuntu.sh”, the FAZZT installation package (Fazzt-Professional-Client-*.sh file) and your license (.kcl file) inside the folder of your preference (must be the same folder). In the example below, we have put them inside the Downloads folder:

Fazzt_Ubuntu_Script_1

Note: In case you don’t have the FAZZT installation file for Ubuntu 16.04, you may download it at the link below (you still need your v 9.0 license!):

3 – As the administrator (root), you must change the file permissions in order to execute the script with the chmod +x * command:

Fazzt_Ubuntu_Script_2

4 – Execute the installation script with the ./install_FAZZT9_Ubuntu.sh command:

Fazzt_Ubuntu_Script_3

5 – Wait some minutes. The script will make all the configurations seen on the “Hard Way” blog post. After finishing the installation, Firefox will open automatically at the following address:

127.0.0.1:4039/admin/

You should see the KenCast FAZZT Professional Client main window:

fazzt_main_1

6 – Smile 😎

Note: You may use the following commands in the terminal, to start, stop or restart FAZZT:

# fazzt9 start

# fazzt9 stop

# fazzt9 restart

Note 2: The directory /fazzt/data was created as a suggestion to ingest the GNC-A data.

7 – Proceed with the FAZZT configuration as seen on PART III of the previous blog post.

Note: Do you already know how to configure your DVB-S2 receiver? If you’re using a NOVRA S300D, please check out our guide at this post.

Installing the GNC-A Ingestion software on Ubuntu 16.04: The Hard Way

FAZZT-Ubuntu.png

In this blog post we’ll learn how to install the GNC-A ingestion software (KENCAST FAZZT PROFESSIONAL CLIENT) on Ubuntu 16.04.

  • This procedure has been tested on Linux Ubuntu version 16.04. If your system has a previous version, the update is advised. Only FAZZT V9.0 currently supports Ubuntu v 9.0. If you have a v 8.2 license, please install CentOS 6.9, as seen here.
  • In order to install FAZZT, is necessary to install Apache and the PostgreSQL Database, as shown on this procedure.
  • It is mandatory to create an account called “fazzt”. This is required for the FAZZT database to work.

fazzt_user_ubuntu.png

PART I: APACHE AND POSTGRESQL INSTALLATION

Disable the error apport and reboot:

# sed -i 's/enabled=1/enabled=0/g' /etc/default/apport
# reeboot

Install Apache:

# sudo apt-get install apache2

Install the PostgreSQL Database

# sudo apt-get install postgresql

– Enter the PostgreSQL:

# su - postgres

– Create the “fazzt” database:

$ createdb fazzt
$ createuser -s -d -r -l -P fazzt

Note: upper case “P”!

– You will be asked for inserting a new password for the database. It may be the same as the root. However, you may use any password you want. Insert the chosen password twice.

 – Quit the PostgreSQL:

$ exit
# service postgresql-9.5 restart

– Return to the database and verify the created account:

# su - postgres
$ psql -h localhost -U fazzt fazzt

– The output of the previous command should be:

Type “help” for help
fazzt=#

– Quit the Database with the “\q” command:

postgres=# \q
$ exit

Your FAZZT Database was successfully created!

PART II: KENCAST FAZZT V 9.0 INSTALLATION

– In case you don’t have the FAZZT installation file for Ubuntu 16.04, you may download it at the link below (you still need your v 9.0 license!):

– Put both installation file and license in the folder of your preference. Here, we’ll put them at home/fazzt/Downloads:

fazzt_files_1

– As the administrator (root), you must change the file permissions in order to install FAZZT, with the following command:

# chmod +x Fazzt-Professional-Client-9.0.0.5.sh

fazzt_files_2

– Install the FAZZT PROFESSIONAL CLIENT with the following command:

# ./Fazzt-Professional-Client-9.0.0.5.sh

– Copy your license file (PCxxxxxx.kcl) to /opt/Fazzt/license/

# cp PCxxxxxx.kcl /opt/Fazzt/license/

– Change the permissions:

# chmod 444 /opt/Fazzt/license/PCxxxxxx.kcl

– Check if the installation was done successfully:

# /etc/init.d/fazzt

– This should be the output:

fazzt_files_3

– Start FAZZT:

# /etc/init.d/fazzt start

The end of the message should be as this:

fazzt_files_4

– Access the KenCast interface in your browser at the following address:

127.0.0.1:4039/admin/

fazzt_main_1.png

– Ir order to be able to stop, start or restart the FAZZT interface via terminal, download the fazzt9_start_stop.zip file at this link.

Unzip the file and you will see four files: fazzt9, fazzt9restart.sh, fazzt9start.sh, fazzt9restart.sh. Change the permissions with the chmod +x fazzt9* command for example:

# chmod +x fazzt9*

fazzt_files_5

And move them to /usr/bin:

# mv fazzt9* /usr/bin

Now you may use the following commands in the terminal, to start, stop or restart FAZZT:

# fazzt9 start

# fazzt9 stop

# fazzt9 restart

Below, an example of the output of the fazzt9 start command:

fazzt_files_6

 – Finnaly, create an ingestion folder. Our suggestion is to create a “/data/fazzt” directory where the data would be stored:

# mkdir /data
# mkdir /data/fazzt

– Change the permissions and groups:

# chmod 777 -R /data/fazzt

PART III: KENCAST FAZZT CONFIGURATION

Let’s configure the FAZZT Client for the GNC-A ingestion. At the menu on the left, choose “Configuration” -> “Channels” and then, at the “Channels” window, choose “Channels Defaults”. At “Interface”, choose the IP address of the Network Card where you have your DVB-S receiver connected. Click on “Save”.

KCa.png

– Select the ingestion folder at “Configurations”“Storage Settings”“Virtual Paths: View/Edit”

KCb.png

– Click at the backslash \” as shown on the image below and choose the folder where the received files will be stored. In this procedure we suggested “/data/fazzt” but it may be any folder you want.

KCc.png

– Back to the “Channels” interface, click at the “1.Main” channel:

KCd.png

– And at “Interface”, choose “default”:

KCe.png

– After a while (a couple of minutes), you should see the GNC-A Broadcast channels being listed at the “Configuration”“Channels” interface.

KCf.png

Congreatulations! Your GNC-A station is ingesting!