Overview

LiDAR data voxelisation software.

Copyright (c) IRD (Institut de Recherche pour le Développement) 2016-2018

Vincent, G., Antin, C., Laurans, M., Heurtebize, J., Durrieu, S., Lavalley, C., & Dauzat, J. (2017). Mapping plant area index of tropical evergreen forest by airborne laser scanning. A cross-validation study using LAI2200 optical sensor. Remote Sensing of Environment, 198, 254-266.
https://doi.org/10.1016/j.rse.2017.05.034

Free download from the Wiki main page.

amapvox: README

AMAPVox

commons-spds: README

SpDS

Spatial data structures to speed up the searches.

Installation with Maven

Add the following repository :

<repository>
    <id>amap-maven-central</id>
    <name>libs-release</name>
    <url>http://manosque.cirad.fr:8081/artifactory/libs-release-local</url>
</repository>

And the following dependency:

<dependency>
    <groupId>fr.amap.commons</groupId>
    <artifactId>commons-spds</artifactId>
    <version>1.0.0</version>
</dependency>

Usage

//create an octree with a maximum number of points of 50 for a leaf node
Octree<Point3D> octree = new Octree(50);

//set array of points
octree.setPoints(new Point3D[]{});

try {
    //don't forget to build the octree
    octree.build();

    //search the closest point of the given point within the given search radius
    Point3D nearestPoint = octree.searchNearestPoint(new Point3D(5.0, 1.0, 6), Octree.INCREMENTAL_SEARCH, 0.0001f);
    if(nearestPoint != null){
        System.out.println("Point found : ("+ nearestPoint.x+" "+nearestPoint.y+" "+nearestPoint.z+")");
    }

} catch (Exception ex) {
    Logger.getLogger(Octree.class.getName()).log(Level.SEVERE, null, ex);
}

format-riegl: README

JRiegl

Installation with Maven

Add the following repository :

<repository>
    <id>amap-maven-central</id>
    <name>libs-release</name>
    <url>http://manosque.cirad.fr:8081/artifactory/libs-release-local</url>
</repository>

And the following dependency:

<dependency>
    <groupId>fr.amap.lidar</groupId>
    <artifactId>format-riegl</artifactId>
    <version>1.0.1</version>
</dependency>

Usage

Use case : read a Riscan Project File (*.rsp) :


Rsp rsp = new Rsp();

try {
    rsp.read(new File("/media/forestview01/BDLidar/TLS/Paracou2014/FTH2014.RiSCAN/project.rsp"));

    System.out.println("Riscan project POP matrix : \n"+rsp.getPopMatrix().toString()+"\n");

    ArrayList<Scans> rxpList = rsp.getRxpList();

    System.out.println("Scans list :\n");

    for(Scans scans : rxpList){

        System.out.println("Scan name :"+scans.getName());

        System.out.println("Scan SOP matrix :\n"+scans.getSopMatrix());

        System.out.println("Rxp file path :"+scans.getScanFull().getAbsolutePath()+"\n");
    }

} catch (JDOMException ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}

Use case : read a *.rxp file :

RxpExtraction rxpReader = new RxpExtraction();

try {
    rxpReader.openRxpFile(new File("/home/Documents/sample.rxp"), RxpExtraction.REFLECTANCE, RxpExtraction.AMPLITUDE, RxpExtraction.DEVIATION, RxpExtraction.TIME); //select echoes attrbutes to import

    Iterator<Shot> iterator = rxpReader.iterator();

    while(iterator.hasNext()){

        Shot shot = iterator.next();

        int ptIndex = 0;

        for(double range : shot.ranges){

            double ptX = shot.origin.x + shot.direction.x * range;
            double ptY = shot.origin.y + shot.direction.y * range;
            double ptZ = shot.origin.z + shot.direction.z * range;

            System.out.println(ptX+"\t"+ptY+"\t"+ptZ+"\t"+shot.reflectances[ptIndex]+"\t"+
                    shot.deviations[ptIndex]+"\t"+shot.amplitudes[ptIndex]+"\t"+shot.times[ptIndex]);

            ptIndex++;
        }                
    }

} catch (Exception ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}finally{
    rxpReader.close();
}

format-las: README

JLas

Installation with Maven

Add the following repository :

<repository>
    <id>amap-maven-central</id>
    <name>libs-release</name>
    <url>http://manosque.cirad.fr:8081/artifactory/libs-release-local</url>
