Example: Data Resample Operation
This example creates a data operation to resample data in a dataset using the IDL CONGRID function.
Example Code
The code for this example operation is included in the file example1_opresample__define.pro in the examples/doc/itools subdirectory of the IDL distribution. Run the example procedure by entering example1_opresample__define at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT example1_opresample__define.pro.
Class Definition File
The class definition for example1_opresample consists of an Init method, an Execute method, GetProperty and SetProperty methods, and a class structure definition routine. As with all object class definition files, the class structure definition routine is the last routine in the file, and the file is given the same name as the class definition routine (with the suffix .pro appended).
Class Definition
PRO example1_opresample__define struc = {example1_opresample, $ inherits IDLitDataOperation, $ _x: 0d, $ _y: 0d, $ _z: 0d, $ _method: 0b $ } END
Discussion
Our class definition routine is very simple. We create an IDL structure variable with the name example1_opresample, specifying that the structure inherits from the IDLitDataOperation class. The structure has three instance data fields named _x, _y, and _z, which contain double-precision floating point values, and a single instance data field named _method which contains a byte value.
Init Method
FUNCTION example1_opresample::Init, _REF_EXTRA = _extra IF (~ self->IDLitDataOperation::Init(NAME='Resample', $ TYPES=['IDLVECTOR','IDLARRAY2D','IDLARRAY3D'], $ DESCRIPTION="Resampling", _EXTRA = _extra)) THEN $ RETURN, 0 ; Default values for resampling factors. self._x = 2 self._y = 2 self._z = 2 ; Register properties self->RegisterProperty, 'X', /FLOAT, $ DESCRIPTION='X resampling factor.' self->RegisterProperty, 'Y', /FLOAT, $ DESCRIPTION='Y resampling factor.' self->RegisterProperty, 'Z', /FLOAT, $ DESCRIPTION='Z resampling factor.' self->RegisterProperty, 'METHOD', $ ENUMLIST=['Nearest neighbor', 'Linear', 'Cubic'], $ NAME='Interpolation method', $ DESCRIPTION='Interpolation method.' IF (N_ELEMENTS(_extra) GT 0) THEN $ self->example1_opresample::SetProperty, _EXTRA = _extra ; Unhide the SHOW_EXECUTION_UI property. self->SetPropertyAttribute, 'SHOW_EXECUTION_UI', HIDE=0 RETURN, 1 END
Discussion
The first item in our class definition file is the Init method. The Init method's function signature is defined first, using the class name example1_opresample. The _REF_EXTRA keyword inheritance mechanism allows any keywords specified in a call to the Init method to be passed through to routines that are called within the Init method even if we do not know the names of those keywords in advance.
Next, we call the Init method of the superclass. In this case, we are creating a subclass of the IDLitDataOperation class; this provides us with all of the standard iTool data operation functionality automatically. We specify three iTool data types on which our operation will work: "IDLVECTOR", "IDLARRAY2D", and "IDLARRAY3D". Any "extra" keywords specified in the call to our Init method are passed to the IDLitDataOperation::Init method via the keyword inheritance mechanism. If the call to the superclass Init method fails, we return immediately with a value of 0.
Next we store the default values for the three resampling factors (one each for the X, Y, and Z dimensions) in the object instance data fields _x, _y, and _z. We register each of these values as a property of the operation. We also register the METHOD property, assigning to it an enumerated list with three strings describing three different interpolation methods ("Nearest Neighbor", "Linear", and "Cubic").
If any "extra" keywords were specified in the call to our Init method, we pass them to the SetProperty method our example1_opresample object.
Finally, we return the value 1 to indicate successful initialization.
Execute Method
FUNCTION example1_opresample::Execute, data dims = SIZE(data, /DIMENSIONS) CASE N_ELEMENTS(dims) OF 1: newdims = dims*ABS([self._x]) > [1] 2: newdims = dims*ABS([self._x, self._y]) > [1, 1] 3: newdims = dims*ABS([self._x, self._y, self._z]) > [1, 1, 1] ELSE: RETURN, 0 ENDCASE ; No change in size. IF (ARRAY_EQUAL(newdims, dims)) THEN RETURN, 1 interp = 0 & cubic = 0 CASE (self._method) OF 0: ; do nothing 1: interp = 1 2: cubic = 1 ENDCASE CASE N_ELEMENTS(dims) OF 1: data = CONGRID(data, newdims[0], $ INTERP = interp, CUBIC = cubic) 2: data = CONGRID(data, newdims[0], newdims[1], $ INTERP = interp, CUBIC = cubic) ; CONGRID always uses linear interp with 3D 3: data = CONGRID(data, newdims[0], newdims[1], newdims[2]) ENDCASE RETURN, 1 END
Discussion
The Execute method does the work of our operation. Since example1_opresample is based on the IDLitDataOperation class, when the operation is requested by a user the Execute method is automatically called with each of the currently selected data objects as the data argument.
First, we use the SIZE function to determine the number of dimensions of the input data item. We use a CASE statement to create a new array (newdims) that stores the number of elements of each dimension multiplied by the scale factor for each dimension. The number of elements in each dimension cannot be less than one.
Next we use the ARRAY_EQUAL function to compare the number of elements of each dimension of the input data with the number of elements of each dimension of our newdims array. If these numbers are equal, no resampling will take place, so we stop processing and return 1 for success.
If our newdims array contains a different number of elements than the original input data, some resampling will take place. We check the value of the METHOD property (stored in the instance data field _method) to determine what type of resampling we should perform.
Finally, we call the CONGRID function with the appropriate arguments and keywords, depending on the dimensionality of the input data and the resampling method specified. We then return 1 for success.
GetProperty Method
PRO example1_opresample::GetProperty, $ X = x, $ Y = y, $ Z = z, $ METHOD = method, $ _REF_EXTRA = _extra ; My properties. IF ARG_PRESENT(x) THEN $ x = self._x IF ARG_PRESENT(y) THEN $ y = self._y IF ARG_PRESENT(z) THEN $ z = self._z IF ARG_PRESENT(method) THEN $ method = self._method ; Superclass properties. IF (N_ELEMENTS(_extra) gt 0) THEN $ self->IDLitDataOperation::GetProperty, _EXTRA = _extra END
Discussion
The GetProperty method for our operation supports four properties named X, Y, Z, and METHOD, stored in instance data fields of the same name (with an underscore prepended). If any of these properties is specified in the call to the GetProperty method, its value is retrieved from the appropriate instance data field. Any other properties included in the method call are passed to the superclass' GetProperty method.
SetProperty Method
PRO example1_opresample::SetProperty, $ X = x, $ Y = y, $ Z = z, $ METHOD = method, $ _REF_EXTRA = _extra ; My properties. IF N_ELEMENTS(x) THEN $ IF (x NE 0) THEN self._x = x IF N_ELEMENTS(y) THEN $ IF (y NE 0) THEN self._y = y IF N_ELEMENTS(z) THEN $ IF (z NE 0) THEN self._z = z IF N_ELEMENTS(method) THEN $ self._method = method ; Superclass properties. IF (N_ELEMENTS(_extra) gt 0) THEN $ self->IDLitDataOperation::SetProperty, _EXTRA = _extra END
Discussion
The SetProperty method for our operation supports four properties named X, Y, Z, and METHOD, stored in instance data fields of the same name (with an underscore prepended). If any of these properties is specified in the call to the SetProperty method, its value is stored in the appropriate instance data field. Any other properties included in the method call are passed to the superclass' SetProperty method.