qgis / QGIS-Enhancement-Proposals

QEP's (QGIS Enhancement Proposals) are used in the process of creating and discussing new enhancements for QGIS
116 stars 37 forks source link

Point Clouds in QGIS #194

Open wonder-sk opened 3 years ago

wonder-sk commented 3 years ago

QGIS Enhancement: Point Clouds in QGIS

Date 2020/08/10

Author Martin Dobias (@wonder-sk)

Contact martin.dobias at lutraconsulting.co.uk

Maintainer @wonder-sk

Version QGIS 3.18

Crowdfunding https://www.lutraconsulting.co.uk/crowdfunding/pointcloud-qgis/

Summary

The aim of this proposal is to prepare infrastructure within QGIS to allow working with point cloud data. In the initial phase we are only concerned with visualization of the data - no editing or processing functionality is considered at this stage. The goal is to read most commonly used formats such as LAS/LAZ. We want to offer both 2D and 3D rendering with basic styling options (such as choosing which attribute to use and choice of colors and point sizes).

point-cloud-trencin Point cloud data from airborne laser scanning, coloring by classification, visualized in CloudCompare (Data from UGKK SR)

Introduction

A point cloud is a set of points in 3D space, most often with some extra attributes such as color, classification and others. Point clouds are most often coming from two main sources:

Point clouds are an increasingly popular type of data used in GIS, especially as the price of the technology is going down. Even consumer grade electronics such as the newer iPad models include lidar functionality, and applications are available which can create point clouds from these devices. Nowadays point clouds from laser scanning are commonly used to create high resolution digital elevation models (DEM).

The main challenge of using point cloud data comes from the sheer number of points in datasets: we need to deal with many millions to billions of points for rather small geographical areas, with country-wide surveys getting to trillions of points. With this amount of data it is not simply possible to convert the dataset to “ordinary” point vector layers - typical GIS tools and data formats like GeoPackage or Shapefile are not optimized for such amounts of data and any operations would take very long time.

point-cloud-top-elev Point cloud data from airborne laser scanning, coloring by elevation, visualized in CloudCompare (Data from UGKK SR)

Design

We will introduce a new map layer type within QGIS: point cloud layer, which will be used for all point cloud data. As mentioned above, we can’t reuse existing vector layer type (with point geometries) as it is not designed to support such large amounts of points.

For access to file-based data we have decided to use PDAL (“point data abstraction library”). It follows the approach taken by GDAL library: it provides a common interface to read, write or process point cloud data and various drivers (“readers” and “writers” in PDAL terminology) take care of specifics of different formats. In addition to that, PDAL comes with a concept of pipelines where one can combine various processing algorithms - probably we would support them at some point later after the initial implementation.

Next thing we need to consider is how to efficiently display the data. Iterating over all points in a dataset and drawing them is simply not going to work well and it would take too long. It would be also quite wasteful - when zoomed out, only a small amount of points need to be rendered. A second case to consider is that when a user zooms in a lot, we should be able to render only points within the area of interest rather than reading points outside of the current view. So we need a way to quickly query points in these different situations. This need is commonly solved by the use of space partitioning data structures (such as octrees) which hierarchically divide space into smaller volumes. An important feature is that not just leaves, but also internal nodes contain data: thanks to that we can not only quickly query points in a small area of interest, but also we can also get smaller portions of points at different zoom levels.

Point cloud data in commonly used formats like LAS are rather simple sets of points which do not have any indexing data structure associated, and the order of points may be arbitrary. Therefore we need to build such indexing data structure when opening these files in order to be able to render them - this is what other viewers (such as CloudCompare) do as well. As a part of indexing, we need to reorganize the points in order to be able to quickly access them when rendering. When indexing is done, we should have a tree data structure where each node contains a subset of the point cloud - such indexed point cloud may be either stored on the disk or kept in memory.

There are already various formats of indexed point clouds used by different pieces of software. All these formats are based on the same concept of using a hierarchical structure to partition the space typically using octree, having subsets of data in internal tree nodes:

It is desired to support some of these indexed formats natively in QGIS. PDAL already has some support, but it does not yet provide APIs for more fine-grained control (e.g. to get description of a particular node in the hierarchy, get data of just that node). It will be discussed with the PDAL community whether access to index hierarchy of such formats could be provided through PDAL APIs.

Implementation

Overview of the proposed new classes:

Indexing

If a data provider has an indexing structure that our implementation understands, it will provide access to its root node (a subclass of QgsPointCloudIndexNode). Point cloud layer will use it for querying data. Otherwise the data provider would return null root node, indicating that QgsPointCloudLayer needs to build its own index. This would be done in a background worker thread after the layer gets loaded. While the index is being built, rendering (2D or 3D) would not be available. Indexing would either be just in-memory or with data stored in a temporary location on disk to avoid high RAM consumption. When using disk storage, it would be worth considering using an indexing format used elsewhere (e.g. by Entwine, Potree, 3D Tiles). Storage of index on disk would also avoid the need to run indexing again when loading data in QGIS later.

To be discussed with the PDAL community what are the options of code reuse: currently PDAL does not offer access to indexing structures even if the underlying formats have them (such as EPT or I3S). The ideal solution would be if PDAL offered an API that QGIS could use for indexed point cloud formats, so that QGIS does not need extra providers for them. Similarly, it would be great if point cloud indexing could be done within PDAL. Currently PDAL does not offer such functionality, however there is a separate project Entwine from the makers of PDAL which does take care of creating indexed point clouds even for huge datasets.