</repository>

And the following dependency:

<dependency>
    <groupId>fr.amap.lidar</groupId>
    <artifactId>format-las</artifactId>
    <version>1.0.0</version>
</dependency>

Usage

The JLas library allows to read files in the .las or .laz format from Java language in the most easiest way.
The .las reader is using pure Java language, however the .laz reader uses a wrapper of the laszip library.

Las reader features:

Read las header
Read las format until 1.4
Read las point format until format 3

You cannot :

Write las file

Laz reader features:

Read las format until 1.4
Read las point format until format 1.0
Read las point attributes : x, y, z, echo range, echo number, echo recording time, echo intensity, echo classification

You cannot :

Write laz file
Read other attributes than x, y, z, echo range, echo number, echo recording time, echo intensity, echo classification
Use this library with 32 bits JVM, and other systems than Windows and Linux.

Use case : read a *.laz file :

LazExtraction lazReader = new LazExtraction();

try {

    lazReader.openLazFile(new File("/home/Documents/sample.laz"));

    //read header
    LasHeader header = lazReader.getHeader();

    System.out.println("Number of point records : "+header.getNumberOfPointrecords());

    //get the points from the file 
    Iterator<LasPoint> iterator = lazReader.iterator();

    while(iterator.hasNext()){

        LasPoint point = iterator.next();

        double ptX = header.getxOffset() + point.x * header.getxScaleFactor();
        double ptY = header.getyOffset() + point.y * header.getyScaleFactor();
        double ptZ = header.getzOffset() + point.z * header.getzScaleFactor();

        System.out.println(ptX+"\t"+ptY+"\t"+ptZ);
    }

} catch (Exception ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}finally{
    lazReader.close();
}

Use case : read a *.las file :

LasReader lasReader = new LasReader();

try {
    lasReader.open(new File("/home/Documents/sample.las"));

    LasHeader header = lasReader.getHeader();

    Iterator<PointDataRecordFormat> iterator = lasReader.iterator();

    while(iterator.hasNext()){

        PointDataRecordFormat point = iterator.next();

        double ptX = header.getxOffset() + point.getX() * header.getxScaleFactor();
        double ptY = header.getyOffset() + point.getY() * header.getyScaleFactor();
        double ptZ = header.getzOffset() + point.getZ() * header.getzScaleFactor();

        System.out.println(ptX+"\t"+ptY+"\t"+ptZ);
    }

} catch (Exception ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}

format-gridded: README

JGridScan

Installation with Maven

Add the following repository :

<repository>
    <id>amap-maven-central</id>
    <name>libs-release</name>
    <url>http://manosque.cirad.fr:8081/artifactory/libs-release-local</url>
</repository>

And the following dependency:

<dependency>
    <groupId>fr.amap.lidar</groupId>
    <artifactId>format-gridded</artifactId>
    <version>1.0.0</version>
</dependency>

Usage

Use case: read a *.ptg file :

PTGReader ptgReader = new PTGReader();

