las-laz-js 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE-APACHE +51 -0
- package/NOTICE +14 -0
- package/README.md +71 -27
- package/package.json +5 -3
- package/src/LasReader.js +134 -42
- package/src/colour/colourize.js +35 -3
- package/src/filter/dedupe.js +31 -14
- package/src/filter/filters.js +14 -10
- package/src/format/LasVlr.js +8 -5
- package/src/index.js +3 -3
- package/src/io/NodeFileSource.js +24 -2
- package/src/laz/ArithmeticDecoder.js +136 -0
- package/src/laz/IntegerDecompressor.js +77 -0
- package/src/laz/LazVlr.js +65 -0
- package/src/laz/chunkTable.js +109 -0
- package/src/laz/decompressChunk.js +115 -0
- package/src/laz/models.js +102 -0
- package/src/laz/v2/byte.js +33 -0
- package/src/laz/v2/gpstime11.js +118 -0
- package/src/laz/v2/point10.js +139 -0
- package/src/laz/v2/rgb12.js +79 -0
- package/src/laz/v3/layered.js +145 -0
- package/src/laz/v3/point14.js +266 -0
- package/src/read/decodeRecords.js +32 -14
- package/src/read/windows.js +21 -0
- package/src/workers/workerProtocol.js +10 -7
- package/src/write/writeLas.js +46 -13
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/laz/v2/rgb12.js
|
|
2
|
+
//==========================================================
|
|
3
|
+
// LASzip RGB12 item, version 2: the 6 colour bytes. A symbol says which
|
|
4
|
+
// bytes changed and whether green and blue differ from red; red's byte
|
|
5
|
+
// differences predict green's, and the mean of red's and green's
|
|
6
|
+
// predicts blue's (clamped to a byte). Shared by the 1.4 RGB14 item.
|
|
7
|
+
//
|
|
8
|
+
// Ported from laz-rs src/las/rgb.rs, module v2 (https://github.com/laz-rs/laz-rs),
|
|
9
|
+
// itself a port of LASzip by rapidlasso. (c) 2007-2014 martin isenburg,
|
|
10
|
+
// rapidlasso; (c) 2014 Uday Verma, Hobu, Inc.; (c) 2019 Thomas Montaigu.
|
|
11
|
+
// Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE and
|
|
12
|
+
// NOTICE). Changes: translated from Rust to JavaScript, Brent Buffham 2026.
|
|
13
|
+
//==========================================================
|
|
14
|
+
|
|
15
|
+
import { ArithmeticModel } from "../models.js";
|
|
16
|
+
|
|
17
|
+
var LE = true;
|
|
18
|
+
|
|
19
|
+
function clamp8(v) { return v < 0 ? 0 : v > 255 ? 255 : v; }
|
|
20
|
+
|
|
21
|
+
/** The seven models of one RGB context. */
|
|
22
|
+
export function RgbModels() {
|
|
23
|
+
this.byteUsed = new ArithmeticModel(128);
|
|
24
|
+
this.lowRed = new ArithmeticModel(256); this.highRed = new ArithmeticModel(256);
|
|
25
|
+
this.lowGreen = new ArithmeticModel(256); this.highGreen = new ArithmeticModel(256);
|
|
26
|
+
this.lowBlue = new ArithmeticModel(256); this.highBlue = new ArithmeticModel(256);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Decode one colour from the last one.
|
|
31
|
+
* @param {object} dec - ArithmeticDecoder
|
|
32
|
+
* @param {RgbModels} m
|
|
33
|
+
* @param {Uint16Array} last - [r, g, b]; overwritten with the new colour
|
|
34
|
+
*/
|
|
35
|
+
export function decodeRgb(dec, m, last) {
|
|
36
|
+
var sym = dec.decodeSymbol(m.byteUsed);
|
|
37
|
+
var lr = last[0], lg = last[1], lb = last[2];
|
|
38
|
+
var r, g, b, diff;
|
|
39
|
+
r = sym & 1 ? (dec.decodeSymbol(m.lowRed) + (lr & 255)) & 255 : lr & 255;
|
|
40
|
+
r += sym & 2 ? ((dec.decodeSymbol(m.highRed) + (lr >>> 8)) & 255) * 256 : lr & 0xFF00;
|
|
41
|
+
if (sym & 64) {
|
|
42
|
+
diff = (r & 255) - (lr & 255);
|
|
43
|
+
g = sym & 4 ? (dec.decodeSymbol(m.lowGreen) + clamp8(diff + (lg & 255))) & 255 : lg & 255;
|
|
44
|
+
if (sym & 16) {
|
|
45
|
+
diff = Math.trunc((diff + (g & 255) - (lg & 255)) / 2);
|
|
46
|
+
b = (dec.decodeSymbol(m.lowBlue) + clamp8(diff + (lb & 255))) & 255;
|
|
47
|
+
} else {
|
|
48
|
+
b = lb & 255;
|
|
49
|
+
}
|
|
50
|
+
diff = (r >>> 8) - (lr >>> 8);
|
|
51
|
+
g += sym & 8 ? ((dec.decodeSymbol(m.highGreen) + clamp8(diff + (lg >>> 8))) & 255) * 256 : lg & 0xFF00;
|
|
52
|
+
if (sym & 32) {
|
|
53
|
+
diff = Math.trunc((diff + (g >>> 8) - (lg >>> 8)) / 2);
|
|
54
|
+
b += ((dec.decodeSymbol(m.highBlue) + clamp8(diff + (lb >>> 8))) & 255) * 256;
|
|
55
|
+
} else {
|
|
56
|
+
b += lb & 0xFF00;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
g = r; b = r;
|
|
60
|
+
}
|
|
61
|
+
last[0] = r; last[1] = g; last[2] = b;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** RGB12 v2 decompressor: one per chunk. */
|
|
65
|
+
export function Rgb12Decompressor() {
|
|
66
|
+
this.size = 6;
|
|
67
|
+
this.models = new RgbModels();
|
|
68
|
+
this.last = new Uint16Array(3);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Rgb12Decompressor.prototype.first = function (src, s, view, o) {
|
|
72
|
+
for (var i = 0; i < 6; i++) view.setUint8(o + i, src[s + i]);
|
|
73
|
+
this.last[0] = view.getUint16(o, LE); this.last[1] = view.getUint16(o + 2, LE); this.last[2] = view.getUint16(o + 4, LE);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
Rgb12Decompressor.prototype.next = function (dec, view, o) {
|
|
77
|
+
decodeRgb(dec, this.models, this.last);
|
|
78
|
+
view.setUint16(o, this.last[0], LE); view.setUint16(o + 2, this.last[1], LE); view.setUint16(o + 4, this.last[2], LE);
|
|
79
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/laz/v3/layered.js
|
|
2
|
+
//==========================================================
|
|
3
|
+
// The LAS 1.4 layered items that follow Point14: RGB14, NIR14 (the
|
|
4
|
+
// second half of RGBNIR14) and Byte14 (extra bytes, one layer per
|
|
5
|
+
// byte). Each keeps up to four contexts keyed by the scanner channel
|
|
6
|
+
// Point14 passes on, with the version-3 quirk preserved: a context
|
|
7
|
+
// first used is seeded from the last one, and in version 3 later
|
|
8
|
+
// values are predicted from whichever context it was entered from.
|
|
9
|
+
//
|
|
10
|
+
// Ported from laz-rs src/las/rgb.rs, nir.rs and extra_bytes.rs, modules
|
|
11
|
+
// v3_v4 (https://github.com/laz-rs/laz-rs), itself a port of LASzip by
|
|
12
|
+
// rapidlasso. (c) 2007-2015 martin isenburg, rapidlasso; (c) 2019-2023
|
|
13
|
+
// Thomas Montaigu.
|
|
14
|
+
// Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE and
|
|
15
|
+
// NOTICE). Changes: translated from Rust to JavaScript, Brent Buffham 2026.
|
|
16
|
+
//==========================================================
|
|
17
|
+
|
|
18
|
+
import { ArithmeticDecoder } from "../ArithmeticDecoder.js";
|
|
19
|
+
import { ArithmeticModel } from "../models.js";
|
|
20
|
+
import { RgbModels, decodeRgb } from "../v2/rgb12.js";
|
|
21
|
+
|
|
22
|
+
var LE = true;
|
|
23
|
+
|
|
24
|
+
// laz-rs's context switch, shared by the three items: returns the index whose
|
|
25
|
+
// last value predicts this point (`last_idx`), after switching to `ctx`.
|
|
26
|
+
function enterContext(item, ctx, seed) {
|
|
27
|
+
var lastIdx = item.lastContext;
|
|
28
|
+
if (item.lastContext !== ctx) {
|
|
29
|
+
item.lastContext = ctx;
|
|
30
|
+
if (item.unused[ctx]) {
|
|
31
|
+
seed(lastIdx, ctx);
|
|
32
|
+
item.unused[ctx] = false;
|
|
33
|
+
if (item.version === 3) lastIdx = ctx;
|
|
34
|
+
}
|
|
35
|
+
if (item.version === 4) lastIdx = ctx;
|
|
36
|
+
}
|
|
37
|
+
return lastIdx;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function layer(item, bytes, q, n, fail, e, base) {
|
|
41
|
+
if (q + n > e) fail(q);
|
|
42
|
+
if (n === 0) return null;
|
|
43
|
+
var d = new ArithmeticDecoder(bytes, q, q + n, base);
|
|
44
|
+
d.readInitBytes();
|
|
45
|
+
return d;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** RGB14 (version 3 or 4). */
|
|
49
|
+
export function Rgb14Decompressor(version) {
|
|
50
|
+
this.size = 6;
|
|
51
|
+
this.version = version;
|
|
52
|
+
this.models = [new RgbModels(), new RgbModels(), new RgbModels(), new RgbModels()];
|
|
53
|
+
this.last = [new Uint16Array(3), new Uint16Array(3), new Uint16Array(3), new Uint16Array(3)];
|
|
54
|
+
this.unused = [true, true, true, true];
|
|
55
|
+
this.lastContext = 0;
|
|
56
|
+
this.dec = null;
|
|
57
|
+
}
|
|
58
|
+
Rgb14Decompressor.prototype.first = function (src, s, view, o, ctx) {
|
|
59
|
+
for (var i = 0; i < 6; i++) view.setUint8(o + i, src[s + i]);
|
|
60
|
+
var c = ctx.v;
|
|
61
|
+
this.last[c][0] = view.getUint16(o, LE); this.last[c][1] = view.getUint16(o + 2, LE); this.last[c][2] = view.getUint16(o + 4, LE);
|
|
62
|
+
this.unused = [true, true, true, true];
|
|
63
|
+
this.unused[c] = false;
|
|
64
|
+
this.lastContext = c;
|
|
65
|
+
};
|
|
66
|
+
Rgb14Decompressor.prototype.readLayerSizes = function (dv, q) { this.layerSize = dv.getUint32(q, LE); return q + 4; };
|
|
67
|
+
Rgb14Decompressor.prototype.readLayers = function (bytes, q, e, fail, base) { this.dec = layer(this, bytes, q, this.layerSize, fail, e, base); return q + this.layerSize; };
|
|
68
|
+
Rgb14Decompressor.prototype.next = function (view, o, ctx) {
|
|
69
|
+
var self = this;
|
|
70
|
+
var idx = enterContext(this, ctx.v, function (from, to) { self.last[to].set(self.last[from]); });
|
|
71
|
+
var rgb = this.last[idx];
|
|
72
|
+
if (this.dec) decodeRgb(this.dec, this.models[this.lastContext], rgb);
|
|
73
|
+
view.setUint16(o, rgb[0], LE); view.setUint16(o + 2, rgb[1], LE); view.setUint16(o + 4, rgb[2], LE);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** NIR14 (version 3 or 4): the near-infrared word of RGBNIR14. */
|
|
77
|
+
export function Nir14Decompressor(version) {
|
|
78
|
+
this.size = 2;
|
|
79
|
+
this.version = version;
|
|
80
|
+
this.used = []; this.low = []; this.high = [];
|
|
81
|
+
for (var i = 0; i < 4; i++) { this.used.push(new ArithmeticModel(4)); this.low.push(new ArithmeticModel(256)); this.high.push(new ArithmeticModel(256)); }
|
|
82
|
+
this.last = new Uint16Array(4);
|
|
83
|
+
this.unused = [true, true, true, true];
|
|
84
|
+
this.lastContext = 0;
|
|
85
|
+
this.dec = null;
|
|
86
|
+
}
|
|
87
|
+
Nir14Decompressor.prototype.first = function (src, s, view, o, ctx) {
|
|
88
|
+
view.setUint8(o, src[s]); view.setUint8(o + 1, src[s + 1]);
|
|
89
|
+
this.last[ctx.v] = view.getUint16(o, LE);
|
|
90
|
+
this.unused = [true, true, true, true];
|
|
91
|
+
this.unused[ctx.v] = false;
|
|
92
|
+
this.lastContext = ctx.v;
|
|
93
|
+
};
|
|
94
|
+
Nir14Decompressor.prototype.readLayerSizes = Rgb14Decompressor.prototype.readLayerSizes;
|
|
95
|
+
Nir14Decompressor.prototype.readLayers = Rgb14Decompressor.prototype.readLayers;
|
|
96
|
+
Nir14Decompressor.prototype.next = function (view, o, ctx) {
|
|
97
|
+
var self = this;
|
|
98
|
+
var idx = enterContext(this, ctx.v, function (from, to) { self.last[to] = self.last[from]; });
|
|
99
|
+
if (this.dec) {
|
|
100
|
+
var c = this.lastContext, last = this.last[idx];
|
|
101
|
+
var sym = this.dec.decodeSymbol(this.used[c]);
|
|
102
|
+
var v = sym & 1 ? (this.dec.decodeSymbol(this.low[c]) + (last & 255)) & 255 : last & 255;
|
|
103
|
+
v += sym & 2 ? ((this.dec.decodeSymbol(this.high[c]) + (last >>> 8)) & 255) * 256 : last & 0xFF00;
|
|
104
|
+
this.last[idx] = v;
|
|
105
|
+
}
|
|
106
|
+
view.setUint16(o, this.last[idx], LE);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** Byte14 (version 3 or 4): `count` extra bytes, one layer each. */
|
|
110
|
+
export function Byte14Decompressor(count, version) {
|
|
111
|
+
this.size = count;
|
|
112
|
+
this.version = version;
|
|
113
|
+
this.models = [null, null, null, null];
|
|
114
|
+
this.last = [new Uint8Array(count), new Uint8Array(count), new Uint8Array(count), new Uint8Array(count)];
|
|
115
|
+
this.unused = [true, true, true, true];
|
|
116
|
+
this.lastContext = 0;
|
|
117
|
+
this.sizes = new Array(count);
|
|
118
|
+
this.dec = new Array(count);
|
|
119
|
+
}
|
|
120
|
+
function byteModels(count) { var m = []; for (var i = 0; i < count; i++) m.push(new ArithmeticModel(256)); return m; }
|
|
121
|
+
Byte14Decompressor.prototype.first = function (src, s, view, o, ctx) {
|
|
122
|
+
for (var i = 0; i < this.size; i++) view.setUint8(o + i, src[s + i]);
|
|
123
|
+
this.last[ctx.v].set(src.subarray(s, s + this.size));
|
|
124
|
+
this.unused = [true, true, true, true];
|
|
125
|
+
this.unused[ctx.v] = false;
|
|
126
|
+
this.models[ctx.v] = byteModels(this.size);
|
|
127
|
+
this.lastContext = ctx.v;
|
|
128
|
+
};
|
|
129
|
+
Byte14Decompressor.prototype.readLayerSizes = function (dv, q) {
|
|
130
|
+
for (var i = 0; i < this.size; i++) { this.sizes[i] = dv.getUint32(q, LE); q += 4; }
|
|
131
|
+
return q;
|
|
132
|
+
};
|
|
133
|
+
Byte14Decompressor.prototype.readLayers = function (bytes, q, e, fail, base) {
|
|
134
|
+
for (var i = 0; i < this.size; i++) { this.dec[i] = layer(this, bytes, q, this.sizes[i], fail, e, base); q += this.sizes[i]; }
|
|
135
|
+
return q;
|
|
136
|
+
};
|
|
137
|
+
Byte14Decompressor.prototype.next = function (view, o, ctx) {
|
|
138
|
+
var self = this;
|
|
139
|
+
var idx = enterContext(this, ctx.v, function (from, to) { self.models[to] = byteModels(self.size); self.last[to].set(self.last[from]); });
|
|
140
|
+
var last = this.last[idx], models = this.models[ctx.v];
|
|
141
|
+
for (var i = 0; i < this.size; i++) {
|
|
142
|
+
if (this.dec[i]) last[i] = (last[i] + this.dec[i].decodeSymbol(models[i])) & 255;
|
|
143
|
+
view.setUint8(o + i, last[i]);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// src/laz/v3/point14.js
|
|
2
|
+
//==========================================================
|
|
3
|
+
// LASzip Point14 item, versions 3 and 4 (LAS 1.4 point formats 6-10):
|
|
4
|
+
// the 30-byte extended record, coded in nine LAYERS — channel/returns/
|
|
5
|
+
// XY, Z, classification, flags, intensity, scan angle, user data,
|
|
6
|
+
// point source, GPS time — each its own arithmetic stream, so a layer
|
|
7
|
+
// that never changes in a chunk costs nothing (size 0: the value stays
|
|
8
|
+
// the last one). Up to four contexts follow the scanner channel.
|
|
9
|
+
//
|
|
10
|
+
// Ported from laz-rs src/las/point6.rs, module v3_v4, and the 16x16
|
|
11
|
+
// return tables of src/las/utils.rs (https://github.com/laz-rs/laz-rs),
|
|
12
|
+
// itself a port of LASzip by rapidlasso. (c) 2007-2015 martin isenburg,
|
|
13
|
+
// rapidlasso; (c) 2019-2023 Thomas Montaigu.
|
|
14
|
+
// Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE and
|
|
15
|
+
// NOTICE). Changes: translated from Rust to JavaScript, working on raw
|
|
16
|
+
// record bytes, GPS time as two u32 halves, Brent Buffham 2026.
|
|
17
|
+
//==========================================================
|
|
18
|
+
|
|
19
|
+
import { ArithmeticDecoder } from "../ArithmeticDecoder.js";
|
|
20
|
+
import { ArithmeticModel } from "../models.js";
|
|
21
|
+
import { IntegerDecompressor } from "../IntegerDecompressor.js";
|
|
22
|
+
import { StreamingMedian } from "../v2/point10.js";
|
|
23
|
+
|
|
24
|
+
var LE = true;
|
|
25
|
+
var MULTI = 500, MULTI_MINUS = -10;
|
|
26
|
+
var CODE_FULL = MULTI - MULTI_MINUS + 1; // 511
|
|
27
|
+
var MULTI_TOTAL = MULTI - MULTI_MINUS + 5; // 515
|
|
28
|
+
|
|
29
|
+
/** NUMBER_RETURN_MAP_6CTX[n][r] (LASzip). */
|
|
30
|
+
var MAP_6CTX = [
|
|
31
|
+
[0, 1, 2, 3, 4, 5, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5], [1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
|
|
32
|
+
[2, 1, 2, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3], [3, 3, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
|
|
33
|
+
[4, 3, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], [5, 3, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
|
|
34
|
+
[3, 3, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4], [4, 3, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4],
|
|
35
|
+
[4, 3, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4], [5, 3, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4],
|
|
36
|
+
[5, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4], [5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4],
|
|
37
|
+
[5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 4, 4], [5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 4],
|
|
38
|
+
[5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5], [5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5]
|
|
39
|
+
];
|
|
40
|
+
/** NUMBER_RETURN_LEVEL_8CT[n][r] (LASzip): |n - r|, capped at 7. */
|
|
41
|
+
var LEVEL_8CT = [];
|
|
42
|
+
for (var ln = 0; ln < 16; ln++) { LEVEL_8CT.push([]); for (var lr = 0; lr < 16; lr++) LEVEL_8CT[ln].push(Math.min(7, Math.abs(ln - lr))); }
|
|
43
|
+
|
|
44
|
+
function medians(n) { var out = []; for (var i = 0; i < n; i++) out.push(new StreamingMedian()); return out; }
|
|
45
|
+
|
|
46
|
+
// One context's state, started from the last point of another (laz-rs from_last_point)
|
|
47
|
+
function Context(p) {
|
|
48
|
+
this.unused = false;
|
|
49
|
+
this.p = { x: p.x, y: p.y, z: p.z, intensity: p.intensity, bits: p.bits, flags: p.flags, cls: p.cls, angle: p.angle, user: p.user, psid: p.psid, gpsHi: p.gpsHi, gpsLo: p.gpsLo, gpsChange: false };
|
|
50
|
+
this.lastIntensity = new Uint16Array(8).fill(p.intensity);
|
|
51
|
+
this.xMedian = medians(12);
|
|
52
|
+
this.yMedian = medians(12);
|
|
53
|
+
this.lastZ = new Int32Array(8).fill(p.z);
|
|
54
|
+
this.changed = []; for (var i = 0; i < 8; i++) this.changed.push(new ArithmeticModel(128));
|
|
55
|
+
this.channel = new ArithmeticModel(3);
|
|
56
|
+
this.nret = new Array(16);
|
|
57
|
+
this.rnum = new Array(16);
|
|
58
|
+
this.rnumGpsSame = new ArithmeticModel(13);
|
|
59
|
+
this.clsM = new Array(64);
|
|
60
|
+
this.flagsM = new Array(64);
|
|
61
|
+
this.userM = new Array(64);
|
|
62
|
+
this.gpsMulti = new ArithmeticModel(MULTI_TOTAL);
|
|
63
|
+
this.gpsNoDiff = new ArithmeticModel(5);
|
|
64
|
+
this.icDx = new IntegerDecompressor(32, 2);
|
|
65
|
+
this.icDy = new IntegerDecompressor(32, 22);
|
|
66
|
+
this.icZ = new IntegerDecompressor(32, 20);
|
|
67
|
+
this.icIntensity = new IntegerDecompressor(16, 4);
|
|
68
|
+
this.icAngle = new IntegerDecompressor(16, 2);
|
|
69
|
+
this.icPsid = new IntegerDecompressor(16, 1);
|
|
70
|
+
this.icGps = new IntegerDecompressor(32, 9);
|
|
71
|
+
this.gLast = 0; this.gNext = 0;
|
|
72
|
+
this.gHi = new Int32Array(4); this.gLo = new Float64Array(4);
|
|
73
|
+
this.gHi[0] = p.gpsHi; this.gLo[0] = p.gpsLo;
|
|
74
|
+
this.gDiff = new Int32Array(4);
|
|
75
|
+
this.gExtreme = new Int32Array(4);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function lazy(arr, i, symbols) { return arr[i] || (arr[i] = new ArithmeticModel(symbols)); }
|
|
79
|
+
|
|
80
|
+
Context.prototype.addGps = function (i, d) {
|
|
81
|
+
var lo = this.gLo[i] + d;
|
|
82
|
+
if (lo >= 4294967296) { lo -= 4294967296; this.gHi[i] = (this.gHi[i] + 1) | 0; }
|
|
83
|
+
else if (lo < 0) { lo += 4294967296; this.gHi[i] = (this.gHi[i] - 1) | 0; }
|
|
84
|
+
this.gLo[i] = lo;
|
|
85
|
+
};
|
|
86
|
+
Context.prototype.fullGps = function (dec) {
|
|
87
|
+
this.gNext = (this.gNext + 1) & 3;
|
|
88
|
+
this.gHi[this.gNext] = this.icGps.decompress(dec, this.gHi[this.gLast], 8);
|
|
89
|
+
this.gLo[this.gNext] = dec.readInt();
|
|
90
|
+
this.gLast = this.gNext;
|
|
91
|
+
this.gDiff[this.gLast] = 0;
|
|
92
|
+
this.gExtreme[this.gLast] = 0;
|
|
93
|
+
};
|
|
94
|
+
Context.prototype.extremeGps = function (d) {
|
|
95
|
+
if (++this.gExtreme[this.gLast] > 3) { this.gDiff[this.gLast] = d; this.gExtreme[this.gLast] = 0; }
|
|
96
|
+
};
|
|
97
|
+
// laz-rs read_gps_time (the 1.4 variant: no "unchanged" code, full = 511)
|
|
98
|
+
Context.prototype.readGps = function (dec) {
|
|
99
|
+
for (;;) {
|
|
100
|
+
var L = this.gLast, multi, d;
|
|
101
|
+
if (this.gDiff[L] === 0) {
|
|
102
|
+
multi = dec.decodeSymbol(this.gpsNoDiff);
|
|
103
|
+
if (multi === 0) {
|
|
104
|
+
this.gDiff[L] = this.icGps.decompress(dec, 0, 0);
|
|
105
|
+
this.addGps(L, this.gDiff[L]);
|
|
106
|
+
this.gExtreme[L] = 0;
|
|
107
|
+
} else if (multi === 1) {
|
|
108
|
+
this.fullGps(dec);
|
|
109
|
+
} else {
|
|
110
|
+
this.gLast = (L + multi - 1) & 3;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
multi = dec.decodeSymbol(this.gpsMulti);
|
|
115
|
+
if (multi === 1) {
|
|
116
|
+
this.addGps(L, this.icGps.decompress(dec, this.gDiff[L], 1));
|
|
117
|
+
this.gExtreme[L] = 0;
|
|
118
|
+
} else if (multi < CODE_FULL) {
|
|
119
|
+
if (multi === 0) {
|
|
120
|
+
d = this.icGps.decompress(dec, 0, 7);
|
|
121
|
+
this.extremeGps(d);
|
|
122
|
+
} else if (multi < MULTI) {
|
|
123
|
+
d = this.icGps.decompress(dec, Math.imul(multi, this.gDiff[L]), multi < 10 ? 2 : 3);
|
|
124
|
+
} else if (multi === MULTI) {
|
|
125
|
+
d = this.icGps.decompress(dec, Math.imul(MULTI, this.gDiff[L]), 4);
|
|
126
|
+
this.extremeGps(d);
|
|
127
|
+
} else {
|
|
128
|
+
multi = MULTI - multi;
|
|
129
|
+
if (multi > MULTI_MINUS) {
|
|
130
|
+
d = this.icGps.decompress(dec, Math.imul(multi, this.gDiff[L]), 5);
|
|
131
|
+
} else {
|
|
132
|
+
d = this.icGps.decompress(dec, Math.imul(MULTI_MINUS, this.gDiff[L]), 6);
|
|
133
|
+
this.extremeGps(d);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
this.addGps(L, d);
|
|
137
|
+
} else if (multi === CODE_FULL) {
|
|
138
|
+
this.fullGps(dec);
|
|
139
|
+
} else {
|
|
140
|
+
this.gLast = (L + multi - CODE_FULL) & 3;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
var LAYERS = 9; // channel/returns/xy, z, classification, flags, intensity, scan angle, user data, point source, gps time
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Point14 decompressor: one per chunk.
|
|
152
|
+
* @param {number} version - LASzip item version, 3 or 4 (they pass the scanner channel on differently)
|
|
153
|
+
*/
|
|
154
|
+
export function Point14Decompressor(version) {
|
|
155
|
+
this.size = 30;
|
|
156
|
+
this.version = version;
|
|
157
|
+
this.contexts = [null, null, null, null];
|
|
158
|
+
this.current = 0;
|
|
159
|
+
this.sizes = new Array(LAYERS);
|
|
160
|
+
this.dec = new Array(LAYERS);
|
|
161
|
+
this.on = new Array(LAYERS);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Point14Decompressor.prototype.first = function (src, s, view, o, ctx) {
|
|
165
|
+
for (var i = 0; i < 30; i++) view.setUint8(o + i, src[s + i]);
|
|
166
|
+
var p = {
|
|
167
|
+
x: view.getInt32(o, LE), y: view.getInt32(o + 4, LE), z: view.getInt32(o + 8, LE),
|
|
168
|
+
intensity: view.getUint16(o + 12, LE), bits: view.getUint8(o + 14), flags: view.getUint8(o + 15),
|
|
169
|
+
cls: view.getUint8(o + 16), user: view.getUint8(o + 17), angle: view.getInt16(o + 18, LE),
|
|
170
|
+
psid: view.getUint16(o + 20, LE), gpsLo: view.getUint32(o + 22, LE), gpsHi: view.getInt32(o + 26, LE)
|
|
171
|
+
};
|
|
172
|
+
this.current = (p.flags >>> 4) & 3;
|
|
173
|
+
this.contexts = [null, null, null, null];
|
|
174
|
+
this.contexts[this.current] = new Context(p);
|
|
175
|
+
ctx.v = this.current;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
Point14Decompressor.prototype.readLayerSizes = function (dv, q) {
|
|
179
|
+
for (var i = 0; i < LAYERS; i++) { this.sizes[i] = dv.getUint32(q, LE); q += 4; }
|
|
180
|
+
return q;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
Point14Decompressor.prototype.readLayers = function (bytes, q, e, fail, base) {
|
|
184
|
+
if (this.sizes[0] === 0) fail(q); // channel / returns / XY is coded for every point after the first
|
|
185
|
+
for (var i = 0; i < LAYERS; i++) {
|
|
186
|
+
var n = this.sizes[i];
|
|
187
|
+
if (q + n > e) fail(q);
|
|
188
|
+
this.on[i] = n > 0;
|
|
189
|
+
if (n > 0) { this.dec[i] = new ArithmeticDecoder(bytes, q, q + n, base); this.dec[i].readInitBytes(); }
|
|
190
|
+
q += n;
|
|
191
|
+
}
|
|
192
|
+
return q;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
Point14Decompressor.prototype.next = function (view, o, ctx) {
|
|
196
|
+
var c = this.contexts[this.current];
|
|
197
|
+
var p = c.p, d0 = this.dec[0];
|
|
198
|
+
var r = p.bits & 15, n = p.bits >>> 4;
|
|
199
|
+
var lpr = (r === 1 ? 1 : 0) + (r >= n ? 2 : 0) + (p.gpsChange ? 4 : 0);
|
|
200
|
+
var changed = d0.decodeSymbol(c.changed[lpr]);
|
|
201
|
+
if (changed & 64) {
|
|
202
|
+
var channel = (this.current + d0.decodeSymbol(c.channel) + 1) % 4;
|
|
203
|
+
if (!this.contexts[channel]) this.contexts[channel] = new Context(p);
|
|
204
|
+
this.current = channel;
|
|
205
|
+
if (this.version === 3) ctx.v = channel;
|
|
206
|
+
c = this.contexts[channel];
|
|
207
|
+
p = c.p;
|
|
208
|
+
p.flags = (p.flags & 0xCF) | (channel << 4);
|
|
209
|
+
}
|
|
210
|
+
if (this.version !== 3) ctx.v = this.current;
|
|
211
|
+
var psidChanged = changed & 32, gpsChanged = changed & 16 ? 1 : 0, angleChanged = changed & 8;
|
|
212
|
+
var lastN = p.bits >>> 4, lastR = p.bits & 15;
|
|
213
|
+
n = changed & 4 ? d0.decodeSymbol(lazy(c.nret, lastN, 16)) : lastN;
|
|
214
|
+
var low = changed & 3;
|
|
215
|
+
if (low === 0) r = lastR;
|
|
216
|
+
else if (low === 1) r = (lastR + 1) % 16;
|
|
217
|
+
else if (low === 2) r = (lastR + 15) % 16;
|
|
218
|
+
else if (gpsChanged) r = d0.decodeSymbol(lazy(c.rnum, lastR, 16));
|
|
219
|
+
else r = (lastR + d0.decodeSymbol(c.rnumGpsSame) + 2) % 16;
|
|
220
|
+
p.bits = r + n * 16;
|
|
221
|
+
var m = MAP_6CTX[n][r], l = LEVEL_8CT[n][r];
|
|
222
|
+
var cpr = (r === 1 ? 2 : 0) + (r >= n ? 1 : 0);
|
|
223
|
+
var single = n === 1 ? 1 : 0;
|
|
224
|
+
var idx = m * 2 + gpsChanged;
|
|
225
|
+
var diff = c.icDx.decompress(d0, c.xMedian[idx].get(), single);
|
|
226
|
+
p.x = (p.x + diff) | 0;
|
|
227
|
+
c.xMedian[idx].add(diff);
|
|
228
|
+
var kx = c.icDx.k;
|
|
229
|
+
diff = c.icDy.decompress(d0, c.yMedian[idx].get(), single + (kx < 20 ? kx & 0xFFFFFFFE : 20));
|
|
230
|
+
p.y = (p.y + diff) | 0;
|
|
231
|
+
c.yMedian[idx].add(diff);
|
|
232
|
+
if (this.on[1]) {
|
|
233
|
+
var kz = Math.floor((c.icDx.k + c.icDy.k) / 2);
|
|
234
|
+
p.z = c.icZ.decompress(this.dec[1], c.lastZ[l], single + (kz < 18 ? kz & 0xFFFFFFFE : 18));
|
|
235
|
+
c.lastZ[l] = p.z;
|
|
236
|
+
}
|
|
237
|
+
if (this.on[2]) p.cls = this.dec[2].decodeSymbol(lazy(c.clsM, (p.cls & 31) * 2 + (cpr === 3 ? 1 : 0), 256));
|
|
238
|
+
if (this.on[3]) {
|
|
239
|
+
var lastFlags = ((p.flags >>> 7) & 1) * 32 + ((p.flags >>> 6) & 1) * 16 + (p.flags & 15);
|
|
240
|
+
var f = this.dec[3].decodeSymbol(lazy(c.flagsM, lastFlags, 64));
|
|
241
|
+
p.flags = ((f >>> 5) & 1) * 128 + ((f >>> 4) & 1) * 64 + (p.flags & 0x30) + (f & 15);
|
|
242
|
+
}
|
|
243
|
+
if (this.on[4]) {
|
|
244
|
+
var ii = cpr * 2 + gpsChanged;
|
|
245
|
+
p.intensity = c.icIntensity.decompress(this.dec[4], c.lastIntensity[ii], cpr) & 0xFFFF;
|
|
246
|
+
c.lastIntensity[ii] = p.intensity;
|
|
247
|
+
}
|
|
248
|
+
if (this.on[5] && angleChanged) p.angle = (c.icAngle.decompress(this.dec[5], p.angle, gpsChanged) << 16) >> 16; // as i16
|
|
249
|
+
if (this.on[6]) p.user = this.dec[6].decodeSymbol(lazy(c.userM, Math.floor(p.user / 4), 256));
|
|
250
|
+
if (this.on[7] && psidChanged) p.psid = c.icPsid.decompress(this.dec[7], p.psid, 0) & 0xFFFF;
|
|
251
|
+
p.gpsChange = !!gpsChanged;
|
|
252
|
+
if (this.on[8] && gpsChanged) {
|
|
253
|
+
c.readGps(this.dec[8]);
|
|
254
|
+
p.gpsHi = c.gHi[c.gLast]; p.gpsLo = c.gLo[c.gLast];
|
|
255
|
+
}
|
|
256
|
+
view.setInt32(o, p.x, LE); view.setInt32(o + 4, p.y, LE); view.setInt32(o + 8, p.z, LE);
|
|
257
|
+
view.setUint16(o + 12, p.intensity, LE);
|
|
258
|
+
view.setUint8(o + 14, p.bits);
|
|
259
|
+
view.setUint8(o + 15, p.flags);
|
|
260
|
+
view.setUint8(o + 16, p.cls);
|
|
261
|
+
view.setUint8(o + 17, p.user);
|
|
262
|
+
view.setInt16(o + 18, p.angle, LE);
|
|
263
|
+
view.setUint16(o + 20, p.psid, LE);
|
|
264
|
+
view.setUint32(o + 22, p.gpsLo, LE);
|
|
265
|
+
view.setInt32(o + 26, p.gpsHi, LE);
|
|
266
|
+
};
|
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
// data, never on positions (marked // bits-ok for the guard test).
|
|
12
12
|
//
|
|
13
13
|
// opts.keep(view, recordOffset) rejects a record before it is
|
|
14
|
-
// decoded (filters, see filter/filters.js). opts.
|
|
15
|
-
//
|
|
16
|
-
//
|
|
14
|
+
// decoded (filters, see filter/filters.js). opts.thin
|
|
15
|
+
// { total, target, acc } then keeps exactly target of the total
|
|
16
|
+
// records that pass, spread evenly: an integer accumulator gains
|
|
17
|
+
// target per passing record and keeps the record each time it
|
|
18
|
+
// reaches total (Bresenham), so record ceil(j * total / target) - 1
|
|
19
|
+
// is the j-th kept. acc is carried from window to window.
|
|
17
20
|
//==========================================================
|
|
18
21
|
|
|
19
22
|
import { LasError, ERROR_CODES } from "../errors.js";
|
|
@@ -69,7 +72,9 @@ export function resolveFields(header, fields) {
|
|
|
69
72
|
* Decode a window of whole point records.
|
|
70
73
|
* @param {Uint8Array} bytes - whole records, the first at bytes[0].
|
|
71
74
|
* @param {object} header - from parseHeader.
|
|
72
|
-
* @param {object} [opts] - { fields, keep,
|
|
75
|
+
* @param {object} [opts] - { fields, keep, thin, localOrigin, float32, into }
|
|
76
|
+
* into: { cols, at } decodes straight into existing columns from index at
|
|
77
|
+
* (each sized for every record that can be kept) and returns only { count }
|
|
73
78
|
* @returns {object} PointColumns: { count, <field>: TypedArray, ... }
|
|
74
79
|
*/
|
|
75
80
|
export function decodeRecords(bytes, header, opts) {
|
|
@@ -79,8 +84,7 @@ export function decodeRecords(bytes, header, opts) {
|
|
|
79
84
|
var len = header.recordLength;
|
|
80
85
|
var n = Math.floor(bytes.length / len);
|
|
81
86
|
var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
82
|
-
var
|
|
83
|
-
var first = opts.firstIndex || 0;
|
|
87
|
+
var thin = opts.thin && opts.thin.target < opts.thin.total ? opts.thin : null;
|
|
84
88
|
var keep = typeof opts.keep === "function" ? opts.keep : null;
|
|
85
89
|
var origin = opts.localOrigin || null;
|
|
86
90
|
var ox = origin && isFinite(origin.x) ? origin.x : 0;
|
|
@@ -88,12 +92,9 @@ export function decodeRecords(bytes, header, opts) {
|
|
|
88
92
|
var oz = origin && isFinite(origin.z) ? origin.z : 0;
|
|
89
93
|
var f32 = !!(opts.float32 && origin);
|
|
90
94
|
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (f32 && (name === "x" || name === "y" || name === "z")) Type = Float32Array;
|
|
95
|
-
cols[name] = new Type(n);
|
|
96
|
-
});
|
|
95
|
+
var into = opts.into || null;
|
|
96
|
+
var cols = into ? into.cols : {};
|
|
97
|
+
if (!into) fields.forEach(function (name) { cols[name] = new (columnType(name, opts))(n); });
|
|
97
98
|
var F = fmt.fields;
|
|
98
99
|
var sx = header.scale.x, sy = header.scale.y, sz = header.scale.z;
|
|
99
100
|
var tx = header.offset.x - ox, ty = header.offset.y - oy, tz = header.offset.z - oz;
|
|
@@ -101,11 +102,15 @@ export function decodeRecords(bytes, header, opts) {
|
|
|
101
102
|
var want = {};
|
|
102
103
|
fields.forEach(function (name) { want[name] = true; });
|
|
103
104
|
|
|
104
|
-
var k = 0;
|
|
105
|
+
var k = into ? into.at : 0;
|
|
105
106
|
for (var i = 0; i < n; i++) {
|
|
106
|
-
if (stride > 1 && (first + i) % stride !== 0) continue;
|
|
107
107
|
var at = i * len;
|
|
108
108
|
if (keep && !keep(view, at)) continue;
|
|
109
|
+
if (thin) {
|
|
110
|
+
thin.acc += thin.target;
|
|
111
|
+
if (thin.acc < thin.total) continue;
|
|
112
|
+
thin.acc -= thin.total;
|
|
113
|
+
}
|
|
109
114
|
if (want.x) cols.x[k] = view.getInt32(at, LE) * sx + tx;
|
|
110
115
|
if (want.y) cols.y[k] = view.getInt32(at + 4, LE) * sy + ty;
|
|
111
116
|
if (want.z) cols.z[k] = view.getInt32(at + 8, LE) * sz + tz;
|
|
@@ -140,7 +145,20 @@ export function decodeRecords(bytes, header, opts) {
|
|
|
140
145
|
if (want.nir) cols.nir[k] = view.getUint16(at + F.nir.offset, LE);
|
|
141
146
|
k++;
|
|
142
147
|
}
|
|
148
|
+
if (into) return { count: k - into.at };
|
|
143
149
|
if (k < n) fields.forEach(function (name) { cols[name] = cols[name].slice(0, k); });
|
|
144
150
|
cols.count = k;
|
|
145
151
|
return cols;
|
|
146
152
|
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The typed-array type a field is decoded into (x / y / z are Float32
|
|
156
|
+
* when float32 is asked for with a local origin).
|
|
157
|
+
* @param {string} name
|
|
158
|
+
* @param {{float32?:boolean, localOrigin?:object}} [opts]
|
|
159
|
+
* @returns {Function}
|
|
160
|
+
*/
|
|
161
|
+
export function columnType(name, opts) {
|
|
162
|
+
var f32 = !!(opts && opts.float32 && opts.localOrigin);
|
|
163
|
+
return f32 && (name === "x" || name === "y" || name === "z") ? Float32Array : FIELD_TYPES[name];
|
|
164
|
+
}
|
package/src/read/windows.js
CHANGED
|
@@ -22,3 +22,24 @@ export function windows(header, windowSize) {
|
|
|
22
22
|
}
|
|
23
23
|
return out;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Group LAZ chunks into windows of whole chunks, about windowSize bytes of
|
|
28
|
+
* DECOMPRESSED records each (one chunk at least).
|
|
29
|
+
* @param {Array<{firstRecord:number, count:number, start:number, end:number}>} chunks - from readChunks
|
|
30
|
+
* @param {number} recordLength
|
|
31
|
+
* @param {number} windowSize
|
|
32
|
+
* @returns {Array<{start:number, end:number, firstRecord:number, count:number, chunks:Array}>}
|
|
33
|
+
*/
|
|
34
|
+
export function lazWindows(chunks, recordLength, windowSize) {
|
|
35
|
+
var out = [], cur = null;
|
|
36
|
+
chunks.forEach(function (c) {
|
|
37
|
+
if (cur && (cur.count + c.count) * recordLength > windowSize) { out.push(cur); cur = null; }
|
|
38
|
+
if (!cur) cur = { start: c.start, end: c.end, firstRecord: c.firstRecord, count: 0, chunks: [] };
|
|
39
|
+
cur.end = c.end;
|
|
40
|
+
cur.count += c.count;
|
|
41
|
+
cur.chunks.push(c);
|
|
42
|
+
});
|
|
43
|
+
if (cur) out.push(cur);
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
// Worker globals so it can be driven directly (tests, custom hosts).
|
|
5
5
|
// Same shape as ecw-js's.
|
|
6
6
|
// In: { id, type, payload }
|
|
7
|
-
// open { blob | bytes, windowSize? } -> { header, vlrs, evlrs, crs, compressed }
|
|
7
|
+
// open { blob | bytes, windowSize? } -> { header, vlrs, evlrs, crs, compressed, laz }
|
|
8
8
|
// readPoints readPoints options + colour? -> PointColumns (+ colours)
|
|
9
9
|
// chunks chunks options + colour? -> streams { id, type: "chunk", chunk } per
|
|
10
|
-
// window, then result { count }
|
|
10
|
+
// window, then result { count }; colours
|
|
11
|
+
// follow one rule for the whole stream
|
|
12
|
+
// (createChunkColourizer)
|
|
11
13
|
// readPointsToScratch readPointsToScratch options -> { count, fields, types, bounds }
|
|
12
14
|
// colourize { points, by, ramp?, range? } -> Uint8Array
|
|
13
15
|
// abort { id } -> cancels that job (it rejects ABORTED)
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
|
|
20
22
|
import { LasError, ERROR_CODES } from "../errors.js";
|
|
21
23
|
import { openLas } from "../LasReader.js";
|
|
22
|
-
import { colourize } from "../colour/colourize.js";
|
|
24
|
+
import { colourize, createChunkColourizer } from "../colour/colourize.js";
|
|
23
25
|
|
|
24
26
|
function buffersOf(cols) {
|
|
25
27
|
var out = [];
|
|
@@ -66,16 +68,17 @@ export function createWorkerHandler(post) {
|
|
|
66
68
|
if (reader) await reader.close();
|
|
67
69
|
reader = null;
|
|
68
70
|
reader = await openLas((payload && (payload.blob || payload.bytes)), { windowSize: payload && payload.windowSize });
|
|
69
|
-
return { result: { header: reader.header, vlrs: reader.vlrs, evlrs: reader.evlrs, crs: reader.crs, compressed: reader.compressed } };
|
|
71
|
+
return { result: { header: reader.header, vlrs: reader.vlrs, evlrs: reader.evlrs, crs: reader.crs, compressed: reader.compressed, laz: reader.laz } };
|
|
70
72
|
},
|
|
71
73
|
readPoints: async function (id, payload, controller) {
|
|
72
74
|
var cols = withColour(await needReader().readPoints(readOptions(id, payload, controller)), payload && payload.colour);
|
|
73
75
|
return { result: cols, transfer: buffersOf(cols) };
|
|
74
76
|
},
|
|
75
77
|
chunks: async function (id, payload, controller) {
|
|
76
|
-
var count = 0;
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
var count = 0, r = needReader();
|
|
79
|
+
var paint = payload.colour ? createChunkColourizer(payload.colour, r.header.bounds, payload.localOrigin) : null;
|
|
80
|
+
for await (var c of r.chunks(readOptions(id, payload, controller))) {
|
|
81
|
+
if (paint) c.colours = paint(c);
|
|
79
82
|
count += c.count;
|
|
80
83
|
post({ id: id, type: "chunk", chunk: c }, buffersOf(c));
|
|
81
84
|
}
|