• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iTowns / itowns / 9838883567

08 Jul 2024 11:39AM UTC coverage: 89.735% (-0.006%) from 89.741%
9838883567

push

github

Desplandis
feat: introducing workers for LAS parser

2830 of 3731 branches covered (75.85%)

Branch coverage included in aggregate %.

67 of 77 new or added lines in 2 files covered. (87.01%)

2 existing lines in 1 file now uncovered.

24673 of 26918 relevant lines covered (91.66%)

902.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.53
/src/Parser/LASParser.js
1
import * as THREE from 'three';
1✔
2
import { spawn, Thread, Transfer } from 'threads';
1✔
3

1✔
4
let _lazPerf;
1✔
5
let _thread;
1✔
6

3✔
7
function workerInstance() {
3✔
8
    return new Worker(
3✔
9
        /* webpackChunkName: "itowns_lasparser" */
3✔
10
        new URL('../Worker/LASLoaderWorker.js', import.meta.url),
3✔
11
        { type: 'module' },
1✔
12
    );
3✔
13
}
3✔
14

3✔
15
async function loader() {
3!
16
    if (_thread) { return _thread; }
3✔
17
    _thread = await spawn(workerInstance());
3✔
18
    if (_lazPerf) { _thread.lazPerf(_lazPerf); }
1✔
19
    return _thread;
2✔
20
}
2✔
21

2✔
22
function buildBufferGeometry(attributes) {
2✔
23
    const geometry = new THREE.BufferGeometry();
2✔
24

2✔
25
    const positionBuffer = new THREE.BufferAttribute(attributes.position, 3);
2✔
26
    geometry.setAttribute('position', positionBuffer);
2✔
27

2✔
28
    const intensityBuffer = new THREE.BufferAttribute(attributes.intensity, 1);
2✔
29
    geometry.setAttribute('intensity', intensityBuffer);
2✔
30

2✔
31
    const returnNumber = new THREE.BufferAttribute(attributes.returnNumber, 1);
2✔
32
    geometry.setAttribute('returnNumber', returnNumber);
2✔
33

2✔
34
    const numberOfReturns = new THREE.BufferAttribute(attributes.numberOfReturns, 1);
2✔
35
    geometry.setAttribute('numberOfReturns', numberOfReturns);
2✔
36

2✔
37
    const classBuffer = new THREE.BufferAttribute(attributes.classification, 1);
2✔
38
    geometry.setAttribute('classification', classBuffer);
2✔
39

2✔
40
    const pointSourceID = new THREE.BufferAttribute(attributes.pointSourceID, 1);
2✔
41
    geometry.setAttribute('pointSourceID', pointSourceID);
2✔
42

1✔
43
    if (attributes.color) {
1✔
44
        const colorBuffer = new THREE.BufferAttribute(attributes.color, 4, true);
2✔
45
        geometry.setAttribute('color', colorBuffer);
2✔
46
    }
2✔
47
    const scanAngle = new THREE.BufferAttribute(attributes.scanAngle, 1);
2✔
48
    geometry.setAttribute('scanAngle', scanAngle);
2✔
49

2✔
50
    geometry.userData.origin = new THREE.Vector3().fromArray(attributes.origin);
1✔
51

1✔
52
    return geometry;
1✔
53
}
1✔
54

1✔
55
/** The LASParser module provides a [parse]{@link
1✔
56
 * module:LASParser.parse} method that takes a LAS or LAZ (LASZip) file in, and
1✔
57
 * gives a `THREE.BufferGeometry` containing all the necessary attributes to be
1✔
58
 * displayed in iTowns. It uses the
1✔
59
 * [copc.js](https://github.com/connormanning/copc.js/) library.
1✔
60
 *
1✔
61
 * @module LASParser
1✔
62
 */
