gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
535 lines • 21.6 kB
JavaScript
import { ArithmeticModel, IntegerCompressor, LASpoint10, LASrgba, numberReturnLevel, numberReturnMap, } from './index.js';
import { U64I64F64, u32ZeroBit0, u8Clamp, u8Fold } from '../util.js';
const LASZIP_GPSTIME_MULTI = 500;
const LASZIP_GPSTIME_MULTI_MINUS = -10;
const LASZIP_GPSTIME_MULTI_UNCHANGED = 511; // LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1;
const LASZIP_GPSTIME_MULTI_CODE_FULL = 512; // LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 2;
const LASZIP_GPSTIME_MULTI_TOTAL = 516; // LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 6;
/** Streaming Median 5 */
export class StreamingMedian5 {
values = new Array(5); // i32
high = false;
/** Runs the initialization */
constructor() {
this.init();
}
/** initialize the first value set */
init() {
this.values[0] = this.values[1] = this.values[2] = this.values[3] = this.values[4] = 0;
this.high = true;
}
/**
* add a new value
* @param v - the new value to add
*/
add(v) {
const { high, values } = this;
if (high) {
if (v < values[2]) {
values[4] = values[3];
values[3] = values[2];
if (v < values[0]) {
values[2] = values[1];
values[1] = values[0];
values[0] = v;
}
else if (v < values[1]) {
values[2] = values[1];
values[1] = v;
}
else {
values[2] = v;
}
}
else {
if (v < values[3]) {
values[4] = values[3];
values[3] = v;
}
else {
values[4] = v;
}
this.high = false;
}
}
else {
if (values[2] < v) {
values[0] = values[1];
values[1] = values[2];
if (values[4] < v) {
values[2] = values[3];
values[3] = values[4];
values[4] = v;
}
else if (values[3] < v) {
values[2] = values[3];
values[3] = v;
}
else {
values[2] = v;
}
}
else {
if (values[1] < v) {
values[0] = values[1];
values[1] = v;
}
else {
values[0] = v;
}
this.high = true;
}
}
}
/** @returns the median value */
get() {
return this.values[2];
}
}
/** LAZ Point10 2.0 Reader */
export class LAZPoint10v2Reader {
dec;
lastItem = new LASpoint10();
lastIncr = 0; // I32 last_incr;
icDx; // IntegerCompressor* icDx;
icDy; // IntegerCompressor* icDy;
icZ; // IntegerCompressor* icZ;
icIntensity; // IntegerCompressor* this.icIntensity;
lastIntensity = new Uint16Array(16);
lastXDiffMedian5;
lastYDiffMedian5;
lastHeight = new Int32Array(8);
icPointSourceID; // IntegerCompressor* icPointSourceID;
mChangedValues; // ArithmeticModel* m_changedValues;
mBitByte; // ArithmeticModel* mBitByte[256];
mClassification; // ArithmeticModel* mClassification[256];
mUserData; // ArithmeticModel* mUserData[256];
mScanAngleRank;
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create models and integer compressors */
this.mChangedValues = new ArithmeticModel(64, false);
this.icIntensity = new IntegerCompressor(dec, 16, 4);
this.mScanAngleRank = [new ArithmeticModel(256), new ArithmeticModel(256)];
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);
this.icDx = new IntegerCompressor(dec, 32, 2); // 32 bits, 2 context
this.icDy = new IntegerCompressor(dec, 32, 22); // 32 bits, 22 contexts
this.icZ = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts
this.lastXDiffMedian5 = new Array(16);
this.lastYDiffMedian5 = new Array(16);
for (let i = 0; i < 16; i++) {
this.lastXDiffMedian5[i] = new StreamingMedian5();
this.lastYDiffMedian5[i] = new StreamingMedian5();
}
}
/**
* 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) {
let i;
/* init state */
for (i = 0; i < 16; i++) {
this.lastXDiffMedian5[i].init();
this.lastYDiffMedian5[i].init();
this.lastIntensity[i] = 0;
this.lastHeight[Math.trunc(i / 2)] = 0;
}
/* init models and integer compressors */
this.mChangedValues.init();
this.icIntensity.initDecompressor();
this.mScanAngleRank[0].init();
this.mScanAngleRank[1].init();
this.icPointSourceID.initDecompressor();
for (i = 0; i < 256; i++) {
this.mBitByte[i]?.init();
this.mClassification[i]?.init();
this.mUserData[i]?.init();
}
this.icDx.initDecompressor();
this.icDy.initDecompressor();
this.icZ.initDecompressor();
/* init last item */
this.lastItem.copyFrom(item, 20);
/* but set intensity to zero */
this.lastItem.intensity = 0;
}
/** @param item - the current item to be read into */
read(item) {
let r, n, m, l;
let kBits;
let median, diff;
// decompress which other values have changed
const changedValues = this.dec.decodeSymbol(this.mChangedValues);
// console.log('changedValues', changedValues);
if (changedValues !== 0) {
// decompress the edge_of_flight_line, scanDirectionFlag, ... if it has changed
if ((changedValues & 32) !== 0) {
if (this.mBitByte[this.lastItem.flags] === undefined) {
this.mBitByte[this.lastItem.flags] = new ArithmeticModel(256);
this.mBitByte[this.lastItem.flags]?.init();
}
this.lastItem.flags = this.dec.decodeSymbol(this.mBitByte[this.lastItem.flags]);
}
r = this.lastItem.returnNumber;
n = this.lastItem.numberOfReturns;
m = numberReturnMap[n][r];
l = numberReturnLevel[n][r];
// decompress the intensity if it has changed
if ((changedValues & 16) !== 0) {
this.lastItem.intensity = this.icIntensity.decompress(this.lastIntensity[m], {
value: m < 3 ? m : 3,
});
this.lastIntensity[m] = this.lastItem.intensity;
}
else {
this.lastItem.intensity = this.lastIntensity[m];
}
// 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);
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) {
const val = this.dec.decodeSymbol(this.mScanAngleRank[this.lastItem.scanDirectionFlag]);
this.lastItem.scanAngleRank = u8Fold(val + this.lastItem.scanAngleRank);
}
// 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);
this.mUserData[this.lastItem.userData]?.init();
}
this.lastItem.userData = this.dec.decodeSymbol(this.mUserData[this.lastItem.userData]);
}
// decompress the pointSourceID ... if it has changed
if ((changedValues & 1) !== 0) {
this.lastItem.pointSourceID = this.icPointSourceID.decompress(this.lastItem.pointSourceID);
}
}
else {
r = this.lastItem.returnNumber;
n = this.lastItem.numberOfReturns;
m = numberReturnMap[n][r];
l = numberReturnLevel[n][r];
}
// decompress x coordinate
median = this.lastXDiffMedian5[m].get();
diff = this.icDx.decompress(median, { value: n === 1 ? 1 : 0 });
this.lastItem.x += diff;
this.lastXDiffMedian5[m].add(diff);
// decompress y coordinate
median = this.lastYDiffMedian5[m].get();
kBits = this.icDx.getK();
diff = this.icDy.decompress(median, {
value: Number(n === 1) + (kBits < 20 ? u32ZeroBit0(kBits) : 20),
});
this.lastItem.y += diff;
this.lastYDiffMedian5[m].add(diff);
// decompress z coordinate
kBits = Math.trunc((this.icDx.getK() + this.icDy.getK()) / 2);
this.lastItem.z = this.icZ.decompress(this.lastHeight[l], {
value: Number(n === 1) + (kBits < 18 ? u32ZeroBit0(kBits) : 18),
});
this.lastHeight[l] = this.lastItem.z;
// copy in the last point
this.lastItem.copyTo(item, 20);
}
}
/** Parse LAZ GPS Time 1.1v2 */
export class LAZgpstime11v2Reader {
dec;
mGpstimeMulti;
mGpstime0diff;
icGpstime;
last = 0; // U32
next = 0; // U32
lastGpstime = [
new U64I64F64(0, 'u64'),
new U64I64F64(0, 'u64'),
new U64I64F64(0, 'u64'),
new U64I64F64(0, 'u64'),
];
lastGpstimeDiff = [0, 0, 0, 0]; // I32
multiExtremeCounter = [0, 0, 0, 0]; // I32
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
/* create entropy models and integer compressors */
this.mGpstimeMulti = new ArithmeticModel(LASZIP_GPSTIME_MULTI_TOTAL);
this.mGpstime0diff = new ArithmeticModel(6);
this.icGpstime = new IntegerCompressor(dec, 32, 9); // 32 bits, 9 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.last = 0;
this.next = 0;
this.lastGpstimeDiff = [0, 0, 0, 0];
this.multiExtremeCounter = [0, 0, 0, 0];
/* init models and integer compressors */
this.mGpstimeMulti.init();
this.mGpstime0diff.init();
this.icGpstime.initDecompressor();
/* init last item */
this.lastGpstime[0].u64 = item.getBigUint64(0, true);
this.lastGpstime[1].u64 = 0;
this.lastGpstime[2].u64 = 0;
this.lastGpstime[3].u64 = 0;
}
/** @param item - the current item to be read into */
read(item) {
let multi; // I32
if (this.lastGpstimeDiff[this.last] === 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.lastGpstimeDiff[this.last] = this.icGpstime.decompress(0, { value: 0 });
this.lastGpstime[this.last].i64 += BigInt(this.lastGpstimeDiff[this.last]);
this.multiExtremeCounter[this.last] = 0;
}
else if (multi === 2) {
// the difference is huge
this.next = (this.next + 1) & 3;
this.lastGpstime[this.next].u64 = this.icGpstime.decompress(Number(this.lastGpstime[this.last].u64 >> 32n), { value: 8 });
this.lastGpstime[this.next].u64 = BigInt(this.lastGpstime[this.next].u64) << 32n;
this.lastGpstime[this.next].u64 |= BigInt(this.dec.readInt());
this.last = this.next;
this.lastGpstimeDiff[this.last] = 0;
this.multiExtremeCounter[this.last] = 0;
}
else if (multi > 2) {
// we switch to another sequence
this.last = (this.last + multi - 2) & 3;
this.read(item);
}
}
else {
multi = this.dec.decodeSymbol(this.mGpstimeMulti);
if (multi === 1) {
this.lastGpstime[this.last].i64 += BigInt(this.icGpstime.decompress(this.lastGpstimeDiff[this.last], { value: 1 }));
this.multiExtremeCounter[this.last] = 0;
}
else if (multi < LASZIP_GPSTIME_MULTI_UNCHANGED) {
let gpstimeDiff;
if (multi === 0) {
gpstimeDiff = this.icGpstime.decompress(0, { value: 7 });
this.multiExtremeCounter[this.last]++;
if (this.multiExtremeCounter[this.last] > 3) {
this.lastGpstimeDiff[this.last] = gpstimeDiff;
this.multiExtremeCounter[this.last] = 0;
}
}
else if (multi < LASZIP_GPSTIME_MULTI) {
if (multi < 10)
gpstimeDiff = this.icGpstime.decompress(multi * this.lastGpstimeDiff[this.last], {
value: 2,
});
else
gpstimeDiff = this.icGpstime.decompress(multi * this.lastGpstimeDiff[this.last], {
value: 3,
});
}
else if (multi === LASZIP_GPSTIME_MULTI) {
gpstimeDiff = this.icGpstime.decompress(LASZIP_GPSTIME_MULTI * this.lastGpstimeDiff[this.last], { value: 4 });
this.multiExtremeCounter[this.last]++;
if (this.multiExtremeCounter[this.last] > 3) {
this.lastGpstimeDiff[this.last] = gpstimeDiff;
this.multiExtremeCounter[this.last] = 0;
}
}
else {
multi = LASZIP_GPSTIME_MULTI - multi;
if (multi > LASZIP_GPSTIME_MULTI_MINUS) {
gpstimeDiff = this.icGpstime.decompress(multi * this.lastGpstimeDiff[this.last], {
value: 5,
});
}
else {
gpstimeDiff = this.icGpstime.decompress(LASZIP_GPSTIME_MULTI_MINUS * this.lastGpstimeDiff[this.last], { value: 6 });
this.multiExtremeCounter[this.last]++;
if (this.multiExtremeCounter[this.last] > 3) {
this.lastGpstimeDiff[this.last] = gpstimeDiff;
this.multiExtremeCounter[this.last] = 0;
}
}
}
this.lastGpstime[this.last].i64 += BigInt(gpstimeDiff);
}
else if (multi === LASZIP_GPSTIME_MULTI_CODE_FULL) {
this.next = (this.next + 1) & 3;
this.lastGpstime[this.next].u64 = this.icGpstime.decompress(Number(Number(this.lastGpstime[this.last].u64 >> 32n)), { value: 8 });
this.lastGpstime[this.next].u64 = this.lastGpstime[this.next].u64 << 32n;
this.lastGpstime[this.next].u64 |= BigInt(this.dec.readInt());
this.last = this.next;
this.lastGpstimeDiff[this.last] = 0;
this.multiExtremeCounter[this.last] = 0;
}
else if (multi >= LASZIP_GPSTIME_MULTI_CODE_FULL) {
this.last = (this.last + multi - LASZIP_GPSTIME_MULTI_CODE_FULL) & 3;
this.read(item);
}
}
item.setBigInt64(0, this.lastGpstime[this.last].i64, true);
}
}
/** Parse LAZ RGB 1.2v2 */
export class LAZrgb12v2Reader {
dec;
lastItem = new LASrgba();
mByteUsed = new ArithmeticModel(128);
mRgbDiff0 = new ArithmeticModel(256);
mRgbDiff1 = new ArithmeticModel(256);
mRgbDiff2 = new ArithmeticModel(256);
mRgbDiff3 = new ArithmeticModel(256);
mRgbDiff4 = new ArithmeticModel(256);
mRgbDiff5 = new ArithmeticModel(256);
/** @param dec - the arithmetic decoder */
constructor(dec) {
this.dec = dec;
}
/**
* 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.mRgbDiff0.init();
this.mRgbDiff1.init();
this.mRgbDiff2.init();
this.mRgbDiff3.init();
this.mRgbDiff4.init();
this.mRgbDiff5.init();
/* init last item */
this.lastItem.copyFrom(item, 6);
}
/**
* read in the next item
* @param item - the next item
*/
read(item) {
const currItem = new LASrgba(item);
currItem.copyFrom(this.lastItem.data, 6);
let corr;
let diff = 0;
const sym = this.dec.decodeSymbol(this.mByteUsed);
if ((sym & 1) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff0);
currItem.r = u8Fold(corr + (this.lastItem.r & 255));
}
else {
currItem.r = this.lastItem.r & 0xff;
}
if ((sym & (1 << 1)) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff1);
currItem.r |= u8Fold(corr + (this.lastItem.r >> 8)) << 8;
}
else {
currItem.r |= this.lastItem.r & 0xff00;
}
if ((sym & (1 << 6)) !== 0) {
diff = (currItem.r & 0x00ff) - (this.lastItem.r & 0x00ff);
if ((sym & (1 << 2)) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff2);
currItem.g = u8Fold(corr + u8Clamp(diff + (this.lastItem.g & 255)));
}
else {
currItem.g = this.lastItem.g & 0xff;
}
if ((sym & (1 << 4)) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff4);
diff = Math.trunc((diff + ((currItem.g & 0x00ff) - (this.lastItem.g & 0x00ff))) / 2);
currItem.b = u8Fold(corr + u8Clamp(diff + (this.lastItem.b & 255)));
}
else {
currItem.b = this.lastItem.b & 0xff;
}
diff = (currItem.r >> 8) - (this.lastItem.r >> 8);
if ((sym & (1 << 3)) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff3);
currItem.g |= u8Fold(corr + u8Clamp(diff + (this.lastItem.g >> 8))) << 8;
}
else {
currItem.g |= this.lastItem.g & 0xff00;
}
if ((sym & (1 << 5)) !== 0) {
corr = this.dec.decodeSymbol(this.mRgbDiff5);
diff = Math.trunc((diff + ((currItem.g >> 8) - (this.lastItem.g >> 8))) / 2);
currItem.b |= u8Fold(corr + u8Clamp(diff + (this.lastItem.b >> 8))) << 8;
}
else {
currItem.b |= this.lastItem.b & 0xff00;
}
}
else {
currItem.g = currItem.r;
currItem.b = currItem.r;
}
currItem.copyTo(this.lastItem.data, 6);
}
}
/** LAZ byte reader V2 */
export class LAZbyte10v2Reader {
dec;
number;
mByte;
lastItem;
/**
* @param dec - the arithmetic decoder
* @param number - the number of bytes to read
*/
constructor(dec, number) {
this.dec = dec;
this.number = number;
/* create models and integer compressors */
this.mByte = new Array(number).map(() => new ArithmeticModel(256));
/* create last item */
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 */
for (let i = 0; i < this.number; i++)
this.mByte[i].init();
/* init last item */
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) {
let value;
const itemUint8 = new Uint8Array(item.buffer, item.byteOffset, this.number);
for (let i = 0; i < this.number; i++) {
value = this.lastItem[i] + this.dec.decodeSymbol(this.mByte[i]);
itemUint8[i] = u8Fold(value);
}
// update lastItem
this.lastItem.set(itemUint8);
}
}
//# sourceMappingURL=v2.js.map