try {
    File ptgFile = new File("/home/Documents/scan-1.ptg");

    ptgReader.openPTGFile(ptgFile);

    if(ptgReader.isAsciiFile()){ //the opened file contains the scan list

        List<File> scanList = ptgReader.getScanList();

        System.out.println("Scan list :\n");

        for(File file : scanList){
            System.out.println(file.getAbsolutePath());
        }

    }else{ //the opened file is a binary scan

        PTGScan scan = new PTGScan();

        try {
            scan.openScanFile(ptgFile);

            PTGHeader header = scan.getHeader();

            //get the transformation matrix of the scan
            Mat4D transfMatrix = header.getTransfMatrix();

            Iterator<LPoint> iterator = scan.iterator();

            while(iterator.hasNext()){

                LPoint point = iterator.next();

                double ptX, ptY, ptZ;

                if(header.isPointInFloatFormat()){
                    ptX = ((LFloatPoint)point).x;
                    ptY = ((LFloatPoint)point).y;
                    ptZ = ((LFloatPoint)point).z;
                }else{
                    ptX = ((LDoublePoint)point).x;
                    ptY = ((LDoublePoint)point).y;
                    ptZ = ((LDoublePoint)point).z;
                }

                Vec4D ptTransformed = Mat4D.multiply(transfMatrix, new Vec4D(ptX, ptY, ptZ, 1));

                System.out.println(ptTransformed.x+"\t"+ptTransformed.y+"\t"+ptTransformed.z);
            }


        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

} catch (IOException ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}

Use case : read a *.ptx file :

PTXReader ptxReader = new PTXReader();

try {
    ptxReader.openPTXFile(new File("/home/Documents/sample.ptx"));

    List<PTXScan> singlesScans = ptxReader.getSinglesScans();

    if(ptxReader.getNbScans() > 0){

        PTXScan scan = singlesScans.get(0);
        PTXHeader header = scan.getHeader();

        Mat4D transfMatrix = header.getTransfMatrix();

        Iterator<LPoint> iterator = scan.iterator();

        while(iterator.hasNext()){

            LPoint point = iterator.next();

            double ptX, ptY, ptZ;

            if(header.isPointInFloatFormat()){
                ptX = ((LFloatPoint)point).x;
                ptY = ((LFloatPoint)point).y;
                ptZ = ((LFloatPoint)point).z;
            }else{
                ptX = ((LDoublePoint)point).x;
                ptY = ((LDoublePoint)point).y;
                ptZ = ((LDoublePoint)point).z;
            }

            Vec4D ptTransformed = Mat4D.multiply(transfMatrix, new Vec4D(ptX, ptY, ptZ, 1));

            System.out.println(ptTransformed.x+"\t"+ptTransformed.y+"\t"+ptTransformed.z);
        }
    }

} catch (IOException ex) {
    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}

threed-viewer: README

ThreedViewer

Simple 3D viewer based on jogl.

Installation with Maven

Add the following repository :

<repository>
    <id>amap-maven-central</id>
    <name>libs-release</name>
    <url>http://manosque.cirad.fr:8081/artifactory/libs-release-local</url>
</repository>

And the following dependency:

<dependency>
    <groupId>fr.amap.threed</groupId>
    <artifactId>threed-viewer</artifactId>
    <version>1.0.0</version>
</dependency>

Usage

1. Create a window with an OpenGL context

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 400, "OpenGL window");
viewer3D.show();

The code above create a window at location 0, 0 with a size of 500x400 and creates an OpenGL context.

2. Add a scene object

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 400, "OpenGL window");

SceneObject cube = new SimpleSceneObject(GLMeshFactory.createCube(2));        
viewer3D.getScene().addSceneObject(cube);
viewer3D.show();

The code above create a new SceneObject with a mesh representing a cube.
GLMeshFactory provides some methods to generate current primitives.

3. Bind a shader to an object

SceneObject cube = new SimpleSceneObject(GLMeshFactory.createCube(2));

//use a global shader, shared by any objects using this shader
cube.setShader(Scene.colorShader);

//or use an unique shader
cube.setShader(new ColorShader());

viewer3D.getScene().addSceneObject(cube);
Shader Variables Usage
SimpleShader uniform mat4 viewMatrix Camera transformation
uniform mat4 projMatrix Camera projection
uniform mat4 transformation Object transformation
uniform vec3 color Unique color
attribute vec4 position Vertex positions
ColorShader uniform mat4 viewMatrix Camera transformation
uniform mat4 projMatrix Camera projection
uniform mat4 transformation Object transformation
uniform vec3 color Vertex colors
attribute vec4 position Vertex positions
PhongShader uniform mat4 viewMatrix Camera transformation
uniform mat4 projMatrix Camera projection
uniform mat4 transformation Object transformation
uniform vec3 color Vertex colors
attribute vec3 position Vertex positions
attribute vec3 normal Vertex normales
InstanceShader/InstanceLightedShader uniform mat4 viewMatrix Camera transformation
uniform mat4 projMatrix Camera projection
attribute vec3 position Vertex positions
attribute vec3 instance_position Position of the object's instance
attribute vec4 instance_color Color of the object's instance
TextureShader uniform mat4 viewMatrixOrtho Camera transformation
uniform mat4 projMatrixOrtho Camera projection
attribute vec4 position Vertex positions
attribute vec2 textureCoordinates Vertex linked texture coordinates

The SimpleShader can be used when you want to draw an object with a single color and be able to move the object.

The ColorShader can be used to draw an object with a different color for each vertex.
This shader allows the object transformation too.

The PhongShader simulates a light with a phong model, built with specular, diffuse and ambiant colors. Those color values are fixed in the shader.

