Going beyond the basics |
Note:In what follows, whenever "NCL procedures" is used, it should be understood to mean "NCL procedures and functions."
An NCL wrapper function intercepts an NCL procedure call, checks the arguments in that call, does the appropriate type conversions, and then calls the external Fortran or C procedure. NCL wrapper functions play a central role in incorporating external procedures into NCL.
There are many possibilities for designing NCL procedures that improve upon the equivalent Fortran procedure. To do this requires that you write a custom NCL wrapper function, instead of using wrapit77. For example, if a Fortran subroutine takes an adjustable array and its dimension as arguments, then the dimension argument can be removed from the NCL procedure, since NCL can determine that information. How to write custom NCL wrappers is described in the "Extending the NCL function and procedure set" section of the NCL Reference Manual. Writing custom NCL wrappers for Fortran codes will ultimately require your calling the existing Fortran code from the custom NCL wrapper. Writing custom wrappers is required for C functions.
C NCLFORTSTARTat the start and
C NCLENDat the end. These comment lines are known as wrapit interface block delimiters. Any blanks in the above two lines are ignored and the lines are case-insensitive.
The following is an example of a wrapit interface block for a Fortran function that finds the arc length of a polyline:
C NCLFORTSTART FUNCTION ARCLN(NUMPNT, POINTX, POINTY) DIMENSION POINTX(NUMPNT), POINTY(NUMPNT) C NCLENDThe statements between wrapit interface block delimiters should contain only a procedure statement and non-executable declarations describing its arguments. Any other statements (except comment statements) in a wrapit interface block will cause wrapit77 to exit with an error. Unless specifically typed, all variables are typed according to Fortran default typing.
Fortran 90 codes can use wrapit77 to generate an NCL wrapper function if you can provide a wrapit interface block for it, i.e. provided that you can specify the Fortran 90 procedure interface with Fortran 77 syntax. In some cases this will not be possible, for example if you are using pointers or structures as arguments, if the Fortran 90 procedure can have missing arguments, if the procedure is recursive, or if you are using keyword arguments. Many times, however, it is easy to convert Fortran 90 syntax to Fortran 77. For example, the Fortran 90 statements:
CHARACTER (LEN=*)::NAME REAL,INTENT(IN)::NUMBERcould be specified as
CHARACTER*(*) NAME REAL NUMBERin the wrapit interface block.
Note: wrapit77 scans its input file for wrapit interface blocks, so if you can easily arrange your Fortran codes so that the wrapit interface blocks are embedded in them, it may be more convenient for you to use your original Fortran source file as input to wrapit77, rather than use a separate file containing only wrapit interface blocks. See example 2 in this section for details on this.
For example, if your input wrapit interface block is in file wrapper_input, then executing
wrapit77 < wrapper_input >! wrapper_W.cwill create the NCL wrapper function wrapper_W.c. See the examples in this section for sample uses of wrapit77. You can name your NCL wrapper function anything you want, but the usual convention is to append the "_W" followed by ".c" (since the wrapper function is C code).
nhlcc -c wrapper_W.cto create wrapper_W.o. For consistency with the use of nhlcc, you may want to use the command nhlf77 to create the ".o" file for your Fortran source, but any way is fine.
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -shared -o fcode.so fcode_W.o fcode.o
nhlcc +z -c fcode_W.c nhlf77 +z -c fcode.f ld -b -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -64 -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -n32 -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -o fcode.so fcode_W.o fcode.o -G
Once you have created your dynamic shared object containing the NCL wrapper function and the object for the original code, there are three ways to tell NCL where to look for the dynamic shared object:
In this case, you insert a statement of the form:
external SO_NAME "path_name"at the beginning of your NCL script. In this statement SO_NAME indicates the name you want the dynamic shared object to be known by in the NCL program and path_name is a relative or full path name of the dynamic shared object file you created. The final argument to the external statement is an NCL string variable, so the quotes are required. The case where path_name is simply the name of the dynamic shared object itself (and not a path name) is special and is treated in the "Accessing dynamic shared objects using LD_LIBRARY_PATH" section.
Once the dynamic shared object has been accessed and named, you can call your new NCL function or procedure with an NCL statement of the form:
SO_NAME::fortran_name(arguments)where SO_NAME is the name that you gave the dynamic shared object in your NCL external statement and fortran_name is the name of the Fortran function or subroutine that you specified in your wrapit interface block. fortran_name must be entered as either all uppercase, or all lowercase, regardless of what case the original Fortran procedure name was. Using an NCL name that is all uppercase or lowercase will produce the same results. For example, if your original Fortran procedure was named "CalcX", then you can use either "CALCX" or "calcx" to invoke that function from NCL, but you cannot use "CalcX" or any other mixed-case form of the name. Most NCL programmers tend to use lowercase for procedure names. See the examples in this section for complete examples of using dynamic shared objects and calling your new NCL procedures. Accessing dynamic shared objects using LD_LIBRARY_PATH
In this case, you insert a statement of the form:
external SO_NAME "shared_object_name"at the beginning of your NCL script. In this statement, shared_object_name is the name of your dynamic shared object file. NCL will look in the directories listed in the environment variable LD_LIBRARY_PATH for the specified dynamic shared object. If it does not find it there, an error will be reported. LD_LIBRARY_PATH is a system environment variable and is a colon-separated list of directory pathnames. In attempting to set this environment variable, make sure that it is not already set for use by other applications; if it is, just add the path name of the dynamic shared object to it.
Once NCL has located the dynamic shared object, you can invoke the function or procedure in the same way that was discussed above under "Accessing dynamic shared objects using an external statement." Accessing dynamic shared objects using NCL_DEF_LIB_DIR
If the NCL environment variable NCL_DEF_LIB_DIR is defined and is a valid directory pathname, NCL will look in that directory first for dynamic shared objects (you can have as many dynamic shared objects in this directory as you want). Any entry in any dynamic shared object in the directory specified by NCL_DEF_LIB_DIR will be loaded automatically as an NCL built-in. In particular this means that, in order to invoke your new function or procedure, you should not prepend SO_NAME:: to the function or procedure name as was required in either of the two other methods for accessing dynamic shared objects discussed above. NCL will try to load everything in the directory specified by NCL_DEF_LIB_DIR, so you will get warning messages about files in the directory that are not shared objects.
This approach can be more convenient than the other two approaches, but it lacks the advantage of having the SO_NAME:: prepended to externally defined procedures which makes their externally defined status quickly recognized from the syntax.
The environment variables NCL_DEF_LIB_DIR and LD_LIBRARY_PATH are independent. The dynamic shared objects in the directory specified by NCL_DEF_LIB_DIR are loaded before any dynamic shared object specified on an external statement, whether that dynamic shared object is specified by way of a relative or direct path name or by way of a directory specified using the LD_LIBRARY_PATH environment variable.
After you have generated a custom NCL wrapper for your C code, you should proceed as described in Steps 3-5 as presented for Fortran codes above.
SUBROUTINE CQUAD (A, B, C, NQ, X, QUAD) REAL X(NQ),QUAD(NQ) C C Calculate quadratic polynomial values. C DO 10 I=1,NQ QUAD(I) = A*X(I)**2 + B*X(I) + C 10 CONTINUE C RETURN END FUNCTION ARCLN (NUMPNT, POINTX, POINTY) DIMENSION POINTX(NUMPNT),POINTY(NUMPNT) C C Calculate arc lengths. C IF (NUMPNT .LT. 2) THEN PRINT *, 'ARCLN: Number of points must be at least 2' STOP ENDIF ARCLN = 0. DO 10 I=2,NUMPNT PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 + + (POINTY(I)-POINTY(I-1))**2) ARCLN = ARCLN + PDIST 10 CONTINUE C RETURN ENDThis first example follows in detail the five-step process described above.
Step 1 - define the wrapit interface block.
Create a file ex01.wib that contains the following two wrapit interface blocks:
C NCLFORTSTART SUBROUTINE CQUAD (A,B,C,NQ,X,QUAD) REAL X(NQ),QUAD(NQ) C NCLEND C NCLFORTSTART FUNCTION ARCLN (NUMPNT,POINTX,POINTY) DIMENSION POINTX(NUMPNT),POINTY(NUMPNT) C NCLENDStep 2 - create an NCL wrapper function.
Execute the following:
wrapit77 < ex01.wib >! ex01_W.cto create the NCL wrapper function ex01_W.c.
Step 3 - create the object modules (the ".o" files).
Execute the following:
nhlcc -c ex01_W.c nhlf77 -c ex01.fto create ex01_W.o and ex01.o (on an HP system you will need to add a "+z" flag to each of the above commands).
Step 4 - create a dynamic shared object module.
For this, use the appropriate ld command listed in the section above discussing this. For example, if you are running on a Sun Solaris machine, then you would execute:
ld -o ex01.so ex01_W.o ex01.o -Gto create the dynamic shared object ex01.so.
Step 5 - tell NCL where your dynamic shared object is.
The external statement in the following example script tells NCL where to look for the dynamic shared object you just created.
external EX01 "./ex01.so" begin ; ; calculate three values of a quadratic equation ; nump = 3 x = (/ -1., 0.0, 1.0 /) qval = new(nump,float) EX01::cquad(-1, 2, 3, nump, x, qval) ; call the new NCL version of ; your original Fortran subroutine print("Polynomial value = " + qval) ; ; calculate an arc length. ; xc = (/ 0., 1., 2. /) yc = (/ 0., 1., 0. /) arclen = EX01::arcln(nump,xc,yc) ; call the new NCL version of ; your original Fortran function print("Arc length = " + arclen) endIf you submit the above script to the NCL interpreter, it produces the output:
opening: ./ex01.so (0) Polynomial value = 0 (1) Polynomial value = 3 (2) Polynomial value = 4 (0) Arc length = 2.82843The numbers in parentheses at the left in the above printout are an artifact of how the NCL print function works and are of no relevance in this example.
C NCLFORTSTART SUBROUTINE CQUAD (A,B,C,NQ,X,QUAD) REAL X(NQ),QUAD(NQ) C NCLEND C C Calculate quadratic polynomial values. C DO 10 I=1,NQ QUAD(I) = A*X(I)**2 + B*X(I) + C 10 CONTINUE C RETURN END C NCLFORTSTART FUNCTION ARCLN (NUMPNT, POINTX, POINTY) DIMENSION POINTX(NUMPNT),POINTY(NUMPNT) C NCLEND C C Calculate arc lengths. C IF (NUMPNT .LT. 2) THEN PRINT *, 'ARCLN: Number of points must be at least 2' STOP ENDIF ARCLN = 0. DO 10 I=2,NUMPNT PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 + + (POINTY(I)-POINTY(I-1))**2) ARCLN = ARCLN + PDIST 10 CONTINUE C RETURN ENDIn this example, the wrapit interface blocks are embedded directly into the Fortran code, thus avoiding the need to create a separate file containing them. All that wrapit77 is looking for in its input is blocks delimited by comment lines containing NCLFORTSTART and NCLEND.
Now execute:
wrapit77 < ex02.f >! ex02_W.cand proceed as in steps 3-5 of Example 1.
SUBROUTINE LIBSUB(ARG1)from a commercial library named commercial.a. Using the following wrapit interface block (in file libsub.wib):
C NCLFORTSTART SUBROUTINE LIBSUB(ARG1) C NCLENDCreate an NCL wrapper function and its object module by executing:
wrapit77 < libsub.wib > libsub_W.c nhlcc -c libsub_W.cTo create a dynamic shared object named libsub.so, use the appropriate ld command by loading libsub_W.o and commercial.a. For example, if you are running on a Sun Solaris machine, you would execute:
ld -o libsub.so libsub_W.o commercial.a -Gto create the dynamic shared object libsub.so.
SUBROUTINE EX04 (STRIN,STROUT) CHARACTER*(*) STRIN CHARACTER*26 ABET,STROUT DATA ABET/'ABCDEFGHIJKLMNOPQRSTUVWXYZ'/ C IMX = MIN(LEN(STRIN),26) STROUT = ABET(1:IMX) C RETURN ENDYou could use embedded wrapit interface blocks, as in example 2 above, or use the following wrapit interface block:
C NCLFORTSTART SUBROUTINE EX04 (STRIN,STROUT) CHARACTER*(*) STRIN CHARACTER*26 STROUT C NCLENDto create the NCL wrapper and dynamic shared object (named ex04.so). Passing the following NCL script to the NCL interpreter:
external EXAMPLE04_SO "./ex04.so" begin cstr = new(26,character) ; create a character array of length 26 EXAMPLE04_SO::ex04("fifteen letters",cstr) str = chartostring(cstr) print(str) endproduces the output:
opening: ./ex04.so Variable: str Type: string Total Size: 4 bytes 1 values Number of Dimensions: 1 Dimensions and sizes: [1] Coordinates: (0) ABCDEFGHIJKLMNO
SUBROUTINE EX05(M,N,X,Y,FXY) REAL X(M),Y(N),FXY(M,N) C C Calculate FXY(I,J) = 2*I+J C DO 10 J=1,N DO 20 I=1,M FXY(I,J) = 2.*REAL(I) + REAL(J) 20 CONTINUE 10 CONTINUE C RETURN END SUBROUTINE PRT2D(M,N,A) REAL A(M,N) C C Print the array A by rows using an F6.1 format with C 7 values per line. C DO 10 J=1,N PRINT *,'Row',J,':' DO 20 I=1,M/7 WRITE(6,500) (A(LL,J),LL=(I-1)*7+1,I*7) 500 FORMAT(7F6.1) 20 CONTINUE IF (MOD(M,7) .NE. 0) WRITE(6,500) (A(LL,J),LL=(M/7)*7+1,M) PRINT *,' ' 10 CONTINUE C RETURN ENDUse the following wrapit interface block:
C NCLFORTSTART SUBROUTINE EX05(M,N,X,Y,FXY) REAL X(M),Y(N),FXY(M,N) C NCLEND C NCLFORTSTART SUBROUTINE PRT2D(M,N,A) REAL A(M,N) C NCLENDto create the NCL wrapper function and the dynamic shared object (named ex05.so). Then the following NCL script:
external EX05 "./ex05.so" begin ; ; calculate three values of a quadratic equation ; m = 11 n = 3 x = new(m,float) y = new(n,float) fxy = new((/n,m/),float) EX05::ex05(m,n,x,y,fxy) EX05::prt2d(m,n,fxy) endwill create the 2-dimensional array fxy in a manner compatible with other NCL procedures. Passing the above NCL script to the NCL interpreter produces the output:
opening: ./ex05.so Row 1: 3.0 5.0 7.0 9.0 11.0 13.0 15.0 17.0 19.0 21.0 23.0 Row 2: 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0 22.0 24.0 Row 3: 5.0 7.0 9.0 11.0 13.0 15.0 17.0 19.0 21.0 23.0 25.0
An object is something that contains attributes and methods. The attributes describe the object, and the methods describe operations that can be performed on the object.
An object can be something concrete. For example, you can consider a car an object, and its attributes might be its color, its make, the year made, whether it's a 2-door or 4-door, etc. A method of the car object might be to describe how to move it from point A to point B.
An object can also be something more abstract. For example, in NCL a contour plot is considered an object, and its attributes (or what we call resources) are things like the 2-dimensional data to be contoured, the number of contour levels, the thickness of the contour lines, the fill patterns used for each contour level, the colors used for the contour lines, etc. One method of the contour plot object describes how to draw it to a workstation.
OO definition: class and instance
Since you will most likely have different kinds of cars (or contour plots), it is useful to have a template that describes what attributes and methods a certain object can have. This template is known as a class, and once you create an object from this template or class, it is called an instance of that class.
For example, going back to the car object, a class might be the specifications that a customer can choose for buying a new car. This would include some of the attributes listed earlier: the car's color, its make, whether it's 2-door or 4-door, and whether it has automatic or manual transmission. Once you decide on a 4-door white Honda Accord with automatic transmission, you call this object an "instance of the car class".
NCL definition: ContourPlot class
In NCL, the class for defining the attributes and methods for a contour plot object is known as a ContourPlot class. This class defines almost 200 attributes (resources) whose values you can set to create a particular contour plot object (an instance of the ContourPlot class). There are many other classes defined in NCL, and most of them are covered later.
OO definition: subclass, superclass, and inheritance
It is possible to have a class B that is a special case of another class A. In this case, class B is called a subclass of A, and A is called a superclass of B. The superclass passes its attributes and methods to its subclass in a concept known as inheritance. It is possible for a superclass to have multiple subclasses, and these subclasses in turn, can have subclasses.
For example, you could have a general "vehicle class" (a superclass) that defines a template for the different types of vehicles it can have, like a "truck class" and a "car class" (subclasses). A vehicle class might define things that can be specified for both cars and trucks, like their body color, their make, and whether they have manual or automatic transmission. The "truck class," in turn, would define a template for things that are specific to trucks, like the bed length and whether it has a cap, and the "car class" would define a template for things specific to cars, like whether or not it's a hatchback. The truck and car classes inherit the attributes from the vehicle class.
In NCL, the ContourPlot class is a subclass of the View class. The View class contains classes that can be "viewed;" that is, that can be drawn to a workstation. Other subclasses of the View class include (but are not limited to) the XyPlot class, the VectorPlot class, the MapPlot class, and the StreamlinePlot class. These classes are mentioned in the examples in the "Learning NCL by example" chapter in the context of defining their resources. If you create an instance of one of these classes, then this object inherits the View class' resources, and you can set these resources when you set the object's specific resources.
Note: The subclasses listed above are not direct subclasses of the View class. For example, the ContourPlot class is a subclass of the DataComm class, which is a subclass of the Transform class, which is a subclass of the View class. It is possible to have several levels of subclasses, but for now, you don't need to worry about these other subclasses.
The View class defines resources that are common to all drawable classes, like where to draw instances of these classes in the viewport and what their width and height should be. You should already be familiar with some of these resources (vpXF, vpYF, vpWidthF, and vpHeightF) as they are used in many of the examples (see example 5). The subclasses of the View class, in turn, define resources that are specific to those classes. For example, cnFillOn is a resource specific to the ContourPlot class indicating whether or not contours should be filled, and xyLineColor is a resource specific to the XyPlot class indicating the color of the curves. Many of these specific resources appear in the examples as well.
OO definition: composite class
Classes that contain references to and manage other classes are called composite classes. For a composite class to "manage" another class means that it can send messages to the class it contains. For a class to contain another class means that it has access to the attributes and methods of the class it contains. Composite classes can contain other composite classes, and this nesting of composite classes can go to any level.
For example, in the car class described above, it could have an attribute for a stereo system. The stereo system could be considered an object in itself, whose attributes would be the brand, the number of speakers, whether it contains a CD player, etc. So, it makes sense to have a "stereo system class" that defines the attributes of a stereo system, and have it contained and managed by the car class (making the car class a composite class). The car class would "manage" where a stereo system object would be installed in a car object, which would vary depending on what kind of car object was created. The car class may contain other classes as well (like an "engine class"). Since the car class contains the stereo system class, this means that when a car object is created, you can specify the attributes of the stereo system object at the same time.
Note: It is important to understand the difference between composite classes and super/subclasses. If class A is a superclass of class B (making B a subclass of A), then B inherits A's attributes and methods. A does not manage B or send messages to it. If A contains B, however, then A is a composite class and A manages B by sending messages to it (and B is known as a composite member of A). Also, A has access to B's attributes and methods, but B does not have access to A's attributes and methods.
NCL definition: PlotManager class
In NCL, the ContourPlot, XyPlot, MapPlot, VectorPlot, and StreamlinePlot classes all contain the PlotManager class (in addition to other classes), so each one is considered a composite class. The PlotManger class contains the LabelBar class, the Legend class, the TickMark class, and the Title class, so it is a composite class as well.
The PlotManager class is a special class for "managing" the classes it contains on behalf of the classes that contain it. For example, whenever you create a ContourPlot object, tick marks are drawn automatically. This is because the PlotManager is managing the TickMark object by controlling where the tick marks and tick mark labels are drawn on the ContourPlot object. If the ContourPlot object is resized or repositioned, then the PlotManager resizes and/or repositions the TickMark object accordingly.
The PlotManager and its resources are discussed in more detail in example 2 when it is used to draw a LabelBar object with a ContourPlot object, and in example 8 when it is used to draw a Legend object with an XyPlot object.
Note: It is possible to independently create your own LabelBar object and other objects that are normally part of the PlotManager class. If you do this, however, then you need to manage these objects yourself, and this can be tedious if you reposition or resize your plot.
NCL definition: DataItem class
To create a ContourPlot object, you need to provide it with a data field to contour. Data fields have their own attributes, such as coordinate arrays, minimum and maximum values, and a missing data value, to name a few. It makes sense, then, to create a data field class from which data field objects can be created. The classes XyPlot, VectorPlot, and StreamlinePlot also require data fields, but of different types, so NCL defines a general DataItem class that has four subclasses to define the specific types of data field objects:
OO definition: class hierarchy
As noted in the definition for subclasses and superclasses, it is possible to have several levels of classes. These multi-levels form what's called a class hierarchy. A class hierarchy diagram is a useful way to understand how all of your classes relate to each other. Though they might seem confusing now, you might want to view the various class hierarchy diagrams for NCL classes at the URL:
http://ngwww.ucar.edu/ngdoc/ng/ug/hlu/classes/classcharts.htmlNCL definition: Workstation class
As noted in the View class definition, the View class defines classes that can be drawn to a workstation (an X11 window, a PostScript file, or a metafile). Workstations have attributes of their own, like a color map which is an attribute common to all three types of workstations. They also have attributes specific to each type of workstation, like a file name (PostScript and metafile workstations only), an X color allocation scheme (X11 window workstation only), and orientation of the plot (Postscript workstation only). NCL defines a Workstation class that has three subclasses: an XWorkstation class, an NcgmWorkstation class, and a PSWorkstation class. The Workstation class passes its attributes to its subclasses, which means you can set the color map (and other Workstation resources) when you create an XWorkstation, NcgmWorkstation, or PSWorkstation object. Creating an object from one of these three classes provides you with a workstation in which to draw your View objects.
The App class ("App" is just a shortened name for "Application") is a special class in NCL for managing resource files. An App object only needs to be created if you plan to have a resource file. If you don't create an App object, then one is created for you. The App object is discussed in more detail in the next section "Putting it all together."
NCL definition: Child/parent relationship between NCL objects
In addition to classes having relationships with each other via subclasses, superclasses, and composite classes, there's a child/parent relationship between objects once they are created. For example, if object B is a child of object A, then the following conditions apply:
App_obj_name.parent_of_A.name_of_A.name_of_B.resource_of_B : resource_valueThere is more information about setting resources in the section "Resource files".
Note: Do not confuse the child/parent relationship between objects with the subclass/superclass relationship between classes. They are independent concepts.
The six basics steps for creating a plot are:
obj_id = create "obj__name" class_name parent_id "resource_name1" : resource_value1 "resource_name2" : resource_value2 . . . end create
http://ngwww.ucar.edu/ngdoc/ng/ref/hlu/HluClasses.html
The name you use for class_name is the name you see in this list, with the first letter lowercase, followed by the word "Class". For example, if you want to create a ContourPlot object, then you use "contourPlotClass" as the class name. Please note that not all of these objects are creatable! In the URL given above for the class hierarchy diagram, the creatable objects are indicated in yellow.
"resource_name1" : resource_value1 "resource_name2" : resource_value2 . . .from the create block statement above indicate a list of resources (attributes) that you can set for the object you are creating. Each resource name must be enclosed in double quotes, followed by a colon, followed by the resource value. This style of setting resources is identical to how you set resources in a setvalues block statement, which is covered in examples 9 and 11. Resources within an object creation call can be set in any order. There is more information about setting resources in the section "Resource files".
As noted in example 1, there are hundreds of resources you can set for various objects. Most of these resources have default values that are either hard-coded or set dynamically when you run NCL on one of your scripts. Some resources do not have default values, like those resources for setting data fields in a DataItem object, or for referencing a DataItem object from one of the View objects. You only need to set a resource value if you want to change its value from the default, or if it doesn't have a default value.
Note: There may be cases where you don't need to set any resources when you create an object, which means that your create call will look like this:
obj_id = create "obj__name" class_name parent_id end create
begin x = new(9,float) ; Define two 1D arrays of 9 elements each. y = new(9,float) x = (/10.,20.,30.,40.,50.,60.,70.,80.,90./) y = (/0.,0.71,1.,0.7,0.002,-0.71,-1.,-0.71,-0.003/) ; ; Create a Workstation object to draw the XY plot to. Note ; that no resources are being set here (the default values are ; acceptable in this case). The parent is being set to "defaultapp" ; since no App object was created. ; wks_object = create "x11" xWorkstationClass defaultapp end create ; ; Create a CoordArrays (DataItem) object. Set two resources for ; defining the X and Y arrays whose points you want to plot. ; "caXArray" and "caYArray" are examples of resources that don't ; have default values, so at least one of them must be set when a ; CoordArrays object is created. If only one of these resources ; is set, then the one that isn't set defaults to integer ; values from 0 to n-1, where "n" is the number of points in the ; one that is set. ; data_object = create "data" coordArraysClass defaultapp "caXArray" : x "caYArray" : y end create ; ; Create an XyPlot plot object. Reference the CoordArrays object ; created above (data_object) through the XyPlot resource "xyCoordData". ; Note that the parent is being set to the Workstation ; object identifier (wks_object). ; plot_object = create "plot" xyPlotClass wks_object "xyCoordData" : data_object end create ; ; Draw the XyPlot object (represented by the identifier ; "plot_object"). Since "wks_object" is the parent of ; "plot_object" in the above create call, "draw" draws ; the plot to this workstation. ; draw(plot_object) ; ; Advance the frame of the Workstation object (object ; identifier "wks_object"). ; frame(wks_object) end
begin ; ; Open a netCDF file from which to read data. ; cdf_file = addfile("$NCARG_ROOT/lib/ncarg/data/cdf/contour.cdf","r") Z = cdf_file->Z(0,0,:,:) ; Read geopotential height, latitude, and lat = cdf_file->lat ; longitude values and store to local NCL lon = cdf_file->lon ; variables "Z", "lat", and "lon". ; ; Create an X11 workstation, using all of the default resources. ; xwks = create "x11" xWorkstationClass defaultapp end create ; ; Create a ScalarField data object to be referenced by the ContourPlot ; object created later. When a ScalarField object is created, the ; one resource that must be set is "sfDataArray". ; If "sfXArray" or "sfYArray" are not set, they default to integer ; values from 0 to m-1 and 0 to n-1, where m and n are the dimensions ; of the scalar field. ; ; "sfMissingValueV" is another resource that has no default value, ; and it only needs to be set if the data have missing values (as these ; data do). ; dataid = create "data" scalarFieldClass defaultapp "sfDataArray" : Z ; The 2D field to contour. "sfXArray" : lon ; The X coordinate array. "sfYArray" : lat ; The Y coordinate array. "sfMissingValueV" : Z@_FillValue ; The missing value value. end create ; ; Create a ContourPlot object. Reference the ScalarField object ; created above (dataid) through the ContourPlot resource ; "cnScalarFieldData". ; ; There are several other contour resources being set here. Notice also ; that the PlotManager resource "pmLabelBarDisplayMode" is being set to ; "Always" to indicate that it should turn on the drawing of a label ; bar. The PlotManager "manages" this label bar by controlling ; where it should be drawn with respect to the contour plot. It also ; matches the colors in the label bar boxes with the colors in the ; filled contours, and labels them according to the data in the contour ; plot. ; ; Because the ContourPlot class is a composite class that contains the ; PlotManager class, which in turn contains the LabelBar and Title ; classes, then by definition you can set PlotManager, LabelBar, and ; Title resources when you create a ContourPlot object. ; cnid = create "contourplot" contourPlotClass xwks "cnScalarFieldData" : dataid ; Reference to the data object. "cnFillOn" : True ; Turn on contour level fill. "cnMonoFillColor" : False ; Use multiple colors. "cnLineLabelsOn" : False ; Turn off line labels. "cnInfoLabelOn" : False ; Turn off informational label. "cnLinesOn" : False ; Turn off contour lines. "pmLabelBarDisplayMode" : "Always" ; Turn on label bar. "lbPerimOn" : False ; Turn off perimeter on label bar. "tiXAxisString" : lon@long_name ; X axis title "tiYAxisString" : lat@long_name ; Y axis title "tiMainString" : Z@long_name ; Main title "tiMainFont" : 26 ; Main title font "tiXAxisFont" : 26 ; X axis title font "tiYAxisFont" : 26 ; Y axis title font end create ; ; Draw the ContourPlot object. Note that the contours are filled in ; color, even though no color map has been specified. This is ; because NCL provides a default color map. ; draw(cnid) ; ; Advance the frame of the Workstation object. ; frame(xwks) endFor more examples of NCL scripts that use straight NCL code, see the appendix.
If you need more control of these objects, then you always have the option of creating these objects directly (rather than turning them on via the PlotManager) and managing them yourself.
For example, to make the curve color yellow in NCL object example 1 above, you would add the following code before the draw(plot_object) line:
getvalues plot_object ; Retrieve the DataSpec object. "xyCoordDataSpec" : dspec end getvalues setvalues dspec "xyLineColor" : 5 ; Index 5 is yellow in the default color table. end setvaluesFor some more advanced examples on how the DataSpec object works, see XyPlot examples 6, 16, and 17 of the Quick Start Guide.
For example, in the "Learning NCL by example" chapter, example 9 has a resource file associated with it called "gsun09n.res" that contains the lines:
*vpXF : 0.05 *cnFillOn : True *mpProjection : StereographicThese resources change the X position of the plot in the viewport, turn contour fill on, and change the map projection to "Stereographic."
Resource files are loaded only once by the NCL interpreter, when NCL is initially executed. Any resources you set in the resource file that are misspelled or don't apply to the kind of plots you are creating are ignored. No error messages are printed, so you must be careful to spell the resource names correctly.
Note: Any resources set in a resource file can be overridden by setting them in your NCL script.
*Font : name_of_fontallows you to change all of the fonts in your plot to a particular font. There is currently no way to change all the fonts in your plot with one line inside an NCL script.
! This is a comment. *tiMainFont : 25 *tiMainString : This is a title *mpFillOn : True *cnLevelSpacingF : 0.10the first line is a comment, and the next four lines set the resources for changing the title font to 25 ("times-roman"), creating a title, turning map fill on, and setting the contour level spacing to 0.10. Note that the string "This is a title" does not require double quotes around it as it would in an NCL script. If you put quotes around the string, then the quotes will appear as part of the title.
To set a scalar resource, succeed the ":" character with the value of the resource as a float, integer, string, or logical. You've already seen how to do this in the example above:
*tiMainFont : 25 *tiMainString : This is a title *cnLevelSpacingF : 0.10 *mpFillOn : TrueThe first resource is being set with the integer "25", the second resource with the string "This is a title", the third resource with the float "0.10", and the fourth resource with the logical "True".
The number "25" that is being used to set the font is an enumerated value (an integer that represents a predefined string). When used with a font resource, the number "25" is an index into a font table and represents the font "times-roman". This resource could also be set with:
*tiMainFont : times-romanAgain, note that there are no quotes around "times-roman". If you had put quotes around this resource:
*tiMainFont : "times-roman"then you would have gotten an error message that looks like the following:
fatal:NhlCvtStringToEnum: Unable to convert string ""times-roman"" to requested typeLogical values can be set with the strings True or False, or the integers 0 or 1. Both True and False must be capitalized and must not have quotes around them.
To set a resource that takes a multi-dimensional array as its value, use "(/" and "/)" around the array values in the same manner that you would in an NCL script.
For example, to set the resource xyExplicitLabels to a 1-dimensional string array, do the following:
*xyExplicitLabels: (/curve 1, curve 2, curve 3, curve 4/)To set the resource wkColorMap to a 15 x 3 float array, do the following:
*wkColorMap: (/(/ 0.00, 0.00, 0.00 /),(/ 0.66, 0.66, 0.66 /),\ (/ 0.40, 0.40, 0.40 /),(/ 0.00, 1.00, 1.00 /),\ (/ 0.20, 0.56, 0.80 /),(/ 0.00, 0.00, 1.00 /),\ (/ 0.50, 0.00, 1.00 /),(/ 1.00, 0.00, 1.00 /),\ (/ 0.14, 0.56, 0.14 /),(/ 0.00, 1.00, 0.00 /),\ (/ 1.00, 1.00, 0.00 /),(/ 0.86, 0.58, 0.44 /),\ (/ 0.65, 0.16, 0.16 /),(/ 1.00, 0.50, 0.00 /),\ (/ 1.00, 0.00, 0.00 /)/)Note that the "\" character is used as a continuation line in a resource file.
*cnFillOn : Truecauses contour fill to be turned on for all contour plots in an NCL script to which the resource file is applied, unless cnFillOn is set to False for a particular contour plot within the NCL script. Resources set in an NCL script override resources set in an NCL resource file.
You can force resources in a resource file to only apply to certain plots by replacing the wildcard asterisk character with "qualifiers". Whenever you call a GSUN function that creates a plot, a name is given internally to that plot (in addition to the function returning a reference to the plot), and this name can be used as a qualifier in a resource file. The name given to a plot created by a call to a GSUN function is the second string passed to gsn_open_wks, followed by an underscore ("_"), followed by a string indicating type of plot being created.
For example, if you create a contour plot and a vector plot with the following calls:
. . . wks = gsn_open_wks("x11","myplot") . . . contour = gsn_contour(wks,cdata,cnresources) vector = gsn_vector(wks,vdata,vcresources) . . .then the function gsn_contour assigns the name "myplot_contour" to the contour plot, and the function gsn_vector assigns the name "myplot_vector" to the vector plot. If you call a GSUN function that creates two plots, like gsn_contour_map, then this function assigns the name "xxx_contour" to the contour plot and "xxx_map" to the map plot, where xxx is the second string passed to gsn_open_wks.
For example, in the following two lines in the resource file for example 9:
*gsun09n_contour.pmLabelBarDisplayMode : Always *gsun09n_map.pmLabelBarDisplayMode : Neverthe strings "gsun09n_contour" and "gsun09n_map" are internal names assigned by gsn_contour_map to the contour and map plots. These strings are used as qualifiers to force a label bar to be drawn for the contour plot and not for the map plot. If you had just set:
*pmLabelBarDisplayMode : Alwaysthen you would have gotten a label bar with both the map plot and the contour plot.
You can fully qualify a resource to the point where there are no wildcard characters. Here's a basic template for what a fully-qualified resource name would look like in a resource file:
application_name.workstation_name.plot_name.resource_name : resource_valueIn the GSUN suite of functions, application_name is the second string passed to gsn_open_wks, and workstation_name is the same string with "_x11", "_ps", or "_ncgm" appended, depending on what kind of workstation was opened in the call to gsn_open_wks.
If you are using create to create your application, workstation, and plot objects as described in the section "NCL and object-oriented programming", then the qualifying names are whatever names you give your objects (the string after the create keyword).
Any place where you don't want to use one of the qualifying names, you can just replace it with a wildcard character. For example, let's assume you opened two workstations with the following lines:
xwks = gsn_open_wks("x11","example") pswks = gsn_open_wks("ps","example")Let's further assume you want to draw a contour plot, but that you only want to have a title drawn with it in the PostScript file. Then, you would have an entry in your resource file that looks like this:
*example_ps*tiMainString : This is a titleThe string "example_ps" is the name given to the PostScript workstation by the call to gsn_open_wks. Note that a wildcard character appears between this qualifier and the resource name. This is because normally the qualifier plot_name would appear after the workstation name, but since you don't need it in this case, you can replace it with a wildcard character.
The fully qualified path of the above resource would be:
example.example_ps.example_contour.tiMainString : This is a titleThe documentation for the GSUN functions and procedures contains information about the internal names given to the various plots, workstations, and data objects.
It is important to note that this internal naming scheme of the GSUN functions and procedures is somewhat limiting. For example, if you create two separate contour plots with a call to gsn_contour and they are both being drawn to the same workstation, then they will both have the same internal name. This means that if you want to set the same resource for both of them, but with different values, you can't do it from a resource file because there is no way to distinguish the two contour plots from each other. Instead, you have to set the resource for both plots in the NCL script.
For more information on setting resources in a resource file, see the section "Understanding resources" in the NCAR Graphics 4.1 User Guide.
. . . resname = "plotxy" resname@appUsrDir = "/usr/home/smith/resfiles" wks = gsn_open_wks("x11",resname) . . .Remember, the NCL interpreter looks for a resource file by the name resname.res, where resname is the second argument (a string) passed to gsn_open_wks.
It is possible to have up to four resource files applied to a single NCL script. For more information, see the section "Using resource files" in the NCAR Graphics 4.1 User Guide.
More information in general on resource files appears in the "Resource database" section of the NCAR Graphics 4.1 Reference Manual and in the "Understanding resources" and "Resource types" sections of the NCAR Graphics 4.1 User Guide.