Contour Widget Class Introduction

The Contour widget provides methods for calculating and displaying grids and contours which represent the solution to surface estimation problems. The input to the Contour widget is either a set of scattered points, or a grid, with an optional fault definition.

Gridding Procedure

The Contour widget accepts the input data set along with a set of control parameters, and, using one of the gridding algorithms supplied, it generates a grid whose node values correspond to a surface from which the input data is assumed to have been extracted. This process has three basic steps.
Primary Estimates
Each of the gridding methods computes a set of primary node value estimates using nearby Z values as control. If source data samples are very close together, then the primary estimates will contain averages of the data. The Global method terminates upon calculation of the primary estimates. The other methods continue to the Secondary estimates and Smoothing stages.
Secondary Estimates
Secondary estimates are calculated at grid nodes that were too far from the input data to be considered in the primary estimates. The secondary estimates use previously defined node values as control. Several iterations may be required to achieve a good representation of the surface.

In each secondary estimation pass, only grid nodes that are adjacent to defined grid nodes are computed; multiple passes fill in the entire grid. During subsequent passes, only every other node in each direction is computed, and skipped nodes are filled in by a bicubic interpolation method. This method provides very good performance on large grids.


Smoothing
Smoothing tries to minimize the curvature of the surface by adjusting the secondary estimates. Each smoothing pass tends to make the surface more closely represent the solution of the biharmonic equation which models a thin plate that is deformed into the desired shape.

Each smoothing pass modifies the secondary estimates of node values that do not conform to the minimum curvature surface model. These modified values are then used as node value inputs to the next smoothing pass. While this iterative process converges rather slowly, as few as ten passes frequently produce very good results.


Fault Treatment

Faults are represented as line segments across which the data are to be considered to be discontinuous. If a fault sequence is closed, then the grid node values interior to the resulting fault polygon are considered to be undefined. A set of fault definitions, expressed in the same units as the input data set, can be supplied for use in the gridding methods discussed below. The presence of faults usually results in longer computational time for the final grid.

Gridding Methods

Several grid methods are provided by the Contour widget. These have been constructed to provide algorithms that are suitable for a wide range of data sets. The discussion below describes some of the criteria that should be considered in choosing one of these gridding methods for a particular data set.
Global
The Global method is recommended for relatively small data sets (fewer that 10,000 points). It is fast and provides visually pleasing results. The interpolation algorithm provides an approximation to a minimum curvature surface that passes through the input data points. If more than 1,000 points are input, the Global method averages the data locally; in this case the resulting surface passes through the averaged points, not the original data. This method will not handle data sets that contain faults.
Scatter
The Scatter method is designed for large data sets and for data which is faulted. Since this method approximates both the value and local gradient of the input data, it is not recommended for situations in which the data is known to contain significant noise.
Cluster
The Cluster method is very similar to the Scatter method except that it averages data locally and then uses the averaged data as if it had been the input data. This method is recommended for large data sets in which many points are close together or datasets which are known to contain noise. This method is suitable for either faulted or unfaulted data sets.
Weighted
The Weighted method is designed to visualize very large data sets where the data density is very high relative to the output grid. This is a fast method which uses only the data values and makes no attempt to estimate the gradients of the data. In this method, the grid node value is a weighted average of the data points surrounding it, and secondary gridding is used to fill in the unassigned node values. This method is suitable for either faulted or non faulted data sets.

Gridding Example

