Issues with Accessing UserData in Point Cloud in CesiumJS

Hello, everyone!

I’m currently working on a project in CesiumJS to visualize a point cloud (LAZ format) that includes floor information stored in the UserData field. My goal is to color the points based on the floor level. The point cloud itself displays correctly, but I am facing difficulties when trying to use the UserData field to color the points.

To provide some context:

  1. LAZ File Generation:
    I generated the LAZ file using PDAL with a pipeline that includes the floor information as UserData. Here’s a simplified version of the PDAL pipeline I used:
  {
    "pipeline": [
      {
        "type": "readers.text",
        "filename": "temp_csv_path",
        "separator": ",",
        "skip": 1,
        "header": "X,Y,Z,GpsTime,PointId,UserData"
      },
      {
        "type": "filters.reprojection",
        "in_srs": "EPSG:4326",
        "out_srs": "EPSG:4326"
      },
      {
        "type": "writers.las",
        "filename": "output_laz",
        "compression": "laszip",
        "minor_version": 2,
        "dataformat_id": 1,
        "filesource_id": 0,
        "global_encoding": 0,
        "system_id": "PDAL",
        "a_srs": "EPSG:4326",
        "scale_x": 0.0000001,
        "scale_y": 0.0000001,
        "scale_z": 0.01,
        "offset_x": "auto",
        "offset_y": "auto",
        "offset_z": "auto"
      }
    ]
  }

In this pipeline, the ‘UserData’ column from the input CSV is directly mapped to the UserData field in the LAZ file. The pipeline also includes reprojection to ensure the coordinate system is correctly set to EPSG:4326.

  1. Previous Working Code with Classification:
    Previously, I used the Classification field instead of UserData, and it worked correctly. Here’s a snippet of the working code:

    const floorColorStyle = new Cesium.Cesium3DTileStyle({
      color: {
        conditions: [
          ['${Classification} === 0', 'color("blue")'],
          ['${Classification} === 1', 'color("green")'],
          ['${Classification} === 2', 'color("red")'],
          // ... more conditions
          ['true', 'color("gray")']
        ]
      }
    });
    tileset.style = floorColorStyle;
    
  2. Current Setup:

    • I’m using Cesium version 1.118.

    • The LAZ file was converted to 3D Tiles using Cesium ion.

    • The tileset is loaded into the scene using the following code:

      const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(YOUR_ASSET_ID);
      viewer.scene.primitives.add(tileset);
      

Now, with the switch to UserData, I’m encountering issues. Initially, I tried accessing UserData through the getProperty() method, but I encountered the following error:

Uncaught TypeError: Cannot read properties of undefined (reading 'getProperty')

I inspected the picked object’s structure, and it appears to be different from what I expected for 3D Tileset properties. Here’s what the picked object looks like:

Picked feature type: {content: ys, primitive: jo, detail: {…}, id: undefined}

I also tried accessing the batchTable property from the primitive.content, but couldn’t retrieve UserData. Below is a simplified version of the code I am using:

viewer.screenSpaceEventHandler.setInputAction(function (movement) {
  const pickedObject = viewer.scene.pick(movement.position);
  if (Cesium.defined(pickedObject)) {
    const primitive = pickedObject.primitive;
    if (primitive && primitive instanceof Cesium.Cesium3DTileset) {
      const content = primitive.content;
      if (content && content.batchTable) {
        const userData = content.batchTable.getProperty(pickedObject.batchId, 'UserData');
        console.log('UserData:', userData);
      } else {
        console.log("Property not found");
      }
    }
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

Additionally, I am trying to apply a Cesium3DTileStyle to color the points based on the floor information in the UserData field. Here’s the code for the styling:

const floorColors = [
  'rgb(0, 0, 128)',  // Basement -2
  'rgb(0, 0, 255)',  // Basement -1
  'rgb(0, 128, 255)',  // Ground Floor
  // ...additional colors for higher floors
  'rgb(128, 0, 0)'   // 40th Floor
];
const floorColorStyle = new Cesium.Cesium3DTileStyle({
  color: {
    conditions: floorColors.map((color, index) =>
      [`\${UserData} === ${index}`, `color("${color}")`]
    ).concat([["true", "color('gray')"]])  // Default color
  },
  pointSize: 3,
  pointOutlineColor: "color('white', 0.3)"
});
tileset.style = floorColorStyle;

Despite this setup, I’m not seeing the points colored according to UserData. When using Classification for this purpose previously, everything worked fine, but now the transition to UserData seems to have caused an issue.

Any insights on how to correctly access and use the UserData field for styling, or if there’s an issue with how I’m retrieving properties via pickedFeature.getProperty()? Your help would be greatly appreciated!

Thank you in advance!

I have managed to resolve this issue on my own. As mentioned in the Cesium documentation [Styling and Filtering 3D Tiles – Cesium], Cesium ION provides access to various point cloud attributes, including Classification and Intensity.

Initially, I tried using Classification, but it only uses 5 bits, which wasn’t sufficient for my needs. Instead, I found success using the Intensity attribute, which allowed me to represent a wider range of floor levels.

However, it’s important to note that this is not the intended usage of the Intensity attribute. In my case, I’m using it to represent floor levels, which works for my specific use case but may not be suitable for all scenarios.

If anyone has experience with a more standard or efficient approach to handling extended information in Cesium point clouds, I would be very interested in learning about it.

Thank you for your help and insights!

1 Like