Convert LAS/LAZ point clouds to Cesium 3D Tiles format with support for custom attributes.
This tool wraps py3dtiles to provide a simple, standardized interface for converting point cloud data into web-ready 3D Tiles. Unlike other conversion tools (e.g., gocesiumtiler), py3dtiles supports custom point attributes, which is essential for visualizing segmentation results and other specialized data.
Key features:
- Automatic CRS detection and ECEF transformation for proper Cesium visualization
- Support for custom point attributes (e.g., segmentation IDs)
- Automatic LAZ decompression
- Post-processing to fix tileset URI issues
- Designed for both Galaxy workflows and standalone use
cd tools/tool_py3dtiles
conda env create -f environment.yml
conda activate py3dtiles_envpip install py3dtiles>=11.0.0 laspy[lazrs,laszip]>=2.5 pyproj>=3.6python src/run.py \
--input /path/to/pointcloud.laz \
--output-dir /path/to/output \
--extra-fields PredInstance \
--srs-out 4978--input/--dataset-path: Input LAS or LAZ file (required)--output-dir: Output directory for 3D tiles (required)--extra-fields: Comma-separated list of extra attributes to include (e.g., "PredInstance")--srs-out: Output CRS EPSG code (default: "4978" for ECEF). Set to empty string to preserve original CRS--overwrite: Clear output directory before conversion
The tool is available as a Galaxy tool at galaxy/tools/py3dtiles.xml. It will:
- Create a conda environment with py3dtiles and dependencies
- Run the conversion with selected parameters
- Output tileset.json and tile files
The runner (runner/workflow.py) imports the converter module directly:
from converter import convert_las_to_3dtiles
# In workflow
convert_las_to_3dtiles(
input_path=tiles_input,
output_dir=tiles_dir,
extra_fields="PredInstance",
srs_out="4978",
overwrite=False,
)This ensures both Galaxy and the local runner use identical conversion logic.
- Validates file exists and is LAS/LAZ format
- Automatically decompresses LAZ to LAS if needed (py3dtiles works better with uncompressed LAS)
las_obj = laspy.read(input_path)
crs = las_obj.header.parse_crs()
has_crs = bool(crs)If CRS metadata is present, transforms to ECEF (EPSG:4978):
py3dtiles convert --srs_out 4978 --pyproj-always-xy --out output/ input.lasWithout CRS metadata, preserves original coordinates (assumes local/relative system).
Includes extra fields in tiles for specialized visualization:
py3dtiles convert --extra-fields PredInstance --out output/ input.lasAutomatically fixes URI issues in tileset structure:
- Rewrites child tileset URIs if they're under
points/subfolder - Normalizes redundant
points/points/prefixes in sub-tilesets
The tool generates a complete 3D Tileset directory structure:
output/
├── tileset.json # Main tileset metadata
├── tiles_log.txt # Conversion log
└── points/ # Tile files
├── r0.pnts
├── r1.pnts
├── tileset.1.json
└── ...
The main metadata file that describes the tile structure. This is what Cesium loads to display the point cloud.
Binary 3D Tiles in point cloud format, organized in an octree structure for efficient LOD rendering.
- Earth-Centered, Earth-Fixed coordinate system
- Required for proper Cesium camera controls
- Prevents issues with large UTM coordinates causing incorrect camera rotation
- Used when input file has CRS metadata
- Used when input file has no CRS metadata
- Assumes coordinates are already in suitable local/relative system
While gocesiumtiler is faster, we use py3dtiles because:
- Custom Attributes: Supports extra dimensional attributes (essential for segmentation visualization)
- Bug Fixes: Includes fixes for specific coordinate transformation issues
- Python Integration: Better integration with Python-based workflows
- Maintained: Active development with recent improvements
Uses pyproj with --pyproj-always-xy flag to ensure consistent axis order (East, North) regardless of CRS definition.
- Processes ~1M points/second (varies by hardware)
- Scales well to large datasets (100M+ points)
- Memory usage proportional to point count
- Python 3.10+
- Works on Linux, macOS, Windows
- No GPU required
Ensure py3dtiles is installed and the executable is in PATH:
which py3dtiles # Should show path to executable
pip list | grep py3dtiles # Should show version 11.0.0+Install laspy with compression support:
pip install laspy[lazrs,laszip]Check that pyproj is installed and can find PROJ data:
python -c "import pyproj; print(pyproj.datadir.get_data_dir())"If the point cloud has a CRS but camera rotates incorrectly, ensure srs_out is set to "4978" (ECEF).
tools/tool_py3dtiles/
├── src/
│ ├── converter.py # Core conversion logic
│ ├── parameters.py # Argument parsing
│ └── run.py # Main entry point
├── environment.yml # Conda environment
└── README.md # This file
# Standalone test
python src/run.py --input test.laz --output-dir ./test_output
# Check output
ls -la test_output/
cat test_output/tiles_log.txtDeveloped by the 3D Trees Project. Py3dtiles is developed by the py3dtiles community.
For issues specific to this tool wrapper, open an issue in the 3D Trees repository. For py3dtiles issues, see the py3dtiles issue tracker.