PyNGL > tutorial examples > 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11


Example 2 - Contour plots

This example reads a netCDF file and creates five contour plots using three different datasets, and it sets resources to get different types of contour plots. This example also writes some of the netCDF data to an ASCII file.

To find out more about netCDF, see http://my.unidata.ucar.edu/content/software/netcdf/

To run this example, you must have Numerical Python installed in your Python implementation. Numerical Python (also known as "NumPy") is a Python module allowing for efficient array processing.

This example reads in a netCDF file, so you will need to have the Nio module (this module comes with PyNGL). netCDF is a self-documenting and network-transparent data format - see the netCDF User Guide for details.

There are two ways to run this example:

execute

    pynglex ngl02p
or download
ngl02p.py
and type:
python ngl02p.py

Output from example 2

Frame 1 Frame 2 Frame 3 Frame 4 Frame 5

(Click on any frame to see it enlarged.)


  0. #
  1. #  Import Python modules to be used.
  2. #
  3. import numpy,sys,os
  4. 
  5. #
  6. #  Import Nio (for reading netCDF files).
  7. #
  8. import Nio
  9. 
 10. #
 11. #  Import Ngl support functions.
 12. #
 13. import Ngl
 14. 
 15. #  
 16. #  Open the netCDF file.
 17. #  
 18. cdf_file = Nio.open_file(Ngl.pynglpath("data") + "/cdf/contour.cdf","r")
 19. 
 20. #
 21. #  Associate Python variables with NetCDF variables.
 22. #  These variables have associated attributes.
 23. #
 24. temp = cdf_file.variables["T"]    # temperature
 25. Z    = cdf_file.variables["Z"]    # geopotential height
 26. pres = cdf_file.variables["Psl"]  # pressure at mean sea level
 27. lat  = cdf_file.variables["lat"]  # latitude
 28. lon  = cdf_file.variables["lon"]  # longitude
 29. 
 30. #
 31. #  Open a workstation and specify a different color map.
 32. #
 33. wkres = Ngl.Resources()
 34. wkres.wkColorMap = "default"
 35. wks_type = "ps"
 36. wks = Ngl.open_wks(wks_type,"ngl02p",wkres)
 37. 
 38. resources = Ngl.Resources()
 39. #
 40. #  Define a NumPy data array containing the temperature for the
 41. #  first time step and first level. This array does not have the
 42. #  attributes that are associated with the variable temp.
 43. #
 44. tempa = temp[0,0,:,:]
 45. 
 46. #
 47. #  Convert tempa from Kelvin to Celcius while retaining the missing values.
 48. #  If you were to write the new temp data to a netCDF file, then you
 49. #  would want to change the temp units to "(C)".
 50. #
 51. if hasattr(temp,"_FillValue"):
 52.   tempa = ((tempa-273.15)*numpy.not_equal(tempa,temp._FillValue)) +   \
 53.         temp._FillValue*numpy.equal(tempa,temp._FillValue)
 54. else:
 55.   tempa = tempa - 273.15
 56. 
 57. #
 58. #  Set the scalarfield missing value if temp has one specified.
 59. #
 60. if hasattr(temp,"_FillValue"):
 61.   resources.sfMissingValueV = temp._FillValue[0]
 62. 
 63. #
 64. #  Specify the main title base on the long_name attribute of temp.
 65. #
 66. if hasattr(temp,"long_name"):
 67.   resources.tiMainString = temp.long_name
 68. 
 69. plot = Ngl.contour(wks,tempa,resources)
 70. 
 71. #----------- Begin second plot -----------------------------------------
 72. 
 73. resources.cnMonoLineColor = False  # Allow multiple colors for contour lines.
 74. resources.tiMainString    = "Temperature (C)"
 75. 
 76. plot = Ngl.contour(wks,tempa,resources)  # Draw a contour plot.
 77. 
 78. #----------- Begin third plot -----------------------------------------
 79. 
 80. resources.cnFillOn          = True    # Turn on contour line fill.
 81. resources.cnMonoFillPattern = False   # Turn off using a single fill pattern.
 82. resources.cnMonoFillColor   = True
 83. resources.cnMonoLineColor   = True
 84. 
 85. if hasattr(lon,"long_name"):
 86.   resources.tiXAxisString = lon.long_name
 87. if hasattr(lat,"long_name"):
 88.   resources.tiYAxisString = lat.long_name
 89. resources.sfXArray        = lon[:]
 90. resources.sfYArray        = lat[:]
 91. resources.pmLabelBarDisplayMode = "Never" # Turn off label bar.
 92. 
 93. plot = Ngl.contour(wks,tempa,resources)   # Draw a contour plot.
 94. 
 95. #---------- Begin fourth plot ------------------------------------------
 96. 
 97. #
 98. #  Specify a new color map.
 99. #
