Individual coloured lines in polydata file

I’m trying to understand how to write a .vtp correctly to display different cell data for each line.

I’ve created a simple case with 7 points, which should give me a 6 lines, and therefore 6 cells, each of which can be set independently. I’ve written the .vtp as this which I though was correct:

<VTKFile type="PolyData" version="1.0" byte_order="LittleEndian" header_type="UInt64">
  <PolyData>
    <Piece NumberOfPoints="7" NumberOfVerts="0" NumberOfLines="7" NumberOfStrips="0" NumberOfPolys="0">
      <PointData Scalars="1_temp">
        <DataArray Name="1_temp" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.03043433541242735" RangeMax="0.9947671646588059">
          0.5901740250590545 0.9947671646588059 0.7489206394212843 0.0304343354124273 0.9234538424010137 0.9798888990537769
          0.3373415663707561
        </DataArray>
        <DataArray Name="2_temp NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.1426086960632018" RangeMax="0.8792924894608538">
          0.3916196439944402 0.6046746919418857 0.8792924894608538 0.1426086960632018 0.3544317346711524 0.5498989637627353
          0.8375566887711513
        </DataArray>
      </PointData>
      <CellData Scalars="velocity">
        <DataArray Name="velocity" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="1.0" RangeMax="5.0">
          1.0 2.0 3.0 4.0 5.0 6.0
        </DataArray>
      </CellData>
      <Points>
        <DataArray Name="points" NumberOfComponents="3" type="Float64" format="ascii" RangeMin="0.0" RangeMax="3.0">
          0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
          2.0000000000000000 0.0000000000000000 0.0000000000000000 3.0000000000000000 1.5000000000000000 0.0000000000000000
          0.0000000000000000 0.0000000000000000 3.0000000000000000 1.0000000000000000 1.0000000000000000 3.0000000000000000
          2.0000000000000000 0.0000000000000000 3.0000000000000000
        </DataArray>
      </Points>
      <Verts>
      </Verts>
      <Lines>
        <DataArray Name="connectivity" NumberOfComponents="1" type="Int32" format="ascii" RangeMin="0" RangeMax="6">
          0 1 2 3 4 5
          6
        </DataArray>
        <DataArray Name="offsets" NumberOfComponents="1" type="Int64" format="ascii" RangeMin="2" RangeMax="7">
          2 3 4 5 6 7
        </DataArray>
      </Lines>
      <Strips>
      </Strips>
      <Polys>
      </Polys>
    </Piece>
  </PolyData>
</VTKFile>

However, when I try to load into Paraview, it throws an error about not enough points:

ERROR: In vtkXMLDataReader.cxx, line 433
vtkXMLPolyDataReader (0x56178872f700): Cannot read cell data array "velocity" from PointData in piece 0.  The data array in the element may be too short.

I’m a little confused as to what the correct data for offsets is to specify the 6 expected lines. I’m also confused by the warning which says that the cell data is missing from the PointData as I have placed this in the CellData section, which is what is done with polylines.

OK, after a break from this I realised I was treating lines the same as poly-line without thinking that the lines need to be pairs of points, even if two lines share a point.

So now my working solution looks like this:

<?xml version="1.0"?>
<VTKFile type="PolyData" version="1.0" byte_order="LittleEndian" header_type="UInt64">
  <PolyData>
    <Piece NumberOfPoints="10" NumberOfVerts="0" NumberOfLines="5" NumberOfStrips="0" NumberOfPolys="0">
      <PointData Scalars="1_temp">
        <DataArray Name="1_temp" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.03043433541242735" RangeMax="0.9798888990537769">
          0.0304343354124273 0.9234538424010137 0.9798888990537769 0.3373415663707561 0.6533569100468444 0.5805916314301794
          0.9231814078620875 0.8286460163085659 0.8841588239981764 0.8735697354630155
        </DataArray>
        <DataArray Name="2_pressure" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.1426086960632018" RangeMax="0.9947671646588059">
          0.3916196439944402 0.6046746919418857 0.8792924894608538 0.1426086960632018 0.3544317346711524 0.5498989637627353
          0.8375566887711513 0.5901740250590545 0.9947671646588059 0.7489206394212843
        </DataArray>
      </PointData>
      <CellData Scalars="velocity">
        <DataArray Name="velocity" NumberOfComponents="1" type="Int32" format="ascii" RangeMin="0" RangeMax="4">
          0 1 2 3 4
        </DataArray>
      </CellData>
      <Points>
        <DataArray Name="points" NumberOfComponents="3" type="Float64" format="ascii" RangeMin="0.0" RangeMax="3.0">
          0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
          1.0000000000000000 1.0000000000000000 0.0000000000000000 2.0000000000000000 0.0000000000000000 0.0000000000000000
          2.0000000000000000 0.0000000000000000 0.0000000000000000 3.0000000000000000 1.5000000000000000 0.0000000000000000
          0.0000000000000000 0.0000000000000000 3.0000000000000000 1.0000000000000000 1.0000000000000000 3.0000000000000000
          1.0000000000000000 1.0000000000000000 3.0000000000000000 2.0000000000000000 0.0000000000000000 3.0000000000000000
        </DataArray>
      </Points>
      <Verts>
      </Verts>
      <Lines>
        <DataArray Name="connectivity" NumberOfComponents="1" type="Int32" format="ascii" RangeMin="0" RangeMax="9">
          0 1 2 3 4 5
          6 7 8 9
        </DataArray>
        <DataArray Name="offsets" NumberOfComponents="1" type="Int64" format="ascii" RangeMin="2" RangeMax="10">
          2 4 6 8 10
        </DataArray>
      </Lines>
      <Strips>
      </Strips>
      <Polys>
      </Polys>
    </Piece>
  </PolyData>
