gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
419 lines • 17.5 kB
JavaScript
import { U64I64F64 } from '../util.js';
import { ArithmeticModel, IntegerCompressor, LASWavePacket13, LASpoint10, LASrgba, } from './index.js';
const LASZIP_GPSTIME_MULTIMAX = 512;
/** Parse LAZ Point 1.0 */
export class LAZPoint10v1Reader {
dec;
lastItem = new LASpoint10();
lastXDiff = new Int32Array([0, 0, 0]); // I32 last_x_diff[3];
lastYDiff = new Int32Array([0, 0, 0]); // I32 last_yDiff[3];
lastIncr = 0; // I32 last_incr;
icDx; // IntegerCompressor* ic_dx;
icDy; // IntegerCompressor* ic_dy;
icZ; // IntegerCompressor* ic_z;
icIntensity; // IntegerCompressor* ic_intensity;
icScanAngleRank; // IntegerCompressor* ic_scan_angle_rank;
icPointSourceID; // IntegerCompressor* ic_point_source_ID;
mChangedValues; // ArithmeticModel* m_changedValues;
mBitByte; // ArithmeticModel* m_bit_byte[256];
mClassification; // ArithmeticModel* m_classification[256];
mUserData; // ArithmeticModel* m_user_data[256];
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create models and integer compressors */
this.icDx = new IntegerCompressor(dec, 32); // 32 bits, 1 context
this.icDy = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts
this.icZ = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts
this.icIntensity = new IntegerCompressor(dec, 16);
this.icScanAngleRank = new IntegerCompressor(dec, 8, 2);
this.icPointSourceID = new IntegerCompressor(dec, 16);
this.mChangedValues = new ArithmeticModel(64, false);
this.mBitByte = new Array(256).fill(undefined);
this.mClassification = new Array(256).fill(undefined);
this.mUserData = new Array(256).fill(undefined);
}
/**
* Read in chunk sizes
* @param _reader - the full data store
*/
chunkSizes(_reader) { }
/** @param item - the first raw item needs to be injected for future reads */
init(item) {
/* init state */
this.lastXDiff = new Int32Array([0, 0, 0]);
this.lastYDiff = new Int32Array([0, 0, 0]);
this.lastIncr = 0;
/* init models and integer compressors */
this.icDx.initDecompressor();
this.icDy.initDecompressor();
this.icZ.initDecompressor();
this.icIntensity.initDecompressor();
this.icScanAngleRank.initDecompressor();
this.icPointSourceID.initDecompressor();
this.mChangedValues.init();
/* init "last item" to current item */
this.lastItem.copyFrom(item, 20);
}
/**
* @param item - the current item to be read into
* @param context - the current context
*/
read(item, context) {
// find median difference for x and y from 3 preceding differences
let medianX;
if (this.lastXDiff[0] < this.lastXDiff[1]) {
if (this.lastXDiff[1] < this.lastXDiff[2])
medianX = this.lastXDiff[1];
else if (this.lastXDiff[0] < this.lastXDiff[2])
medianX = this.lastXDiff[2];
else
medianX = this.lastXDiff[0];
}
else {
if (this.lastXDiff[0] < this.lastXDiff[2])
medianX = this.lastXDiff[0];
else if (this.lastXDiff[1] < this.lastXDiff[2])
medianX = this.lastXDiff[2];
else
medianX = this.lastXDiff[1];
}
let median_y;
if (this.lastYDiff[0] < this.lastYDiff[1]) {
if (this.lastYDiff[1] < this.lastYDiff[2])
median_y = this.lastYDiff[1];
else if (this.lastYDiff[0] < this.lastYDiff[2])
median_y = this.lastYDiff[2];
else
median_y = this.lastYDiff[0];
}
else {
if (this.lastYDiff[0] < this.lastYDiff[2])
median_y = this.lastYDiff[0];
else if (this.lastYDiff[1] < this.lastYDiff[2])
median_y = this.lastYDiff[2];
else
median_y = this.lastYDiff[1];
}
// decompress x y z coordinates
const xDiff = this.icDx.decompress(medianX, context);
this.lastItem.x += xDiff;
// we use the number k of bits corrector bits to switch contexts
let kBits = this.icDx.getK();
const yDiff = this.icDy.decompress(median_y, { value: kBits < 19 ? kBits : 19 });
this.lastItem.y += yDiff;
kBits = Math.trunc((kBits + this.icDy.getK()) / 2);
this.lastItem.z = this.icZ.decompress(this.lastItem.z, { value: kBits < 19 ? kBits : 19 });
// decompress which other values have changed
const changedValues = this.dec.decodeSymbol(this.mChangedValues);
if (changedValues !== 0) {
// decompress the intensity if it has changed
if ((changedValues & 32) !== 0) {
this.lastItem.intensity = this.icIntensity.decompress(this.lastItem.intensity, context);
}
// decompress the edge_of_flight_line, scan_direction_flag, ... if it has changed
if ((changedValues & 16) !== 0) {
if (this.mBitByte[this.lastItem.flags] === undefined) {
this.mBitByte[this.lastItem.flags] = new ArithmeticModel(256, false);
this.mBitByte[this.lastItem.flags]?.init();
}
this.lastItem.flags = this.dec.decodeSymbol(this.mBitByte[this.lastItem.flags]);
}
// decompress the classification ... if it has changed
if ((changedValues & 8) !== 0) {
if (this.mClassification[this.lastItem.class] === undefined) {
this.mClassification[this.lastItem.class] = new ArithmeticModel(256, false);
this.mClassification[this.lastItem.class]?.init();
}
this.lastItem.class = this.dec.decodeSymbol(this.mClassification[this.lastItem.class]);
}
// decompress the scan_angle_rank ... if it has changed
if ((changedValues & 4) !== 0) {
this.lastItem.scanAngleRank = this.icScanAngleRank.decompress(this.lastItem.scanAngleRank, {
value: kBits < 3 ? 1 : 0,
});
}
// decompress the user_data ... if it has changed
if ((changedValues & 2) !== 0) {
if (this.mUserData[this.lastItem.userData] === undefined) {
this.mUserData[this.lastItem.userData] = new ArithmeticModel(256, false);
this.mUserData[this.lastItem.userData]?.init();
}
this.lastItem.userData = this.dec.decodeSymbol(this.mUserData[this.lastItem.userData]);
}
// decompress the point_source_ID ... if it has changed
if ((changedValues & 1) !== 0) {
this.lastItem.pointSourceID = this.icPointSourceID.decompress(this.lastItem.pointSourceID, context);
}
}
// record the difference
this.lastXDiff[this.lastIncr] = xDiff;
this.lastYDiff[this.lastIncr] = yDiff;
this.lastIncr++;
if (this.lastIncr > 2)
this.lastIncr = 0;
// copy in the last point
this.lastItem.copyTo(item, 20);
}
}
/** Parse LAZ GPS Time 1.1v1 */
export class LAZgpstime11v1Reader {
dec;
mGpstimeMulti;
mGpstime0diff;
icGpstime;
lastItemDiff = 0;
multiExtremeCounter = 0;
lastItem = new U64I64F64(0, 'u64');
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create entropy models and integer compressors */
this.mGpstimeMulti = new ArithmeticModel(LASZIP_GPSTIME_MULTIMAX, false); // dec.createSymbolModel(LASZIP_GPSTIME_MULTIMAX);
this.mGpstime0diff = new ArithmeticModel(3, false); // dec.createSymbolModel(3);
this.icGpstime = new IntegerCompressor(dec, 32, 6); // 32 bits, 6 contexts
}
/**
* Read in chunk sizes
* @param _reader - the full data store
*/
chunkSizes(_reader) { }
/** @param item - the first raw item needs to be injected for future reads */
init(item) {
/* init state */
this.lastItemDiff = 0;
this.multiExtremeCounter = 0;
/* init models and integer compressors */
this.mGpstimeMulti.init();
this.mGpstime0diff.init();
this.icGpstime.initDecompressor();
/* init last item */
this.lastItem.u64 = item.getBigUint64(0, true);
}
/** @param item - the current item to be read into */
read(item) {
let multi;
if (this.lastItemDiff === 0) {
// if the last integer difference was zero
multi = this.dec.decodeSymbol(this.mGpstime0diff);
if (multi === 1) {
// the difference can be represented with 32 bits
this.lastItemDiff = this.icGpstime.decompress(0, { value: 0 });
this.lastItem.i64 += BigInt(this.lastItemDiff);
}
else if (multi === 2) {
// the difference is huge
this.lastItem.u64 = this.dec.readInt64();
}
}
else {
multi = this.dec.decodeSymbol(this.mGpstimeMulti);
if (multi < LASZIP_GPSTIME_MULTIMAX - 2) {
let gpstimeDiff; // I32
if (multi === 1) {
gpstimeDiff = this.icGpstime.decompress(this.lastItemDiff, { value: 1 });
this.lastItemDiff = gpstimeDiff;
this.multiExtremeCounter = 0;
}
else if (multi === 0) {
gpstimeDiff = this.icGpstime.decompress(Math.trunc(this.lastItemDiff / 4), { value: 2 });
this.multiExtremeCounter++;
if (this.multiExtremeCounter > 3) {
this.lastItemDiff = gpstimeDiff;
this.multiExtremeCounter = 0;
}
}
else if (multi < 10) {
gpstimeDiff = this.icGpstime.decompress(multi * this.lastItemDiff, { value: 3 });
}
else if (multi < 50) {
gpstimeDiff = this.icGpstime.decompress(multi * this.lastItemDiff, { value: 4 });
}
else {
gpstimeDiff = this.icGpstime.decompress(multi * this.lastItemDiff, { value: 5 });
if (multi === LASZIP_GPSTIME_MULTIMAX - 3) {
this.multiExtremeCounter++;
if (this.multiExtremeCounter > 3) {
this.lastItemDiff = gpstimeDiff;
this.multiExtremeCounter = 0;
}
}
}
this.lastItem.i64 += BigInt(gpstimeDiff);
}
else if (multi < LASZIP_GPSTIME_MULTIMAX - 1) {
this.lastItem.u64 = this.dec.readInt64();
}
}
// *((I64*)item) = lastItem.i64;
item.setBigInt64(0, this.lastItem.i64, true);
}
}
/** Parse LAZ RGB 1.2v1 */
export class LAZrgb12v1Reader {
dec;
lastItem = new LASrgba();
mByteUsed;
icRgb;
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create models and integer compressors */
this.mByteUsed = new ArithmeticModel(64, false);
this.icRgb = new IntegerCompressor(dec, 8, 6);
}
/**
* Read in chunk sizes
* @param _reader - the full data store
*/
chunkSizes(_reader) { }
/** @param item - the first raw item needs to be injected for future reads */
init(item) {
/* init models and integer compressors */
this.mByteUsed.init();
this.icRgb.initDecompressor();
this.lastItem.copyFrom(item, 6);
}
/** @param item - the current item to be read into */
read(item) {
const currItem = new LASrgba(item);
const sym = this.dec.decodeSymbol(this.mByteUsed);
if ((sym & (1 << 0)) !== 0)
currItem.r = this.icRgb.decompress(this.lastItem.r & 255, { value: 0 });
else
currItem.r = this.lastItem.r & 0xff;
if ((sym & (1 << 1)) !== 0)
currItem.r |= this.icRgb.decompress(this.lastItem.r >> 8, { value: 1 }) << 8;
else
currItem.r |= this.lastItem.r & 0xff00;
if ((sym & (1 << 2)) !== 0)
currItem.g = this.icRgb.decompress(this.lastItem.g & 255, { value: 2 });
else
currItem.g = this.lastItem.g & 0xff;
if ((sym & (1 << 3)) !== 0)
currItem.g |= this.icRgb.decompress(this.lastItem.g >> 8, { value: 3 }) << 8;
else
currItem.g |= this.lastItem.g & 0xff00;
if ((sym & (1 << 4)) !== 0)
currItem.b = this.icRgb.decompress(this.lastItem.b & 255, { value: 4 });
else
currItem.b = this.lastItem.b & 0xff;
if ((sym & (1 << 5)) !== 0)
currItem.b |= this.icRgb.decompress(this.lastItem.b >> 8, { value: 5 }) << 8;
else
currItem.b |= this.lastItem.b & 0xff00;
currItem.copyTo(this.lastItem.data, 6);
}
}
/** Parse LAZ wavepacket 1.3v1 */
export class LAZwavepacket13v1Reader {
dec;
lastItem = new LASWavePacket13();
mPacketIndex;
mOffsetDiff;
icOffsetDiff;
icPacketSize;
icReturnPoint;
icXyz;
lastDiff32 = 0;
symLastOffsetDiff = 0;
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create models and integer compressors */
this.mPacketIndex = new ArithmeticModel(256, false);
this.mOffsetDiff = [
new ArithmeticModel(4, false),
new ArithmeticModel(4, false),
new ArithmeticModel(4, false),
new ArithmeticModel(4, false),
];
this.icOffsetDiff = new IntegerCompressor(dec, 32);
this.icPacketSize = new IntegerCompressor(dec, 32);
this.icReturnPoint = new IntegerCompressor(dec, 32);
this.icXyz = new IntegerCompressor(dec, 32, 3);
}
/**
* Read in chunk sizes
* @param _reader - the full data store
*/
chunkSizes(_reader) { }
/** @param item - the first raw item needs to be injected for future reads */
init(item) {
/* init state */
this.lastDiff32 = 0;
this.symLastOffsetDiff = 0;
/* init models and integer compressors */
this.mPacketIndex.init();
for (const m of this.mOffsetDiff)
m.init();
this.icOffsetDiff.initDecompressor();
this.icPacketSize.initDecompressor();
this.icReturnPoint.initDecompressor();
this.icXyz.initDecompressor();
this.lastItem.copyFrom(item, 28);
}
/** @param item - the current item to be read into */
read(item) {
const thisItemM = new LASWavePacket13(item);
thisItemM.index = this.dec.decodeSymbol(this.mPacketIndex);
this.symLastOffsetDiff = this.dec.decodeSymbol(this.mOffsetDiff[this.symLastOffsetDiff]);
if (this.symLastOffsetDiff === 0) {
thisItemM.offset = this.lastItem.offset;
}
else if (this.symLastOffsetDiff === 1) {
thisItemM.offset = this.lastItem.offset + this.lastItem.packetSize;
}
else if (this.symLastOffsetDiff === 2) {
this.lastDiff32 = this.icOffsetDiff.decompress(this.lastDiff32);
thisItemM.offset = this.lastItem.offset + this.lastDiff32;
}
else {
thisItemM.offset = this.dec.readInt64();
}
thisItemM.packetSize = this.icPacketSize.decompress(this.lastItem.packetSize);
thisItemM.returnPoint = this.icReturnPoint.decompress(this.lastItem.returnPoint);
thisItemM.x = this.icXyz.decompress(this.lastItem.x, { value: 0 });
thisItemM.y = this.icXyz.decompress(this.lastItem.y, { value: 1 });
thisItemM.z = this.icXyz.decompress(this.lastItem.z, { value: 2 });
thisItemM.copyTo(this.lastItem.data, 29);
}
}
/** Parse LAZ byte 1.0v1 */
export class LAZbyte10v1Reader {
dec;
number;
lastItem;
icByte;
/**
* @param dec - the arithmetic decoder
* @param number - the number of bytes to read at a time
*/
constructor(dec, number) {
this.dec = dec;
this.number = number;
this.icByte = new IntegerCompressor(dec, 8, number);
this.lastItem = new Uint8Array(number);
}
/**
* Read in chunk sizes
* @param _reader - the full data store
*/
chunkSizes(_reader) { }
/** @param item - the first raw item needs to be injected for future reads */
init(item) {
/* init models and integer compressors */
this.icByte.initDecompressor();
const itemUint8 = new Uint8Array(item.buffer, item.byteOffset, this.number);
this.lastItem.set(itemUint8);
}
/** @param item - the current item to be read into */
read(item) {
const thisItem = new Uint8Array(item.buffer, item.byteOffset, this.number);
for (let i = 0; i < this.number; i++) {
thisItem[i] = this.icByte.decompress(this.lastItem[i], { value: i });
}
this.lastItem.set(thisItem);
}
}
//# sourceMappingURL=v1.js.map