100. rlist = Ngl.Resources()
101. wkres.wkColorMap = "BlGrYeOrReVi200"
102. Ngl.set_values(wks,rlist)
103. 
104. resources.cnMonoFillPattern     = True     # Turn solid fill back on.
105. resources.cnMonoFillColor       = False    # Use multiple colors.
106. resources.cnLineLabelsOn        = False    # Turn off line labels.
107. resources.cnInfoLabelOn         = False    # Turn off informational
108.                                              # label.
109. resources.pmLabelBarDisplayMode = "Always" # Turn on label bar.
110. resources.cnLinesOn             = False    # Turn off contour lines.
111. 
112. resources.tiMainFont      = 26
113. resources.tiXAxisFont     = 26
114. resources.tiYAxisFont     = 26
115. 
116. if hasattr(Z,"_FillValue"):
117.   resources.sfMissingValueV = Z._FillValue
118. if hasattr(Z,"long_name"):
119.   resources.tiMainString = Z.long_name
120. plot = Ngl.contour(wks,Z[0,0,:,:],resources)    # Draw a contour plot.
121. 
122. #---------- Begin fifth plot ------------------------------------------
123. 
124. cmap = numpy.array([[0.00, 0.00, 0.00], [1.00, 1.00, 1.00], \
125.                     [0.10, 0.10, 0.10], [0.15, 0.15, 0.15], \
126.                     [0.20, 0.20, 0.20], [0.25, 0.25, 0.25], \
127.                     [0.30, 0.30, 0.30], [0.35, 0.35, 0.35], \
128.                     [0.40, 0.40, 0.40], [0.45, 0.45, 0.45], \
129.                     [0.50, 0.50, 0.50], [0.55, 0.55, 0.55], \
130.                     [0.60, 0.60, 0.60], [0.65, 0.65, 0.65], \
131.                     [0.70, 0.70, 0.70], [0.75, 0.75, 0.75], \
132.                     [0.80, 0.80, 0.80], [0.85, 0.85, 0.85]],'f')
133. 
134. rlist.wkColorMap = cmap
135. Ngl.set_values(wks,rlist)
136. 
137. #
138. #  If the pressure field has a long_name attribute, use it for a title.
139. #
140. if hasattr(pres,"long_name"):
141.   resources.tiMainString = pres.long_name
142. 
143. #
144. #  Convert the pressure to millibars while retaining the missing values.
145. #
146. presa = pres[0,:,:]
147. if hasattr(pres,"_FillValue"):
148.   presa = (0.01*presa*numpy.not_equal(presa,pres._FillValue)) +   \
149.         pres._FillValue*numpy.equal(presa,pres._FillValue)
150. else:
151.   presa = 0.01*presa
152. 
153. plot = Ngl.contour(wks,presa,resources)  # Draw a contour plot.
154. 
155. print "\nSubset [2:6,7:10] of temp array:" # Print subset of "temp" variable.
156. print tempa[2:6,7:10]
157. print "\nDimensions of temp array:"        # Print dimension names of T.
158. print temp.dimensions
159. print "\nThe long_name attribute of T:"    # Print the long_name attribute of T.
160. print temp.long_name 
161. print "\nThe nlat data:"                   # Print the lat data.
162. print lat[:]           
163. print "\nThe nlon data:"                   # Print the lon data.
164. print lon[:]          
165. 
166. #
167. #  Write a subsection of tempa to an ASCII file.
168. #
169. os.system("/bin/rm -f data.asc")
170. sys.stdout = open("data.asc","w")
171. for i in range(7,2,-2):
172.   for j in range(0,5):
173.     print "%9.5f" % (tempa[i,j])
174. 
175. # Clean up (not really necessary, but a good practice).
176. 
177. del plot 
178. del resources
179. del temp
180. 
181. Ngl.end()

Explanation of example 2

Line 3:

  import numpy,sys,os

Import the Numerical Python module. You must have this module installed in your Python implementation. This module is used for efficient array processing. The names of the functions in the Numerical Python package are not exposed by this import, meaning that all functions and attributes in that package will have to be qualified with "numpy".

The Numerical Python package is also known as "NumPy". The Python modules os and sys will also be used in this example.

Line 8:

  import Nio

Import the Nio module for a netCDF reader. This module comes with the PyNGL package. netCDF is a self-documenting and network-transparent data format - see the netCDF User Guide for details.