1✔
63
export default {
1✔
64
    /*
1✔
65
     * Set the laz-perf decoder path.
1✔
66
     * @param {string} path - path to `laz-perf.wasm` folder.
2!
67
     */
×
68
    enableLazPerf(path) {
2✔
69
        if (!path) {
1✔
70
            throw new Error('Path to laz-perf is mandatory');
1✔
71
        }
1✔
72
        _lazPerf = path;
1✔
73
    },
3✔
74

3✔
75
    /**
3✔
76
     * Terminate all worker instances.
3✔
77
     * @returns {Promise<void>}
3✔
78
     */
3✔
79
    terminate() {
1✔
80
        const currentThread = _thread;
1✔
81
        _thread = undefined;
1✔
82
        return Thread.terminate(currentThread);
1✔
83
    },
1✔
84

1✔
85
    /**
1✔
86
     * Parses a chunk of a LAS or LAZ (LASZip) and returns the corresponding
1✔
87
     * `THREE.BufferGeometry`.
1✔
88
     *
1✔
89
     * @param {ArrayBuffer} data - The file content to parse.
1✔
90
     * @param {Object} options
1✔
91
     * @param {Object} options.in - Options to give to the parser.
1✔
92
     * @param {number} options.in.pointCount - Number of points encoded in this
1✔
93
     * data chunk.
1✔
94
     * @param {Object} options.in.header - Partial LAS file header.
1✔
95
     * @param {number} options.in.header.pointDataRecordFormat - Type of Point
1✔
96
     * Data Record contained in the LAS file.
1✔
97
     * @param {number} options.in.header.pointDataRecordLength - Size (in bytes)
1✔
98
     * of the Point Data Record.
1✔
99
     * @param {Object} [options.eb] - Extra bytes LAS VLRs headers.
1✔
100
     * @param { 8 | 16 } [options.in.colorDepth] - Color depth (in bits).
1✔
101
     * Defaults to 8 bits for LAS 1.2 and 16 bits for later versions
1✔
UNCOV
102
     * (as mandatory by the specification)
×
103
     *
×
104
     * @return {Promise<THREE.BufferGeometry>} A promise resolving with a
×
105
     * `THREE.BufferGeometry`.
×
106
     */
×
NEW
107
    async parseChunk(data, options = {}) {
×
NEW
108
        const lasLoader = await loader();
×
NEW
109
        const parsedData = await lasLoader.parseChunk(Transfer(data), {
×
NEW
110
            pointCount: options.in.pointCount,
×
NEW
111
            header: options.in.header,
×
NEW
112
            eb: options.eb,
×
113
            colorDepth: options.in.colorDepth,
1✔
114
        });
1✔
115

1✔
116
        const geometry = buildBufferGeometry(parsedData.attributes);
1✔
117
        geometry.computeBoundingBox();
1✔
118
        return geometry;
1✔
119
    },
1✔
120

1✔
121
    /**
1✔
122
     * Parses a LAS file or a LAZ (LASZip) file and return the corresponding
1✔
123
     * `THREE.BufferGeometry`.
1✔
124
     *
1✔
125
     * @param {ArrayBuffer} data - The file content to parse.
1✔
126
     * @param {Object} [options]
1✔
127
     * @param {Object} [options.in] - Options to give to the parser.
1✔
128
     * @param { 8 | 16 } [options.in.colorDepth] - Color depth (in bits).
1✔
129
     * Defaults to 8 bits for LAS 1.2 and 16 bits for later versions
1✔
130
     * (as mandatory by the specification)
1✔
131
     *
3✔
132
     * @return {Promise} A promise resolving with a `THREE.BufferGeometry`. The
3✔
133
     * header of the file is contained in `userData`.
3!
UNCOV
134
     */
×
135
    async parse(data, options = {}) {
3✔
136
        if (options.out?.skip) {
3✔
137
            console.warn("Warning: options 'skip' not supported anymore");
3✔
138
        }
3✔
139

3✔
140
        const input = options.in;
3✔
141

1✔
142
        const lasLoader = await loader();
3✔
143
        const parsedData = await lasLoader.parseFile(Transfer(data), {
2✔
144
            colorDepth: input?.colorDepth,
2✔
145
        });
2✔
146

2✔
147
        const geometry = buildBufferGeometry(parsedData.attributes);
1✔
148
        geometry.userData.header = parsedData.header;
1✔
149
        geometry.computeBoundingBox();
1✔
150
        return geometry;
1✔
151
    },
1✔
152
};
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc