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

iTowns / itowns / 8896060322

30 Apr 2024 01:56PM UTC coverage: 89.361% (-0.04%) from 89.402%
8896060322

push

github

Desplandis
docs(COPC): expose doc for COPCLayer and COPCSource

2712 of 3600 branches covered (75.33%)

Branch coverage included in aggregate %.

23226 of 25426 relevant lines covered (91.35%)

342.94 hits per line

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

94.32
/src/Parser/LASLoader.js
1
import { LazPerf } from 'laz-perf';
1✔
2
import { Las } from 'copc';
1✔
3

1✔
4
/**
1✔
5
 * @typedef {Object} Header - Partial LAS header.
1✔
6
 * @property {number} header.pointDataRecordFormat - Type of point data
1✔
7
 * records contained by the buffer.
1✔
8
 * @property {number} header.pointDataRecordLength - Size (in bytes) of the
1✔
9
 * point data records. If the specified size is larger than implied by the
1✔
10
 * point data record format (see above) the remaining bytes are user-specfic
1✔
11
 * "extra bytes". Those are described by an Extra Bytes VLR.
1✔
12
 * @property {number[]} header.scale - Scale factors (an array `[xScale,
1✔
13
 * yScale, zScale]`) multiplied to the X, Y, Z point record values.
1✔
14
 * @property {number[]} header.offset - Offsets (an array `[xOffset,
1✔
15
 * xOffset, zOffset]`) added to the scaled X, Y, Z point record values.
1✔
16
 */
1✔
17

1✔
18
function defaultColorEncoding(header) {
2✔
19
    return (header.majorVersion === 1 && header.minorVersion <= 2) ? 8 : 16;
1✔
20
}
1✔
21

1✔
22
/**
1✔
23
 * @classdesc
1✔
24
 * Loader for LAS and LAZ (LASZip) point clouds. It uses the copc.js library and
1✔
25
 * the laz-perf decoder under the hood.
1✔
26
 *
1✔
27
 * The laz-perf web assembly module is lazily fetched at runtime when a parsing
1✔
28
 * request is initiated. Location of laz-perf wasm defaults to the unpkg
1✔
29
 * repository.
1✔
30
 */
