PointCloudEptGeometry.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import {PointCloudTreeNode} from "./PointCloudTree.js";
  2. import {PointAttributes, PointAttribute, PointAttributeTypes} from "./loader/PointAttributes.js";
  3. import * as THREE from "../libs/three.js/build/three.module.js";
  4. class U {
  5. static toVector3(v, offset) {
  6. return new THREE.Vector3().fromArray(v, offset || 0);
  7. }
  8. static toBox3(b) {
  9. return new THREE.Box3(U.toVector3(b), U.toVector3(b, 3));
  10. };
  11. static findDim(schema, name) {
  12. var dim = schema.find((dim) => dim.name == name);
  13. if (!dim) throw new Error('Failed to find ' + name + ' in schema');
  14. return dim;
  15. }
  16. static sphereFrom(b) {
  17. return b.getBoundingSphere(new THREE.Sphere());
  18. }
  19. };
  20. export class PointCloudEptGeometry {
  21. constructor(url, info) {
  22. let version = info.version;
  23. let schema = info.schema;
  24. let bounds = info.bounds;
  25. let boundsConforming = info.boundsConforming;
  26. let xyz = [
  27. U.findDim(schema, 'X'),
  28. U.findDim(schema, 'Y'),
  29. U.findDim(schema, 'Z')
  30. ];
  31. let scale = xyz.map((d) => d.scale || 1);
  32. let offset = xyz.map((d) => d.offset || 0);
  33. this.eptScale = U.toVector3(scale);
  34. this.eptOffset = U.toVector3(offset);
  35. this.url = url;
  36. this.info = info;
  37. this.type = 'ept';
  38. this.schema = schema;
  39. this.span = info.span || info.ticks;
  40. this.boundingBox = U.toBox3(bounds);
  41. this.tightBoundingBox = U.toBox3(boundsConforming);
  42. this.offset = U.toVector3([0, 0, 0]);
  43. this.boundingSphere = U.sphereFrom(this.boundingBox);
  44. this.tightBoundingSphere = U.sphereFrom(this.tightBoundingBox);
  45. this.version = new Potree.Version('1.7');
  46. this.projection = null;
  47. this.fallbackProjection = null;
  48. if (info.srs && info.srs.horizontal) {
  49. this.projection = info.srs.authority + ':' + info.srs.horizontal;
  50. }
  51. if (info.srs.wkt) {
  52. if (!this.projection) this.projection = info.srs.wkt;
  53. else this.fallbackProjection = info.srs.wkt;
  54. }
  55. {
  56. // TODO [mschuetz]: named projections that proj4 can't handle seem to cause problems.
  57. // remove them for now
  58. try{
  59. proj4(this.projection);
  60. }catch(e){
  61. this.projection = null;
  62. }
  63. }
  64. {
  65. const attributes = new PointAttributes();
  66. attributes.add(PointAttribute.POSITION_CARTESIAN);
  67. attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4));
  68. attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1));
  69. attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  70. attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1));
  71. attributes.add(new PointAttribute("returnNumber", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  72. attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  73. attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1));
  74. attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1));
  75. this.pointAttributes = attributes;
  76. }
  77. this.spacing =
  78. (this.boundingBox.max.x - this.boundingBox.min.x) / this.span;
  79. let hierarchyType = info.hierarchyType || 'json';
  80. const dataType = info.dataType;
  81. if (dataType == 'laszip') {
  82. this.loader = new Potree.EptLaszipLoader();
  83. }
  84. else if (dataType == 'binary') {
  85. this.loader = new Potree.EptBinaryLoader();
  86. }
  87. else if (dataType == 'zstandard') {
  88. this.loader = new Potree.EptZstandardLoader();
  89. }
  90. else {
  91. throw new Error('Could not read data type: ' + dataType);
  92. }
  93. }
  94. };
  95. export class EptKey {
  96. constructor(ept, b, d, x, y, z) {
  97. this.ept = ept;
  98. this.b = b;
  99. this.d = d;
  100. this.x = x || 0;
  101. this.y = y || 0;
  102. this.z = z || 0;
  103. }
  104. name() {
  105. return this.d + '-' + this.x + '-' + this.y + '-' + this.z;
  106. }
  107. step(a, b, c) {
  108. let min = this.b.min.clone();
  109. let max = this.b.max.clone();
  110. let dst = new THREE.Vector3().subVectors(max, min);
  111. if (a) min.x += dst.x / 2;
  112. else max.x -= dst.x / 2;
  113. if (b) min.y += dst.y / 2;
  114. else max.y -= dst.y / 2;
  115. if (c) min.z += dst.z / 2;
  116. else max.z -= dst.z / 2;
  117. return new Potree.EptKey(
  118. this.ept,
  119. new THREE.Box3(min, max),
  120. this.d + 1,
  121. this.x * 2 + a,
  122. this.y * 2 + b,
  123. this.z * 2 + c);
  124. }
  125. children() {
  126. var result = [];
  127. for (var a = 0; a < 2; ++a) {
  128. for (var b = 0; b < 2; ++b) {
  129. for (var c = 0; c < 2; ++c) {
  130. var add = this.step(a, b, c).name();
  131. if (!result.includes(add)) result = result.concat(add);
  132. }
  133. }
  134. }
  135. return result;
  136. }
  137. }
  138. export class PointCloudEptGeometryNode extends PointCloudTreeNode {
  139. constructor(ept, b, d, x, y, z) {
  140. super();
  141. this.ept = ept;
  142. this.key = new Potree.EptKey(
  143. this.ept,
  144. b || this.ept.boundingBox,
  145. d || 0,
  146. x,
  147. y,
  148. z);
  149. this.id = PointCloudEptGeometryNode.IDCount++;
  150. this.geometry = null;
  151. this.boundingBox = this.key.b;
  152. this.tightBoundingBox = this.boundingBox;
  153. this.spacing = this.ept.spacing / Math.pow(2, this.key.d);
  154. this.boundingSphere = U.sphereFrom(this.boundingBox);
  155. // These are set during hierarchy loading.
  156. this.hasChildren = false;
  157. this.children = { };
  158. this.numPoints = -1;
  159. this.level = this.key.d;
  160. this.loaded = false;
  161. this.loading = false;
  162. this.oneTimeDisposeHandlers = [];
  163. let k = this.key;
  164. this.name = this.toPotreeName(k.d, k.x, k.y, k.z);
  165. this.index = parseInt(this.name.charAt(this.name.length - 1));
  166. }
  167. isGeometryNode() { return true; }
  168. getLevel() { return this.level; }
  169. isTreeNode() { return false; }
  170. isLoaded() { return this.loaded; }
  171. getBoundingSphere() { return this.boundingSphere; }
  172. getBoundingBox() { return this.boundingBox; }
  173. url() { return this.ept.url + 'ept-data/' + this.filename(); }
  174. getNumPoints() { return this.numPoints; }
  175. filename() { return this.key.name(); }
  176. getChildren() {
  177. let children = [];
  178. for (let i = 0; i < 8; i++) {
  179. if (this.children[i]) {
  180. children.push(this.children[i]);
  181. }
  182. }
  183. return children;
  184. }
  185. addChild(child) {
  186. this.children[child.index] = child;
  187. child.parent = this;
  188. }
  189. load() {
  190. if (this.loaded || this.loading) return;
  191. if (Potree.numNodesLoading >= Potree.maxNodesLoading) return;
  192. this.loading = true;
  193. ++Potree.numNodesLoading;
  194. if (this.numPoints == -1) this.loadHierarchy();
  195. this.loadPoints();
  196. }
  197. loadPoints(){
  198. this.ept.loader.load(this);
  199. }
  200. async loadHierarchy() {
  201. let nodes = { };
  202. nodes[this.filename()] = this;
  203. this.hasChildren = false;
  204. let eptHierarchyFile =
  205. `${this.ept.url}ept-hierarchy/${this.filename()}.json`;
  206. let response = await fetch(eptHierarchyFile);
  207. let hier = await response.json();
  208. // Since we want to traverse top-down, and 10 comes
  209. // lexicographically before 9 (for example), do a deep sort.
  210. var keys = Object.keys(hier).sort((a, b) => {
  211. let [da, xa, ya, za] = a.split('-').map((n) => parseInt(n, 10));
  212. let [db, xb, yb, zb] = b.split('-').map((n) => parseInt(n, 10));
  213. if (da < db) return -1; if (da > db) return 1;
  214. if (xa < xb) return -1; if (xa > xb) return 1;
  215. if (ya < yb) return -1; if (ya > yb) return 1;
  216. if (za < zb) return -1; if (za > zb) return 1;
  217. return 0;
  218. });
  219. keys.forEach((v) => {
  220. let [d, x, y, z] = v.split('-').map((n) => parseInt(n, 10));
  221. let a = x & 1, b = y & 1, c = z & 1;
  222. let parentName =
  223. (d - 1) + '-' + (x >> 1) + '-' + (y >> 1) + '-' + (z >> 1);
  224. let parentNode = nodes[parentName];
  225. if (!parentNode) return;
  226. parentNode.hasChildren = true;
  227. let key = parentNode.key.step(a, b, c);
  228. let node = new Potree.PointCloudEptGeometryNode(
  229. this.ept,
  230. key.b,
  231. key.d,
  232. key.x,
  233. key.y,
  234. key.z);
  235. node.level = d;
  236. node.numPoints = hier[v];
  237. parentNode.addChild(node);
  238. nodes[key.name()] = node;
  239. });
  240. }
  241. doneLoading(bufferGeometry, tightBoundingBox, np, mean) {
  242. bufferGeometry.boundingBox = this.boundingBox;
  243. this.geometry = bufferGeometry;
  244. this.tightBoundingBox = tightBoundingBox;
  245. this.numPoints = np;
  246. this.mean = mean;
  247. this.loaded = true;
  248. this.loading = false;
  249. --Potree.numNodesLoading;
  250. }
  251. toPotreeName(d, x, y, z) {
  252. var name = 'r';
  253. for (var i = 0; i < d; ++i) {
  254. var shift = d - i - 1;
  255. var mask = 1 << shift;
  256. var step = 0;
  257. if (x & mask) step += 4;
  258. if (y & mask) step += 2;
  259. if (z & mask) step += 1;
  260. name += step;
  261. }
  262. return name;
  263. }
  264. dispose() {
  265. if (this.geometry && this.parent != null) {
  266. this.geometry.dispose();
  267. this.geometry = null;
  268. this.loaded = false;
  269. // this.dispatchEvent( { type: 'dispose' } );
  270. for (let i = 0; i < this.oneTimeDisposeHandlers.length; i++) {
  271. let handler = this.oneTimeDisposeHandlers[i];
  272. handler();
  273. }
  274. this.oneTimeDisposeHandlers = [];
  275. }
  276. }
  277. }
  278. PointCloudEptGeometryNode.IDCount = 0;