las-laz-js 0.3.0 → 0.4.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 +46 -0
- package/NOTICE +1 -1
- package/README.md +56 -14
- package/package.json +2 -2
- package/src/index.js +1 -1
- package/src/laz/ArithmeticEncoder.js +123 -0
- package/src/laz/IntegerCompressor.js +76 -0
- package/src/laz/LazVlr.js +63 -1
- package/src/laz/chunkTable.js +139 -109
- package/src/laz/chunkWorker.js +51 -18
- package/src/laz/compressChunk.js +107 -0
- package/src/laz/decompressChunk.js +40 -10
- package/src/laz/pool.js +77 -34
- package/src/laz/v2/byte.js +23 -7
- package/src/laz/v2/gpstime11.js +165 -25
- package/src/laz/v2/point10.js +91 -22
- package/src/laz/v2/rgb12.js +62 -7
- package/src/laz/v3/layered.js +138 -40
- package/src/laz/v3/point14.js +231 -33
- package/src/write/encodeRecords.js +3 -2
- package/src/write/writeLas.js +259 -6
package/src/laz/v3/point14.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
// XY, Z, classification, flags, intensity, scan angle, user data,
|
|
6
6
|
// point source, GPS time — each its own arithmetic stream, so a layer
|
|
7
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.
|
|
8
|
+
// the last one). Up to four contexts follow the scanner channel. The
|
|
9
|
+
// compressor and decompressor below share each context's models,
|
|
10
|
+
// medians and coders (Context), so they stay in step point for point.
|
|
9
11
|
//
|
|
10
12
|
// Ported from laz-rs src/las/point6.rs, module v3_v4, and the 16x16
|
|
11
13
|
// return tables of src/las/utils.rs (https://github.com/laz-rs/laz-rs),
|
|
@@ -13,13 +15,21 @@
|
|
|
13
15
|
// rapidlasso; (c) 2019-2023 Thomas Montaigu.
|
|
14
16
|
// Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE and
|
|
15
17
|
// NOTICE). Changes: translated from Rust to JavaScript, working on raw
|
|
16
|
-
// record bytes, GPS time as two u32 halves, Brent Buffham 2026
|
|
18
|
+
// record bytes, GPS time as two u32 halves, Brent Buffham 2026; the
|
|
19
|
+
// compressor (Point14Compressor) added beside the decompressor, sharing
|
|
20
|
+
// its contexts, deciding which layers to write as LAZperf
|
|
21
|
+
// (hobuinc/laz-perf, field_point14.cpp) does, with one change: a GPS
|
|
22
|
+
// time is "changed" when its bits differ (or it is NaN), so -0 after +0
|
|
23
|
+
// survives, where LAZperf's double compare would drop the sign.
|
|
17
24
|
//==========================================================
|
|
18
25
|
|
|
19
26
|
import { ArithmeticDecoder } from "../ArithmeticDecoder.js";
|
|
27
|
+
import { ArithmeticEncoder } from "../ArithmeticEncoder.js";
|
|
20
28
|
import { ArithmeticModel } from "../models.js";
|
|
21
29
|
import { IntegerDecompressor } from "../IntegerDecompressor.js";
|
|
22
|
-
import {
|
|
30
|
+
import { IntegerCompressor } from "../IntegerCompressor.js";
|
|
31
|
+
import { StreamingMedian, yContext, zContext } from "../v2/point10.js";
|
|
32
|
+
import { quantizeMulti, diff32 } from "../v2/gpstime11.js";
|
|
23
33
|
|
|
24
34
|
var LE = true;
|
|
25
35
|
var MULTI = 500, MULTI_MINUS = -10;
|
|
@@ -43,8 +53,20 @@ for (var ln = 0; ln < 16; ln++) { LEVEL_8CT.push([]); for (var lr = 0; lr < 16;
|
|
|
43
53
|
|
|
44
54
|
function medians(n) { var out = []; for (var i = 0; i < n; i++) out.push(new StreamingMedian()); return out; }
|
|
45
55
|
|
|
46
|
-
// One
|
|
47
|
-
function
|
|
56
|
+
// One record's fields from a view (into q, or a new object)
|
|
57
|
+
function readPoint14(v, o, q) {
|
|
58
|
+
q = q || {};
|
|
59
|
+
q.x = v.getInt32(o, LE); q.y = v.getInt32(o + 4, LE); q.z = v.getInt32(o + 8, LE);
|
|
60
|
+
q.intensity = v.getUint16(o + 12, LE); q.bits = v.getUint8(o + 14); q.flags = v.getUint8(o + 15);
|
|
61
|
+
q.cls = v.getUint8(o + 16); q.user = v.getUint8(o + 17); q.angle = v.getInt16(o + 18, LE);
|
|
62
|
+
q.psid = v.getUint16(o + 20, LE); q.gpsLo = v.getUint32(o + 22, LE); q.gpsHi = v.getInt32(o + 26, LE);
|
|
63
|
+
return q;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// One context's state, started from the last point of another (laz-rs from_last_point);
|
|
67
|
+
// IC is the integer coder (IntegerCompressor or IntegerDecompressor), so both halves get
|
|
68
|
+
// the same widths and contexts
|
|
69
|
+
function Context(p, IC) {
|
|
48
70
|
this.unused = false;
|
|
49
71
|
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
72
|
this.lastIntensity = new Uint16Array(8).fill(p.intensity);
|
|
@@ -61,13 +83,13 @@ function Context(p) {
|
|
|
61
83
|
this.userM = new Array(64);
|
|
62
84
|
this.gpsMulti = new ArithmeticModel(MULTI_TOTAL);
|
|
63
85
|
this.gpsNoDiff = new ArithmeticModel(5);
|
|
64
|
-
this.icDx = new
|
|
65
|
-
this.icDy = new
|
|
66
|
-
this.icZ = new
|
|
67
|
-
this.icIntensity = new
|
|
68
|
-
this.icAngle = new
|
|
69
|
-
this.icPsid = new
|
|
70
|
-
this.icGps = new
|
|
86
|
+
this.icDx = new IC(32, 2);
|
|
87
|
+
this.icDy = new IC(32, 22);
|
|
88
|
+
this.icZ = new IC(32, 20);
|
|
89
|
+
this.icIntensity = new IC(16, 4);
|
|
90
|
+
this.icAngle = new IC(16, 2);
|
|
91
|
+
this.icPsid = new IC(16, 1);
|
|
92
|
+
this.icGps = new IC(32, 9);
|
|
71
93
|
this.gLast = 0; this.gNext = 0;
|
|
72
94
|
this.gHi = new Int32Array(4); this.gLo = new Float64Array(4);
|
|
73
95
|
this.gHi[0] = p.gpsHi; this.gLo[0] = p.gpsLo;
|
|
@@ -77,20 +99,37 @@ function Context(p) {
|
|
|
77
99
|
|
|
78
100
|
function lazy(arr, i, symbols) { return arr[i] || (arr[i] = new ArithmeticModel(symbols)); }
|
|
79
101
|
|
|
102
|
+
// The changed-values model context from the last point: single / first / last / intermediate,
|
|
103
|
+
// and whether its GPS time changed
|
|
104
|
+
function lastReturnContext(p) {
|
|
105
|
+
var r = p.bits & 15, n = p.bits >>> 4;
|
|
106
|
+
return (r === 1 ? 1 : 0) + (r >= n ? 2 : 0) + (p.gpsChange ? 4 : 0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The flags symbol: classification flags (bits 0-3), scan direction (4), edge of flight line (5)
|
|
110
|
+
// — the flags byte without the scanner channel, which layer 0 codes
|
|
111
|
+
function flagsSymbol(flags) { return ((flags >>> 7) & 1) * 32 + ((flags >>> 6) & 1) * 16 + (flags & 15); }
|
|
112
|
+
|
|
80
113
|
Context.prototype.addGps = function (i, d) {
|
|
81
114
|
var lo = this.gLo[i] + d;
|
|
82
115
|
if (lo >= 4294967296) { lo -= 4294967296; this.gHi[i] = (this.gHi[i] + 1) | 0; }
|
|
83
116
|
else if (lo < 0) { lo += 4294967296; this.gHi[i] = (this.gHi[i] - 1) | 0; }
|
|
84
117
|
this.gLo[i] = lo;
|
|
85
118
|
};
|
|
86
|
-
|
|
119
|
+
// A full time (hi, lo) starts the next of the four sequences, with no difference yet
|
|
120
|
+
Context.prototype.startGps = function (hi, lo) {
|
|
87
121
|
this.gNext = (this.gNext + 1) & 3;
|
|
88
|
-
this.gHi[this.gNext] =
|
|
89
|
-
this.gLo[this.gNext] =
|
|
122
|
+
this.gHi[this.gNext] = hi;
|
|
123
|
+
this.gLo[this.gNext] = lo;
|
|
90
124
|
this.gLast = this.gNext;
|
|
91
125
|
this.gDiff[this.gLast] = 0;
|
|
92
126
|
this.gExtreme[this.gLast] = 0;
|
|
93
127
|
};
|
|
128
|
+
Context.prototype.fullGps = function (dec) {
|
|
129
|
+
var hi = this.icGps.decompress(dec, this.gHi[this.gLast], 8);
|
|
130
|
+
this.startGps(hi, dec.readInt());
|
|
131
|
+
};
|
|
132
|
+
// After three extreme differences in a row, the latest becomes the new reference
|
|
94
133
|
Context.prototype.extremeGps = function (d) {
|
|
95
134
|
if (++this.gExtreme[this.gLast] > 3) { this.gDiff[this.gLast] = d; this.gExtreme[this.gLast] = 0; }
|
|
96
135
|
};
|
|
@@ -145,6 +184,70 @@ Context.prototype.readGps = function (dec) {
|
|
|
145
184
|
}
|
|
146
185
|
};
|
|
147
186
|
|
|
187
|
+
// The first other sequence (1-3 on from the last) the time (hi, lo) is within i32 of, or 0
|
|
188
|
+
Context.prototype.otherGps = function (hi, lo) {
|
|
189
|
+
for (var i = 1; i < 4; i++) { var j = (this.gLast + i) & 3; if (diff32(hi, lo, this.gHi[j], this.gLo[j]) !== null) return i; }
|
|
190
|
+
return 0;
|
|
191
|
+
};
|
|
192
|
+
Context.prototype.writeFullGps = function (enc, hi, lo) {
|
|
193
|
+
this.icGps.compress(enc, this.gHi[this.gLast], hi, 8);
|
|
194
|
+
enc.writeInt(lo);
|
|
195
|
+
this.startGps(hi, lo);
|
|
196
|
+
};
|
|
197
|
+
// The mirror of readGps (LAZperf encodeGpsTime): the time (hi, lo), known to have changed
|
|
198
|
+
Context.prototype.writeGps = function (enc, hi, lo) {
|
|
199
|
+
for (;;) {
|
|
200
|
+
var L = this.gLast, d = diff32(hi, lo, this.gHi[L], this.gLo[L]), j, multi;
|
|
201
|
+
if (this.gDiff[L] === 0) {
|
|
202
|
+
if (d !== null) {
|
|
203
|
+
enc.encodeSymbol(this.gpsNoDiff, 0);
|
|
204
|
+
this.icGps.compress(enc, 0, d, 0);
|
|
205
|
+
this.gDiff[L] = d;
|
|
206
|
+
this.gExtreme[L] = 0;
|
|
207
|
+
} else if ((j = this.otherGps(hi, lo))) {
|
|
208
|
+
enc.encodeSymbol(this.gpsNoDiff, j + 1);
|
|
209
|
+
this.gLast = (L + j) & 3;
|
|
210
|
+
continue; // code again against the other sequence
|
|
211
|
+
} else {
|
|
212
|
+
enc.encodeSymbol(this.gpsNoDiff, 1);
|
|
213
|
+
this.writeFullGps(enc, hi, lo);
|
|
214
|
+
}
|
|
215
|
+
} else if (d !== null) {
|
|
216
|
+
multi = quantizeMulti(d, this.gDiff[L]);
|
|
217
|
+
if (multi > 0 && multi < MULTI) {
|
|
218
|
+
enc.encodeSymbol(this.gpsMulti, multi);
|
|
219
|
+
this.icGps.compress(enc, Math.imul(multi, this.gDiff[L]), d, multi === 1 ? 1 : multi < 10 ? 2 : 3);
|
|
220
|
+
if (multi === 1) this.gExtreme[L] = 0;
|
|
221
|
+
} else if (multi >= MULTI) {
|
|
222
|
+
enc.encodeSymbol(this.gpsMulti, MULTI);
|
|
223
|
+
this.icGps.compress(enc, Math.imul(MULTI, this.gDiff[L]), d, 4);
|
|
224
|
+
this.extremeGps(d);
|
|
225
|
+
} else if (multi < 0 && multi > MULTI_MINUS) {
|
|
226
|
+
enc.encodeSymbol(this.gpsMulti, MULTI - multi);
|
|
227
|
+
this.icGps.compress(enc, Math.imul(multi, this.gDiff[L]), d, 5);
|
|
228
|
+
} else if (multi < 0) {
|
|
229
|
+
enc.encodeSymbol(this.gpsMulti, MULTI - MULTI_MINUS);
|
|
230
|
+
this.icGps.compress(enc, Math.imul(MULTI_MINUS, this.gDiff[L]), d, 6);
|
|
231
|
+
this.extremeGps(d);
|
|
232
|
+
} else {
|
|
233
|
+
enc.encodeSymbol(this.gpsMulti, 0);
|
|
234
|
+
this.icGps.compress(enc, 0, d, 7);
|
|
235
|
+
this.extremeGps(d);
|
|
236
|
+
}
|
|
237
|
+
} else if ((j = this.otherGps(hi, lo))) {
|
|
238
|
+
enc.encodeSymbol(this.gpsMulti, CODE_FULL + j);
|
|
239
|
+
this.gLast = (L + j) & 3;
|
|
240
|
+
continue;
|
|
241
|
+
} else {
|
|
242
|
+
enc.encodeSymbol(this.gpsMulti, CODE_FULL);
|
|
243
|
+
this.writeFullGps(enc, hi, lo);
|
|
244
|
+
}
|
|
245
|
+
this.gHi[this.gLast] = hi;
|
|
246
|
+
this.gLo[this.gLast] = lo;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
148
251
|
var LAYERS = 9; // channel/returns/xy, z, classification, flags, intensity, scan angle, user data, point source, gps time
|
|
149
252
|
|
|
150
253
|
/**
|
|
@@ -163,15 +266,10 @@ export function Point14Decompressor(version) {
|
|
|
163
266
|
|
|
164
267
|
Point14Decompressor.prototype.first = function (src, s, view, o, ctx) {
|
|
165
268
|
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
|
-
};
|
|
269
|
+
var p = readPoint14(view, o);
|
|
172
270
|
this.current = (p.flags >>> 4) & 3;
|
|
173
271
|
this.contexts = [null, null, null, null];
|
|
174
|
-
this.contexts[this.current] = new Context(p);
|
|
272
|
+
this.contexts[this.current] = new Context(p, IntegerDecompressor);
|
|
175
273
|
ctx.v = this.current;
|
|
176
274
|
};
|
|
177
275
|
|
|
@@ -195,12 +293,10 @@ Point14Decompressor.prototype.readLayers = function (bytes, q, e, fail, base) {
|
|
|
195
293
|
Point14Decompressor.prototype.next = function (view, o, ctx) {
|
|
196
294
|
var c = this.contexts[this.current];
|
|
197
295
|
var p = c.p, d0 = this.dec[0];
|
|
198
|
-
var
|
|
199
|
-
var lpr = (r === 1 ? 1 : 0) + (r >= n ? 2 : 0) + (p.gpsChange ? 4 : 0);
|
|
200
|
-
var changed = d0.decodeSymbol(c.changed[lpr]);
|
|
296
|
+
var changed = d0.decodeSymbol(c.changed[lastReturnContext(p)]);
|
|
201
297
|
if (changed & 64) {
|
|
202
298
|
var channel = (this.current + d0.decodeSymbol(c.channel) + 1) % 4;
|
|
203
|
-
if (!this.contexts[channel]) this.contexts[channel] = new Context(p);
|
|
299
|
+
if (!this.contexts[channel]) this.contexts[channel] = new Context(p, IntegerDecompressor);
|
|
204
300
|
this.current = channel;
|
|
205
301
|
if (this.version === 3) ctx.v = channel;
|
|
206
302
|
c = this.contexts[channel];
|
|
@@ -209,7 +305,7 @@ Point14Decompressor.prototype.next = function (view, o, ctx) {
|
|
|
209
305
|
}
|
|
210
306
|
if (this.version !== 3) ctx.v = this.current;
|
|
211
307
|
var psidChanged = changed & 32, gpsChanged = changed & 16 ? 1 : 0, angleChanged = changed & 8;
|
|
212
|
-
var lastN = p.bits >>> 4, lastR = p.bits & 15;
|
|
308
|
+
var lastN = p.bits >>> 4, lastR = p.bits & 15, n, r;
|
|
213
309
|
n = changed & 4 ? d0.decodeSymbol(lazy(c.nret, lastN, 16)) : lastN;
|
|
214
310
|
var low = changed & 3;
|
|
215
311
|
if (low === 0) r = lastR;
|
|
@@ -225,19 +321,16 @@ Point14Decompressor.prototype.next = function (view, o, ctx) {
|
|
|
225
321
|
var diff = c.icDx.decompress(d0, c.xMedian[idx].get(), single);
|
|
226
322
|
p.x = (p.x + diff) | 0;
|
|
227
323
|
c.xMedian[idx].add(diff);
|
|
228
|
-
|
|
229
|
-
diff = c.icDy.decompress(d0, c.yMedian[idx].get(), single + (kx < 20 ? kx & 0xFFFFFFFE : 20));
|
|
324
|
+
diff = c.icDy.decompress(d0, c.yMedian[idx].get(), yContext(single, c.icDx));
|
|
230
325
|
p.y = (p.y + diff) | 0;
|
|
231
326
|
c.yMedian[idx].add(diff);
|
|
232
327
|
if (this.on[1]) {
|
|
233
|
-
|
|
234
|
-
p.z = c.icZ.decompress(this.dec[1], c.lastZ[l], single + (kz < 18 ? kz & 0xFFFFFFFE : 18));
|
|
328
|
+
p.z = c.icZ.decompress(this.dec[1], c.lastZ[l], zContext(single, c.icDx, c.icDy));
|
|
235
329
|
c.lastZ[l] = p.z;
|
|
236
330
|
}
|
|
237
331
|
if (this.on[2]) p.cls = this.dec[2].decodeSymbol(lazy(c.clsM, (p.cls & 31) * 2 + (cpr === 3 ? 1 : 0), 256));
|
|
238
332
|
if (this.on[3]) {
|
|
239
|
-
var
|
|
240
|
-
var f = this.dec[3].decodeSymbol(lazy(c.flagsM, lastFlags, 64));
|
|
333
|
+
var f = this.dec[3].decodeSymbol(lazy(c.flagsM, flagsSymbol(p.flags), 64));
|
|
241
334
|
p.flags = ((f >>> 5) & 1) * 128 + ((f >>> 4) & 1) * 64 + (p.flags & 0x30) + (f & 15);
|
|
242
335
|
}
|
|
243
336
|
if (this.on[4]) {
|
|
@@ -264,3 +357,108 @@ Point14Decompressor.prototype.next = function (view, o, ctx) {
|
|
|
264
357
|
view.setUint32(o + 22, p.gpsLo, LE);
|
|
265
358
|
view.setInt32(o + 26, p.gpsHi, LE);
|
|
266
359
|
};
|
|
360
|
+
|
|
361
|
+
// A DataView over the caller's records, made once per buffer
|
|
362
|
+
function viewOf(item, src) {
|
|
363
|
+
if (item._src !== src) { item._src = src; item._view = new DataView(src.buffer, src.byteOffset, src.byteLength); }
|
|
364
|
+
return item._view;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Whether a point's GPS time changed from the last: its bits differ, or it is NaN (a NaN never
|
|
368
|
+
// equals itself, as LAZperf's double compare has it). Unlike that compare, -0 and +0 differ.
|
|
369
|
+
function gpsChangedFrom(p, q) {
|
|
370
|
+
if (q.gpsHi !== p.gpsHi || q.gpsLo !== p.gpsLo) return true;
|
|
371
|
+
return (q.gpsHi & 0x7FF00000) === 0x7FF00000 && ((q.gpsHi & 0xFFFFF) !== 0 || q.gpsLo !== 0);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Point14 compressor: one per chunk, the mirror of Point14Decompressor. Each layer has its own
|
|
376
|
+
* encoder; channel/returns/XY and Z are always written, the others once a value changes (LAZperf).
|
|
377
|
+
* @param {number} version - LASzip item version, 3 or 4
|
|
378
|
+
*/
|
|
379
|
+
export function Point14Compressor(version) {
|
|
380
|
+
this.size = 30;
|
|
381
|
+
this.version = version;
|
|
382
|
+
this.contexts = [null, null, null, null];
|
|
383
|
+
this.current = 0;
|
|
384
|
+
this.enc = [];
|
|
385
|
+
this.used = [];
|
|
386
|
+
for (var i = 0; i < LAYERS; i++) { this.enc.push(new ArithmeticEncoder()); this.used.push(i < 2); }
|
|
387
|
+
this.q = {};
|
|
388
|
+
this._src = null;
|
|
389
|
+
this._view = null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** The first point of a chunk is stored raw; it starts its channel's context. */
|
|
393
|
+
Point14Compressor.prototype.first = function (src, s, ctx) {
|
|
394
|
+
var p = readPoint14(viewOf(this, src), s);
|
|
395
|
+
this.current = (p.flags >>> 4) & 3;
|
|
396
|
+
this.contexts[this.current] = new Context(p, IntegerCompressor);
|
|
397
|
+
ctx.v = this.current;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
Point14Compressor.prototype.next = function (src, s, ctx) {
|
|
401
|
+
var q = readPoint14(viewOf(this, src), s, this.q);
|
|
402
|
+
var prev = this.contexts[this.current], sc = (q.flags >>> 4) & 3;
|
|
403
|
+
var c = this.contexts[sc];
|
|
404
|
+
// the last point of this channel, or (a channel not seen yet) of the last one
|
|
405
|
+
var p = c ? c.p : prev.p, e0 = this.enc[0];
|
|
406
|
+
var gpsChanged = gpsChangedFrom(p, q) ? 1 : 0;
|
|
407
|
+
var psidChanged = q.psid !== p.psid, angleChanged = q.angle !== p.angle;
|
|
408
|
+
var lastN = p.bits >>> 4, lastR = p.bits & 15, n = q.bits >>> 4, r = q.bits & 15;
|
|
409
|
+
var low = r === lastR ? 0 : r === (lastR + 1) % 16 ? 1 : r === (lastR + 15) % 16 ? 2 : 3;
|
|
410
|
+
var changed = (sc !== this.current ? 64 : 0) + (psidChanged ? 32 : 0) + gpsChanged * 16 + (angleChanged ? 8 : 0) + (n !== lastN ? 4 : 0) + low;
|
|
411
|
+
e0.encodeSymbol(prev.changed[lastReturnContext(prev.p)], changed);
|
|
412
|
+
if (changed & 64) {
|
|
413
|
+
e0.encodeSymbol(prev.channel, (sc - this.current + 3) % 4);
|
|
414
|
+
if (!c) { c = this.contexts[sc] = new Context(prev.p, IntegerCompressor); p = c.p; }
|
|
415
|
+
this.current = sc;
|
|
416
|
+
if (this.version === 3) ctx.v = sc;
|
|
417
|
+
}
|
|
418
|
+
if (this.version !== 3) ctx.v = this.current;
|
|
419
|
+
if (changed & 4) e0.encodeSymbol(lazy(c.nret, lastN, 16), n);
|
|
420
|
+
if (low === 3) {
|
|
421
|
+
if (gpsChanged) e0.encodeSymbol(lazy(c.rnum, lastR, 16), r);
|
|
422
|
+
else e0.encodeSymbol(c.rnumGpsSame, (r - lastR + 14) % 16); // r - lastR - 2, mod 16
|
|
423
|
+
}
|
|
424
|
+
var m = MAP_6CTX[n][r], l = LEVEL_8CT[n][r];
|
|
425
|
+
var cpr = (r === 1 ? 2 : 0) + (r >= n ? 1 : 0);
|
|
426
|
+
var single = n === 1 ? 1 : 0;
|
|
427
|
+
var idx = m * 2 + gpsChanged;
|
|
428
|
+
var diff = (q.x - p.x) | 0; // i32 wrapping_sub
|
|
429
|
+
c.icDx.compress(e0, c.xMedian[idx].get(), diff, single);
|
|
430
|
+
c.xMedian[idx].add(diff);
|
|
431
|
+
diff = (q.y - p.y) | 0;
|
|
432
|
+
c.icDy.compress(e0, c.yMedian[idx].get(), diff, yContext(single, c.icDx));
|
|
433
|
+
c.yMedian[idx].add(diff);
|
|
434
|
+
c.icZ.compress(this.enc[1], c.lastZ[l], q.z, zContext(single, c.icDx, c.icDy));
|
|
435
|
+
c.lastZ[l] = q.z;
|
|
436
|
+
var used = this.used;
|
|
437
|
+
if (q.cls !== p.cls) used[2] = true;
|
|
438
|
+
this.enc[2].encodeSymbol(lazy(c.clsM, (p.cls & 31) * 2 + (cpr === 3 ? 1 : 0), 256), q.cls);
|
|
439
|
+
var lastF = flagsSymbol(p.flags), f = flagsSymbol(q.flags);
|
|
440
|
+
if (f !== lastF) used[3] = true;
|
|
441
|
+
this.enc[3].encodeSymbol(lazy(c.flagsM, lastF, 64), f);
|
|
442
|
+
var ii = cpr * 2 + gpsChanged;
|
|
443
|
+
if (q.intensity !== p.intensity) used[4] = true;
|
|
444
|
+
c.icIntensity.compress(this.enc[4], c.lastIntensity[ii], q.intensity, cpr);
|
|
445
|
+
c.lastIntensity[ii] = q.intensity;
|
|
446
|
+
if (angleChanged) { used[5] = true; c.icAngle.compress(this.enc[5], p.angle, q.angle, gpsChanged); }
|
|
447
|
+
if (q.user !== p.user) used[6] = true;
|
|
448
|
+
this.enc[6].encodeSymbol(lazy(c.userM, Math.floor(p.user / 4), 256), q.user);
|
|
449
|
+
if (psidChanged) { used[7] = true; c.icPsid.compress(this.enc[7], p.psid, q.psid, 0); }
|
|
450
|
+
if (gpsChanged) { used[8] = true; c.writeGps(this.enc[8], q.gpsHi, q.gpsLo); }
|
|
451
|
+
p.x = q.x; p.y = q.y; p.z = q.z; p.intensity = q.intensity; p.bits = q.bits; p.flags = q.flags;
|
|
452
|
+
p.cls = q.cls; p.angle = q.angle; p.user = q.user; p.psid = q.psid; p.gpsHi = q.gpsHi; p.gpsLo = q.gpsLo;
|
|
453
|
+
p.gpsChange = gpsChanged === 1;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Finish the chunk's layers.
|
|
458
|
+
* @returns {Array<Uint8Array>} the nine layers in readLayerSizes order; an unchanged layer is empty
|
|
459
|
+
*/
|
|
460
|
+
Point14Compressor.prototype.finish = function () {
|
|
461
|
+
var out = [];
|
|
462
|
+
for (var i = 0; i < LAYERS; i++) out.push(this.used[i] ? this.enc[i].done() : new Uint8Array(0));
|
|
463
|
+
return out;
|
|
464
|
+
};
|
|
@@ -25,8 +25,9 @@ function col(cols, name) { var c = cols[name]; return c ? function (i) { return
|
|
|
25
25
|
* @param {DataView} out
|
|
26
26
|
* @param {number} at
|
|
27
27
|
* @param {object} totals - updated: bounds (quantised world values) and byReturn counts
|
|
28
|
+
* @param {number} [first=0] - the index of cols' first point in the caller's points (named in errors)
|
|
28
29
|
*/
|
|
29
|
-
export function encodeRecords(cols, pointFormat, recordLength, so, origin, out, at, totals) {
|
|
30
|
+
export function encodeRecords(cols, pointFormat, recordLength, so, origin, out, at, totals, first) {
|
|
30
31
|
var fmt = POINT_FORMATS[pointFormat];
|
|
31
32
|
var F = fmt.fields;
|
|
32
33
|
var ox = origin && isFinite(origin.x) ? origin.x : 0;
|
|
@@ -41,7 +42,7 @@ export function encodeRecords(cols, pointFormat, recordLength, so, origin, out,
|
|
|
41
42
|
var Y = Math.round((cols.y[i] + oy - so.offset.y) / so.scale.y);
|
|
42
43
|
var Z = Math.round(((cols.z ? cols.z[i] : 0) + oz - so.offset.z) / so.scale.z);
|
|
43
44
|
if (X > INT32_MAX || X < INT32_MIN || Y > INT32_MAX || Y < INT32_MIN || Z > INT32_MAX || Z < INT32_MIN) {
|
|
44
|
-
throw new LasError(ERROR_CODES.INVALID_ARGUMENT, "point " + i + " does not fit the scale and offset (int32 overflow); use a coarser scale");
|
|
45
|
+
throw new LasError(ERROR_CODES.INVALID_ARGUMENT, "point " + ((first || 0) + i) + " does not fit the scale and offset (int32 overflow); use a coarser scale");
|
|
45
46
|
}
|
|
46
47
|
out.setInt32(p, X, LE); out.setInt32(p + 4, Y, LE); out.setInt32(p + 8, Z, LE);
|
|
47
48
|
var wx = X * so.scale.x + so.offset.x, wy = Y * so.scale.y + so.offset.y, wz = Z * so.scale.z + so.offset.z;
|