1✔
31
class LASLoader {
1✔
32
    constructor() {
1✔
33
        this._wasmPath = 'https://cdn.jsdelivr.net/npm/laz-perf@0.0.6/lib';
1✔
34
        this._wasmPromise = null;
1✔
35
    }
3✔
36

3✔
37
    _initDecoder() {
3✔
38
        if (this._wasmPromise) {
3✔
39
            return this._wasmPromise;
3✔
40
        }
1✔
41

1✔
42
        this._wasmPromise = LazPerf.create({
1✔
43
            locateFile: file => `${this._wasmPath}/${file}`,
1✔
44
        });
2✔
45

2✔
46
        return this._wasmPromise;
2✔
47
    }
2✔
48

2✔
49
    _parseView(view, options) {
2!
50
        const colorDepth = options.colorDepth ?? 16;
2✔
51

2✔
52
        const getPosition = ['X', 'Y', 'Z'].map(view.getter);
2✔
53
        const getIntensity = view.getter('Intensity');
2✔
54
        const getReturnNumber = view.getter('ReturnNumber');
2✔
55
        const getNumberOfReturns = view.getter('NumberOfReturns');
2✔
56
        const getClassification = view.getter('Classification');
2✔
57
        const getPointSourceID = view.getter('PointSourceId');
2✔
58
        const getColor = view.dimensions.Red ?
2✔
59
            ['Red', 'Green', 'Blue'].map(view.getter) : undefined;
2✔
60
        const getScanAngle = view.getter('ScanAngle');
2✔
61

2✔
62
        const positions = new Float32Array(view.pointCount * 3);
2✔
63
        const intensities = new Uint16Array(view.pointCount);
2✔
64
        const returnNumbers = new Uint8Array(view.pointCount);
2✔
65
        const numberOfReturns = new Uint8Array(view.pointCount);
2✔
66
        const classifications = new Uint8Array(view.pointCount);
2✔
67
        const pointSourceIDs = new Uint16Array(view.pointCount);
2✔
68
        const colors = getColor ? new Uint8Array(view.pointCount * 4) : undefined;
2✔
69
        /*
2✔
70
        As described by the LAS spec, Scan Angle is encoded:
2✔
71
        - as signed char in a valid range from -90 to +90 (degrees) prior to the LAS 1.4 Point Data Record Formats (PDRF) 6
2✔
72
        - as a signed short in a valid range from -30 000 to +30 000. Those values represents scan angles from -180 to +180
2✔
73
          degrees with an increment of 0.006 for PDRF >= 6.
2✔
74
        The copc.js library does the degree convertion and stores it as a `Float32`.
2✔
75
        */
2✔
76
        const scanAngles = new Float32Array(view.pointCount);
2✔
77

6✔
78
        // For precision we take the first point that will be use as origin for a local referentiel.
2✔
79
        const origin = getPosition.map(f => f(0)).map(val => Math.floor(val));
100,106✔
80

100,106✔
81
        for (let i = 0; i < view.pointCount; i++) {
100,106✔
82
            // `getPosition` apply scale and offset transform to the X, Y, Z
100,106✔
83
            // values. See https://github.com/connormanning/copc.js/blob/master/src/las/extractor.ts.
100,106✔
84
            const [x, y, z] = getPosition.map(f => f(i));
100,106✔
85
            positions[i * 3] = x - origin[0];
100,106✔
86
            positions[i * 3 + 1] = y - origin[1];
100,106✔
87
            positions[i * 3 + 2] = z - origin[2];
100,000✔
88

100,000✔
89
            intensities[i] = getIntensity(i);
100,000✔
90
            returnNumbers[i] = getReturnNumber(i);
100,000✔
91
            numberOfReturns[i] = getNumberOfReturns(i);
100,000✔
92

100,000✔
93
            if (getColor) {
100,000✔
94
                // Note that we do not infer color depth as it is expensive
100,000✔
95
                // (i.e. traverse the whole view to check if there exists a red,
100,000✔
96
                // green or blue value > 255).
100,000✔
97
                let [r, g, b] = getColor.map(f => f(i));
100,106✔
98

100,106✔
99
                if (colorDepth === 16) {
100,106✔
100
                    r /= 256;
100,106✔
101
                    g /= 256;
100,106✔
102
                    b /= 256;
100,106✔
103
                }
100,106✔
104

100,106✔
105
                colors[i * 4] = r;
100,106✔
106
                colors[i * 4 + 1] = g;
100,106✔
107
                colors[i * 4 + 2] = b;
100,106✔
108
                colors[i * 4 + 3] = 255;
100,106✔
109
            }
100,106✔
110

100,106✔
111
            classifications[i] = getClassification(i);
100,106✔
112
            pointSourceIDs[i] = getPointSourceID(i);
1✔
113
            scanAngles[i] = getScanAngle(i);
1✔
114
        }
1✔
115

1✔
116
        return {
1✔
117
            position: positions,
1✔
118
            intensity: intensities,
1✔
119
            returnNumber: returnNumbers,
1✔
120
            numberOfReturns,
1✔
121
            classification: classifications,
1✔
122
            pointSourceID: pointSourceIDs,
1✔
123
            color: colors,
1✔
124
            scanAngle: scanAngles,
1✔
125
            origin,
1✔
126
        };
1✔
127
    }
1✔
128

1✔
129
    /**
1✔
130
     * Set LazPerf decoder path.
1✔
131
     * @param {string} path - path to `laz-perf.wasm` folder.
1✔
132
     */
1✔
133
    set lazPerf(path) {
1✔
134
        this._wasmPath = path;
1✔
135
        this._wasmPromise = null;
1✔
136
    }
1✔
137

1✔
138
    /**
1✔
139
     * Parses a LAS or LAZ (LASZip) chunk. Note that this function is
1✔
140
     * **CPU-bound** and shall be parallelised in a dedicated worker.
1✔
141
     * @param {Uint8Array} data - File chunk data.
1✔
142
     * @param {Object} options - Parsing options.
1✔
143
     * @param {Header} options.header - Partial LAS header.
×
144
     * @param {number} options.pointCount - Number of points encoded in this
×
145
     * data chunk.
×
146
     * @param {Las.ExtraBytes[]} [options.eb] - Extra bytes LAS VLRs
×
147
     * headers.
×
148
     * @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
×
149
     * Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
×
150
     * versions (as mandatory by the specification).
×
151
     */
×
152
    async parseChunk(data, options) {
×
153
        const { header, eb, pointCount } = options;
×
154
        const { pointDataRecordFormat, pointDataRecordLength } = header;
1✔
155

1✔
156
        const colorDepth = options.colorDepth ?? defaultColorEncoding(header);
1✔
157

1✔
158
        const bytes = new Uint8Array(data);
1✔
159
        const pointData = await Las.PointData.decompressChunk(bytes, {
1✔
160
            pointCount,
1✔
161
            pointDataRecordFormat,
1✔
162
            pointDataRecordLength,
1✔
163
        }, this._initDecoder());
1✔
164

1✔
165
        const view = Las.View.create(pointData, header, eb);
1✔
166
        const attributes = this._parseView(view, { colorDepth });
1✔
167
        return { attributes };
3✔
168
    }
3✔
169

3✔
170
    /**
3✔
171
     * Parses a LAS or LAZ (LASZip) file. Note that this function is
3!
172
     * **CPU-bound** and shall be parallelised in a dedicated worker.
3✔
173
     * @param {ArrayBuffer} data - Binary data to parse.
3✔
174
     * @param {Object} [options] - Parsing options.
2✔
175
     * @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
3✔
176
     * Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
3✔
177
     * versions (as mandatory by the specification)
3✔
178
     */
2✔
179
    async parseFile(data, options = {}) {
3✔
180
        const bytes = new Uint8Array(data);
1✔
181

1✔
182
        const pointData = await Las.PointData.decompressFile(bytes, this._initDecoder());
3✔
183

3✔
184
        const header = Las.Header.parse(bytes);
3✔
185
        const colorDepth = options.colorDepth ?? defaultColorEncoding(header);
1✔
186

1✔
187
        const getter = async (begin, end) => bytes.slice(begin, end);
1✔
188
        const vlrs = await Las.Vlr.walk(getter, header);
1✔
189
        const ebVlr = Las.Vlr.find(vlrs, 'LASF_Spec', 4);
1✔
190
        const eb = ebVlr && Las.ExtraBytes.parse(await Las.Vlr.fetch(getter, ebVlr));
1✔
191

1✔
192
        const view = Las.View.create(pointData, header, eb);
1✔
193
        const attributes = this._parseView(view, { colorDepth });
1✔
194
        return {
1✔
195
            header,
1✔
196
            attributes,
1✔
197
        };
1✔
198
    }
1✔
199
}
1✔
200

1✔
201
export default LASLoader;
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