The following example (see file contour_1.c in the examples directory) illustrates how to grid and contour a set of scattered points.

   #include <stdio.h>
   #include <Xm/Xm.h>
   #include <Xm/PushB.h>
   #include <Xm/Separator.h>
   #include <Xm/Text.h>
   #include <Xint/Box.h>
   #include <Xint/Scroll.h>
   #include <Xint/Contour.h>

   /*  Declare local and extern subroutines */
   void BuildUI();
   static void LocatorCallback();
   static void ExitCallback();
   extern double drand48();

   /* Declare global variables */
   Widget toplevel_widget;
   Widget locator_text;
   int i, j, n;
   
   /* define constant */
   #define NUM_POINTS 100

   /* Main subroutine */
   main(argc, argv)
   int argc;
   char *argv[];
   {
      XtAppContext app_context;

      toplevel_widget = XtAppInitialize (&app_context, "Example", NULL, 0,
                                         &argc, argv, NULL,NULL, 0);
      BuildUI ();

      XtRealizeWidget(toplevel_widget);
      XtAppMainLoop(app_context);
   }

   /* Subroutine to build the user interface */
   void BuildUI()
   {
      Widget main_vbox;
      Widget control_hbox;
      Widget sep, exit_pb;
      Widget int_scroll;
      Widget contour;
      float start_x = 0;
      float start_y = 0;
      float end_x = 1.0;
      float end_y = 1.0;
      XintSourcePoint points[NUM_POINTS];
      XmString cstring;

      /* create a vertical box */
      main_vbox = XtVaCreateManagedWidget("main_vbox", xintBoxWidgetClass,
                                          toplevel_widget, NULL);

      /* create a Scroll widget */
      int_scroll = XtVaCreateManagedWidget("int_scroll", 
                                           xintScrollWidgetClass,
                                           main_vbox,
                                           XmNwidth,             500,
                                           XmNheight,            500,
                                         /* constraint resources from box */
                                           XmNhorizontalStretch, XintFILL,
                                           XmNhorizontalShrink , XintFILL,
                                           XmNverticalStretch,   XintFILL,
                                           XmNverticalShrink,    XintFILL,
                                           NULL);

      /* generate a random set of points for input to the contour */
      srand48((long) NUM_POINTS);

      for (i=0; i<NUM_POINTS; i++) {
           points[i].x = drand48();
           points[i].y = drand48();
           points[i].z = drand48();
      }

      /* create the contour widget */
      contour = XtVaCreateManagedWidget ("contour", xintContourWidgetClass,
                                         int_scroll,
                                         XmNwidth, 900,
                                         XmNheight, 900,
                                         XmNsourcePointArray, points,
                                         XmNsourcePointCount, NUM_POINTS,
                                         XmNcontourDisplayMode, 
                                                   XintCONTOUR_FILLED_AND_LINE,
                                         XmNgriddingMethod, XintGRIDDING_GLOBAL,
                                         XmNcolormapFile, "contour.cmp",
                                         XmNcolorScale, XintCOLOR_SCALE_LEFT,
                                         XmNautoMarginAdjust, XintADJUST_ALL,
                                         XmNstartDataX, &start_x,
                                         XmNstartDataY, &start_y,
                                         XmNendDataX, &end_x,
                                         XmNendDataY, &end_y,
                                         XmNverticalMajorThickness, 1,
                                         XmNverticalMinorGridLineStyle, 
                                                          XintGRID_LINE_DASHED,
                                         XmNhorizontalMajorThickness, 1,
                                         XmNhorizontalMajorThickness, 1,
                                         XmNhorizontalMinorGridLineStyle, 
                                                          XintGRID_LINE_DASHED,
                                         NULL);

      XtAddCallback(contour, XmNlocatorCallback, LocatorCallback, NULL);

      /* create a separator */
      sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass,
                                    main_vbox,
                                    /* constraint resources from box */
                                    XmNhorizontalStretch, XintFILL,
                                    XmNhorizontalShrink , XintFILL, NULL);
 
      /* create a horizontal box */
      control_hbox = XtVaCreateManagedWidget("control_hbox",
                                             xintBoxWidgetClass,
                                             main_vbox,
                                             XmNorientation, XintHORIZONTAL, 
                                             NULL);

      /* create Exit Push Button  */
      cstring = XmStringCreateLtoR("Exit", XmSTRING_DEFAULT_CHARSET);
      exit_pb = XtVaCreateManagedWidget("exit_pb", 
                                        xmPushButtonWidgetClass,
                                        control_hbox,
                                        XmNlabelString, cstring, NULL);
      XmStringFree(cstring);
      XtAddCallback(exit_pb, XmNactivateCallback, ExitCallback, NULL);

      /* locator label */
      locator_text = XtVaCreateManagedWidget("locator", xmTextWidgetClass,
                                             control_hbox,
                                             XmNeditable, False,
                                             XmNautoShowCursorPosition, False,
                                             XmNeditMode, XmSINGLE_LINE_EDIT,
                                             XmNtraversalOn, False,
                                             XmNcolumns, 32,
                                          /* constraint resources from box */
                                             XmNhorizontalStretch, XintFILL,
                                             XmNhorizontalShrink , XintFILL,
                                             NULL);
   }

   /* Locator callback */
   static void
   LocatorCallback (Widget widget, XtPointer data,
                    XintGridLocatorCallbackStruct *cb)
   {
      char s[140];
      float z;

      XintContourGetZValue(widget, cb->user_x, cb->user_y, &z);
      sprintf(s, "X: %.3f   Y: %.3f   Z: %.3f",cb->user_x, cb->user_y, z);
      XmTextSetString(locator_text, s);
   }

   /* Exit Callback */
   static void ExitCallback (Widget widget, XtPointer data,
                             XmAnyCallbackStruct *cb)
   {
      exit(0);
   }

