Wednesday, 29 October 2014

LAS1.3 file Reader in C++


today I am going to talk about a part of my code I wrote for my research. Well, my research is about visualising and classifying Airborne Remote Sensing data. About a year ago, I was provided a python script ( that reads the LAS file but working in python was very very slow, so eventually I had to write my own LAS1.3 reader in C++.

The code for reading a LAS1.3 file has now been released as an open source code under the GNU General Public Licence, version 3, and it is available at:

In this blogpost I am planning to explain how you can use the code and also how the code works.

Please note that this work is supported by the Centre for DIgital Entertainment at the University of Bath, and Plymouth Marine Laboratory. 

1. Data Specifications

The data that I used to test the program were provided by ARSF NERC and they were collected using a small footprint Leica ALS50-II LiDAR system.

The file specifications of LAS1.3 files were published in 2010 and they are available here:

The point data record format is assumed to always be  of format 4 and it is also assume that waveform packets exists. Otherwise the reader prints an error and terminates.

2. Compile and run the Program

Before compiling the program, make sure that c++11 and gmtl libraries are installed on your computer.

Further in the main.cpp file replace the <DIRECTORY OF LAS FILE> with the name of the LAS.13 file of your interest. Unfortunately due to license restrictions I couldn't provide a sample file. But I may get to do it later.

Compile the program using the makefile:

and run the progrma:

The output is the following one. It first print the related pulse information, then all the waveform samples and at the end the associated discrete values:
1199653 waveforms found
1511062 discrete points found
There are 2477840 Discrete Without Waveforms
the pulse manager has : 1199653 pulses
Point                            4.3787e+05 1.0486e+05 16.316
Return Number                    
Number of returns for this pulse 
Time                             3.8836e+05
Scan Angle                       
Temporal Sample Spacing          2
AGC gain                         
Digitiser Gain                   0.017291
Digitiser Offset                 0
No. of Samples                   256
Sample Length                    0.29979
Return Point Location            21.816
Point in Waveform                3.2702
Offset                           0.021667 0.0059378 0.29887
Origin                           4.3787e+05 1.0486e+05 19.576
Waveform Samples: ( x , y , z , I )
( 4.3787e+05 , 1.0486e+05 , 19.576 , 15 )
( 4.3787e+05 , 1.0486e+05 , 19.277 , 16 )
( 4.3787e+05 , 1.0486e+05 , 18.978 , 14 )
( 4.3787e+05 , 1.0486e+05 , 18.679 , 14 )
( 4.3787e+05 , 1.0486e+05 , 18.381 , 14 )
( 4.3787e+05 , 1.0486e+05 , 18.082 , 14 )
( 4.3787e+05 , 1.0486e+05 , -55.739 , 14 )
( 4.3787e+05 , 1.0486e+05 , -56.038 , 14 )
( 4.3787e+05 , 1.0486e+05 , -56.337 , 13 )
( 4.3787e+05 , 1.0486e+05 , -56.636 , 14 )

Associated discrete points (x , y  , z , I)
( 4.3787e+05 , 1.0486e+05 , 16.316 , 141

3. Classes Information

There are three classes in the LAS reader: Las1_3_handler, PulseManager and Pulse.The Las1_3_handler takes as input a LAS1.3 file, read the binary file according to the specifications, saves all the information read inside a PulseManager and returns the PulseManager. (For more information on how to read binary files please look at:

The following code shows how to use the Las1_3_handler class. Please note that the Pulse Manager is dynamic allocated and the user is responsible for realising the memory afterwards.

Las1_3_handler lasHandler("/local1/data/scratch/mmi/2010_098_FW/classified_manually/LDR-FW10_01-201009822.LAS");
PulseManager *p = lasHandler.readFileAndGetPulseManager();
// do stuff with p
delete p;

The PulseManager contains and manages multiple Pulses. Each Pulse contains the point data record information, the associated waveform packet and all the discrete returns, which are the peak reflectances of that Pulse. Nevertheless, due to the speed of the flight most of the time waveforms are only saved for about half of the pulses. The position and intensity of the discrete returns with no waveform associated are saved into m_discretePoints and m_discreteIntensities inside the PulseManager. So far the only thing you can do is to get the number of Pulses and print all the information of a Pulse of your interest.
std::cout << "the pulse manager has : " << p->getNumOfPulses() << " pulses\n";

An example of output is shown in Section 2. Further if the input pulse number doesn't exist a error message is printed instead.

4. Future Work

To extend the code and add more stuff is considerable easy. The only thing that need to be pointed out is the way of calculating the position of the waveform samples. To reduce the amount of memory used only the origin and offset of the waveform samples are saved. So, you have to calculate the position of each sample before used and this can be done inside the Pulse class as follow:

gmtl::Vec3f tempPosition = m_origin;
for(unsigned short int i=0; i< m_noOfSamples; ++i)
   std::cout << "( " << tempPosition[0] << " , " << tempPosition[1] << " , "
             << tempPosition[2] << " , " <<  (int) m_returns[i] <<  " )\n";
More information about coding are given in the Doxygen Documentation. Also feel free to contact me if you have further questions.

I would also like to give you an insignt of my future posts and publications with the following demo of visualising fw LiDAR data with hyperspectral iamges:

The approach of generating polygon meshes from full-waveform LiDAR data has been presented at RSPSoc Conference 2014 and if you are interested you can either download the extended abstract from here:, or wait for one of my following blogpost where I will explain that in more details. Please don't forget to reference if you use any of this work.

1 comment:

  1. Hello,
    here is a simpler version of my code. Modified by Dr. Mark A. Warren.