Kitware Source Feature Article: April 2009

Writing a ParaView Reader Plug-in

ParaView is a general-purpose, open-source application for scientific visualization. A common challenge for new ParaView users is figuring out how to load data. The first step is simply to go to File Open, and look any related input types. This is a long list, and chances are you will see something that matches your data format. But what if your type is not there? You could write custom utilities that convert your data format into one supported by ParaView. However, a better option in many cases is to write a reader which will read your data into a data structure that ParaView can use. A large part of ParaView’s immense flexibility comes from its plug-in architecture. This article will show you how to write a commonly used type of plug-in called a reader plug-in. A reader plug-in allows ParaView to open a new file type from the File Open dialog.

Step 1: Write the reader
The hardest part of this process is writing the reader itself. We will use the simple example of reading a 2D image from a text file with this simple delimited format. This will allow someone to export simple matrices of data from Excel and visualize them as heat maps or height fields in ParaView. We assume the files will consist of strictly numeric values separated by commas or some other delimiter.

While this is not a full tutorial on writing a VTK reader, we will provide most of the details here for completeness (see the VTK User’s Guide for more information on writing VTK readers). The header file vtkCSVImageReader.h contains the basic interface for the source. Since we are creating an image as the output, our source will subclass vtkImageAlgorithm. To help with the implementation of this class, we will use a vtkDelimitedTextReader instance which will read delimited text into a vtkTable. Most of the methods here are boilerplate, with the exception of the methods to set and get the file name and field delimiter characters, which are delegated to the vtkDelimitedTextReader instance variable.

class vtkCSVImageReader : public vtkImageAlgorithm
{
public:
    static vtkCSVImageReader* New();
    vtkTypeRevisionMacro(
       vtkCSVImageReader,vtkImageAlgorithm);
    void PrintSelf(ostream& os, vtkIndent indent);

    virtual void SetFileName(const char* fname);
    virtual const char* GetFileName();
    virtual void SetFieldDelimiterCharacters(
       const char* delim);
    virtual const char* GetFieldDelimiterCharacters();

protected:
   vtkCSVImageReader();
   ~vtkCSVImageReader();

   int RequestInformation(
      vtkInformation*,
      vtkInformationVector**,
      vtkInformationVector*);

   int RequestData(
      vtkInformation*,
      vtkInformationVector**,
      vtkInformationVector*);

   vtkDelimitedTextReader* Reader;

private:
   vtkCSVImageReader(const vtkCSVImageReader&);
   void operator=(const vtkCSVImageReader&);
};

The implementation of RequestInformation tells the VTK pipeline information about the data before creating the output. In the case of image algorithms, this means populating the extent, spacing and origin flags. In order to get this information, we need to update our internal filter to retrieve the number of rows and columns in the table; these rows and columns correspond to the x and y extent of the image. We also tell the pipeline that we want the scalar data on the image to be stored in a “float” data array.

int vtkCSVImageReader::RequestInformation (
   vtkInformation*,
   vtkInformationVector**,
   vtkInformationVector* outputVector)
{
   vtkInformation* outInfo =
      outputVector->GetInformationObject(0);

   this->Reader->Update();
   vtkTable* output = this->Reader->GetOutput();
   vtkIdType rows = output->GetNumberOfRows();
   vtkIdType columns = output->GetNumberOfColumns();

   int ext[6] = {0, rows, 0, columns, 0, 0};
   double spacing[3] = {1, 1, 1};
   double origin[3] = {0, 0, 0};

   outInfo->Set(
      vtkStreamingDemandDrivenPipeline::
      WHOLE_EXTENT(),
      ext, 6);
   outInfo->Set(vtkDataObject::SPACING(),
                      spacing, 3);
   outInfo->Set(vtkDataObject::ORIGIN(), origin, 3);
   vtkDataObject::SetPointDataActiveScalarInfo(
      outInfo, VTK_FLOAT, 1);
   return 1;
}

The RequestData method populates the vtkImageData from the data in the file. Using GetValue(), we retrieve each cell from the vtkTable and insert the value into the vtkImage- Data’s scalar data.

int vtkCSVImageReader::RequestData(
   vtkInformation*,
   vtkInformationVector**,
   vtkInformationVector* outputVector)
{
   vtkImageData* image =
      vtkImageData::GetData(outputVector);
   vtkTable* output = this->Reader->GetOutput();
   vtkIdType rows = output->GetNumberOfRows();
   vtkIdType columns = output->GetNumberOfColumns();
   image->SetDimensions(rows, columns, 1);
   image->AllocateScalars();
   vtkDataArray* scalars =
      image->GetPointData()->GetScalars();
   scalars->SetName(“Data”);
   for (vtkIdType r = 0; r < rows; ++r)
      {
      for (vtkIdType c = 0; c < columns; ++c)
         {
         vtkVariant val = output->GetValue(r, c);
         float f = val.ToFloat();
         scalars->SetTuple1(c*rows + r, f);
         }
      }
   return 1;
}

