Picking Library Introduction

The Picking Library is a set of data structures, functions, actions and translations that supports the Seismic widget class and all subclasses of the Seismic widget class (such as the Segy widget class). An application can use the Picking Library in conjunction with a Seismic widget to implement end-user picking of horizons from seismic sections displayed by a Seismic widget. The horizons picked are displayed by the Seismic widget as points connected by line segments. The application has control over the line and point display attributes. There are data structures defined in the Picking Library for containing the picks as well as actions, callbacks and convenience functions for managing the picking data structures. A single set of picking data structures can be shared among several Seismic widgets. The Picking Library also implements an automatic tracking feature that allows the automatic picking of horizons. The application can substitute its own tracking and snapping functions in place of the ones provided in the Picking Library.

Picking Example

The code example below (see file pickinglib_1.c in directory examples) illustrates how to create a picking record and to define a set of translations to implement picking. This example also illustrates how to zoom a seismic section and display the picks in the zoom window.
   #include <stdio.h>
   #include <Xm/Xm.h>
   #include <Xm/RowColumn.h>
   #include <Xm/Form.h>
   #include <Xm/Separator.h>
   #include <Xm/Label.h>
   #include <Xm/PushBG.h>
   #include <Xm/ScrolledW.h>
   #include <Xint/Segy.h>
   #include <Xint/Picking.h>
   
   #define k_num_controls          1
   #define k_num_zoom_controls     1
   #define k_exit          0
   #define k_zoom_OK       0
   
   /* Declare subroutines */
   void BuildUI();

   /* Declare callbacks */
   static void ControlsProc(Widget, int, XtPointer);
   static void ZoomProc(Widget, XtPointer, 
                        XintSeismicAreaSelectionCallbackStruct *);
   static void ZoomControlsProc(Widget, int, XtPointer);

   /* Declare global variables */
   Widget toplevel_widget;
   int i, j, n;
   Arg arg[20];

   static XtTranslations    pick_translations_parsed;
   static XintPickingRecord *picking_record;
   
   static char pick_translations[] = "Ctrl<Btn1Down>:HorizonCreate() \n\
                                  Ctrl<Btn3Down>: HorizonDelete() \n\
                                  Shift<Btn1Down>: InitAreaSelection() \n\
                                  <Btn1Motion>:  ExtendAreaSelection() \n\
                                  <Btn1Up>:      EndAreaSelection() \n\
                                  <Btn1Down>:    PointInsert(i) \n\
                                  <Btn2Down>:    PointStartMove() \n\
                                  <Btn2Motion>:  PointMove() \n\
                                  <Btn2Up>:      PointEndMove() \n\
                                  <Btn3Down>:    PointDelete()";
   
   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);
   }
   
   /***
    *** BuildUI
    ***/
   void BuildUI ()
   {
       Widget main_form;
       Widget scrolled_window;
       Widget segy_widget;
       Widget sep;
       Widget controls_box;
       Widget controls_pb[k_num_controls];
       static char *controls_pb_names[] =  {"Exit"};
       XintHorizon *horizon;
   
       /* Create form to contain all other widgets */
       n = 0;
       XtSetArg(arg[n], XmNwidth, 500); n++;
       XtSetArg(arg[n], XmNheight, 550); n++;
       main_form = XtCreateManagedWidget("main_form", xmFormWidgetClass, 
                                         toplevel_widget, arg, n);
      
       /* create separator */
       n = 0;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomOffset, 50); n++;
       sep = XtCreateManagedWidget("sep", xmSeparatorWidgetClass, 
                                   main_form, arg, n);
      
       /* create a ScrolledWindow widget */
       n = 0;
       XtSetArg(arg[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNtopOffset, 0); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftOffset, 0); n++;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNrightOffset, 0); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(arg[n], XmNbottomWidget, sep); n++;
       XtSetArg(arg[n], XmNbottomOffset, 10); n++;
       XtSetArg(arg[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
       scrolled_window = XtCreateManagedWidget("scrolled_window",
                                               xmScrolledWindowWidgetClass, 
                                               main_form, arg, n);
      
       /* create the segy widget */
       n = 0;
       XtSetArg(arg[n], XmNplotTitle, "Picking1 Example "); n++;
       XtSetArg(arg[n], XmNsegyFilename, "segy2.dat"); n++;
       segy_widget = XtCreateManagedWidget("segy_widget", xintSegyWidgetClass, 
                                           scrolled_window, arg, n);
      
       /* add callback procedure for the zoom operation */
       XtAddCallback(segy_widget, XmNareaSelectionCallback, ZoomProc, NULL);
      
       /* parse pick translations */
       pick_translations_parsed = XtParseTranslationTable(pick_translations);
      
       /* create the picking record and install tranlations */
       picking_record = XintPickingRecordCreate(segy_widget,
                                                pick_translations_parsed, 
                                                XintOVERRIDE_TRANSLATIONS);
      
       /* create the first horizon and make it the current horizon */
       horizon = XintHorizonCreate(picking_record, "horizon1", "red", 2, True);
      
       /* create controls box */
       n = 0;
       XtSetArg(arg[n], XmNspacing, 15); n++;
       XtSetArg(arg[n], XmNorientation, XmHORIZONTAL); n++;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNrightOffset, 20); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftOffset, 20); n++;
       XtSetArg(arg[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(arg[n], XmNtopWidget, sep); n++;
       XtSetArg(arg[n], XmNtopOffset, 10); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomOffset, 10); n++;
       controls_box = XtCreateManagedWidget("controls_box",
                                            xmRowColumnWidgetClass, 
                                            main_form, arg, n);
      
       /* create control pushbuttons */
       n = 0;
       XtSetArg(arg[n], XmNpushButtonEnabled, True); n++;
       XtSetArg(arg[n], XmNmarginWidth, 4); n++;
       XtSetArg(arg[n], XmNmarginHeight, 4); n++;

       for (i=0; i < k_num_controls; i++) {
            j = n;
            XtSetArg(arg[j], XmNlabelString,
                             XmStringCreateLtoR(controls_pb_names[i], 
                             XmSTRING_DEFAULT_CHARSET)); 
            j++;
            controls_pb[i] = XtCreateManagedWidget("control_buttons",
                                                   xmPushButtonGadgetClass, 
                                                   controls_box, arg, j);
      
            XtAddCallback(controls_pb[i], XmNactivateCallback, ControlsProc,
                          (XtPointer)i);
       }
   }
   
   /***
    *** ZoomProc
    ***/
   static void ZoomProc(Widget widget, XtPointer client_data,
                        XintSeismicAreaSelectionCallbackStruct *call_data)
   {
       Widget zoom_dialog;
       Widget zoom_scrolled_window;
       Widget zoom_segy_widget;
       Widget zoom_sep;
       Widget zoom_controls_box;
       Widget zoom_controls_pb[k_num_controls];
       static char *zoom_pb_names[] =  {"OK"};
   
       /* Create form dialog to contain the zoomed area */
       n = 0;
       XtSetArg(arg[n], XmNwidth, 450); n++;
       XtSetArg(arg[n], XmNheight, 330); n++;
       zoom_dialog = XmCreateFormDialog(toplevel_widget, "zoom_dialog", 
                                        arg, n);
   
       /* create separator */
       n = 0;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomOffset, 50); n++;
       zoom_sep = XtCreateManagedWidget("zoom_sep", xmSeparatorWidgetClass,
                                        zoom_dialog, arg, n);
   
       /* create a ScrolledWindow widget */
       n = 0;
       XtSetArg(arg[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNtopOffset, 0); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftOffset, 0); n++;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNrightOffset, 0); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(arg[n], XmNbottomWidget, zoom_sep); n++;
       XtSetArg(arg[n], XmNbottomOffset, 10); n++;
       XtSetArg(arg[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
       zoom_scrolled_window = XtCreateManagedWidget("zoom_scrolled_window",
                                                    xmScrolledWindowWidgetClass,
                                                    zoom_dialog, arg, n);
   
       /* create Seismic Zoom widget */
       zoom_segy_widget = XintSeismicZoom(widget, zoom_scrolled_window,
                                          call_data, 2.5, 2.5);
   
       /* connect picking record to zoom_segy_widget */
       XintPickingRecordConnectWidget(picking_record, zoom_segy_widget);
   
       /* create controls box */
       n = 0;
       XtSetArg(arg[n], XmNspacing, 15); n++;
       XtSetArg(arg[n], XmNorientation, XmHORIZONTAL); n++;
       XtSetArg(arg[n], XmNrightAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNrightOffset, 20); n++;
       XtSetArg(arg[n], XmNleftAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNleftOffset, 20); n++;
       XtSetArg(arg[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(arg[n], XmNtopWidget, zoom_sep); n++;
       XtSetArg(arg[n], XmNtopOffset, 10); n++;
       XtSetArg(arg[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(arg[n], XmNbottomOffset, 10); n++;
       zoom_controls_box = XtCreateManagedWidget("zoom_controls_box",
                                                 xmRowColumnWidgetClass, 
                                                 zoom_dialog, arg, n);
   
       /* create zoom control pushbuttons */
       n = 0;
       XtSetArg(arg[n], XmNpushButtonEnabled, True); n++;
       XtSetArg(arg[n], XmNmarginWidth, 4); n++;
       XtSetArg(arg[n], XmNmarginHeight, 4); n++;
       for (i=0; i < k_num_zoom_controls; i++) {
            j = n;
            XtSetArg(arg[j], XmNlabelString, 
                             XmStringCreateLtoR(zoom_pb_names[i], 
                             XmSTRING_DEFAULT_CHARSET)); j++;
            zoom_controls_pb[i] = XtCreateManagedWidget("zoom_control_buttons",
                                                        xmPushButtonGadgetClass,
                                                        zoom_controls_box, 
                                                        arg, j);
            XtAddCallback(zoom_controls_pb[i], XmNactivateCallback, 
                          ZoomControlsProc, (XtPointer)i);
       }

       /* Manage the zoom_dialog */
        XtManageChild(zoom_dialog);
   }
   
   /***
    *** ZoomControlsProc
    ***/
   static void ZoomControlsProc(Widget widget, int client_data,
                                XtPointer call_data)
   {
        if (client_data == k_zoom_OK) XtDestroyWidget(IntGetShell(widget));
   }
   
   /***
    *** ControlsProc
    ***/
   static void ControlsProc(Widget widget, int client_data,
                            XtPointer call_data)
   {
        if (client_data == k_exit) exit (0);
   }

The output produced by example pickinglib_1.c is shown below:


Figure 18: Output Produced by Example pickinglib_1.c.


Picking Data Structures

The Picking Library defines three types of data structures for managing the picking data. The primary data structure is called a Picking Record and references the other two types of data structures that contain information about the points and horizons that have been defined. A Picking Record can be shared among several Seismic widgets simultaneously.

Creating a Picking Record

A Picking Record is created using a Picking Library function, and it is associated with a Seismic widget during the Picking Record creation process. After a Picking Record is created it can be associated with additional Seismic widgets using a Picking Library function.

Using a Picking Record

Once created, a Picking Record can be associated with other widgets by connecting it to those widgets. The picking operation can be managed totally during the picking operation via action procedures defined by the Picking Library. Alternatively, the application can maintain control of the picking operation by associating callback procedures with the Picking Record after it is created. When the picking record is no longer needed by any widget, the application can destroy the picking record with a supplied function.

Horizons

A horizon is a series of points that has been defined as a named collection. The horizon is drawn on the seismic section as a series of lines connecting these points. Horizons can be created, edited, deleted, moved, and selected. A selected horizon becomes the current horizon and can then be edited, deleted, or moved. A horizon is edited by changing its display attributes or by changing the point list. Horizons may be given a name, but the application usually manipulates a horizon using its sequence number or a pointer to the horizon's data structure.

Points

Points created by either the application or the end-user are used to represent the location of a horizon. Points have a symbol size and may be inserted, deleted, selected or moved in the current horizon. A point callback identifies a selected point to the application so that the application can decide what is to be done with the point. A point is manipulated by the application through a pointer to the point's data structure.

Coordinate system

The coordinate system used by a picking record is the same as that used in the Seismic widgets to which it is connected. Therefore, all widgets using a shared picking record should use the same coordinate system. Convenience functions are defined in the Picking Library for getting the horizontal and vertical coordinates of a point in the application defined coordinate system.

Actions and Translations

The Picking Library defines a large set of actions for creating and editing horizons and points. Using these action routines an application can easily implement a horizon editor. The application can define a translation table for associating user events with the action procedures. The translation table is associated with a Picking Record when the Picking Record is created.

Callbacks

The application can implement callback procedures that will be executed when the automatic picking actions, point actions and horizon actions are executed. In this way the application can know about and control the picking, point and horizon operations. The callbacks are associated with a Picking Record after it is created via Picking Library functions. There are also provisions for assigning snap and tracker callbacks so that the application can substitute its own algorithms for the default ones.

Snap Mode

By default, when a point is picked on the seismic section, the point nearest to the cursor is selected as the picked point. The application can specify other ways for the point to be selected by using a Picking Library function to define the snap mode. If the application specifies snap to the maximum amplitude, then the point at the local maximum amplitude on the trace nearest to the mouse cursor is selected as the picked point. If the application specifies snap to the minimum amplitude, then the point at the local minimum amplitude on the trace nearest to the mouse cursor is selected as the picked point. If the application specifies to snap to the plus/minus zero crossing amplitude, then the point where the amplitude of the trace nearest to the mouse pointer crosses the zero amplitude from the positive side is selected. If the application specifies to snap to the minus/plus zero crossing amplitude, then the point where the amplitude of the trace nearest to the mouse pointer crosses the zero amplitude from the negative direction is selected. The application can substitute its own snapping algorithm by assigning a snap callback to the Picking Record. The description of how to integrate your own snapping algorithm is not contained within this document.

Automatic Picking

There is an action implemented by the Picking Library for automatically picking a horizon in the seismic section. The default Tracker picks points in the horizon based upon the maximum amplitude, minimum amplitude, or zero crossing (both -/+ and +/-). The Tracker iteratively picks points between the last defined point in the horizon and a trace to the right of that point. To guide the Tracker's search for the next point in the horizon, the tracking search mode can be set to project linearly (the default) or project horizontally. If the projection is linear, then the Tracker begins its search in the next trace at the point of intersection between the next trace and a line projected through the last two points in the horizon. If the projection is horizontal, then the Tracker begins its search in the next trace at the point with the same time value as the last point in the horizon. The application can substitute its own tracking algorithm by assigning a tracker callback to the Picking Record. The description of how to integrate your own tracking algorithm is not contained within this document.

Current Horizon

The current horizon is the one that can be edited. The application or end-user selects the current horizon via actions or Picking Library functions. When a new horizon is created, it always becomes the current horizon and is active for editing operations such as adding new points. The current horizon can be highlighted if the application defines a highlight color and/or a size change for the symbols in the current horizon.

Creating a Horizon

A horizon can be created by the end-user via an action or by the application via a supplied function. If the end-user creates a horizon via an action procedure, then the horizon takes on the default horizon display attributes which have been set by the application, and the new horizon has no name. (The application can register a callback procedure so that the application can give the newly created horizon a name.) If the application creates the horizon, then the display attributes and the horizon name are set in the function call. When a horizon is created it has no points and it becomes the current horizon.

Setting the Display Attributes of a Horizon

When a new horizon is created, its display attributes (such as color, point symbol size and line width) will be set to default values. There are Picking Library functions for setting the default horizon display attributes, setting the display attributes of the current horizon and for changing the display attributes of a horizon.

Editing a Horizon

A horizon may be edited by the end-user only if it is the currently selected horizon. The application, however, can edit any horizon. A horizon can be edited by changing its graphics attributes (such as line width) or by adding, deleting or moving points in the horizon. The end-user can edit the current horizon using actions, or the application can edit a horizon using Picking Library functions.

Moving a Horizon

The end-user moves the current horizon using the event sequences associated with the move action procedures defined in the translation table. The application can move a horizon by directly changing the coordinates of each point in the horizon. When the end-user moves a horizon, the widget draws a highlighted copy of the horizon at the location of the cursor to show where the horizon would be located if the end-user terminated the move operation at that location.

Deleting a Horizon

The end-user deletes a horizon using the event sequences associated with the delete action in the translation table. The application can delete a horizon by destroying the horizon using a Picking Library function.

Creating a Point

A point is automatically created when the end-user inserts a point into the current horizon. The application program creates a point explicitly using a Picking Library function that allows the application to define a data structure and a date to be associated with the point.

Inserting a Point into a Horizon

A point can be added to the current horizon by the end-user via an action procedure or added to any horizon by the application via a Picking Library function. A point can be inserted at the beginning of the current horizon, inserted at the end of the current horizon, inserted between two points or inserted according to its time coordinate. If the application adds a point to a horizon, then the application must create the point first.

Deleting a Point

A point can be deleted from the current horizon by the end-user via an action procedure or deleted from any horizon by the application via a Picking Library function. When a point is deleted, the display of the horizon that formerly contained the point is updated automatically.

Moving a Point

A point is moved by the end-user via a collection of actions that allow the end-user to select a point and to move it to a new location. The move operation can be constrained so that the new location is within the point's original trace. The application can move a point by directly changing the point's coordinates in the picking data structures.

Data structures

There are three types of data structures used when displaying and manipulating picked horizons. The primary data structure is an instance of the data type, XintPickingRecord. It contains the global information about the picking operation and refers to a list of data structures, each of which is of data type XintHorizon. Each instance of the XintHorizon structure contains information about the display attributes (e.g. line width, line color) of the horizon and refers to a list of data structures, each of which is of data type XintPoint. Each instance of the XintPoint data structure contains the coordinates of the point, the point symbology, and a pointer to the next point structure in the horizon.

You should use actions and Picking Library functions to create, manipulate and get information about these data structures. The Picking Library function, XintPickingRecordCreate is used to create an instance of the XintPickingRecord structure. A Picking Record can be associated with more than one Seismic widget. This can be done at Picking Record creation time by specifying the widget ID in the argument list of the function XintPickingRecordCreate. After widget creation time a Picking Record can be associated with a Seismic widget using the function XintPickingRecordConnectWidget. When the end-user or application adds a new horizon or a new point, the Picking Library creates the necessary supporting data structures and manages those appropriately. You use Picking Library functions to modify the contents of the data structures or to get information about their contents. Most of the callbacks associated with a Picking Record return a pointer to a horizon or point structure. That pointer can be used with Picking Library functions to obtain further information about the horizon or point of interest.