The output from example contour_1.c is shown below:


Figure 13: Output produced by example contour_1.c


Coordinate System

The Contour widget uses the coordinate system defined in the Grid class. The visible portion of the contour is set using Grid resources XmNhorizontalStart, XmNhorizontalEnd, XmNverticalStart and XmNverticalEnd. Those resources will default to the grid mesh boundaries if not set at creation time and each time the data is changed. The boundaries of the internal grid mesh are defined using Contour resources XmNstartDataX, XmNendDataX, XmNstartDataY and XmNendDataY. These resources must be set if the input to the Contour widget is a grid. If the input to the Contour widget is a set of scattered points, these resources will default to the minimum and maximum coordinates of the specified points.

Polygon Clipping

Additional control of the contoured area is provided by the XmNclipPolygonArray and XmNclipPolygonCount resources. These resources specify an arbitrary polygon that is imposed upon the Contour data. Only the data within this intersection is used by the gridding procedure. The visible portion of the contour is the intersection of the area described in the Contour System section above and the superimposed polygon.

Contouring Example

The Contour widget can also accept its data from a Grid. The following example (see contour_2.c) illustrates how to contour a grid:

   #include <stdio.h>
   #include <Xm/Xm.h>
   #include <Xm/Separator.h>
   #include <Xm/PushB.h>
   #include <Xint/Box.h>
   #include <Xint/Contour.h>
   
   /*
    * Declare local and extern subroutines
    */
   void BuildUI();
   static void ExitCallback();
   static float *ReadGrid();

   /*
    * Declare global variables
    */
   Widget toplevel_widget;
   int i, j, n;

   /* Main subroutine */
   main(argc, argv)
   int argc;
   char *argv[];
   {
      XtAppContext app_context;

      toplevel_widget = XtAppInitialize (&app_context, "Example", NULL, 0,
                                         &argc, argv, NULL, NULL, 0);
      BuildUI ();

      XtRealizeWidget(toplevel_widget);
      XtAppMainLoop(app_context);
   }

   /* Subroutine to build the user interface */
   void BuildUI()
   {
      Widget main_vbox;
      Widget control_hbox;
      Widget sep, exit_pb;
      Widget contour;
      float start_x;
      float start_y ;
      float end_x;
      float end_y;
      float *grid;
      float null_value = 999999.0;
      int num_grid_x, num_grid_y;
      XmString cstring;
    
      /* create a vertical box */
      main_vbox = XtVaCreateManagedWidget("main_vbox", xintBoxWidgetClass,
                                          toplevel_widget, NULL);

      /*  read grid */
      grid = ReadGrid(&num_grid_x, &num_grid_y, &start_x, &start_y,
                      &end_x, &end_y);

      /* create the contour widget */
      contour = XtVaCreateManagedWidget ("contour", xintContourWidgetClass,
                                         main_vbox,
                                         XmNwidth, 700,
                                         XmNheight, 700,
                                         XmNgridData, grid,
                                         XmNnullValue, &null_value,
                                         XmNnumGridX, num_grid_x,
                                         XmNnumGridY, num_grid_y,
                                         XmNautoMarginAdjust, XintADJUST_ALL,
                                         XmNstartDataX, &start_x,
                                         XmNstartDataY, &start_y,
                                         XmNendDataX, &end_x,
                                         XmNendDataY, &end_y,
                                         XmNverticalMajorGridLineStyle,  
                                                            XintGRID_LINE_NONE,
                                         XmNhorizontalMajorGridLineStyle,
                                                            XintGRID_LINE_NONE,
                                       /* constraint resources from box */
                                         XmNhorizontalStretch, XintFILL,
                                         XmNverticalStretch, XintFILL,
                                         XmNhorizontalShrink, XintFILL,
                                         XmNverticalShrink, XintFILL, 
                                         NULL);

      /* create a separator */
      sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass,
                               main_vbox,
                               /* constraint resources from box */
                               XmNhorizontalStretch, XintFILL,
                               XmNhorizontalShrink , XintFILL, NULL);

      /* create a horizontal box */
      control_hbox = XtVaCreateManagedWidget("control_hbox",
                                             xintBoxWidgetClass,
                                             main_vbox,
                                             XmNorientation, XintHORIZONTAL, 
                                             NULL);

      /* create Exit Push Button */
      cstring = XmStringCreateLtoR("Exit", XmSTRING_DEFAULT_CHARSET);
      exit_pb = XtVaCreateManagedWidget("exit_pb", 
                                        xmPushButtonWidgetClass,
                                        control_hbox,
                                        XmNlabelString, cstring, NULL);
      XmStringFree(cstring);
      XtAddCallback(exit_pb, XmNactivateCallback, ExitCallback, NULL);
   }

   /*  Exit Callback **/
   static void
   ExitCallback (Widget widget, XtPointer data, XmAnyCallbackStruct *cb)
   {
      exit(0);
   }

   /* ReadGrid */
   static float * ReadGrid(int *num_grid_x, int *num_grid_y, 
                           float *start_x,  float *start_y, 
                           float *end_x,    float *end_y)