Step 2: Write the plug-in XML
Information about plug-ins is given to ParaView through a few simple XML files. In general, plug-ins will have two related XML files: the GUI XML and the Server Manager XML. The GUI XML gives ParaView the higher-level information about where new elements will be placed in the user interface. In our example, CSVImageGUI.xml is a simple file that tells ParaView to create a new reader called “CSVImageReader” associated with the file extension “csvimg”. This will create a new entry in the File Open dialog for CSV image files and will automatically associate files ending in “.csvimg” with our new reader.

<ParaViewReaders>
   <Reader
       name=”CSVImageReader”
       extensions=”csvimg”
       file_description=”CSV image”>
   </Reader>
</ParaViewReaders>

The Server Manager XML file CSVImage.xml contains information defining the client-server proxy named “CSVImageReader”, which is referenced in the GUI XML. A proxy is simply an object that represents an underlying VTK object, and has mechanisms for accessing properties and calling methods across a network (when ParaView is run in client/server mode), along with serialization, undo/redo support, and more. The “class” property of the proxy tells ParaView the name of the class to instantiate when creating this reader. ParaView receives information about how to bind property names with method calls on the class and what default values to use from the StringVectorProperty elements. Since both properties on vtkCSVImageReader are just scalar values, we set the “number_of_elements” attribute to 1. This simple piece of XML code gives ParaView enough information to interact with your reader and it will automatically create a GUI panel for you to specify parameters.

<ServerManagerConfiguration>
   <ProxyGroup name=”sources”>
      <SourceProxy
          name=”CSVImageReader”
          class=”vtkCSVImageReader”>
          <StringVectorProperty
              name=”FileName”
              command=”SetFileName”
              number_of_elements=”1”>
              <FileListDomain name=”files”/>
          </StringVectorProperty>
          <StringVectorProperty
              name=”FieldDelimiterCharacters”
              command=”SetFieldDelimiterCharacters”
              number_of_elements=”1”
              default_values=”,”/>
      </SourceProxy>
   </ProxyGroup>
</ServerManagerConfiguration>

Step 3: Write the CMakeLists file
The CMakeLists.txt file ties all these things together in order to build the ParaView plug-in. We must locate ParaView with FIND_PACKAGE and then import the CMake configuration parameters from ParaView by including PARAVIEW_USE_FILE.

ADD_PARAVIEW_PLUG-IN is a general-purpose macro that allows you to specify all the information needed to build the plug-in. The first two arguments are the name and version of the plug-in. Next are a series of argument lists separated by special identifiers. SERVER_MANAGER_XML and GUI_RESOURCE_FILES are the places for our client-server and GUI XML files, respectively. SERVER_MANAGER_SOURCES tells ParaView the source files to wrap into the Server Manager. All classes used in Server Manager XML must be listed here.

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
ADD_PARAVIEW_PLUG-IN(CSVImage “1.0”
    SERVER_MANAGER_XML CSVImage.xml
    SERVER_MANAGER_SOURCES vtkCSVImageReader.cxx
    GUI_RESOURCE_FILES CSVImageGUI.xml)

Step 4: Build and test
When you configure your build with CMake, set ParaView_DIR to the path where ParaView was built or to the binaries of a ParaView install. When the plug-in is built, run ParaView and load the plug-in by selecting “Manage Plug-ins/Extensions…” in the Tools menu. This will bring up the Plug-in Manager.

Plugin Manager

Click the Load button and browse to your plug-in shared library (CSVImage.dll on Windows). After the plug-in is loaded, you should see the new type in the drop-down menu in the File Open dialog. Files ending with .csvimg will also be part of the default filter in the dialog. Select a .csvimg file and click OK. This will bring up the options for the reader in the Properties panel, which is automatically populated with our property for setting the field delimiter characters.

Properties Panel

After hitting Apply, the data will be loaded into ParaView. The figure below demonstrates visualizing data exported from an Excel spreadsheet using the CSVImage plug-in. The spreadsheet was simply a block of cells containing the equation “SIN(ROW()/10)*COS(COLUMN()/10)”. In the image below, we show the result after processing the image with Extract Surface, Warp and Generate Surface Normals.

Applied Properties

Acknowledgements
This work has been partially funded by Sandia National Laboratories through contract number DE-AC04-94AL85000.

Jeff Baumes  Dr. Jeff Baumes is an R&D Engineer in Kitware’s Clifton Park, NY office. Dr. Baumes is the main developer for Titan, whose goal is to bring information visualization and informatics capabilities to VTK.