I am using VTK-9.3.0. My C++ application builds a VtkPoints collection (variable ‘gridPoints_’) from topography data.
for (row = 0; row < nRows; row++) {
for (col = 0; col < nCols; col++) {
# Get x, y, and z. Note that missing z-value is represented as NaN
getMyData(row, col, &x, &y, &z);
# Insert point
vtkIdType id = myPoints->InsertNextPoint(x, y, z);
}
}
Some of my datasets are “sparse”, i.e. some of the z-values have value NaN. How to properly set the z-value in this case? If I insert these z-values into myPoints as NaN, myPoints actually display as expected, with ‘gaps’ in the surface where z-data is missing. But picking from this sparse surface does not work properly; vtkPicker->GetPointId() always returns -1 no matter which point is picked.
What is the proper way to represent missing z-values? Thanks!
On a general note: I would stay away from using NaNs. It could be a convenience for users to assign some meaning to NaN (e.g., it could mean missing data), but if you allow such values then they may show up in completely unexpected places (basically anywhere).
but the poster does not suggest an alternative - how to do it???
Is there a reason to plot unsumpled locations? I’d simply skip them. Now, if you do need to render unvalued points, you can inpterpolate them (e.g. nearest neighbor, spline, kriging, inverse distance, etc.) or use a default value (e.g. zero, data mean, etc.). You name it, but leaving NaNs will likely cause the spatial index used in picking operations to misbehave.
I modified my vtkReader subclass to skip unvalued z-data like this when loading the vtkPoints:
for (row = 0; row < nRows; row++) {
for (col = 0; col < nCols; col++) {
// Get x, y, and z. Note that missing z-value is represented as NaN
getMyData(row, col, &x, &y, &z);
if (isnan(z)) {
// Don't insert unvalued z points
continue;
}
// Insert point
vtkIdType id = myPoints->InsertNextPoint(x, y, z);
}
}
But how to load triangle vertices into a vtkPolyData (e.g. myPolygons) in this case where I’m subsampling the original data?
Thanks!
In that case, do as proposed by Joachim above: remesh from the remaining points. I believe that is the easiest way to fill those holes. Other methods like interpolating involve the use of regular grids, parameter tuning, etc.
Thanks @Joachim_P and @Paulo_Carvalho for your suggestion; I’m now skipping data points with NaN z-value when populating vtkPoints, then generating triangles with vtkDelaunay2D. I think I am almost there, but still missing something. My vtkReader subclass is called TopoGridReader, and does this:
In TopoGridReader.h:
/**
TopoGridReader reads data stored in a data file
and outputs the data into a vtkPoints (vertices) and vtkCellArray
(triangles) where data can be accessed by the VTK visualizaation pipeline.
*/
class TopoGridReader : public vtkAbstractPolyDataReader {
[...stuff here...]
vtkSmartPointer<vtkPoints> gridPoints_;
};
In TopoGridReader.cpp:
int TopoGridReader::RequestData(vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector) {
vtkInformation* outInfo = outputVector->GetInformationObject(0);
vtkDataSet* output = vtkDataSet::GetData(outInfo);
vtkPolyData* polyOutput = vtkPolyData::SafeDownCast(output);
// Read gridPoints_ from input file, skipping points with NaN z-values
[...]
// Add gridPoints_ to a vtkPolyData object
vtkNew<vtkPolyData> polyData;
polyData->SetPoints(gridPoints_);
// Add polyData to a Delaunay2D object
vtkNew<vtkDelaunay2D> delaunay;
delaunay->SetInputData(polyData);
// Point reader output points and polygons to grid vtkPoints and
// Delaunay2D output.
polyOutput->SetPoints(gridPoints_);
// vtkPolyData::SetPolys() takes vtkCellArray as input.
polyOutput->SetPolys(delaunay->???()) // What goes here?
return 1;
}
What vtkDelaunay2D method provides a vtkCellArray object needed by polyOutput->SetPolys()? For example, how can I convert the vtkPolyData from delaunay->Output() into the vtkCellArray() needed by polyOutput->SetPolys()?
Thanks!
You don’t need to do that. You can simply do polyOutput->deepCopy( delaunay->GetOutput() );. You can also try delaunay->SetOutput( polyOutput ); before running the algorithm to have the results stored in the polyOutput object.