Note: you can also use the netCDF module from the ScientificPython package to read netCDF files. To do this, you need to install ScientificPython, and then import the module:

  from Scientific.IO.NetCDF import NetCDFFile

Line 13:

  import Ngl

Import all of the Ngl functions. The Ngl module must be on your Python search path.

Line 18:

  cdf_file = Nio.open_file(Ngl.ncargpath("data") + "/cdf/contour.cdf","r")

The built-in function Ngl.pynglpath returns the full pathname for a given PyNGL system directory, or the default value for a particular internal parameter.

To use the netCDF module from the ScientificPython package to read the netcdf file, change "Nio.open_file" to "NetCDFFile".

Lines 24-28:

  temp = cdf_file.variables["T"]    # temperature
  Z    = cdf_file.variables["Z"]    # geopotential height
  pres = cdf_file.variables["Psl"]  # pressure at mean sea level
  lat  = cdf_file.variables["lat"]  # latitude
  lon  = cdf_file.variables["lon"]  # longitude

These assignments associate Python NumPy objects with netCDF variables. No data is actually read in making these assignments.

Lines 33-36:

  wkres = Ngl.Resources()
  wkres.wkColorMap = "default"
  wks_type = "ps"
  wks = Ngl.open_wks(wks_type,"ngl02p",wkres)

Open a workstation for drawing the contour plots, and use a resource list to change the colormap to one called "default". The creation of a resource list will be described in the next section.

Line 38:

  resources = Ngl.Resources()

A resource list will be used to change the look of the plot (see example 1 for an explanation on how resources are set). Contour plot resources are part of the "ContourPlot" group and all start with the letters "cn". They are documented in the ContourPlot resource descriptions section.

Data associated with a contour plot is called a "ScalarField." ScalarField resources start with "sf" and are documented in the ScalarField resource descriptions section.

Line 44:

  tempa = temp[0,0,:,:]

Define a NumPy data array containing the temperature for the first time step and first level. This assignment reads the specified data into the NumPy array tempa. This array does not have the attributes that are associated with the variable temp.

Lines 51-55:

  if hasattr(temp,"_FillValue"):
    tempa = ((tempa-273.15)*numpy.not_equal(tempa,temp._FillValue)) +   \
          temp._FillValue*numpy.equal(tempa,temp._FillValue)
  else:
    tempa = tempa - 273.15

These calculations convert the tempa array from Kelvin to Celcius. If temp has a _FillValue set, then we have to make sure that those values are not converted; if temp does not have a _FillValue set, then the conversion can be done without care for missing values.

Lines 60-61:

  if hasattr(temp,"_FillValue"):
    resources.sfMissingValueV = temp._FillValue[0]

Specify the missing value for the scalar filed data if there is one. The Ngl.contour function will ignore missing value data.

Lines 66-67:

  if hasattr(temp,"long_name"):
    resources.tiMainString = temp.long_name

If the temperature variable in the netCDF file has a long_name attribute, then use that for the main plot title.

Line 69:

  plot = Ngl.contour(wks,tempa,resources)

Create and draw a contour plot of the 2-dimensional array tempa. The first argument of the Ngl.contour function is the workstation object returned from the previous call to Ngl.open_wks. The next argument is the 2-dimensional NumPy scalar field to be contoured, which can be of the NumPy float, double, or integer types. The first dimension must be the Y dimension, and the second the X. The last argument is a resource list.

The default contour plot drawn contains labeled tick marks and an "informational" label at the bottom right of the plot, indicating the range and spacing of the contour levels. Later examples show how to turn this informational label off, and how to customize the tick marks. Also, since no ranges have been defined for the X and Y axes in this plot, the range values default to 0 to n-1, where n is the number of points in that dimension.

Line 73:

  resources.cnMonoLineColor = False  # Allow multiple colors for contour lines.

Occasionally you will see resource names with the word "Mono" in them. In this case, by setting cnMonoLineColor to False, you are telling PyNgl that you don't want to use a single color for all the contour lines, so it uses the "plural" resource cnLineColors to determine the line color for each line. If cnMonoLineColor had been set to True, then all contour lines would have been drawn in the same color determined by the "singular" resource cnLineColor.

The cnLineColors resource is an example of a resource that is set dynamically when you run the PyNGL script. PyNGL determines how many contour levels you have, then sets cnLineColors with enough color indices to color each line.

Line 74:

  resources.tiMainString    = "Temperature (C)"

Create a title for the second plot (thus overriding the default title provided by the "long_name" attribute).