The InstanceShader/InstanceLighted shaders both allows to render multiple instances of an object, those instances can only be parametrized with their locations.
The InstanceLightedShader is the same as InstanceShader but simulate a prebuilt light. This one is specific and should not be used.

TextureShader is a shader handling texture coordinates. It can be used to draw head up display (HUD) on the screen.

Global uniforms :

The uniforms « viewMatrix » and « projMatrix » are updated from the Scene when the camera is updated.
This means you can create your own shader and those two variables will be automatically udpated.
In another hand, if you want to update your own view matrix and projection matrix, don't name your variables like the two above.

4. Scene object types

4.1. PointCloudSceneObject

Represents a point cloud.

-Provides methods to add points with attributes easily.

-Can be mouse pickable (explained after).

Use case : spherical point cloud creation with mouse picking

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 500, "3d view");
viewer3D.setDynamicDraw(true);
viewer3D.getAnimator().setUpdateFPSFrames(1, null);

PointCloudSceneObject pointCloud = new PointCloudSceneObject();
pointCloud.setMousePickable(true);

for (int i = 1; i < 180; i++) {

    for (int j = -179; j < 179; j++) {

        double theta = Math.toRadians(i);
        double phi = Math.toRadians(j);

        float x = (float)(10*Math.sin(theta)*Math.cos(phi));
        float y = (float)(10*Math.sin(theta)*Math.sin(phi));
        float z = (float)(10*Math.cos(theta));

        pointCloud.addPoint(x, y ,z);
        pointCloud.addValue("attribut", (float) (Math.random()*255));

    }
}

pointCloud.initMesh();
pointCloud.setShader(Scene.colorShader);

viewer3D.getScene().addSceneObject(pointCloud);

SceneObjectListener pointcloudListener = new SceneObjectListener() {
    @Override
    public void clicked(SceneObject sceneObject, MousePicker mousePicker) {

        Point3F point = (Point3F) sceneObject.doPicking(
                viewer3D.getScene().getMousePicker());

        if(point != null){
            viewer3D.getScene().getCamera().setLocation(
                    new Vec3F(point.x, point.y, point.z));
        }
    }
};

pointCloud.addSceneObjectListener(pointcloudListener);
viewer3D.show();

4.2. VoxelSpaceSceneObject

Scene object dedicated to display voxels.

-All voxels are drawn with a single draw call (instance rendering)
-Can be mouse pickable
-Handle filtering

4.3. RasterSceneObject

Represents a DTM.

-Is mouse pickable

Use case : load a dtm from a file and handle selection events
```java
final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 500, "3d view");
viewer3D.setDynamicDraw(false);

RasterSceneObject dtm = new RasterSceneObject(
AsciiGridHelper.readFromAscFile(
new File("/media/example.asc")));

dtm.setMousePickable(true);

viewer3D.getScene().addSceneObject(dtm);

//add a camera listener
viewer3D.getScene().getCamera().addCameraListener(new CameraAdapter() {
@Override
public void locationChanged(Vec3F location) {
viewer3D.getScene().setLightPosition(viewer3D.getScene().getCamera().getLocation());
}
});

//add a scene object listener
SceneObjectListener objectListener = new SceneObjectListener() {
@Override
public void clicked(SceneObject sceneObject, MousePicker mousePicker) {

    Integer index = ((RasterSceneObject)sceneObject).doPicking(mousePicker);

    if(index != null){
        Point3F position = ((RasterSceneObject)sceneObject).getVertex(index);
        System.out.println(position.x + " "+position.y+ " "+position.z);
    }
}

};

dtm.addSceneObjectListener(objectListener);

viewer3D.show();
```

4.4. SimpleSceneObject

Represents a simple scene object.

-Draw the provided mesh

Use case : create a colored triangle

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 500, "3d view");
viewer3D.setDynamicDraw(false);

GLMesh mesh = new SimpleGLMesh();

float vertexData[] = new float[]
{-2.0f, 2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
-2.0f, -2.0f, -2.0f};

float colorData[] = new float[]
{1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f};

int indexData[] = new int[]
{0, 1, 2};

mesh.setVertexBuffer(Buffers.newDirectFloatBuffer(vertexData));
mesh.indexBuffer = Buffers.newDirectIntBuffer(indexData);
mesh.colorBuffer = Buffers.newDirectFloatBuffer(colorData);
mesh.vertexCount = indexData.length;