QgsPointCloudIndexNode will be the base class for nodes in the hierarchy of point cloud indexes. It is up to the underlying implementation (which can be provider-specific) what kind of tree structure will be used as long as the following rules are fulfilled:

One complication is that for various point cloud data sources the whole hierarchy may not be immediately available - for example, when fetching data about root node, we also get information about few more levels deeper, but to access further descendants, another network request may need to be done. Loading of nodes may therefore need to be implemented in an asynchronous way and a signal would get emitted whenever a new node gets loaded.

2D Rendering

The whole rendering process will be driven by the QgsPointCloudLayerRenderer class (where the bulk of the work will be done in a worker thread as is the case with other layer renderers). In the first stage, we will figure out the acceptable geometry error (i.e. acceptable spacing between points) based on the map scale - for example, at the scale 1:1.000.000 we may be fine with 1km spacing between points, but at scale 1:1.000 we would need much lower, e.g 1 meter spacing. This setting will be exposed to users to control as a “dynamic filtering” setting, in case the user desires to override the default setting (e.g. for a higher quality render or faster rendering time).

Then we will traverse the index hierarchy and find nodes intersecting the map view extent and having sufficient geometry error. Finally we would fetch point cloud data from these nodes - either from the layer’s cache (if they are cached in memory already) or from the index nodes, and draw points one by one. Drawing of individual points would be handled by a subclass of QgsPointCloudRenderer.

In the initial work we plan one renderer implementation: QgsPointCloudSimpleRenderer. It would draw points with fixed size, the color of points would be determined by one of the attributes picked by the user (e.g. elevation, classification, return number) and chosen color ramp. Additionally, some basic filtering options should be supported (e.g. only the last return, only particular value(s) from classification, only a particular range of elevations).

In the future more renderers may be added, such as display of interpolated surface (calculated on the fly from point data), hillshade or contours.

3D Rendering

At the core, rendering of point clouds in 3D follows the same principles as the 2D rendering: using the hierarchical index, it will be determined which nodes should be displayed, based on that the point data would be loaded and displayed in the 3D scene. Some of the necessary infrastructure is already in place: QGIS 3D views support “chunked” rendering of terrain since the beginning (as the user gets closer to the terrain, more detailed elevation and map textures get rendered). As the mechanism is fairly generic and has been extended to vector layer data in the past, it can be extended for point clouds as well. Currently it has the limitation of always expecting a quadtree hierarchy, but this can be updated to allow more general tree hierarchies (octree or others).

The implementation would start with QgsPointCloud3DRenderer class, which would contain styling configuration and it would provide implementation of a 3D entity (Qt3DCore::QEntity) based on the existing QgsChunkedEntity class. The implementation will add a class derived from QgsChunkLoader which will handle fetching of point cloud data of a single node (chunk) and setting up vertex buffers, attributes and material for each node.

Styling options for 3D views should be similar to the options in 2D rendering: configuration of point size, coloring based on a single attribute and some simple data filtering options.

It would be very useful to also add the eye-dome lighting effect which improves the depth perception. For comparison - the same point cloud (colors according to classification), left image without eye-dome lighting, right image with eye-dome lighting:

Point cloud rendering, color coding based on classification (ground = blue, green = vegetation). Left: without eye-dome lighting. Right: with eye-dome lighting.

References

roya0045 commented 3 years ago

@wonder-sk Good. I guess that a short term option could be to use the json edit widget to create pipeline and send those to pdal directly. Or some other way to interface such as with python-pdal.

luipir commented 3 years ago

I'm curious to know when and if the processing followup is planned. Currently the only way seem to be the way the plugin by @luipir, but usage is not user friendly, and I'm not sure if the errors I'm getting are caused by my pipeline or implementation limitations ( no offense Luigi, I just have no clue what I'm doing trying to convert ept to gdal with 'min').

no problem... the plugin was a suboptimal proof of concept trying to attract more community developing model and pipeline on that. Didn't work, I suppose caused by my fault or believe too much in community contribution when pdal was still almost unknown in wide community.

luipir commented 3 years ago

Or some other way to interface such as with python-pdal.

do a simple processing script to use python-pdal... DO NOT USE my plugin dev when a python-pdal binding was not possible in at the beginning of qgis3 (python-pdal was binded to python2)

vpicavet commented 1 year ago

I saw there is another crowdfunding effort launched. Could you update this QEP according to the content of the crowdfunding targets, or add new QEP for the various improvement planned ? @wonder-sk

wonder-sk commented 1 year ago

hi @vpicavet the new campaign (https://www.lutraconsulting.co.uk/crowdfunding/pointcloud-processing-qgis/) is not affecting this QEP, so it should not need any additions. If the new crowdfunding gets to a successful end, we will be adding a new QEP for PDAL integration in QGIS...

vpicavet commented 1 year ago

Ok, although I guess QEP for planned features of the crowdfunding effort would be useful even if the crowdfunding is not successful. It would keep track of wanted feature, allow community discussion, and maybe gather funding outside of the CF effort. This may not be the best place to discuss it though.