Line 76:

  plot = Ngl.contour(wks,tempa,resources)  # Draw a contour plot.

Draw a new contour plot using the resources you just set.

Line 78:

  #----------- Begin third plot -----------------------------------------

Draw the same contour plot, only this time fill the contours with the default hatching patterns. Also, explicitly define the ranges for the X and Y axes.

Lines 80-83:

  resources.cnFillOn          = True    # Turn on contour line fill.
  resources.cnMonoFillPattern = False   # Turn off using a single fill pattern.
  resources.cnMonoFillColor   = True
  resources.cnMonoLineColor   = True

By default, contour levels are not filled, so to get filled contours you need to set the resource cnFillOn to True. Also by default, when you fill contour levels, they are all filled in a solid color, so you need to set cnMonoFillPattern to False, telling PyNGL to use different fill patterns for each contour level.

A default set of fill patterns is provided dynamically through the cnFillPatterns resource. There are 17 different fill patterns available, each one represented by an integer from 1 to 17 (0 represents solid fill). So, to change the fill patterns, set the cnFillPatterns resource to an integer array with the same number of elements as you have contour levels.

You can see all the available fill patterns in the "Fill patterns" section.

By setting cnMonoFillColor and cnMonoLineColor both to True, you are telling PyNGL to use the same color for all the fills and lines (the default is the foreground color).

Lines 85-88:

  if hasattr(lon,"long_name"):
    resources.tiXAxisString = lon.long_name
  if hasattr(lat,"long_name"):
    resources.tiYAxisString = lat.long_name

Create labels for the X and Y axes in the contour plot, using the attribute long_name defined for both lat and lon.

Lines 89-90:

  resources.sfXArray        = lon[:]
  resources.sfYArray        = lat[:]

By setting the ScalarField resources sfXArray and sfYArray to the 1-dimensional arrays lon and lat, you can explicitly define the range for the X and Y axes.

Line 91:

  resources.pmLabelBarDisplayMode = "Never" # Turn off label bar.

Do not display a label bar.

Line 93:

  plot = Ngl.contour(wks,tempa,resources)   # Draw a contour plot.

Draw the contour plot. Note the new ranges for the X and Y axes, and the new title and X/Y axis labels.

Line 95:

  #---------- Begin fourth plot ------------------------------------------

Draw a contour plot of the Z variable, fill the contour lines in solid colors and add a label bar on the side.

Lines 100-102:

  rlist = Ngl.Resources()
  wkres.wkColorMap = "BlGrYeOrReVi200"
  Ngl.set_values(wks,rlist)

The PyNGL function Ngl.set_values allows you to set values of resources associated with an object after it has been created. In the case here we are assigning a new color map called "BlGrYeOrReVi200" to the workstation where the plots are being drawn.

Lines 104-108:

  resources.cnMonoFillPattern     = True     # Turn solid fill back on.
  resources.cnMonoFillColor       = False    # Use multiple colors.
  resources.cnLineLabelsOn        = False    # Turn off line labels.
  resources.cnInfoLabelOn         = False    # Turn off informational
                                               # label.

To fill the contours with solid fill in different colors, you need to set cnMonoFillPattern back to True, telling Ngl.contour to use solid fills for all contour levels, and cnMonoFillColor to False to get multiple fill colors. The other resources turn off the labeling of contour lines, and the "informational" label you saw at the lower right corner in the previous contour plots.

Lines 109-110:

  resources.pmLabelBarDisplayMode = "Always" # Turn on label bar.
  resources.cnLinesOn             = False    # Turn off contour lines.

There are times when you want to add other graphical objects to a plot, like a label bar, a legend, tick marks, or a title. In PyNGL, there's something called a PlotManager that lets you do this. It's called this because it "manages" the look of these additional objects and tries to make intelligent guesses about where the additional objects should be drawn with respect to the original plot. Also, if you resize the original plot, then these extra objects get resized as well. Some of these objects are always drawn by default, like tick marks and a title (if you specify one). A label bar is not drawn by default, so you need to tell the PlotManager to draw it by setting the PlotManager resource pmLabelBarDisplayMode to the predefined string "Always" (the default is "Never"). PlotManager resources start with "pm", and label bar resources start with "lb". These resources are documented in the Labelbar resource descriptions and "PlotManager" resource descriptions sections.

As noted in example 1, predefined strings are case-insensitive, so the pmLabelBarDisplayMode resource could have also been set using "always" or "ALWAYS" or any another combination of uppercase and lower case characters.

