UNPKG

loaders.gl

Version:

Framework-independent loaders for 3D graphics formats

357 lines (291 loc) 10 kB
/* Modified from Uday Verma and Howard Butler's plasio https://github.com/verma/plasio/ MIT License */ // laslaz.js - treat as compiled code /* eslint-disable */ (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(15, 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(15, 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(15, 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(15, 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 === 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; this.ww = new Worker(`workers/laz-loader-worker.js`); 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) { 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 > 13) throw new Error('Only file versions <= 1.3 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, len, header) { this.arrayb = buffer; this.decoder = pointFormatReaders[header.pointsFormatId]; this.pointsCount = len; this.pointSize = header.pointsStructSize; this.scale = header.scale; this.offset = header.offset; this.mins = header.mins; this.maxs = header.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); }; LASFile.prototype.getUnpacker = function() { return LASDecoder; }; scope.LASFile = LASFile; scope.LASModuleWasLoaded = false; })(module.exports); /* eslint no-use-before-define: 2 */