SimpleSceneObject sceneObject = new SimpleSceneObject(mesh);
sceneObject.setShader(new ColorShader());

viewer3D.getScene().addSceneObject(sceneObject);

viewer3D.show();

5. Set up camera

The default implemented camera is a trackball camera.
You can configure the target of the camera (where it looks at) and its position.

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 500, "3d view");
viewer3D.setDynamicDraw(false);

SimpleSceneObject sceneObject = new SimpleSceneObject(GLMeshFactory.createCube(2));

//set the scene object as the pivot
viewer3D.getScene().getCamera().setPivot(sceneObject);

//set the camera location
viewer3D.getScene().getCamera().setLocation(new Vec3F(10, 50, 40));

viewer3D.getScene().addSceneObject(sceneObject);

viewer3D.show();

By default the pivot of the camera is the first scene object added to the scene.
Its default location is configured as the first scene object gravity center, plus x, y, z offsets.

6. Set up light

This part is useful if you use the phong shader, because this shader use the light position information to compute the mesh color.

final Viewer3D viewer3D = new Viewer3D(0, 0, 500, 500, "3d view");
viewer3D.setDynamicDraw(false);

SimpleSceneObject sceneObject = new SimpleSceneObject(GLMeshFactory.createCube(2));
sceneObject.setShader(new PhongShader());

sceneObject.setMousePickable(true);

viewer3D.getScene().addSceneObject(sceneObject);

//add a camera listener
viewer3D.getScene().getCamera().addCameraListener(new CameraAdapter() {
    @Override
    public void locationChanged(Vec3F location) {
        viewer3D.getScene().setLightPosition(
                viewer3D.getScene().getCamera().getLocation());
    }
});

viewer3D.show();

This code display a cube with a shader that handles light, and move the light according to the camera position.

7. Add keyboard, mouse, window listeners

To get access to the windows events ( window resizing, keyboard, mouse), you should get the GLRenderFrame. It provides access to all of those.

7.1. Add a mouse listener

viewer3D.getRenderFrame().addMouseListener(new MouseListener() {
    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseMoved(MouseEvent e) {}

    @Override
    public void mouseDragged(MouseEvent e) {}

    @Override
    public void mouseWheelMoved(MouseEvent e) {}
});

7.2. Add a keyboard listener

viewer3D.getRenderFrame().addKeyListener(new KeyListener() {
    @Override
    public void keyPressed(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}
});

7.3. Add a window listener

viewer3D.getRenderFrame().addWindowListener(new WindowListener() {
    @Override
    public void windowResized(WindowEvent e) {}

    @Override
    public void windowMoved(WindowEvent e) {}

    @Override
    public void windowDestroyNotify(WindowEvent e) {}

    @Override
    public void windowDestroyed(WindowEvent e) {}

    @Override
    public void windowGainedFocus(WindowEvent e) {}

    @Override
    public void windowLostFocus(WindowEvent e) {}

    @Override
    public void windowRepaint(WindowUpdateEvent e) {}
});

8. Handling events

The Viewer3D add automatically a default event manager at instantiation.
This default event manager provides the following functionnalities :

Command Action
Middle Mouse button clicked update mouse picker
Left mouse button down + mouse dragging rotate camera
Right mouse button down + mouse dragging camera side translation
Mouse wheel down/up  camera forward translation
D key down move light to the right
Q key down move light to the left
Z key down move light to top
S key down move light to bottom
Numpad 1 pressed set camera to look at the front side
Numpad 1 pressed + ctrl left down set camera to look at the back side
Numpad 3 pressed set camera to look at the right side
Numpad 3 pressed + ctrl left down Set camera to look at the left side
Numpad 7 pressed set camera to look at the top side
Numpad 7 pressed + ctrl left down set camera to look at the bottom side

If you wish you can override the default comportment with the call to « viewer3D.removeDefaultEventManager() ».
Then you can add your event manager.

//remove the default event manager
viewer3D.removeDefaultEventManager();

viewer3D.addEventListener(new EventManager(new InputMouseAdapter(), new InputKeyListener()) {
    @Override
    public void updateEvents() {
        //handle the events here

        //get access to the mouse
        mouse.isButtonDown(InputMouseAdapter.Button.LEFT);

        //get access to the keyboard
        keyboard.isKeyDown(KeyEvent.VK_E);
    }
});

Members

Manager: Philippe VERLEY

Developer: Florian de Boissieu, Philippe VERLEY