Files
Andreas Wilms 78481ca337 init
python
2025-09-09 19:40:44 +02:00

399 lines
10 KiB
JavaScript

// laslaz.js
// LAS/LAZ loading
//
//var common = require("./common"),
// Promise = require("bluebird");
(function(scope) {
"use strict";
var pointFormatReaders = {
0: function(dv) {
return {
"position": [ dv.getInt32(0, true), dv.getInt32(4, true), dv.getInt32(8, true)],
"intensity": dv.getUint16(12, true),
"classification": dv.getUint8(16, true)
};
},
1: function(dv) {
return {
"position": [ dv.getInt32(0, true), dv.getInt32(4, true), dv.getInt32(8, true)],
"intensity": dv.getUint16(12, true),
"classification": dv.getUint8(16, true)
};
},
2: function(dv) {
return {
"position": [ dv.getInt32(0, true), dv.getInt32(4, true), dv.getInt32(8, true)],
"intensity": dv.getUint16(12, true),
"classification": dv.getUint8(16, true),
"color": [dv.getUint16(20, true), dv.getUint16(22, true), dv.getUint16(24, true)]
};
},
3: function(dv) {
return {
"position": [ dv.getInt32(0, true), dv.getInt32(4, true), dv.getInt32(8, true)],
"intensity": dv.getUint16(12, true),
"classification": dv.getUint8(16, true),
"color": [dv.getUint16(28, true), dv.getUint16(30, true), dv.getUint16(32, true)]
};
}
};
function readAs(buf, Type, offset, count) {
count = (count === undefined || count === 0 ? 1 : count);
var sub = buf.slice(offset, offset + Type.BYTES_PER_ELEMENT * count);
var r = new Type(sub);
if (count === undefined || count === 1)
return r[0];
var ret = [];
for (var i = 0 ; i < count ; i ++) {
ret.push(r[i]);
}
return ret;
}
function parseLASHeader(arraybuffer) {
var o = {};
o.pointsOffset = readAs(arraybuffer, Uint32Array, 32*3);
o.pointsFormatId = readAs(arraybuffer, Uint8Array, 32*3+8);
o.pointsStructSize = readAs(arraybuffer, Uint16Array, 32*3+8+1);
o.pointsCount = readAs(arraybuffer, Uint32Array, 32*3 + 11);
var start = 32*3 + 35;
o.scale = readAs(arraybuffer, Float64Array, start, 3); start += 24; // 8*3
o.offset = readAs(arraybuffer, Float64Array, start, 3); start += 24;
var bounds = readAs(arraybuffer, Float64Array, start, 6); start += 48; // 8*6;
o.maxs = [bounds[0], bounds[2], bounds[4]];
o.mins = [bounds[1], bounds[3], bounds[5]];
return o;
}
var msgIndex = 0;
var waitHandlers = {};
// This method is scope-wide since the nacl module uses this fuction to notify
// us of events
scope.handleMessage = function(message_event) {
var msg = message_event.data;
var resolver = waitHandlers[msg.id];
delete waitHandlers[msg.id];
// call the callback in a separate context, make sure we've cleaned our
// state out before the callback is invoked since it may queue more doExchanges
setTimeout(function() {
if (msg.error)
return resolver.reject(new Error(msg.message || "Unknown Error"));
if (msg.hasOwnProperty('count') && msg.hasOwnProperty('hasMoreData')) {
return resolver.resolve({
buffer: msg.result,
count: msg.count,
hasMoreData: msg.hasMoreData});
}
resolver.resolve(msg.result);
}, 0);
};
var doDataExchange = function(cmd, callback) {
cmd.id = msgIndex.toString();
msgIndex ++;
var resolver = Promise.defer();
waitHandlers[cmd.id] = resolver;
nacl_module.postMessage(cmd);
return resolver.promise.cancellable();
};
// LAS Loader
// Loads uncompressed files
//
var LASLoader = function(arraybuffer) {
this.arraybuffer = arraybuffer;
};
LASLoader.prototype.open = function() {
// nothing needs to be done to open this file
//
this.readOffset = 0;
return new Promise(function(res, rej) {
setTimeout(res, 0);
});
};
LASLoader.prototype.getHeader = function() {
var o = this;
return new Promise(function(res, rej) {
setTimeout(function() {
o.header = parseLASHeader(o.arraybuffer);
res(o.header);
}, 0);
});
};
LASLoader.prototype.readData = function(count, offset, skip) {
var o = this;
return new Promise(function(res, rej) {
setTimeout(function() {
if (!o.header)
return rej(new Error("Cannot start reading data till a header request is issued"));
var start;
if (skip <= 1) {
count = Math.min(count, o.header.pointsCount - o.readOffset);
start = o.header.pointsOffset + o.readOffset * o.header.pointsStructSize;
var end = start + count * o.header.pointsStructSize;
res({
buffer: o.arraybuffer.slice(start, end),
count: count,
hasMoreData: o.readOffset + count < o.header.pointsCount});
o.readOffset += count;
}
else {
var pointsToRead = Math.min(count * skip, o.header.pointsCount - o.readOffset);
var bufferSize = Math.ceil(pointsToRead / skip);
var pointsRead = 0;
var buf = new Uint8Array(bufferSize * o.header.pointsStructSize);
for (var i = 0 ; i < pointsToRead ; i ++) {
if (i % skip === 0) {
start = o.header.pointsOffset + o.readOffset * o.header.pointsStructSize;
var src = new Uint8Array(o.arraybuffer, start, o.header.pointsStructSize);
buf.set(src, pointsRead * o.header.pointsStructSize);
pointsRead ++;
}
o.readOffset ++;
}
res({
buffer: buf.buffer,
count: pointsRead,
hasMoreData: o.readOffset < o.header.pointsCount
});
}
}, 0);
});
};
LASLoader.prototype.close = function() {
var o = this;
return new Promise(function(res, rej) {
o.arraybuffer = null;
setTimeout(res, 0);
});
};
// LAZ Loader
// Uses NaCL module to load LAZ files
//
var LAZLoader = function(arraybuffer) {
this.arraybuffer = arraybuffer;
let workerPath = Potree.scriptPath + "/workers/LASLAZWorker.js";
this.ww = Potree.workerPool.getWorker(workerPath);
this.nextCB = null;
var o = this;
this.ww.onmessage = function(e) {
if (o.nextCB !== null) {
o.nextCB(e.data);
o.nextCB = null;
}
};
this.dorr = function(req, cb) {
o.nextCB = cb;
o.ww.postMessage(req);
};
};
LAZLoader.prototype.open = function() {
// nothing needs to be done to open this file
//
var o = this;
return new Promise(function(res, rej) {
o.dorr({type:"open", arraybuffer: o.arraybuffer}, function(r) {
if (r.status !== 1)
return rej(new Error("Failed to open file"));
res(true);
});
});
};
LAZLoader.prototype.getHeader = function() {
var o = this;
return new Promise(function(res, rej) {
o.dorr({type:'header'}, function(r) {
if (r.status !== 1)
return rej(new Error("Failed to get header"));
res(r.header);
});
});
};
LAZLoader.prototype.readData = function(count, offset, skip) {
var o = this;
return new Promise(function(res, rej) {
o.dorr({type:'read', count: count, offset: offset, skip: skip}, function(r) {
if (r.status !== 1)
return rej(new Error("Failed to read data"));
res({
buffer: r.buffer,
count: r.count,
hasMoreData: r.hasMoreData
});
});
});
};
LAZLoader.prototype.close = function() {
var o = this;
return new Promise(function(res, rej) {
o.dorr({type:'close'}, function(r) {
let workerPath = Potree.scriptPath + "/workers/LASLAZWorker.js";
Potree.workerPool.returnWorker(workerPath, o.ww);
if (r.status !== 1)
return rej(new Error("Failed to close file"));
res(true);
});
});
};
// A single consistent interface for loading LAS/LAZ files
var LASFile = function(arraybuffer) {
this.arraybuffer = arraybuffer;
this.determineVersion();
if (this.version > 12)
throw new Error("Only file versions <= 1.2 are supported at this time");
this.determineFormat();
if (pointFormatReaders[this.formatId] === undefined)
throw new Error("The point format ID is not supported");
this.loader = this.isCompressed ?
new LAZLoader(this.arraybuffer) :
new LASLoader(this.arraybuffer);
};
LASFile.prototype.determineFormat = function() {
var formatId = readAs(this.arraybuffer, Uint8Array, 32*3+8);
var bit_7 = (formatId & 0x80) >> 7;
var bit_6 = (formatId & 0x40) >> 6;
if (bit_7 === 1 && bit_6 === 1)
throw new Error("Old style compression not supported");
this.formatId = formatId & 0x3f;
this.isCompressed = (bit_7 === 1 || bit_6 === 1);
};
LASFile.prototype.determineVersion = function() {
var ver = new Int8Array(this.arraybuffer, 24, 2);
this.version = ver[0] * 10 + ver[1];
this.versionAsString = ver[0] + "." + ver[1];
};
LASFile.prototype.open = function() {
return this.loader.open();
};
LASFile.prototype.getHeader = function() {
return this.loader.getHeader();
};
LASFile.prototype.readData = function(count, start, skip) {
return this.loader.readData(count, start, skip);
};
LASFile.prototype.close = function() {
return this.loader.close();
};
// Decodes LAS records into points
//
var LASDecoder = function(buffer, pointFormatID, pointSize, pointsCount, scale, offset, mins, maxs) {
this.arrayb = buffer;
this.decoder = pointFormatReaders[pointFormatID];
this.pointsCount = pointsCount;
this.pointSize = pointSize;
this.scale = scale;
this.offset = offset;
this.mins = mins;
this.maxs = maxs;
};
LASDecoder.prototype.getPoint = function(index) {
if (index < 0 || index >= this.pointsCount)
throw new Error("Point index out of range");
var dv = new DataView(this.arrayb, index * this.pointSize, this.pointSize);
return this.decoder(dv);
};
// NACL Module support
// Called by the common.js module.
//
//window.startNaCl = function(name, tc, config, width, height) {
// // check browser support for nacl
// //
// if(!common.browserSupportsNaCl()) {
// return $.event.trigger({
// type: "plasio.nacl.error",
// message: "NaCl support is not available"
// });
// }
// navigator.webkitPersistentStorage.requestQuota(2048 * 2048, function(bytes) {
// common.updateStatus(
// 'Allocated ' + bytes + ' bytes of persistant storage.');
// common.attachDefaultListeners();
// common.createNaClModule(name, tc, config, width, height);
// },
// function(e) {
// $.event.trigger({
// type: "plasio.nacl.error",
// message: "Could not allocate persistant storage"
// });
// });
// $(document).on("plasio.nacl.available", function() {
// scope.LASModuleWasLoaded = true;
// });
//};
scope.LAZLoader = LAZLoader;
scope.LASLoader = LASLoader;
scope.LASFile = LASFile;
scope.LASDecoder = LASDecoder;
scope.LASModuleWasLoaded = false;
//})(module.exports);
})(this);