Skip to content


# LabelContours

VTKEx/Cxx/Visualization/LabelContours

### Description

This example labels isolines created by vtkContourFilter. After the isolines are generated, the resulting disconnected lines segments are gathered into polylines by vtkStripper. Then an appropriate position along each line is chosen to place the label for the contour. Two strategies for label placement are illustrated: a point at the midpoint of the line or a random point. vtkLabeledDataMapper places the labels for the contour lines.

The program can be run with an input XML polydata file (.vtp) or without arguments. If a file is specified then one, two or a range of contours can be specified. If run without a file name, the example generates a random scalar field on a plane.

Note

The labels are represented as vtkActor2D's. If they are generated for a 3D surface, the labels that should be hidden will still be displayed.

Question

If you have a simple question about this example contact us at VTKExProject If your question is more complex and may require extended discussion, please use the VTK Discourse Forum

### Code

LabelContours.cxx

    #include <vtkRenderWindow.h>
    #include <vtkRenderWindowInteractor.h>
    #include <vtkRenderer.h>
    #include <vtkSmartPointer.h>

    #include <vtkActor.h>
    #include <vtkActor2D.h>
    #include <vtkContourFilter.h>
    #include <vtkLabeledDataMapper.h>
    #include <vtkLookupTable.h>
    #include <vtkPlaneSource.h>
    #include <vtkPolyDataMapper.h>
    #include <vtkProperty.h>
    #include <vtkStripper.h>
    #include <vtkXMLPolyDataReader.h>

    #include <vtkCellArray.h>
    #include <vtkDataArray.h>
    #include <vtkDoubleArray.h>
    #include <vtkPointData.h>
    #include <vtkPoints.h>

    #include <vtkMath.h>

    #ifdef VTK_CELL_ARRAY_V2
    #include <vtkCellArrayIterator.h>
    #endif // VTK_CELL_ARRAY_V2

    int main(int argc, char* argv[])
    {
      int pointThreshold = 10;

      vtkSmartPointer<vtkPolyData> polyData;
      auto contours = vtkSmartPointer<vtkContourFilter>::New();

      // If a file is present, read it, otherwise generate some random
      // scalars on a plane
      if (argc > 1)
      {
        auto reader =
          vtkSmartPointer<vtkXMLPolyDataReader>::New();
        reader->SetFileName(argv[1]);
        reader->Update();

        double range[2];
        reader->GetOutput()->GetScalarRange(range);
        polyData = reader->GetOutput();

        std::cout << "range: " << range[0] << ", " << range[1] << std::endl;
        contours->SetValue(0, (range[1] + range[0]) / 2.0);

        contours->SetInputConnection(reader->GetOutputPort());
        if (argc == 3)
        {
          contours->SetValue(0, atof(argv[2]));
        }
        else if (argc == 4)
        {
          contours->SetValue(0, atof(argv[2]));
          contours->SetValue(1, atof(argv[3]));
        }
        else if (argc == 5)
        {
          contours->GenerateValues(atoi(argv[2]), atof(argv[3]), atof(argv[4]));
        }
      }
      else
      {
        auto plane =
          vtkSmartPointer<vtkPlaneSource>::New();
        plane->SetXResolution(10);
        plane->SetYResolution(10);
        plane->Update();

        auto randomScalars =
          vtkSmartPointer<vtkDoubleArray>::New();
        randomScalars->SetNumberOfComponents(1);
        randomScalars->SetName("Isovalues");
        for (int i = 0; i < plane->GetOutput()->GetNumberOfPoints(); i++)
        {
          randomScalars->InsertNextTuple1(vtkMath::Random(-100.0, 100.0));
        }
        plane->GetOutput()->GetPointData()->SetScalars(randomScalars);
        polyData = plane->GetOutput();
        contours->SetInputConnection(plane->GetOutputPort());
        contours->GenerateValues(5, -100, 100);
        pointThreshold = 0;
      }

      // Connect the segments of the conours into polylines
      auto contourStripper =
        vtkSmartPointer<vtkStripper>::New();
      contourStripper->SetInputConnection(contours->GetOutputPort());
      contourStripper->Update();

      auto numberOfContourLines = contourStripper->GetOutput()->GetNumberOfLines();

      std::cout << "There are " << numberOfContourLines << " contours lines."
                << std::endl;

      vtkPoints* points = contourStripper->GetOutput()->GetPoints();
      vtkCellArray* cells = contourStripper->GetOutput()->GetLines();
      vtkDataArray* scalars =
          contourStripper->GetOutput()->GetPointData()->GetScalars();

      // Create a polydata that contains point locations for the contour
      // line labels
      auto labelPolyData =
        vtkSmartPointer<vtkPolyData>::New();
      auto labelPoints =
        vtkSmartPointer<vtkPoints>::New();
      auto labelScalars =
        vtkSmartPointer<vtkDoubleArray>::New();
      labelScalars->SetNumberOfComponents(1);
      labelScalars->SetName("Isovalues");

    #ifdef VTK_CELL_ARRAY_V2

      // Newer versions of vtkCellArray prefer local iterators:
      auto cellIter = vtk::TakeSmartPointer(cells->NewIterator());
      for (cellIter->GoToFirstCell();
           !cellIter->IsDoneWithTraversal();
           cellIter->GoToNextCell())
      {
        vtkIdList *cell = cellIter->GetCurrentCell();
        if (cell->GetNumberOfIds() < pointThreshold)
        {
          continue;
        }
        std::cout << "Line " << cellIter->GetCurrentCellId() << ": " << std::endl;

        // Compute the point id to hold the label
        // Mid point or a random point
        // const vtkIdType samplePtIdx = cell->GetNumberOfIds() / 2;
        const vtkIdType samplePtIdx =
            static_cast<vtkIdType>(vtkMath::Random(0, cell->GetNumberOfIds()));

        auto midPointId = cell->GetId(samplePtIdx);

        double midPoint[3];
        points->GetPoint(midPointId, midPoint);
        std::cout << "\tmidPoint is " << midPointId << " with coordinate "
                  << "(" << midPoint[0] << ", " << midPoint[1] << ", "
                  << midPoint[2] << ")"
                  << " and value " << scalars->GetTuple1(midPointId) << std::endl;
        labelPoints->InsertNextPoint(midPoint);
        labelScalars->InsertNextTuple1(scalars->GetTuple1(midPointId));
      }

    #else // VTK_CELL_ARRAY_V2

      // Older implementations of vtkCellArray use internal iterator APIs (not
      // thread safe):
      vtkIdType* indices;
      vtkIdType numberOfPoints;
      unsigned int lineCount = 0;
      for (cells->InitTraversal(); cells->GetNextCell(numberOfPoints, indices);
           lineCount++)
      {
        if (numberOfPoints < pointThreshold)
        {
          continue;
        }
        std::cout << "Line " << lineCount << ": " << std::endl;

        // Compute the point id to hold the label
        // Mid point or a random point
        // auto midPointId = indices[numberOfPoints / 2];
        auto midPointId =
            indices[static_cast<vtkIdType>(vtkMath::Random(0, numberOfPoints))];

        double midPoint[3];
        points->GetPoint(midPointId, midPoint);
        std::cout << "\tmidPoint is " << midPointId << " with coordinate "
                  << "(" << midPoint[0] << ", " << midPoint[1] << ", "
                  << midPoint[2] << ")"
                  << " and value " << scalars->GetTuple1(midPointId) << std::endl;
        labelPoints->InsertNextPoint(midPoint);
        labelScalars->InsertNextTuple1(scalars->GetTuple1(midPointId));
      }

    #endif // VTK_CELL_ARRAY_V2

      labelPolyData->SetPoints(labelPoints);
      labelPolyData->GetPointData()->SetScalars(labelScalars);

      auto contourMapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
      contourMapper->SetInputConnection(contourStripper->GetOutputPort());
      contourMapper->ScalarVisibilityOff();

      auto isolines =
        vtkSmartPointer<vtkActor>::New();
      isolines->SetMapper(contourMapper);

      auto surfaceLUT =
        vtkSmartPointer<vtkLookupTable>::New();
      surfaceLUT->SetRange(polyData->GetPointData()->GetScalars()->GetRange());
      surfaceLUT->Build();

      auto surfaceMapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
      surfaceMapper->SetInputData(polyData);
      surfaceMapper->ScalarVisibilityOn();
      surfaceMapper->SetScalarRange(
          polyData->GetPointData()->GetScalars()->GetRange());
      surfaceMapper->SetLookupTable(surfaceLUT);

      auto surface =
        vtkSmartPointer<vtkActor>::New();
      surface->SetMapper(surfaceMapper);

      // The labeled data mapper will place labels at the points
      auto labelMapper =
        vtkSmartPointer<vtkLabeledDataMapper>::New();
      labelMapper->SetFieldDataName("Isovalues");
      labelMapper->SetInputData(labelPolyData);
      labelMapper->SetLabelModeToLabelScalars();
      labelMapper->SetLabelFormat("%6.2f");

      auto isolabels =
        vtkSmartPointer<vtkActor2D>::New();
      isolabels->SetMapper(labelMapper);

      // Create a renderer and render window
      auto renderer =
        vtkSmartPointer<vtkRenderer>::New();

      auto renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();
      renderWindow->AddRenderer(renderer);

      // Create an interactor
      auto renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
      renderWindowInteractor->SetRenderWindow(renderWindow);

      // Add the actors to the scene
      renderer->AddActor(isolines);
      renderer->AddActor(isolabels);
      //  renderer->AddActor(surface);

      // Render the scene (lights and cameras are created automatically)
      renderWindow->Render();
      renderWindowInteractor->Start();

      return EXIT_SUCCESS;
    }