The resource cnLinesOn controls whether contour lines are drawn.

Lines 112-114:

  resources.tiMainFont      = "Helvetica-bold"
  resources.tiXAxisFont     = "Helvetica-bold"
  resources.tiYAxisFont     = "Helvetica-bold"

Change the fonts of the title and the X and Y axis labels. A table of all the available fonts with their index values appears in the "Font table" section. In this case, you are changing the font to "Helvetica-bold."

Lines 116-117:

  if hasattr(Z,"_FillValue"):
    resources.sfMissingValueV = Z._FillValue

Specify the missing value for the scalar field data.

Lines 118-119:

  if hasattr(Z,"long_name"):
    resources.tiMainString = Z.long_name

Set the main title using the long_name attribute of Z.

Line 120:

  plot = Ngl.contour(wks,Z[0,0,:,:],resources)    # Draw a contour plot.

Draw the fourth contour plot with a new dataset. Note that there are some areas in the contour plot that aren't being drawn. This is due to the presence of missing values in the data. By default, if the data being passed to any of the Ngl.* plotting routines contains the attribute "_FillValue," then the user must inform the appropriate object of this value as we have done here with sfMissingValueV.

Line 122:

  #---------- Begin fifth plot ------------------------------------------

Draw a contour plot of the pres variable and fill the contour lines in solid colors using a grayscale color map that you define yourself.

Lines 124-132:

  cmap = numpy.array([[0.00, 0.00, 0.00], [1.00, 1.00, 1.00], \
                      [0.10, 0.10, 0.10], [0.15, 0.15, 0.15], \
                      [0.20, 0.20, 0.20], [0.25, 0.25, 0.25], \
                      [0.30, 0.30, 0.30], [0.35, 0.35, 0.35], \
                      [0.40, 0.40, 0.40], [0.45, 0.45, 0.45], \
                      [0.50, 0.50, 0.50], [0.55, 0.55, 0.55], \
                      [0.60, 0.60, 0.60], [0.65, 0.65, 0.65], \
                      [0.70, 0.70, 0.70], [0.75, 0.75, 0.75], \
                      [0.80, 0.80, 0.80], [0.85, 0.85, 0.85]],'f')

For this plot, use grayscale values to fill the contour levels. To do this, you need to define your own color map. Color maps are represented by NumPy arrays of red, green, and blue float values (referred to as RGB values) ranging from 0. to 1. (to indicate the intensity of that particular color). The first entry in a color map is the background color, and the second entry is the foreground color. To get a color map of grayscale values, use equal values for R, G, and B.

For more information on creating your own color map, see the section on "Color maps".

Lines 134-135:

  rlist.wkColorMap = cmap
  Ngl.set_values(wks,rlist)

Again, we're using the PyNGL function Ngl.set_values to change the value of the color map, this time to a color map we've defined ourselves.

Lines 140-141:

  if hasattr(pres,"long_name"):
    resources.tiMainString = pres.long_name

Change the title to reflect the new data you are contouring.

Line 146:

  presa = pres[0,:,:]

Read the data for the pressure field pres into a NumPy array.

Lines 147-151:

  if hasattr(pres,"_FillValue"):
    presa = (0.01*presa*numpy.not_equal(presa,pres._FillValue)) +   \
          pres._FillValue*numpy.equal(presa,pres._FillValue)
  else:
    presa = 0.01*presa

These calculations convert the presa array from to millibars. If pres has a _FillValue set, then we have to make sure that those values are not converted; if pres does not have a _FillValue set, then the conversion can be done without care for missing values.

Line 153:

  plot = Ngl.contour(wks,presa,resources)  # Draw a contour plot.

Draw the contour plot of the pressure variable.

Lines 155-164:

  print "\nSubset [2:6,7:10] of temp array:" # Print subset of "temp" variable.
  print tempa[2:6,7:10]
  print "\nDimensions of temp array:"        # Print dimension names of T.
  print temp.dimensions
  print "\nThe long_name attribute of T:"    # Print the long_name attribute of T.
  print temp.long_name 
  print "\nThe nlat data:"                   # Print the lat data.
  print lat[:]           
  print "\nThe nlon data:"                   # Print the lon data.
  print lon[:]           

Print some various properties of the netCDF variables.

Lines 169-173:

  os.system("/bin/rm -f data.asc")
  sys.stdout = open("data.asc","w")
  for i in range(7,2,-2):
    for j in range(0,5):
      print "%9.5f" % (tempa[i,j])

Write a subsection of tempa to an ASCII file.

Line 181:

  Ngl.end()

Required to end the script.