{
 ... /* see contour_2.c */
}

The output from example contour_2.c is shown below:


Figure 14: Output produced by example contour_2.c


Colors and Contour Levels

The specification of the color pixels or colormap file used by the Contour widget is done using the resources defined in the Image class. The number of contour levels is set using resource XmNnumGridZ. When the contour image is displayed using colors, it is best to have the number of colors in the colormap match the number of contour levels. The Contour widget handles cases where the number of colors and contour levels are different by duplicating or skipping colors. See the Image class for more information on how to set the Contour widget colormap.

Grid X and Y Axes

The Contour annotation, orientation, and grid line style are set via Grid resources. If the axes limits are not explicitly set, then the Contour widget will use the internal grid mesh limits as the axes limits. The orientation of the X axis (specified by Grid resource XmNhorizontalDirection) can be such that the minimum value is on the left side (default) or the right side of the X axis. The orientation of the Y axis (specified by the Grid resource XmNverticalDirection) can be such that the minimum value is at the bottom (default) or the top of the Y axis. The major subdivisions along an axis have a label and a grid line and are specified with XintGrid resources. The minor subdivisions along an axis have tic marks at their location and are specified using Grid resources.

Zooming the Plot Display

The end-user can select a rectangular area of a plot and have that area scaled and displayed in another Contour widget. EditObject callback XmNareaSelectionCallback provides the mechanism for the end-user to select the rectangular area to zoom. Contour function XintContourZoom can be used to create a new zoomed Contour widget, which shares the data structures used by the original Contour widget. Alternatively, you can zoom the plot in place simply by resizing the original Contour widget.

Producing Hardcopy

A scaled image of a contour display can be output to a color or monochrome Postscript file using a supplied function. CGM output capability is available as a separate option. See the description of the CompBase widget class for additional information on hardcopy output.