POCLoader.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {PointCloudOctreeGeometry, PointCloudOctreeGeometryNode} from "../PointCloudOctreeGeometry.js";
  3. import {Version} from "../Version.js";
  4. import {XHRFactory} from "../XHRFactory.js";
  5. import {LasLazLoader} from "./LasLazLoader.js";
  6. import {BinaryLoader} from "./BinaryLoader.js";
  7. import {Utils} from "../utils.js";
  8. import {PointAttribute, PointAttributes, PointAttributeTypes} from "./PointAttributes.js";
  9. function parseAttributes(cloudjs){
  10. let version = new Version(cloudjs.version);
  11. const replacements = {
  12. "COLOR_PACKED": "rgba",
  13. "RGBA": "rgba",
  14. "INTENSITY": "intensity",
  15. "CLASSIFICATION": "classification",
  16. "GPS_TIME": "gps-time",
  17. };
  18. const replaceOldNames = (old) => {
  19. if(replacements[old]){
  20. return replacements[old];
  21. }else{
  22. return old;
  23. }
  24. };
  25. const pointAttributes = [];
  26. if(version.upTo('1.7')){
  27. for(let attributeName of cloudjs.pointAttributes){
  28. const oldAttribute = PointAttribute[attributeName];
  29. const attribute = {
  30. name: oldAttribute.name,
  31. size: oldAttribute.byteSize,
  32. elements: oldAttribute.numElements,
  33. elementSize: oldAttribute.byteSize / oldAttribute.numElements,
  34. type: oldAttribute.type.name,
  35. description: "",
  36. };
  37. pointAttributes.push(attribute);
  38. }
  39. }else{
  40. pointAttributes.push(...cloudjs.pointAttributes);
  41. }
  42. {
  43. const attributes = new PointAttributes();
  44. const typeConversion = {
  45. int8: PointAttributeTypes.DATA_TYPE_INT8,
  46. int16: PointAttributeTypes.DATA_TYPE_INT16,
  47. int32: PointAttributeTypes.DATA_TYPE_INT32,
  48. int64: PointAttributeTypes.DATA_TYPE_INT64,
  49. uint8: PointAttributeTypes.DATA_TYPE_UINT8,
  50. uint16: PointAttributeTypes.DATA_TYPE_UINT16,
  51. uint32: PointAttributeTypes.DATA_TYPE_UINT32,
  52. uint64: PointAttributeTypes.DATA_TYPE_UINT64,
  53. double: PointAttributeTypes.DATA_TYPE_DOUBLE,
  54. float: PointAttributeTypes.DATA_TYPE_FLOAT,
  55. };
  56. for(const jsAttribute of pointAttributes){
  57. const name = replaceOldNames(jsAttribute.name);
  58. const type = typeConversion[jsAttribute.type];
  59. const numElements = jsAttribute.elements;
  60. const description = jsAttribute.description;
  61. const attribute = new PointAttribute(name, type, numElements);
  62. attributes.add(attribute);
  63. }
  64. {
  65. // check if it has normals
  66. let hasNormals =
  67. pointAttributes.find(a => a.name === "NormalX") !== undefined &&
  68. pointAttributes.find(a => a.name === "NormalY") !== undefined &&
  69. pointAttributes.find(a => a.name === "NormalZ") !== undefined;
  70. if(hasNormals){
  71. let vector = {
  72. name: "NORMAL",
  73. attributes: ["NormalX", "NormalY", "NormalZ"],
  74. };
  75. attributes.addVector(vector);
  76. }
  77. }
  78. return attributes;
  79. }
  80. }
  81. function lasLazAttributes(fMno){
  82. const attributes = new PointAttributes();
  83. attributes.add(PointAttribute.POSITION_CARTESIAN);
  84. attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4));
  85. attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1));
  86. attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  87. attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1));
  88. attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  89. attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  90. attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1));
  91. //attributes.add(new PointAttribute("pointSourceID", PointAttributeTypes.DATA_TYPE_INT8, 4));
  92. return attributes;
  93. }
  94. export class POCLoader {
  95. static load(url, callback){
  96. try {
  97. let pco = new PointCloudOctreeGeometry();
  98. pco.url = url;
  99. let xhr = XHRFactory.createXMLHttpRequest();
  100. xhr.open('GET', url, true);
  101. xhr.onreadystatechange = function () {
  102. if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 0)) {
  103. let fMno = JSON.parse(xhr.responseText);
  104. let version = new Version(fMno.version);
  105. // assume octreeDir is absolute if it starts with http
  106. if (fMno.octreeDir.indexOf('http') === 0) {
  107. pco.octreeDir = fMno.octreeDir;
  108. } else {
  109. pco.octreeDir = url + '/../' + fMno.octreeDir;
  110. }
  111. pco.spacing = fMno.spacing;
  112. pco.hierarchyStepSize = fMno.hierarchyStepSize;
  113. pco.pointAttributes = fMno.pointAttributes;
  114. let min = new THREE.Vector3(fMno.boundingBox.lx, fMno.boundingBox.ly, fMno.boundingBox.lz);
  115. let max = new THREE.Vector3(fMno.boundingBox.ux, fMno.boundingBox.uy, fMno.boundingBox.uz);
  116. let boundingBox = new THREE.Box3(min, max);
  117. let tightBoundingBox = boundingBox.clone();
  118. if (fMno.tightBoundingBox) {
  119. tightBoundingBox.min.copy(new THREE.Vector3(fMno.tightBoundingBox.lx, fMno.tightBoundingBox.ly, fMno.tightBoundingBox.lz));
  120. tightBoundingBox.max.copy(new THREE.Vector3(fMno.tightBoundingBox.ux, fMno.tightBoundingBox.uy, fMno.tightBoundingBox.uz));
  121. }
  122. let offset = min.clone();
  123. boundingBox.min.sub(offset);
  124. boundingBox.max.sub(offset);
  125. tightBoundingBox.min.sub(offset);
  126. tightBoundingBox.max.sub(offset);
  127. pco.projection = fMno.projection;
  128. pco.boundingBox = boundingBox;
  129. pco.tightBoundingBox = tightBoundingBox;
  130. pco.boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
  131. pco.tightBoundingSphere = tightBoundingBox.getBoundingSphere(new THREE.Sphere());
  132. pco.offset = offset;
  133. if (fMno.pointAttributes === 'LAS') {
  134. pco.loader = new LasLazLoader(fMno.version, "las");
  135. pco.pointAttributes = lasLazAttributes(fMno);
  136. } else if (fMno.pointAttributes === 'LAZ') {
  137. pco.loader = new LasLazLoader(fMno.version, "laz");
  138. pco.pointAttributes = lasLazAttributes(fMno);
  139. } else {
  140. pco.loader = new BinaryLoader(fMno.version, boundingBox, fMno.scale);
  141. pco.pointAttributes = parseAttributes(fMno);
  142. }
  143. let nodes = {};
  144. { // load root
  145. let name = 'r';
  146. let root = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
  147. root.level = 0;
  148. root.hasChildren = true;
  149. root.spacing = pco.spacing;
  150. if (version.upTo('1.5')) {
  151. root.numPoints = fMno.hierarchy[0][1];
  152. } else {
  153. root.numPoints = 0;
  154. }
  155. pco.root = root;
  156. pco.root.load();
  157. nodes[name] = root;
  158. }
  159. // load remaining hierarchy
  160. if (version.upTo('1.4')) {
  161. for (let i = 1; i < fMno.hierarchy.length; i++) {
  162. let name = fMno.hierarchy[i][0];
  163. let numPoints = fMno.hierarchy[i][1];
  164. let index = parseInt(name.charAt(name.length - 1));
  165. let parentName = name.substring(0, name.length - 1);
  166. let parentNode = nodes[parentName];
  167. let level = name.length - 1;
  168. //let boundingBox = POCLoader.createChildAABB(parentNode.boundingBox, index);
  169. let boundingBox = Utils.createChildAABB(parentNode.boundingBox, index);
  170. let node = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
  171. node.level = level;
  172. node.numPoints = numPoints;
  173. node.spacing = pco.spacing / Math.pow(2, level);
  174. parentNode.addChild(node);
  175. nodes[name] = node;
  176. }
  177. }
  178. pco.nodes = nodes;
  179. callback(pco);
  180. }
  181. };
  182. xhr.send(null);
  183. } catch (e) {
  184. console.log("loading failed: '" + url + "'");
  185. console.log(e);
  186. callback();
  187. }
  188. }
  189. loadPointAttributes(mno){
  190. let fpa = mno.pointAttributes;
  191. let pa = new PointAttributes();
  192. for (let i = 0; i < fpa.length; i++) {
  193. let pointAttribute = PointAttribute[fpa[i]];
  194. pa.add(pointAttribute);
  195. }
  196. return pa;
  197. }
  198. createChildAABB(aabb, index){
  199. let min = aabb.min.clone();
  200. let max = aabb.max.clone();
  201. let size = new THREE.Vector3().subVectors(max, min);
  202. if ((index & 0b0001) > 0) {
  203. min.z += size.z / 2;
  204. } else {
  205. max.z -= size.z / 2;
  206. }
  207. if ((index & 0b0010) > 0) {
  208. min.y += size.y / 2;
  209. } else {
  210. max.y -= size.y / 2;
  211. }
  212. if ((index & 0b0100) > 0) {
  213. min.x += size.x / 2;
  214. } else {
  215. max.x -= size.x / 2;
  216. }
  217. return new THREE.Box3(min, max);
  218. }
  219. }