</VTKFile>

Which I believe is correct (I’ve removed one of the line segments from the original example for testing purposes).

However, this raises a question about duplication of data.

For a small example like this it doesn’t matter, but what if there are thousands of lines where each point could be shared between multiple lines, then there would be significant duplication making the data file much larger than required. Is there a way in the offsets or connectivity to recreate this polydata file with only the bare minimum number of 7 points in the file instead of the 10 (or 12 if I put back the deleted segment) required?

Yes, absolutely! The points array can be de-duplicated. In your case, if you wanted the first two line segments to share point 1, you could specify 0 1 1 2 to share that point. You can just list your 7 points, and then use indices 0-6 in the connectivity array to refer to them.

Sorry I think I’m being a bit thick today. If I understand correctly, I would just leave the connectivity to be 0 1 2 3 4 5 6 and then modify the offsets to include the duplicate points?

In my example, since I have 7 points, I should be able to make 6 continuous lines if they are connected end to end. I’ve added 6 CellData points for the velocity field and listed the connectivity as 0 though 6. I’ve tried to then edit the offsets as suggested, but importing into paraview still throws an error:

ERROR: In vtkXMLDataReader.cxx, line 433
vtkXMLPolyDataReader (0x5617862a1180): Cannot read cell data array "velocity" from PointData in piece 0.  The data array in the element may be too short.


I believe I’ve not managed to follow your instruction correctly. Could you check this simple template for my mistake and point out how to change it? I’ve also tried with the offsets specified as 0 1 1 2 2 3 3 4 4 5 5 6 but that throws the same error.

<VTKFile type="PolyData" version="1.0" byte_order="LittleEndian" header_type="UInt64">
  <PolyData>
    <Piece NumberOfPoints="7" NumberOfVerts="0" NumberOfLines="7" NumberOfStrips="0" NumberOfPolys="0">
      <PointData Scalars="1_temp">
        <DataArray Name="1_temp" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.03043433541242735" RangeMax="0.9947671646588059">
          0.5901740250590545 0.9947671646588059 0.7489206394212843 0.0304343354124273 0.9234538424010137 0.9798888990537769
          0.3373415663707561
        </DataArray>
        <DataArray Name="2_temp" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="0.1426086960632018" RangeMax="0.8792924894608538">
          0.3916196439944402 0.6046746919418857 0.8792924894608538 0.1426086960632018 0.3544317346711524 0.5498989637627353
          0.8375566887711513
        </DataArray>
      </PointData>
      <CellData Scalars="velocity">
        <DataArray Name="velocity" NumberOfComponents="1" type="Float64" format="ascii" RangeMin="1.0" RangeMax="5.0">
          1.0 2.0 3.0 4.0 5.0 6.0
        </DataArray>
      </CellData>
      <Points>
        <DataArray Name="points" NumberOfComponents="3" type="Float64" format="ascii" RangeMin="0.0" RangeMax="3.0">
          0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000 0.0000000000000000
          2.0000000000000000 0.0000000000000000 0.0000000000000000 3.0000000000000000 1.5000000000000000 0.0000000000000000
          0.0000000000000000 0.0000000000000000 3.0000000000000000 1.0000000000000000 1.0000000000000000 3.0000000000000000
          2.0000000000000000 0.0000000000000000 3.0000000000000000
        </DataArray>
      </Points>
      <Verts>
      </Verts>
      <Lines>
        <DataArray Name="connectivity" NumberOfComponents="1" type="Int64" format="ascii" RangeMin="0" RangeMax="6">
          0 1 2 3 4 5 6
        </DataArray>
        <DataArray Name="offsets" NumberOfComponents="1" type="Int64" format="ascii" RangeMin="0" RangeMax="6">
          1 2 2 3 3 4 4 5 5 6 6 7
        </DataArray>
      </Lines>
      <Strips>
      </Strips>
      <Polys>
      </Polys>
    </Piece>
  </PolyData>