### CMakeLists.txt

    cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

    project(LabelContours)

    find_package(VTK COMPONENTS
      vtkvtkCommonCore
      vtkvtkCommonDataModel
      vtkvtkFiltersCore
      vtkvtkFiltersSources
      vtkvtkIOXML
      vtkvtkInteractionStyle
      vtkvtkRenderingContextOpenGL2
      vtkvtkRenderingCore
      vtkvtkRenderingFreeType
      vtkvtkRenderingGL2PSOpenGL2
      vtkvtkRenderingLabel
      vtkvtkRenderingOpenGL2 QUIET)
    if (NOT VTK_FOUND)
      message("Skipping LabelContours: ${VTK_NOT_FOUND_MESSAGE}")
      return ()
    endif()
    message (STATUS "VTK_VERSION: ${VTK_VERSION}")
    if (VTK_VERSION VERSION_LESS "8.90.0")
      # old system
      include(${VTK_USE_FILE})
      add_executable(LabelContours MACOSX_BUNDLE LabelContours.cxx )
      target_link_libraries(LabelContours PRIVATE ${VTK_LIBRARIES})
    else ()
      # include all components
      add_executable(LabelContours MACOSX_BUNDLE LabelContours.cxx )
      target_link_libraries(LabelContours PRIVATE ${VTK_LIBRARIES})
      # vtk_module_autoinit is needed
      vtk_module_autoinit(
        TARGETS LabelContours
        MODULES ${VTK_LIBRARIES}
        )
    endif ()

### Download and Build LabelContours

Click here to download LabelContours and its CMakeLists.txt file. Once the tarball LabelContours.tar has been downloaded and extracted,

    cd LabelContours/build

If VTK is installed:

    cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

    cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

    make

and run it:

    ./LabelContours

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.