</VTKFile>

Perhaps it could be useful to try some simple reverse engineering? Scribble together what you want as a VTK legacy format, load in paraview and save in XML format and see what it generates.

Hi Mark, the problems for me isn’t the difference between legacy and xml formats (I’m not familiar with the legacy format anyway) but just how to specify the offsets and connectivity for a VTK file as this should be the same for a polydata file in either legacy (.vtk) or xml (.vtp) format.

I understand how to specify the offsets and connectivity to generate a single polyline using all the data points or how to create multiple individual lines which requires duplicating shared points but is not ideal for very large datasets where a single point can be the start/end point of up to 20 lines. This could make files many times larger than necessary.

If you know how to create the legacy vtk file that achieves this, that would also be very helpful because it should have the same connectivity and offsets and as you mentioned, I could just just open in something like Parview and save to a different format.

I should probably find if this is explained in a manual somewhere :smiley:

No, you want to leave the offsets array alone, that was correct. It specifies where in the connectivity array to start looking for each cell. Since your cells are lines, each cell has 2 points, so the offsets should be 2 4 6 8 ...

Now you know the first two entries (from offsets) in the connectivity array define the first cell, a line. So the first two numbers there are indices of the points that define that line. If they are 0 1, then points 0 and 1 will define that first line. If the next two entries are 5 6 then points 5 and 6 will define the next line.

To play with this more, I used ParaView, and I suggest you try it out. Here’s the file I produced by creating a Line source, turning off the Use regular refinement checkbox, then choosing File .. Save Data, and choosing a VTP polydata file in ascii mode.

<VTKFile type="PolyData" version="1.0" byte_order="LittleEndian" header_type="UInt64">
  <PolyData>
    <Piece NumberOfPoints="2" NumberOfVerts="0" NumberOfLines="1" NumberOfStrips="0" NumberOfPolys="0">
      <PointData TCoords="Texture Coordinates">
        <DataArray type="Float32" Name="Texture Coordinates" NumberOfComponents="2" format="ascii" RangeMin="0" RangeMax="1">
          0 0 1 0
          <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2">
            <Value index="0">
              0
            </Value>
            <Value index="1">
              1
            </Value>
          </InformationKey>
        </DataArray>
      </PointData>
      <CellData>
      </CellData>
      <Points>
        <DataArray type="Float32" Name="Points" NumberOfComponents="3" format="ascii" RangeMin="0.46425074248860615" RangeMax="0.46818645384005064">
          0.37692267 0.2668703 0.04731071 0.07376513 0.45690945 0.070647135
          <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2">
            <Value index="0">
              0.46425074249
            </Value>
            <Value index="1">
              0.46818645384
            </Value>
          </InformationKey>
          <InformationKey name="L2_NORM_FINITE_RANGE" location="vtkDataArray" length="2">
            <Value index="0">
              0.46425074249
            </Value>
            <Value index="1">
              0.46818645384
            </Value>
          </InformationKey>
        </DataArray>
      </Points>
      <Verts>
        <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
        <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
      </Verts>
      <Lines>
        <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="0" RangeMax="1">
          0 1
        </DataArray>
        <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="2" RangeMax="2">
          2
        </DataArray>
      </Lines>
      <Strips>
        <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
        <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
      </Strips>
      <Polys>
        <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
        <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="1e+299" RangeMax="-1e+299">
        </DataArray>
      </Polys>
    </Piece>
  </PolyData>
</VTKFile>

Note that if you turn on the Use regular refinement checkbox, and save again, you get a connectivity array like 0 1 2 3 4 5 6 and an offsets array of 7, which is a 7 element line strip, so you can do that, too.

@Aron_Helser Thank you so much for explaining. I misunderstood that it was the connectivity that I was supposed to change while leaving the offsets alone. Using this knowledge I was example to make the example work…eventually.

I took me quite a while to spot the obvious mistake preventing it from working:

<Piece NumberOfPoints="7" NumberOfVerts="0" NumberOfLines="7" NumberOfStrips="0" NumberOfPolys="0">

which clearly should have NumberOfLines="6"

If this is in the documentation, then a lot of people have missed it. I’ve only ever seen the .vtp files written with the points duplicated but I guess this is the same way elements in structured grids are also defined. It’s just not obvious that it also applies here for simple lines.

I’m finally learning vtk in a bit of detail instead of blindly writing data in in-efficient ways.