@loaders.gl/pointclouds 0.0.0-stage → 5.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/LAS-README.md +12 -0
  2. package/LICENSE +24 -0
  3. package/PCD-README.md +12 -0
  4. package/PLY-README.md +38 -0
  5. package/README.md +11 -2
  6. package/THIRD-PARTY-NOTICES.md +27 -0
  7. package/dist/dist.dev.js +31514 -0
  8. package/dist/dist.min.js +54 -0
  9. package/dist/index.cjs +55 -0
  10. package/dist/index.cjs.map +7 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +13 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/las/bundled.cjs +69 -0
  16. package/dist/las/bundled.cjs.map +7 -0
  17. package/dist/las/bundled.d.ts +11 -0
  18. package/dist/las/bundled.d.ts.map +1 -0
  19. package/dist/las/bundled.js +11 -0
  20. package/dist/las/bundled.js.map +1 -0
  21. package/dist/las/index.cjs +75 -0
  22. package/dist/las/index.cjs.map +7 -0
  23. package/dist/las/index.d.ts +15 -0
  24. package/dist/las/index.d.ts.map +1 -0
  25. package/dist/las/index.js +12 -0
  26. package/dist/las/index.js.map +1 -0
  27. package/dist/las/las-copc-loader-types.cjs +49 -0
  28. package/dist/las/las-copc-loader-types.cjs.map +7 -0
  29. package/dist/las/las-copc-loader-types.d.ts +67 -0
  30. package/dist/las/las-copc-loader-types.d.ts.map +1 -0
  31. package/dist/las/las-copc-loader-types.js +14 -0
  32. package/dist/las/las-copc-loader-types.js.map +1 -0
  33. package/dist/las/las-copc-loader.cjs +60 -0
  34. package/dist/las/las-copc-loader.cjs.map +7 -0
  35. package/dist/las/las-copc-loader.d.ts +39 -0
  36. package/dist/las/las-copc-loader.d.ts.map +1 -0
  37. package/dist/las/las-copc-loader.js +45 -0
  38. package/dist/las/las-copc-loader.js.map +1 -0
  39. package/dist/las/las-format.cjs +44 -0
  40. package/dist/las/las-format.cjs.map +7 -0
  41. package/dist/las/las-format.d.ts +16 -0
  42. package/dist/las/las-format.d.ts.map +1 -0
  43. package/dist/las/las-format.js +19 -0
  44. package/dist/las/las-format.js.map +1 -0
  45. package/dist/las/las-loader-shared.cjs +84 -0
  46. package/dist/las/las-loader-shared.cjs.map +7 -0
  47. package/dist/las/las-loader-shared.d.ts +68 -0
  48. package/dist/las/las-loader-shared.d.ts.map +1 -0
  49. package/dist/las/las-loader-shared.js +60 -0
  50. package/dist/las/las-loader-shared.js.map +1 -0
  51. package/dist/las/las-loader-types.cjs +51 -0
  52. package/dist/las/las-loader-types.cjs.map +7 -0
  53. package/dist/las/las-loader-types.d.ts +77 -0
  54. package/dist/las/las-loader-types.d.ts.map +1 -0
  55. package/dist/las/las-loader-types.js +17 -0
  56. package/dist/las/las-loader-types.js.map +1 -0
  57. package/dist/las/las-loader.cjs +128 -0
  58. package/dist/las/las-loader.cjs.map +7 -0
  59. package/dist/las/las-loader.d.ts +91 -0
  60. package/dist/las/las-loader.d.ts.map +1 -0
  61. package/dist/las/las-loader.js +108 -0
  62. package/dist/las/las-loader.js.map +1 -0
  63. package/dist/las/las-writer.cjs +1054 -0
  64. package/dist/las/las-writer.cjs.map +7 -0
  65. package/dist/las/las-writer.d.ts +70 -0
  66. package/dist/las/las-writer.d.ts.map +1 -0
  67. package/dist/las/las-writer.js +992 -0
  68. package/dist/las/las-writer.js.map +1 -0
  69. package/dist/las/laz-rs-loader-types.cjs +49 -0
  70. package/dist/las/laz-rs-loader-types.cjs.map +7 -0
  71. package/dist/las/laz-rs-loader-types.d.ts +69 -0
  72. package/dist/las/laz-rs-loader-types.d.ts.map +1 -0
  73. package/dist/las/laz-rs-loader-types.js +16 -0
  74. package/dist/las/laz-rs-loader-types.js.map +1 -0
  75. package/dist/las/laz-rs-loader.cjs +275 -0
  76. package/dist/las/laz-rs-loader.cjs.map +7 -0
  77. package/dist/las/laz-rs-loader.d.ts +39 -0
  78. package/dist/las/laz-rs-loader.d.ts.map +1 -0
  79. package/dist/las/laz-rs-loader.js +43 -0
  80. package/dist/las/laz-rs-loader.js.map +1 -0
  81. package/dist/las/lazperf-loader-types.cjs +49 -0
  82. package/dist/las/lazperf-loader-types.cjs.map +7 -0
  83. package/dist/las/lazperf-loader-types.d.ts +71 -0
  84. package/dist/las/lazperf-loader-types.d.ts.map +1 -0
  85. package/dist/las/lazperf-loader-types.js +17 -0
  86. package/dist/las/lazperf-loader-types.js.map +1 -0
  87. package/dist/las/lazperf-loader.cjs +63 -0
  88. package/dist/las/lazperf-loader.cjs.map +7 -0
  89. package/dist/las/lazperf-loader.d.ts +41 -0
  90. package/dist/las/lazperf-loader.d.ts.map +1 -0
  91. package/dist/las/lazperf-loader.js +49 -0
  92. package/dist/las/lazperf-loader.js.map +1 -0
  93. package/dist/las/lib/copc/parse-las.cjs +177 -0
  94. package/dist/las/lib/copc/parse-las.cjs.map +7 -0
  95. package/dist/las/lib/copc/parse-las.d.ts +17 -0
  96. package/dist/las/lib/copc/parse-las.d.ts.map +1 -0
  97. package/dist/las/lib/copc/parse-las.js +215 -0
  98. package/dist/las/lib/copc/parse-las.js.map +1 -0
  99. package/dist/las/lib/get-las-schema.cjs +67 -0
  100. package/dist/las/lib/get-las-schema.cjs.map +7 -0
  101. package/dist/las/lib/get-las-schema.d.ts +16 -0
  102. package/dist/las/lib/get-las-schema.d.ts.map +1 -0
  103. package/dist/las/lib/get-las-schema.js +52 -0
  104. package/dist/las/lib/get-las-schema.js.map +1 -0
  105. package/dist/las/lib/las-compatibility.cjs +110 -0
  106. package/dist/las/lib/las-compatibility.cjs.map +7 -0
  107. package/dist/las/lib/las-compatibility.d.ts +40 -0
  108. package/dist/las/lib/las-compatibility.d.ts.map +1 -0
  109. package/dist/las/lib/las-compatibility.js +115 -0
  110. package/dist/las/lib/las-compatibility.js.map +1 -0
  111. package/dist/las/lib/las-extra-bytes.cjs +228 -0
  112. package/dist/las/lib/las-extra-bytes.cjs.map +7 -0
  113. package/dist/las/lib/las-extra-bytes.d.ts +35 -0
  114. package/dist/las/lib/las-extra-bytes.d.ts.map +1 -0
  115. package/dist/las/lib/las-extra-bytes.js +197 -0
  116. package/dist/las/lib/las-extra-bytes.js.map +1 -0
  117. package/dist/las/lib/las-types.cjs +19 -0
  118. package/dist/las/lib/las-types.cjs.map +7 -0
  119. package/dist/las/lib/las-types.d.ts +194 -0
  120. package/dist/las/lib/las-types.d.ts.map +1 -0
  121. package/dist/las/lib/las-types.js +5 -0
  122. package/dist/las/lib/las-types.js.map +1 -0
  123. package/dist/las/lib/las-waveform.cjs +196 -0
  124. package/dist/las/lib/las-waveform.cjs.map +7 -0
  125. package/dist/las/lib/las-waveform.d.ts +89 -0
  126. package/dist/las/lib/las-waveform.d.ts.map +1 -0
  127. package/dist/las/lib/las-waveform.js +193 -0
  128. package/dist/las/lib/las-waveform.js.map +1 -0
  129. package/dist/las/lib/laz-perf/laslaz-decoder.cjs +382 -0
  130. package/dist/las/lib/laz-perf/laslaz-decoder.cjs.map +7 -0
  131. package/dist/las/lib/laz-perf/laslaz-decoder.d.ts +129 -0
  132. package/dist/las/lib/laz-perf/laslaz-decoder.d.ts.map +1 -0
  133. package/dist/las/lib/laz-perf/laslaz-decoder.js +377 -0
  134. package/dist/las/lib/laz-perf/laslaz-decoder.js.map +1 -0
  135. package/dist/las/lib/laz-perf/parse-las.cjs +271 -0
  136. package/dist/las/lib/laz-perf/parse-las.cjs.map +7 -0
  137. package/dist/las/lib/laz-perf/parse-las.d.ts +24 -0
  138. package/dist/las/lib/laz-perf/parse-las.d.ts.map +1 -0
  139. package/dist/las/lib/laz-perf/parse-las.js +308 -0
  140. package/dist/las/lib/laz-perf/parse-las.js.map +1 -0
  141. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.cjs +631 -0
  142. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.cjs.map +7 -0
  143. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.d.ts +129 -0
  144. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.d.ts.map +1 -0
  145. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.js +414 -0
  146. package/dist/las/lib/laz-rs-wasm/laslaz-decoder.js.map +1 -0
  147. package/dist/las/lib/laz-rs-wasm/parse-las.cjs +271 -0
  148. package/dist/las/lib/laz-rs-wasm/parse-las.cjs.map +7 -0
  149. package/dist/las/lib/laz-rs-wasm/parse-las.d.ts +24 -0
  150. package/dist/las/lib/laz-rs-wasm/parse-las.d.ts.map +1 -0
  151. package/dist/las/lib/laz-rs-wasm/parse-las.js +308 -0
  152. package/dist/las/lib/laz-rs-wasm/parse-las.js.map +1 -0
  153. package/dist/las/lib/typescript/parse-las.cjs +2889 -0
  154. package/dist/las/lib/typescript/parse-las.cjs.map +7 -0
  155. package/dist/las/lib/typescript/parse-las.d.ts +39 -0
  156. package/dist/las/lib/typescript/parse-las.d.ts.map +1 -0
  157. package/dist/las/lib/typescript/parse-las.js +2703 -0
  158. package/dist/las/lib/typescript/parse-las.js.map +1 -0
  159. package/dist/las/libs/laz-perf/laz-perf.cjs +18093 -0
  160. package/dist/las/libs/laz-perf/laz-perf.cjs.map +7 -0
  161. package/dist/las/libs/laz-perf/laz-perf.d.ts +2 -0
  162. package/dist/las/libs/laz-perf/laz-perf.d.ts.map +1 -0
  163. package/dist/las/libs/laz-perf/laz-perf.js +19003 -0
  164. package/dist/las/libs/laz-perf/laz-perf.js.map +1 -0
  165. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm.d.ts +95 -0
  166. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm.d.ts.map +1 -0
  167. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm.js +442 -0
  168. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm.js.map +1 -0
  169. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm_bg.wasm +0 -0
  170. package/dist/las/libs/laz-rs-wasm/laz_rs_wasm_bg.wasm.d.ts +38 -0
  171. package/dist/las/unbundled.cjs +69 -0
  172. package/dist/las/unbundled.cjs.map +7 -0
  173. package/dist/las/unbundled.d.ts +11 -0
  174. package/dist/las/unbundled.d.ts.map +1 -0
  175. package/dist/las/unbundled.js +11 -0
  176. package/dist/las/unbundled.js.map +1 -0
  177. package/dist/las/workers/las-worker.cjs +23 -0
  178. package/dist/las/workers/las-worker.cjs.map +7 -0
  179. package/dist/las/workers/las-worker.d.ts +2 -0
  180. package/dist/las/workers/las-worker.d.ts.map +1 -0
  181. package/dist/las/workers/las-worker.js +23 -0
  182. package/dist/las/workers/las-worker.js.map +1 -0
  183. package/dist/las-copc-loader-types.cjs +51 -0
  184. package/dist/las-copc-loader-types.cjs.map +7 -0
  185. package/dist/las-copc-loader-types.d.ts +2 -0
  186. package/dist/las-copc-loader-types.d.ts.map +1 -0
  187. package/dist/las-copc-loader-types.js +2 -0
  188. package/dist/las-copc-loader-types.js.map +1 -0
  189. package/dist/las-copc-loader.cjs +62 -0
  190. package/dist/las-copc-loader.cjs.map +7 -0
  191. package/dist/las-copc-loader.d.ts +2 -0
  192. package/dist/las-copc-loader.d.ts.map +1 -0
  193. package/dist/las-copc-loader.js +2 -0
  194. package/dist/las-copc-loader.js.map +1 -0
  195. package/dist/las-loader-types.cjs +53 -0
  196. package/dist/las-loader-types.cjs.map +7 -0
  197. package/dist/las-loader-types.d.ts +2 -0
  198. package/dist/las-loader-types.d.ts.map +1 -0
  199. package/dist/las-loader-types.js +2 -0
  200. package/dist/las-loader-types.js.map +1 -0
  201. package/dist/las-loader.cjs +130 -0
  202. package/dist/las-loader.cjs.map +7 -0
  203. package/dist/las-loader.d.ts +2 -0
  204. package/dist/las-loader.d.ts.map +1 -0
  205. package/dist/las-loader.js +2 -0
  206. package/dist/las-loader.js.map +1 -0
  207. package/dist/las-worker.js +24024 -0
  208. package/dist/las-writer.cjs +1056 -0
  209. package/dist/las-writer.cjs.map +7 -0
  210. package/dist/las-writer.d.ts +2 -0
  211. package/dist/las-writer.d.ts.map +1 -0
  212. package/dist/las-writer.js +2 -0
  213. package/dist/las-writer.js.map +1 -0
  214. package/dist/laz-rs-loader-types.cjs +51 -0
  215. package/dist/laz-rs-loader-types.cjs.map +7 -0
  216. package/dist/laz-rs-loader-types.d.ts +2 -0
  217. package/dist/laz-rs-loader-types.d.ts.map +1 -0
  218. package/dist/laz-rs-loader-types.js +2 -0
  219. package/dist/laz-rs-loader-types.js.map +1 -0
  220. package/dist/laz-rs-loader.cjs +277 -0
  221. package/dist/laz-rs-loader.cjs.map +7 -0
  222. package/dist/laz-rs-loader.d.ts +2 -0
  223. package/dist/laz-rs-loader.d.ts.map +1 -0
  224. package/dist/laz-rs-loader.js +2 -0
  225. package/dist/laz-rs-loader.js.map +1 -0
  226. package/dist/lazperf-loader-types.cjs +51 -0
  227. package/dist/lazperf-loader-types.cjs.map +7 -0
  228. package/dist/lazperf-loader-types.d.ts +2 -0
  229. package/dist/lazperf-loader-types.d.ts.map +1 -0
  230. package/dist/lazperf-loader-types.js +2 -0
  231. package/dist/lazperf-loader-types.js.map +1 -0
  232. package/dist/lazperf-loader.cjs +65 -0
  233. package/dist/lazperf-loader.cjs.map +7 -0
  234. package/dist/lazperf-loader.d.ts +2 -0
  235. package/dist/lazperf-loader.d.ts.map +1 -0
  236. package/dist/lazperf-loader.js +2 -0
  237. package/dist/lazperf-loader.js.map +1 -0
  238. package/dist/pcd/bundled.cjs +31 -0
  239. package/dist/pcd/bundled.cjs.map +7 -0
  240. package/dist/pcd/bundled.d.ts +3 -0
  241. package/dist/pcd/bundled.d.ts.map +1 -0
  242. package/dist/pcd/bundled.js +5 -0
  243. package/dist/pcd/bundled.js.map +1 -0
  244. package/dist/pcd/index.cjs +37 -0
  245. package/dist/pcd/index.cjs.map +7 -0
  246. package/dist/pcd/index.d.ts +6 -0
  247. package/dist/pcd/index.d.ts.map +1 -0
  248. package/dist/pcd/index.js +7 -0
  249. package/dist/pcd/index.js.map +1 -0
  250. package/dist/pcd/lib/decompress-lzf.cjs +80 -0
  251. package/dist/pcd/lib/decompress-lzf.cjs.map +7 -0
  252. package/dist/pcd/lib/decompress-lzf.d.ts +7 -0
  253. package/dist/pcd/lib/decompress-lzf.d.ts.map +1 -0
  254. package/dist/pcd/lib/decompress-lzf.js +62 -0
  255. package/dist/pcd/lib/decompress-lzf.js.map +1 -0
  256. package/dist/pcd/lib/get-pcd-schema.cjs +70 -0
  257. package/dist/pcd/lib/get-pcd-schema.cjs.map +7 -0
  258. package/dist/pcd/lib/get-pcd-schema.d.ts +10 -0
  259. package/dist/pcd/lib/get-pcd-schema.d.ts.map +1 -0
  260. package/dist/pcd/lib/get-pcd-schema.js +53 -0
  261. package/dist/pcd/lib/get-pcd-schema.js.map +1 -0
  262. package/dist/pcd/lib/parse-pcd.cjs +354 -0
  263. package/dist/pcd/lib/parse-pcd.cjs.map +7 -0
  264. package/dist/pcd/lib/parse-pcd.d.ts +20 -0
  265. package/dist/pcd/lib/parse-pcd.d.ts.map +1 -0
  266. package/dist/pcd/lib/parse-pcd.js +341 -0
  267. package/dist/pcd/lib/parse-pcd.js.map +1 -0
  268. package/dist/pcd/lib/pcd-types.cjs +19 -0
  269. package/dist/pcd/lib/pcd-types.cjs.map +7 -0
  270. package/dist/pcd/lib/pcd-types.d.ts +31 -0
  271. package/dist/pcd/lib/pcd-types.d.ts.map +1 -0
  272. package/dist/pcd/lib/pcd-types.js +5 -0
  273. package/dist/pcd/lib/pcd-types.js.map +1 -0
  274. package/dist/pcd/pcd-format.cjs +42 -0
  275. package/dist/pcd/pcd-format.cjs.map +7 -0
  276. package/dist/pcd/pcd-format.d.ts +16 -0
  277. package/dist/pcd/pcd-format.d.ts.map +1 -0
  278. package/dist/pcd/pcd-format.js +19 -0
  279. package/dist/pcd/pcd-format.js.map +1 -0
  280. package/dist/pcd/pcd-loader-types.cjs +63 -0
  281. package/dist/pcd/pcd-loader-types.cjs.map +7 -0
  282. package/dist/pcd/pcd-loader-types.d.ts +82 -0
  283. package/dist/pcd/pcd-loader-types.d.ts.map +1 -0
  284. package/dist/pcd/pcd-loader-types.js +35 -0
  285. package/dist/pcd/pcd-loader-types.js.map +1 -0
  286. package/dist/pcd/pcd-loader.cjs +201 -0
  287. package/dist/pcd/pcd-loader.cjs.map +7 -0
  288. package/dist/pcd/pcd-loader.d.ts +80 -0
  289. package/dist/pcd/pcd-loader.d.ts.map +1 -0
  290. package/dist/pcd/pcd-loader.js +179 -0
  291. package/dist/pcd/pcd-loader.js.map +1 -0
  292. package/dist/pcd/pcd-writer.cjs +134 -0
  293. package/dist/pcd/pcd-writer.cjs.map +7 -0
  294. package/dist/pcd/pcd-writer.d.ts +38 -0
  295. package/dist/pcd/pcd-writer.d.ts.map +1 -0
  296. package/dist/pcd/pcd-writer.js +115 -0
  297. package/dist/pcd/pcd-writer.js.map +1 -0
  298. package/dist/pcd/unbundled.cjs +31 -0
  299. package/dist/pcd/unbundled.cjs.map +7 -0
  300. package/dist/pcd/unbundled.d.ts +3 -0
  301. package/dist/pcd/unbundled.d.ts.map +1 -0
  302. package/dist/pcd/unbundled.js +5 -0
  303. package/dist/pcd/unbundled.js.map +1 -0
  304. package/dist/pcd/workers/pcd-worker.cjs +23 -0
  305. package/dist/pcd/workers/pcd-worker.cjs.map +7 -0
  306. package/dist/pcd/workers/pcd-worker.d.ts +2 -0
  307. package/dist/pcd/workers/pcd-worker.d.ts.map +1 -0
  308. package/dist/pcd/workers/pcd-worker.js +23 -0
  309. package/dist/pcd/workers/pcd-worker.js.map +1 -0
  310. package/dist/pcd-loader-types.cjs +65 -0
  311. package/dist/pcd-loader-types.cjs.map +7 -0
  312. package/dist/pcd-loader-types.d.ts +2 -0
  313. package/dist/pcd-loader-types.d.ts.map +1 -0
  314. package/dist/pcd-loader-types.js +2 -0
  315. package/dist/pcd-loader-types.js.map +1 -0
  316. package/dist/pcd-loader.cjs +203 -0
  317. package/dist/pcd-loader.cjs.map +7 -0
  318. package/dist/pcd-loader.d.ts +2 -0
  319. package/dist/pcd-loader.d.ts.map +1 -0
  320. package/dist/pcd-loader.js +2 -0
  321. package/dist/pcd-loader.js.map +1 -0
  322. package/dist/pcd-worker.js +18561 -0
  323. package/dist/pcd-writer.cjs +136 -0
  324. package/dist/pcd-writer.cjs.map +7 -0
  325. package/dist/pcd-writer.d.ts +2 -0
  326. package/dist/pcd-writer.d.ts.map +1 -0
  327. package/dist/pcd-writer.js +2 -0
  328. package/dist/pcd-writer.js.map +1 -0
  329. package/dist/ply/bundled.cjs +31 -0
  330. package/dist/ply/bundled.cjs.map +7 -0
  331. package/dist/ply/bundled.d.ts +3 -0
  332. package/dist/ply/bundled.d.ts.map +1 -0
  333. package/dist/ply/bundled.js +5 -0
  334. package/dist/ply/bundled.js.map +1 -0
  335. package/dist/ply/index.cjs +37 -0
  336. package/dist/ply/index.cjs.map +7 -0
  337. package/dist/ply/index.d.ts +6 -0
  338. package/dist/ply/index.d.ts.map +1 -0
  339. package/dist/ply/index.js +8 -0
  340. package/dist/ply/index.js.map +1 -0
  341. package/dist/ply/lib/get-ply-schema.cjs +128 -0
  342. package/dist/ply/lib/get-ply-schema.cjs.map +7 -0
  343. package/dist/ply/lib/get-ply-schema.d.ts +16 -0
  344. package/dist/ply/lib/get-ply-schema.d.ts.map +1 -0
  345. package/dist/ply/lib/get-ply-schema.js +117 -0
  346. package/dist/ply/lib/get-ply-schema.js.map +1 -0
  347. package/dist/ply/lib/normalize-ply.cjs +96 -0
  348. package/dist/ply/lib/normalize-ply.cjs.map +7 -0
  349. package/dist/ply/lib/normalize-ply.d.ts +8 -0
  350. package/dist/ply/lib/normalize-ply.d.ts.map +1 -0
  351. package/dist/ply/lib/normalize-ply.js +89 -0
  352. package/dist/ply/lib/normalize-ply.js.map +1 -0
  353. package/dist/ply/lib/parse-ply-arrow.cjs +552 -0
  354. package/dist/ply/lib/parse-ply-arrow.cjs.map +7 -0
  355. package/dist/ply/lib/parse-ply-arrow.d.ts +25 -0
  356. package/dist/ply/lib/parse-ply-arrow.d.ts.map +1 -0
  357. package/dist/ply/lib/parse-ply-arrow.js +493 -0
  358. package/dist/ply/lib/parse-ply-arrow.js.map +1 -0
  359. package/dist/ply/lib/parse-ply-in-batches.cjs +613 -0
  360. package/dist/ply/lib/parse-ply-in-batches.cjs.map +7 -0
  361. package/dist/ply/lib/parse-ply-in-batches.d.ts +9 -0
  362. package/dist/ply/lib/parse-ply-in-batches.d.ts.map +1 -0
  363. package/dist/ply/lib/parse-ply-in-batches.js +630 -0
  364. package/dist/ply/lib/parse-ply-in-batches.js.map +1 -0
  365. package/dist/ply/lib/parse-ply.cjs +758 -0
  366. package/dist/ply/lib/parse-ply.cjs.map +7 -0
  367. package/dist/ply/lib/parse-ply.d.ts +69 -0
  368. package/dist/ply/lib/parse-ply.d.ts.map +1 -0
  369. package/dist/ply/lib/parse-ply.js +855 -0
  370. package/dist/ply/lib/parse-ply.js.map +1 -0
  371. package/dist/ply/lib/ply-types.cjs +19 -0
  372. package/dist/ply/lib/ply-types.cjs.map +7 -0
  373. package/dist/ply/lib/ply-types.d.ts +37 -0
  374. package/dist/ply/lib/ply-types.d.ts.map +1 -0
  375. package/dist/ply/lib/ply-types.js +2 -0
  376. package/dist/ply/lib/ply-types.js.map +1 -0
  377. package/dist/ply/ply-format.cjs +43 -0
  378. package/dist/ply/ply-format.cjs.map +7 -0
  379. package/dist/ply/ply-format.d.ts +18 -0
  380. package/dist/ply/ply-format.d.ts.map +1 -0
  381. package/dist/ply/ply-format.js +22 -0
  382. package/dist/ply/ply-format.js.map +1 -0
  383. package/dist/ply/ply-loader-types.cjs +64 -0
  384. package/dist/ply/ply-loader-types.cjs.map +7 -0
  385. package/dist/ply/ply-loader-types.d.ts +88 -0
  386. package/dist/ply/ply-loader-types.d.ts.map +1 -0
  387. package/dist/ply/ply-loader-types.js +38 -0
  388. package/dist/ply/ply-loader-types.js.map +1 -0
  389. package/dist/ply/ply-loader.cjs +192 -0
  390. package/dist/ply/ply-loader.cjs.map +7 -0
  391. package/dist/ply/ply-loader.d.ts +81 -0
  392. package/dist/ply/ply-loader.d.ts.map +1 -0
  393. package/dist/ply/ply-loader.js +173 -0
  394. package/dist/ply/ply-loader.js.map +1 -0
  395. package/dist/ply/ply-writer.cjs +175 -0
  396. package/dist/ply/ply-writer.cjs.map +7 -0
  397. package/dist/ply/ply-writer.d.ts +38 -0
  398. package/dist/ply/ply-writer.d.ts.map +1 -0
  399. package/dist/ply/ply-writer.js +140 -0
  400. package/dist/ply/ply-writer.js.map +1 -0
  401. package/dist/ply/unbundled.cjs +31 -0
  402. package/dist/ply/unbundled.cjs.map +7 -0
  403. package/dist/ply/unbundled.d.ts +3 -0
  404. package/dist/ply/unbundled.d.ts.map +1 -0
  405. package/dist/ply/unbundled.js +5 -0
  406. package/dist/ply/unbundled.js.map +1 -0
  407. package/dist/ply/workers/ply-worker.cjs +34 -0
  408. package/dist/ply/workers/ply-worker.cjs.map +7 -0
  409. package/dist/ply/workers/ply-worker.d.ts +2 -0
  410. package/dist/ply/workers/ply-worker.d.ts.map +1 -0
  411. package/dist/ply/workers/ply-worker.js +37 -0
  412. package/dist/ply/workers/ply-worker.js.map +1 -0
  413. package/dist/ply-loader-types.cjs +66 -0
  414. package/dist/ply-loader-types.cjs.map +7 -0
  415. package/dist/ply-loader-types.d.ts +2 -0
  416. package/dist/ply-loader-types.d.ts.map +1 -0
  417. package/dist/ply-loader-types.js +2 -0
  418. package/dist/ply-loader-types.js.map +1 -0
  419. package/dist/ply-loader.cjs +194 -0
  420. package/dist/ply-loader.cjs.map +7 -0
  421. package/dist/ply-loader.d.ts +2 -0
  422. package/dist/ply-loader.d.ts.map +1 -0
  423. package/dist/ply-loader.js +2 -0
  424. package/dist/ply-loader.js.map +1 -0
  425. package/dist/ply-worker.js +20962 -0
  426. package/dist/ply-writer.cjs +177 -0
  427. package/dist/ply-writer.cjs.map +7 -0
  428. package/dist/ply-writer.d.ts +2 -0
  429. package/dist/ply-writer.d.ts.map +1 -0
  430. package/dist/ply-writer.js +2 -0
  431. package/dist/ply-writer.js.map +1 -0
  432. package/package.json +238 -4
  433. package/src/index.ts +19 -0
  434. package/src/las/bundled.ts +38 -0
  435. package/src/las/index.ts +66 -0
  436. package/src/las/las-copc-loader-types.ts +18 -0
  437. package/src/las/las-copc-loader.ts +61 -0
  438. package/src/las/las-format.ts +21 -0
  439. package/src/las/las-loader-shared.ts +126 -0
  440. package/src/las/las-loader-types.ts +24 -0
  441. package/src/las/las-loader.ts +163 -0
  442. package/src/las/las-writer.ts +1477 -0
  443. package/src/las/laz-rs-loader-types.ts +21 -0
  444. package/src/las/laz-rs-loader.ts +53 -0
  445. package/src/las/lazperf-loader-types.ts +22 -0
  446. package/src/las/lazperf-loader.ts +65 -0
  447. package/src/las/lib/LASZIP-LICENSE +202 -0
  448. package/src/las/lib/LASZIP-NOTICE +4 -0
  449. package/src/las/lib/copc/parse-las.ts +265 -0
  450. package/src/las/lib/get-las-schema.ts +55 -0
  451. package/src/las/lib/las-compatibility.ts +178 -0
  452. package/src/las/lib/las-extra-bytes.ts +276 -0
  453. package/src/las/lib/las-types.ts +206 -0
  454. package/src/las/lib/las-waveform.ts +292 -0
  455. package/src/las/lib/laz-perf/laslaz-decoder.ts +443 -0
  456. package/src/las/lib/laz-perf/parse-las.ts +411 -0
  457. package/src/las/lib/laz-rs-wasm/laslaz-decoder.ts +488 -0
  458. package/src/las/lib/laz-rs-wasm/parse-las.ts +411 -0
  459. package/src/las/lib/typescript/parse-las.ts +3862 -0
  460. package/src/las/libs/laz-perf/laz-perf.ts +20615 -0
  461. package/src/las/libs/laz-rs-wasm/README.md +5 -0
  462. package/src/las/libs/laz-rs-wasm/laz_rs_wasm.d.ts +89 -0
  463. package/src/las/libs/laz-rs-wasm/laz_rs_wasm.js +497 -0
  464. package/src/las/libs/laz-rs-wasm/laz_rs_wasm_bg.wasm +0 -0
  465. package/src/las/libs/laz-rs-wasm/laz_rs_wasm_bg.wasm.d.ts +38 -0
  466. package/src/las/libs/laz-rs-wasm/package.json +25 -0
  467. package/src/las/unbundled.ts +38 -0
  468. package/src/las/workers/las-worker.ts +24 -0
  469. package/src/las-copc-loader-types.ts +1 -0
  470. package/src/las-copc-loader.ts +1 -0
  471. package/src/las-loader-types.ts +1 -0
  472. package/src/las-loader.ts +1 -0
  473. package/src/las-writer.ts +1 -0
  474. package/src/laz-rs-loader-types.ts +1 -0
  475. package/src/laz-rs-loader.ts +1 -0
  476. package/src/lazperf-loader-types.ts +1 -0
  477. package/src/lazperf-loader.ts +1 -0
  478. package/src/pcd/bundled.ts +6 -0
  479. package/src/pcd/index.ts +9 -0
  480. package/src/pcd/lib/decompress-lzf.ts +68 -0
  481. package/src/pcd/lib/get-pcd-schema.ts +66 -0
  482. package/src/pcd/lib/parse-pcd.ts +474 -0
  483. package/src/pcd/lib/pcd-types.ts +34 -0
  484. package/src/pcd/pcd-format.ts +21 -0
  485. package/src/pcd/pcd-loader-types.ts +52 -0
  486. package/src/pcd/pcd-loader.ts +247 -0
  487. package/src/pcd/pcd-writer.ts +156 -0
  488. package/src/pcd/unbundled.ts +6 -0
  489. package/src/pcd/workers/pcd-worker.ts +24 -0
  490. package/src/pcd-loader-types.ts +1 -0
  491. package/src/pcd-loader.ts +1 -0
  492. package/src/pcd-writer.ts +1 -0
  493. package/src/ply/bundled.ts +6 -0
  494. package/src/ply/index.ts +11 -0
  495. package/src/ply/lib/get-ply-schema.ts +133 -0
  496. package/src/ply/lib/normalize-ply.ts +110 -0
  497. package/src/ply/lib/parse-ply-arrow.ts +676 -0
  498. package/src/ply/lib/parse-ply-in-batches.ts +832 -0
  499. package/src/ply/lib/parse-ply.ts +1084 -0
  500. package/src/ply/lib/ply-types.ts +47 -0
  501. package/src/ply/ply-format.ts +24 -0
  502. package/src/ply/ply-loader-types.ts +60 -0
  503. package/src/ply/ply-loader.ts +255 -0
  504. package/src/ply/ply-writer.ts +219 -0
  505. package/src/ply/unbundled.ts +6 -0
  506. package/src/ply/workers/ply-worker.ts +40 -0
  507. package/src/ply-loader-types.ts +1 -0
  508. package/src/ply-loader.ts +1 -0
  509. package/src/ply-writer.ts +1 -0
  510. package/third-party-licenses/plasio-LICENSE +21 -0
  511. package/third-party-licenses/three-LICENSE +21 -0
@@ -0,0 +1,2703 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { makeArrowMetadataBatch, updateArrowTableMetadata } from '@loaders.gl/core/schema-utils';
5
+ import * as arrow from 'apache-arrow';
6
+ import { makeMetadataFromLasHeader } from "../get-las-schema.js";
7
+ import { makeMeshArrowTable } from '@loaders.gl/core/schema-utils';
8
+ import { BinaryChunkReader, createLAZChunkDecoder, createLAZChunkDecoderCursor, decodeLAZChunk, decodeLAZChunkTable, getLAZChunkDeclaredByteLength, getLAZChunkHeaderByteLength, NeedsMoreData } from '@loaders.gl/core/loader-utils';
9
+ import { getLASSchema } from "../get-las-schema.js";
10
+ import { getLASCompatibilityLayout, populateLASCompatibilityPoint } from "../las-compatibility.js";
11
+ import { createLASTypedExtraBytesAttributes, createLASTypedExtraBytesValue, parseLASExtraBytes, populateLASTypedExtraBytesComponent } from "../las-extra-bytes.js";
12
+ const DEFAULT_BATCH_SIZE = 1000 * 100;
13
+ const LASF_SIGNATURE = 0x4653414c;
14
+ const LAS_14_HEADER_LENGTH = 375;
15
+ const COMPRESSED_POINT_FORMAT_MASK = 0x80;
16
+ const POINT_FORMAT_MASK = 0x3f;
17
+ const LASZIP_USER_ID = 'laszip encoded';
18
+ const LASZIP_RECORD_ID = 22204;
19
+ const VARIABLE_CHUNK_SIZE = 0xffffffff;
20
+ const LAZ_CHUNK_TABLE_POINTER_LENGTH = 8;
21
+ /** Read-ahead block that amortizes legacy cursor feeds while keeping retained input bounded. */
22
+ const LEGACY_LAZ_FEED_BLOCK_SIZE = 64 * 1024;
23
+ /** Build chunk metadata with the item versions declared by the LASzip VLR. */
24
+ function createLAZChunkMetadata(header, laszip, pointCount) {
25
+ return {
26
+ pointDataRecordFormat: header.pointsFormatId,
27
+ pointDataRecordLength: header.pointsStructSize,
28
+ pointCount,
29
+ point10ItemVersion: laszip.items.find(item => item.type === 6)?.version,
30
+ gpsTime11ItemVersion: laszip.items.find(item => item.type === 7)?.version,
31
+ rgb12ItemVersion: laszip.items.find(item => item.type === 8)?.version,
32
+ byteItemVersion: laszip.items.find(item => item.type === 0)?.version,
33
+ point14ItemVersion: laszip.point14ItemVersion ?? undefined,
34
+ rgb14ItemVersion: laszip.rgb14ItemVersion ?? undefined,
35
+ wavePacketItemVersion: laszip.wavePacketItemVersion ?? undefined,
36
+ byte14ItemVersion: laszip.byte14ItemVersion ?? undefined
37
+ };
38
+ }
39
+ /** Parse LAS data with the TypeScript loader variant. */
40
+ export function parseLAS(arrayBuffer, options = {}) {
41
+ const header = parseLASHeader(arrayBuffer);
42
+ if (header.isCompressed) {
43
+ const bytes = new Uint8Array(arrayBuffer);
44
+ const laszip = parseLASZipVLR(bytes, header);
45
+ validateTypeScriptLAZSupport(header, laszip);
46
+ if (header.pointsFormatId <= 5 || (header.pointsFormatId >= 6 && header.pointsFormatId <= 10)) {
47
+ return parseCompleteLAZFileToArrowTable(bytes, header, laszip, options);
48
+ }
49
+ const rawPointData = decodeLAZFileToRawPointData(arrayBuffer, header, laszip);
50
+ const totalPointCount = header.pointsCount;
51
+ return parseLASArrowTableBatch(rawPointData, {
52
+ ...header,
53
+ pointsOffset: 0,
54
+ pointsCount: totalPointCount,
55
+ totalToRead: totalPointCount,
56
+ totalRead: totalPointCount
57
+ }, options);
58
+ }
59
+ const totalPointCount = header.pointsCount;
60
+ return parseLASArrowTableBatch(arrayBuffer, { ...header, totalToRead: totalPointCount, totalRead: totalPointCount }, options);
61
+ }
62
+ /** Decode one complete modern LAZ chunk directly into selected Arrow columns. */
63
+ export function decodeLAZChunkToArrowTable(compressed, metadata, options) {
64
+ if (metadata.pointDataRecordFormat < 6 || metadata.pointDataRecordFormat > 8) {
65
+ throw new Error(`LASLoader: standalone Arrow chunk decode supports PDRF 6-8; received ${metadata.pointDataRecordFormat}`);
66
+ }
67
+ const pointCount = metadata.pointCount;
68
+ const header = {
69
+ pointsOffset: 0,
70
+ pointsFormatId: metadata.pointDataRecordFormat,
71
+ pointsStructSize: metadata.pointDataRecordLength,
72
+ pointsCount: pointCount,
73
+ scale: metadata.scale,
74
+ offset: metadata.offset,
75
+ maxs: [0, 0, 0],
76
+ mins: [0, 0, 0],
77
+ totalToRead: pointCount,
78
+ totalRead: pointCount,
79
+ hasColor: hasPointColor(metadata.pointDataRecordFormat),
80
+ versionAsString: '1.4',
81
+ isCompressed: true,
82
+ headerSize: LAS_14_HEADER_LENGTH,
83
+ vlrCount: 0
84
+ };
85
+ const state = createPointDataBatchState(pointCount, header, options);
86
+ if (state.waveforms || state.extraBytes || state.typedExtraBytes) {
87
+ throw new Error('LASLoader: standalone Arrow chunk decode only supports direct LAZ columns');
88
+ }
89
+ const cursor = createLAZChunkDecoderCursor(compressed, metadata);
90
+ const decodedPointCount = cursor.decodeIntoPointData(state.target, pointCount);
91
+ if (decodedPointCount !== pointCount) {
92
+ throw new Error(`LASLoader: standalone LAZ chunk produced ${decodedPointCount} points; expected ${pointCount}`);
93
+ }
94
+ return makePointDataStateArrowTable(header, state, pointCount, options, true);
95
+ }
96
+ /** Decode one complete supported LAZ file directly into its represented Arrow columns. */
97
+ function parseCompleteLAZFileToArrowTable(bytes, header, laszip, options) {
98
+ const pointCount = header.pointsCount;
99
+ const state = createPointDataBatchState(pointCount, header, options);
100
+ const chunkTable = readLAZFileChunks(bytes, header, laszip, options);
101
+ let decodedPointCount = 0;
102
+ let byteOffset = header.pointsOffset + (laszip.compressor === 1 ? 0 : LAZ_CHUNK_TABLE_POINTER_LENGTH);
103
+ for (const chunk of chunkTable) {
104
+ decodeCompleteLAZChunkToPointData(bytes, byteOffset, chunk.byteLength, chunk.pointCount, header, laszip, state, decodedPointCount);
105
+ byteOffset += chunk.byteLength;
106
+ decodedPointCount += chunk.pointCount;
107
+ }
108
+ if (decodedPointCount !== pointCount) {
109
+ throw new Error(`LASLoader: decoded ${decodedPointCount} LAZ points; expected ${pointCount}`);
110
+ }
111
+ return makePointDataStateArrowTable(header, state, pointCount, options);
112
+ }
113
+ /** Convert one fully populated point-data state into its Arrow table. */
114
+ function makePointDataStateArrowTable(header, state, pointCount, options, preserveRawColors = false) {
115
+ const colors = preserveRawColors && state.rawColors
116
+ ? state.rawColors
117
+ : state.colors
118
+ ? state.colors
119
+ : state.rawColors
120
+ ? convertRawColorsToUint8(state.rawColors, pointCount, options)
121
+ : null;
122
+ return makeLASArrowTableFromAttributes({ ...header, pointsOffset: 0, pointsCount: pointCount, totalRead: pointCount }, state.positions, colors, state.intensities, state.classifications, state.syntheticFlags, state.keyPointFlags, state.withheldFlags, state.overlapFlags, state.gpsTimes, state.nir, state.scanAngles, state.userData, state.pointSourceIds, state.returnNumbers, state.numberOfReturns, state.scannerChannels, state.scanDirectionFlags, state.edgeOfFlightLines, state.waveforms, state.extraBytes, state.typedExtraBytes);
123
+ }
124
+ /** Decode one complete supported LAZ chunk into preallocated Arrow column buffers. */
125
+ function decodeCompleteLAZChunkToPointData(bytes, byteOffset, byteLength, pointCount, header, laszip, state, targetPointOffset) {
126
+ const compressed = bytes.subarray(byteOffset, byteOffset + byteLength);
127
+ if (compressed.byteLength !== byteLength) {
128
+ throw new NeedsMoreData('LASLoader: truncated LAZ chunk');
129
+ }
130
+ const cursor = createLAZChunkDecoderCursor(compressed, createLAZChunkMetadata(header, laszip, pointCount));
131
+ state.target.pointOffset = targetPointOffset;
132
+ const decodedPointCount = cursor.decodeIntoPointData(state.target, pointCount);
133
+ if (decodedPointCount !== pointCount) {
134
+ throw new Error(`LASLoader: decoded ${decodedPointCount} points from a ${pointCount}-point LAZ chunk`);
135
+ }
136
+ populateDecodedTypedExtraBytes(state, targetPointOffset, pointCount);
137
+ }
138
+ /** Parse LAS data from an incoming byte iterator into point batches. */
139
+ export async function* parseLASInBatches(arrayBufferIterator, options = {}) {
140
+ const batchSize = getBatchSize(options);
141
+ const inputIterator = toAsyncIterator(arrayBufferIterator);
142
+ let pending = new Uint8Array(0);
143
+ let absoluteOffset = 0;
144
+ let header = null;
145
+ let sourcePointIndex = 0;
146
+ let totalRead = 0;
147
+ let lastTable;
148
+ while (true) {
149
+ const next = await inputIterator.next();
150
+ if (next.done) {
151
+ break;
152
+ }
153
+ const chunk = next.value;
154
+ pending = concatenateUint8Arrays(pending, toUint8Array(chunk));
155
+ if (!header) {
156
+ header = tryParseHeader(pending, absoluteOffset);
157
+ if (header && pending.byteLength < header.pointsOffset)
158
+ header = null;
159
+ if (header) {
160
+ header.totalToRead = header.pointsCount;
161
+ }
162
+ }
163
+ if (!header) {
164
+ continue;
165
+ }
166
+ if (header.isCompressed) {
167
+ const extendedOffset = header.metadata?.evlrOffset;
168
+ let inputOffset = pending.byteLength;
169
+ const extendedChunks = [];
170
+ if (extendedOffset && extendedOffset < inputOffset) {
171
+ extendedChunks.push(pending.subarray(extendedOffset));
172
+ }
173
+ const trackedIterator = {
174
+ async next() {
175
+ const next = await inputIterator.next();
176
+ if (!next.done && extendedOffset) {
177
+ const bytes = toUint8Array(next.value);
178
+ const start = Math.max(extendedOffset - inputOffset, 0);
179
+ if (start < bytes.byteLength)
180
+ extendedChunks.push(bytes.slice(start));
181
+ inputOffset += bytes.byteLength;
182
+ }
183
+ return next;
184
+ }
185
+ };
186
+ if (header.pointsCount === 0) {
187
+ const { laszip } = await readLASZipVLRFromInput(pending, trackedIterator, header);
188
+ validateTypeScriptLAZSupport(header, laszip);
189
+ lastTable = makeEmptyLASBatch(header, options);
190
+ }
191
+ else {
192
+ for await (const table of parseLAZInBatches(pending, trackedIterator, header, options)) {
193
+ lastTable = updateArrowTableMetadata(table, makeMetadataFromLasHeader({
194
+ ...header,
195
+ totalRead: table.loaderData.totalRead,
196
+ metadata: table.loaderData.metadata || header.metadata
197
+ }));
198
+ yield lastTable;
199
+ }
200
+ }
201
+ if (extendedOffset &&
202
+ lastTable &&
203
+ header.metadata?.evlrCount &&
204
+ (lastTable.loaderData.metadata?.evlrs.length ?? 0) < header.metadata.evlrCount) {
205
+ while (!(await trackedIterator.next()).done) {
206
+ // Finish reading trailing records if the decoder stopped at its chunk table.
207
+ }
208
+ // The tracked iterator retains only the extended-record suffix.
209
+ const suffix = extendedChunks.reduce(concatenateUint8Arrays, new Uint8Array(0));
210
+ const update = makeLASMetadataUpdate(lastTable, header, suffix, extendedOffset, lastTable.loaderData.totalRead);
211
+ if (update) {
212
+ yield update;
213
+ return;
214
+ }
215
+ }
216
+ if (header.pointsCount === 0 && lastTable)
217
+ yield lastTable;
218
+ return;
219
+ }
220
+ for (const batch of readAvailablePointBatches(pending, absoluteOffset, header, sourcePointIndex, totalRead, batchSize)) {
221
+ sourcePointIndex = batch.sourcePointIndex;
222
+ totalRead = batch.totalRead;
223
+ pending = batch.pending;
224
+ absoluteOffset = batch.absoluteOffset;
225
+ lastTable = updateArrowTableMetadata(parseLASArrowTableBatch(batch.arrayBuffer, batch.header, options), makeMetadataFromLasHeader({ ...header, totalRead }));
226
+ yield lastTable;
227
+ }
228
+ }
229
+ if (!header) {
230
+ throw new Error('LASLoader: incomplete LAS header');
231
+ }
232
+ if (totalRead < header.totalToRead) {
233
+ throw new Error('LASLoader: truncated LAS point data');
234
+ }
235
+ if (!lastTable) {
236
+ lastTable = makeEmptyLASBatch(header, options);
237
+ yield lastTable;
238
+ }
239
+ const update = makeLASMetadataUpdate(lastTable, header, pending, absoluteOffset, totalRead);
240
+ if (update)
241
+ yield update;
242
+ }
243
+ /** Creates the declared point schema and original header metadata for a zero-point stream. */
244
+ function makeEmptyLASBatch(header, options) {
245
+ const table = parseLASArrowTableBatch(new ArrayBuffer(0), { ...header, pointsOffset: 0 }, options);
246
+ return updateArrowTableMetadata({ ...table, loaderData: header }, makeMetadataFromLasHeader(header));
247
+ }
248
+ /** Builds a schema-only batch carrying late extended records and their absolute offsets. */
249
+ function makeLASMetadataUpdate(lastTable, header, pending, absoluteOffset, totalRead) {
250
+ const metadata = header.metadata;
251
+ if (metadata?.evlrOffset && metadata.evlrCount) {
252
+ const relativeOffset = metadata.evlrOffset - absoluteOffset;
253
+ const records = parseLASExtendedVariableLengthRecords(new DataView(pending.buffer, pending.byteOffset, pending.byteLength), relativeOffset, metadata.evlrCount).map(record => ({ ...record, offset: record.offset + absoluteOffset }));
254
+ const finalMetadata = {
255
+ ...metadata,
256
+ evlrs: records,
257
+ extraBytes: [...metadata.extraBytes],
258
+ waveformPacketDescriptors: [...metadata.waveformPacketDescriptors]
259
+ };
260
+ for (const record of records)
261
+ parseTypedLASMetadataRecord({ ...record, data: record.data || new Uint8Array() }, finalMetadata);
262
+ resolveLASGeoTIFFKeyDirectory(finalMetadata);
263
+ const finalHeader = { ...header, totalRead, metadata: finalMetadata };
264
+ if (records.length > 0) {
265
+ const data = new arrow.Table([makeArrowMetadataBatch(lastTable.data.schema)]);
266
+ return updateArrowTableMetadata({ ...lastTable, data, loaderData: finalHeader }, makeMetadataFromLasHeader(finalHeader));
267
+ }
268
+ }
269
+ }
270
+ /** Decode LAS data into raw point chunks. */
271
+ export function* parseLASChunkedIterator(arrayBuffer, batchSize = DEFAULT_BATCH_SIZE, options = {}) {
272
+ const header = parseLASHeader(arrayBuffer);
273
+ if (header.isCompressed) {
274
+ yield* parseLAZChunkedIterator(arrayBuffer, header, batchSize, undefined, options);
275
+ return;
276
+ }
277
+ const totalToRead = header.pointsCount;
278
+ let sourcePointIndex = 0;
279
+ let totalRead = 0;
280
+ while (sourcePointIndex < header.pointsCount && totalRead < totalToRead) {
281
+ const batchPointCount = Math.min(batchSize, header.pointsCount - sourcePointIndex);
282
+ const start = header.pointsOffset + sourcePointIndex * header.pointsStructSize;
283
+ const end = start + batchPointCount * header.pointsStructSize;
284
+ sourcePointIndex += batchPointCount;
285
+ totalRead += batchPointCount;
286
+ const batchHeader = {
287
+ ...header,
288
+ pointsOffset: 0,
289
+ pointsCount: batchPointCount,
290
+ totalToRead,
291
+ totalRead
292
+ };
293
+ yield {
294
+ arrayBuffer: arrayBuffer.slice(start, end),
295
+ header: batchHeader
296
+ };
297
+ }
298
+ }
299
+ /** Decode compressed LAZ file data from an incoming byte iterator into raw point chunks. */
300
+ export async function* decodeLAZFileInBatches(arrayBufferIterator, options = {}) {
301
+ const inputIterator = toAsyncIterator(arrayBufferIterator);
302
+ let pending = new Uint8Array(0);
303
+ let header = null;
304
+ while (!header) {
305
+ const next = await inputIterator.next();
306
+ if (next.done) {
307
+ throw new Error('LASLoader: incomplete LAS header');
308
+ }
309
+ pending = concatenateUint8Arrays(pending, toUint8Array(next.value));
310
+ header = tryParseHeader(pending, 0);
311
+ }
312
+ if (!header.isCompressed) {
313
+ throw new Error('LASLoader: decodeLAZFileInBatches requires compressed LAZ input');
314
+ }
315
+ yield* decodePendingLAZFileInBatches(pending, inputIterator, header, options);
316
+ }
317
+ async function* decodePendingLAZFileInBatches(initialPending, inputIterator, header, options = {}) {
318
+ const { pending, laszip } = await readLASZipVLRFromInput(initialPending, inputIterator, header);
319
+ validateTypeScriptLAZSupport(header, laszip);
320
+ yield* decodeLAZFileWithParsedVLRInBatches(pending, inputIterator, header, laszip, options);
321
+ }
322
+ async function* decodeLAZFileWithParsedVLRInBatches(initialPending, inputIterator, header, laszip, options) {
323
+ if (laszip.variableChunks) {
324
+ const bytes = await collectRemainingInputBytes(initialPending, inputIterator);
325
+ yield* decodeLAZFileFromCompleteBytes(bytes, header, laszip, options);
326
+ return;
327
+ }
328
+ if (header.pointsFormatId <= 5) {
329
+ yield* decodePendingFixedLegacyLAZFileInBatches(initialPending, inputIterator, header, laszip, options);
330
+ return;
331
+ }
332
+ const outputHeader = { ...header, totalToRead: header.pointsCount };
333
+ const batchSize = getBatchSize(options);
334
+ const state = createRawPointBatchState(batchSize, header.pointsStructSize, getLAZStreamingDecodeStats(options));
335
+ let sourcePointIndex = 0;
336
+ const chunkByteLengths = [];
337
+ const reader = new BinaryChunkReader();
338
+ reader.write(initialPending);
339
+ await readUntilAvailable(reader, inputIterator, header.pointsOffset + LAZ_CHUNK_TABLE_POINTER_LENGTH, 'LASLoader: incomplete compressed LAZ point data');
340
+ reader.skip(header.pointsOffset);
341
+ const chunkTableOffset = readStreamedLAZChunkTableOffset(reader.getDataView(LAZ_CHUNK_TABLE_POINTER_LENGTH));
342
+ while (sourcePointIndex < header.pointsCount) {
343
+ const chunkPointCount = Math.min(laszip.chunkSize, header.pointsCount - sourcePointIndex);
344
+ const metadata = createLAZChunkMetadata(header, laszip, chunkPointCount);
345
+ const chunkByteLength = await readLAZChunkByteLengthFromReader(reader, inputIterator, metadata);
346
+ chunkByteLengths.push(chunkByteLength);
347
+ recordReadBytesStats(reader, chunkByteLength, state.stats);
348
+ const compressedChunk = reader.readBytes(chunkByteLength);
349
+ if (options.las?.recoverMissingChunkTable && chunkTableOffset === header.pointsOffset) {
350
+ validateRecoveredLayeredPointCount(compressedChunk, metadata);
351
+ }
352
+ for (const batch of appendDecodedLAZChunk(compressedChunk, metadata, outputHeader, state)) {
353
+ yield batch;
354
+ }
355
+ sourcePointIndex += chunkPointCount;
356
+ }
357
+ await validateStreamedFixedLAZChunkTable(reader, inputIterator, header, laszip, chunkTableOffset, chunkByteLengths, options);
358
+ const finalBatch = flushRawPointBatch(outputHeader, state);
359
+ if (finalBatch) {
360
+ yield finalBatch;
361
+ }
362
+ }
363
+ async function* decodePendingFixedLegacyLAZFileInBatches(initialPending, inputIterator, header, laszip, options = {}) {
364
+ const outputHeader = { ...header, totalToRead: header.pointsCount };
365
+ const batchSize = getBatchSize(options);
366
+ const state = createRawPointBatchState(batchSize, header.pointsStructSize, getLAZStreamingDecodeStats(options));
367
+ const chunkByteLengths = [];
368
+ const reader = new BinaryChunkReader();
369
+ reader.write(initialPending);
370
+ await readUntilAvailable(reader, inputIterator, header.pointsOffset + (laszip.compressor === 1 ? 0 : LAZ_CHUNK_TABLE_POINTER_LENGTH), 'LASLoader: incomplete compressed LAZ point data');
371
+ reader.skip(header.pointsOffset);
372
+ const chunkTableOffset = laszip.compressor === 1
373
+ ? 0
374
+ : readStreamedLAZChunkTableOffset(reader.getDataView(LAZ_CHUNK_TABLE_POINTER_LENGTH));
375
+ let sourcePointIndex = 0;
376
+ while (sourcePointIndex < header.pointsCount) {
377
+ const chunkPointCount = laszip.compressor === 1
378
+ ? header.pointsCount
379
+ : Math.min(laszip.chunkSize, header.pointsCount - sourcePointIndex);
380
+ const metadata = createLAZChunkMetadata(header, laszip, chunkPointCount);
381
+ const cursor = createLAZChunkDecoderCursor(new Uint8Array(0), metadata);
382
+ let fedByteLength = 0;
383
+ let consumedByteLength = 0;
384
+ let inputDone = false;
385
+ while (cursor.remainingPointCount > 0) {
386
+ const availableByteLength = reader.getAvailableByteLength();
387
+ const requestedByteLength = getLegacyLAZFeedByteLength(availableByteLength, fedByteLength, cursor.requiredInputByteLength - consumedByteLength);
388
+ if (requestedByteLength > fedByteLength) {
389
+ const checkpoint = reader.checkpoint();
390
+ reader.skip(fedByteLength);
391
+ const addedByteLength = requestedByteLength - fedByteLength;
392
+ recordReadBytesStats(reader, addedByteLength, state.stats);
393
+ cursor.feed(reader.readBytes(addedByteLength));
394
+ reader.restore(checkpoint);
395
+ fedByteLength = requestedByteLength;
396
+ }
397
+ const availableBatchPointCount = batchSize - state.batchPointCount;
398
+ const decodedPointCount = cursor.decodeAvailableInto(state.rawBatch, state.batchPointCount * header.pointsStructSize, availableBatchPointCount, inputDone);
399
+ if (decodedPointCount > 0) {
400
+ if (laszip.compressor === 1) {
401
+ const decodedByteLength = cursor.compressedByteOffset - consumedByteLength;
402
+ reader.skip(decodedByteLength);
403
+ fedByteLength -= decodedByteLength;
404
+ consumedByteLength = cursor.compressedByteOffset;
405
+ cursor.discardConsumedInput();
406
+ }
407
+ state.batchPointCount += decodedPointCount;
408
+ if (state.batchPointCount === batchSize) {
409
+ const batch = flushRawPointBatch(outputHeader, state);
410
+ if (batch) {
411
+ yield batch;
412
+ }
413
+ }
414
+ continue;
415
+ }
416
+ if (inputDone) {
417
+ throw new NeedsMoreData(`LASLoader: truncated legacy LAZ chunk after ${chunkPointCount - cursor.remainingPointCount} of ${chunkPointCount} points with ${availableByteLength} bytes available`);
418
+ }
419
+ const next = await inputIterator.next();
420
+ if (next.done) {
421
+ inputDone = true;
422
+ }
423
+ else {
424
+ reader.write(next.value);
425
+ }
426
+ }
427
+ reader.skip(cursor.compressedByteOffset - consumedByteLength);
428
+ chunkByteLengths.push(cursor.compressedByteOffset);
429
+ sourcePointIndex += chunkPointCount;
430
+ }
431
+ if (laszip.compressor !== 1) {
432
+ await validateStreamedFixedLAZChunkTable(reader, inputIterator, header, laszip, chunkTableOffset, chunkByteLengths, options);
433
+ }
434
+ const finalBatch = flushRawPointBatch(outputHeader, state);
435
+ if (finalBatch) {
436
+ yield finalBatch;
437
+ }
438
+ }
439
+ /**
440
+ * Return the queued prefix length to expose to a legacy LAZ cursor.
441
+ *
442
+ * Legacy arithmetic decoding can request input one point at a time. Feeding only that minimum
443
+ * makes the streaming path repeat reader checkpoints and tiny copies for every point. Growing
444
+ * the exposed prefix in fixed blocks amortizes that work while limiting speculative read-ahead
445
+ * to one block beyond the cursor requirement.
446
+ */
447
+ function getLegacyLAZFeedByteLength(availableByteLength, fedByteLength, requiredInputByteLength) {
448
+ return Math.min(availableByteLength, Math.max(requiredInputByteLength, fedByteLength + LEGACY_LAZ_FEED_BLOCK_SIZE));
449
+ }
450
+ async function* parseLAZInBatches(initialPending, inputIterator, header, options) {
451
+ const { pending, laszip } = await readLASZipVLRFromInput(initialPending, inputIterator, header);
452
+ validateTypeScriptLAZSupport(header, laszip);
453
+ if (!laszip.variableChunks && header.pointsFormatId <= 5) {
454
+ yield* parsePendingFixedLegacyLAZFileInArrowBatches(pending, inputIterator, header, laszip, options);
455
+ return;
456
+ }
457
+ if (!laszip.variableChunks && header.pointsFormatId >= 6 && header.pointsFormatId <= 10) {
458
+ yield* parsePendingLAZFileInArrowBatches(pending, inputIterator, header, laszip, options);
459
+ return;
460
+ }
461
+ for await (const batch of decodeLAZFileWithParsedVLRInBatches(pending, inputIterator, header, laszip, options)) {
462
+ yield parseLASArrowTableBatch(batch.arrayBuffer, batch.header, options);
463
+ }
464
+ }
465
+ /**
466
+ * Decode fixed-size legacy LAZ chunks directly into streaming Arrow batches.
467
+ *
468
+ * This path intentionally parallels the raw legacy stream loop for PDRFs 0-5. Writing into
469
+ * Arrow-owned arrays avoids allocating and reparsing an intermediate raw LAS point-record batch,
470
+ * while the cursor still provides point-atomic suspension when more compressed input is required.
471
+ */
472
+ async function* parsePendingFixedLegacyLAZFileInArrowBatches(initialPending, inputIterator, header, laszip, options) {
473
+ const outputHeader = { ...header, totalToRead: header.pointsCount };
474
+ const state = createPointDataBatchState(getBatchSize(options), header, options);
475
+ const stats = getLAZStreamingDecodeStats(options);
476
+ const chunkByteLengths = [];
477
+ const reader = new BinaryChunkReader();
478
+ reader.write(initialPending);
479
+ await readUntilAvailable(reader, inputIterator, header.pointsOffset + (laszip.compressor === 1 ? 0 : LAZ_CHUNK_TABLE_POINTER_LENGTH), 'LASLoader: incomplete compressed LAZ point data');
480
+ reader.skip(header.pointsOffset);
481
+ const chunkTableOffset = laszip.compressor === 1
482
+ ? 0
483
+ : readStreamedLAZChunkTableOffset(reader.getDataView(LAZ_CHUNK_TABLE_POINTER_LENGTH));
484
+ let sourcePointIndex = 0;
485
+ while (sourcePointIndex < header.pointsCount) {
486
+ const chunkPointCount = laszip.compressor === 1
487
+ ? header.pointsCount
488
+ : Math.min(laszip.chunkSize, header.pointsCount - sourcePointIndex);
489
+ const metadata = createLAZChunkMetadata(header, laszip, chunkPointCount);
490
+ const cursor = createLAZChunkDecoderCursor(new Uint8Array(0), metadata);
491
+ let fedByteLength = 0;
492
+ let consumedByteLength = 0;
493
+ let inputDone = false;
494
+ while (cursor.remainingPointCount > 0) {
495
+ const availableByteLength = reader.getAvailableByteLength();
496
+ const requestedByteLength = getLegacyLAZFeedByteLength(availableByteLength, fedByteLength, cursor.requiredInputByteLength - consumedByteLength);
497
+ if (requestedByteLength > fedByteLength) {
498
+ const checkpoint = reader.checkpoint();
499
+ reader.skip(fedByteLength);
500
+ const addedByteLength = requestedByteLength - fedByteLength;
501
+ recordReadBytesStats(reader, addedByteLength, stats);
502
+ cursor.feed(reader.readBytes(addedByteLength));
503
+ reader.restore(checkpoint);
504
+ fedByteLength = requestedByteLength;
505
+ }
506
+ state.target.pointOffset = state.batchPointCount;
507
+ const availableBatchPointCount = state.batchCapacity - state.batchPointCount;
508
+ const decodedPointCount = cursor.decodeAvailableIntoPointData(state.target, availableBatchPointCount, inputDone);
509
+ if (decodedPointCount > 0) {
510
+ if (laszip.compressor === 1) {
511
+ const decodedByteLength = cursor.compressedByteOffset - consumedByteLength;
512
+ reader.skip(decodedByteLength);
513
+ fedByteLength -= decodedByteLength;
514
+ consumedByteLength = cursor.compressedByteOffset;
515
+ cursor.discardConsumedInput();
516
+ }
517
+ populateDecodedTypedExtraBytes(state, state.batchPointCount, decodedPointCount);
518
+ state.batchPointCount += decodedPointCount;
519
+ if (state.batchPointCount === state.batchCapacity) {
520
+ const batch = flushPointDataBatch(outputHeader, state, options);
521
+ if (batch) {
522
+ yield batch;
523
+ }
524
+ }
525
+ continue;
526
+ }
527
+ if (inputDone) {
528
+ throw new NeedsMoreData(`LASLoader: truncated legacy LAZ chunk after ${chunkPointCount - cursor.remainingPointCount} of ${chunkPointCount} points with ${availableByteLength} bytes available`);
529
+ }
530
+ const next = await inputIterator.next();
531
+ if (next.done) {
532
+ inputDone = true;
533
+ }
534
+ else {
535
+ reader.write(next.value);
536
+ }
537
+ }
538
+ reader.skip(cursor.compressedByteOffset - consumedByteLength);
539
+ chunkByteLengths.push(cursor.compressedByteOffset);
540
+ sourcePointIndex += chunkPointCount;
541
+ }
542
+ if (laszip.compressor !== 1) {
543
+ await validateStreamedFixedLAZChunkTable(reader, inputIterator, header, laszip, chunkTableOffset, chunkByteLengths, options);
544
+ }
545
+ const finalBatch = flushPointDataBatch(outputHeader, state, options);
546
+ if (finalBatch) {
547
+ yield finalBatch;
548
+ }
549
+ }
550
+ async function* parsePendingLAZFileInArrowBatches(initialPending, inputIterator, header, laszip, options) {
551
+ const outputHeader = { ...header, totalToRead: header.pointsCount };
552
+ const state = createPointDataBatchState(getBatchSize(options), header, options);
553
+ let sourcePointIndex = 0;
554
+ const chunkByteLengths = [];
555
+ const reader = new BinaryChunkReader();
556
+ reader.write(initialPending);
557
+ await readUntilAvailable(reader, inputIterator, header.pointsOffset + LAZ_CHUNK_TABLE_POINTER_LENGTH, 'LASLoader: incomplete compressed LAZ point data');
558
+ reader.skip(header.pointsOffset);
559
+ const chunkTableOffset = readStreamedLAZChunkTableOffset(reader.getDataView(LAZ_CHUNK_TABLE_POINTER_LENGTH));
560
+ while (sourcePointIndex < header.pointsCount) {
561
+ const chunkPointCount = Math.min(laszip.chunkSize, header.pointsCount - sourcePointIndex);
562
+ const metadata = createLAZChunkMetadata(header, laszip, chunkPointCount);
563
+ const chunkByteLength = yield* appendProgressiveLAZChunkToPointDataBatches(reader, inputIterator, metadata, outputHeader, state, options, Boolean(options.las?.recoverMissingChunkTable && chunkTableOffset === header.pointsOffset));
564
+ chunkByteLengths.push(chunkByteLength);
565
+ sourcePointIndex += chunkPointCount;
566
+ }
567
+ await validateStreamedFixedLAZChunkTable(reader, inputIterator, header, laszip, chunkTableOffset, chunkByteLengths, options);
568
+ const finalBatch = flushPointDataBatch(outputHeader, state, options);
569
+ if (finalBatch) {
570
+ yield finalBatch;
571
+ }
572
+ }
573
+ /** Feed one layered LAZ chunk and yield requested Arrow rows before its trailing layers arrive. */
574
+ async function* appendProgressiveLAZChunkToPointDataBatches(reader, inputIterator, metadata, header, state, options, validateRecoveryPointCount) {
575
+ const headerByteLength = getLAZChunkHeaderByteLength(metadata);
576
+ await readUntilAvailable(reader, inputIterator, headerByteLength, 'LASLoader: incomplete layered LAZ chunk header');
577
+ const compressedHeader = reader.readBytes(headerByteLength);
578
+ if (validateRecoveryPointCount)
579
+ validateRecoveredLayeredPointCount(compressedHeader, metadata);
580
+ const chunkByteLength = getLAZChunkDeclaredByteLength(compressedHeader, metadata);
581
+ const decoder = createLAZChunkDecoder(metadata);
582
+ decoder.feed(compressedHeader);
583
+ let fedByteLength = headerByteLength;
584
+ while (fedByteLength < chunkByteLength) {
585
+ yield* readAvailableLAZPointDataBatches(decoder, header, state, options);
586
+ if (reader.getAvailableByteLength() === 0) {
587
+ const next = await inputIterator.next();
588
+ if (next.done) {
589
+ throw new NeedsMoreData('LASLoader: incomplete layered LAZ chunk payload');
590
+ }
591
+ reader.write(next.value);
592
+ }
593
+ const byteLength = Math.min(reader.getAvailableByteLength(), chunkByteLength - fedByteLength);
594
+ decoder.feed(reader.readBytes(byteLength));
595
+ fedByteLength += byteLength;
596
+ }
597
+ decoder.close();
598
+ yield* readAvailableLAZPointDataBatches(decoder, header, state, options);
599
+ if (decoder.remainingPointCount !== 0) {
600
+ throw new NeedsMoreData(`LASLoader: layered LAZ chunk produced ${metadata.pointCount - decoder.remainingPointCount} of ${metadata.pointCount} points`);
601
+ }
602
+ return chunkByteLength;
603
+ }
604
+ /** Drain all currently decodable rows from one feedable layered LAZ chunk. */
605
+ function* readAvailableLAZPointDataBatches(decoder, header, state, options) {
606
+ while (decoder.remainingPointCount > 0) {
607
+ const batchRemainingPointCount = state.batchCapacity - state.batchPointCount;
608
+ state.target.pointOffset = state.batchPointCount;
609
+ const pointsDecoded = decoder.readPointDataBatch(state.target, batchRemainingPointCount);
610
+ if (!pointsDecoded) {
611
+ return;
612
+ }
613
+ populateDecodedTypedExtraBytes(state, state.batchPointCount, pointsDecoded);
614
+ state.batchPointCount += pointsDecoded;
615
+ if (state.batchPointCount === state.batchCapacity) {
616
+ const batch = flushPointDataBatch(header, state, options);
617
+ if (batch) {
618
+ yield batch;
619
+ }
620
+ }
621
+ }
622
+ }
623
+ function* parseLAZChunkedIterator(arrayBuffer, header, batchSize, parsedLASZipVLR, options = {}) {
624
+ const laszip = parsedLASZipVLR || parseLASZipVLR(new Uint8Array(arrayBuffer), header);
625
+ validateTypeScriptLAZSupport(header, laszip);
626
+ if (header.pointsFormatId <= 5 || laszip.variableChunks) {
627
+ yield* decodeLAZFileFromCompleteBytes(new Uint8Array(arrayBuffer), header, laszip, {
628
+ ...options,
629
+ core: { ...options.core, batchSize }
630
+ });
631
+ return;
632
+ }
633
+ const outputHeader = { ...header, totalToRead: header.pointsCount };
634
+ const state = createRawPointBatchState(batchSize, header.pointsStructSize);
635
+ const bytes = new Uint8Array(arrayBuffer);
636
+ const chunkTable = readLAZFileChunks(bytes, header, laszip, options);
637
+ let byteOffset = header.pointsOffset + (laszip.compressor === 1 ? 0 : LAZ_CHUNK_TABLE_POINTER_LENGTH);
638
+ for (const chunk of chunkTable) {
639
+ const metadata = createLAZChunkMetadata(header, laszip, chunk.pointCount);
640
+ const compressed = bytes.subarray(byteOffset, byteOffset + chunk.byteLength);
641
+ for (const batch of appendDecodedLAZChunk(compressed, metadata, outputHeader, state)) {
642
+ yield batch;
643
+ }
644
+ byteOffset += chunk.byteLength;
645
+ }
646
+ const finalBatch = flushRawPointBatch(outputHeader, state);
647
+ if (finalBatch) {
648
+ yield finalBatch;
649
+ }
650
+ }
651
+ function* decodeLAZFileFromCompleteBytes(bytes, header, laszip, options = {}) {
652
+ const batchSize = getBatchSize(options);
653
+ const outputHeader = {
654
+ ...header,
655
+ metadata: parseLASMetadata(bytes, header),
656
+ totalToRead: header.pointsCount
657
+ };
658
+ const state = createRawPointBatchState(batchSize, header.pointsStructSize, getLAZStreamingDecodeStats(options));
659
+ const chunkTable = readLAZFileChunks(bytes, header, laszip, options);
660
+ let byteOffset = header.pointsOffset + (laszip.compressor === 1 ? 0 : LAZ_CHUNK_TABLE_POINTER_LENGTH);
661
+ for (const chunk of chunkTable) {
662
+ const compressedChunk = bytes.subarray(byteOffset, byteOffset + chunk.byteLength);
663
+ if (compressedChunk.byteLength < chunk.byteLength) {
664
+ throw new NeedsMoreData('LASLoader: truncated legacy LAZ chunk');
665
+ }
666
+ const metadata = createLAZChunkMetadata(header, laszip, chunk.pointCount);
667
+ if (laszip.compressor === 1) {
668
+ yield* appendDecodedLAZChunk(compressedChunk, metadata, outputHeader, state);
669
+ }
670
+ else {
671
+ const rawChunk = decodeLAZChunk(compressedChunk, metadata);
672
+ recordDecodedChunkAllocation(rawChunk.byteLength, state.stats);
673
+ yield* appendRawPointChunk(rawChunk, outputHeader, state);
674
+ }
675
+ byteOffset += chunk.byteLength;
676
+ }
677
+ const finalBatch = flushRawPointBatch(outputHeader, state);
678
+ if (finalBatch) {
679
+ yield finalBatch;
680
+ }
681
+ }
682
+ function decodeLAZFileToRawPointData(arrayBuffer, header, laszip) {
683
+ const totalPointCount = header.pointsCount;
684
+ const rawPointData = new Uint8Array(totalPointCount * header.pointsStructSize);
685
+ let byteOffset = 0;
686
+ for (const batch of parseLAZChunkedIterator(arrayBuffer, header, DEFAULT_BATCH_SIZE, laszip)) {
687
+ const source = new Uint8Array(batch.arrayBuffer);
688
+ rawPointData.set(source, byteOffset);
689
+ byteOffset += source.byteLength;
690
+ }
691
+ return rawPointData;
692
+ }
693
+ /** Parse the public LAS header block. */
694
+ export function parseLASHeader(arrayBuffer) {
695
+ const dataView = new DataView(arrayBuffer);
696
+ if (arrayBuffer.byteLength < 227 || dataView.getUint32(0, true) !== LASF_SIGNATURE) {
697
+ throw new Error('LASLoader: invalid LAS header');
698
+ }
699
+ const versionMajor = dataView.getUint8(24);
700
+ const versionMinor = dataView.getUint8(25);
701
+ if (versionMajor !== 1 || versionMinor > 5) {
702
+ throw new Error(`LASLoader: unsupported LAS version ${versionMajor}.${versionMinor}`);
703
+ }
704
+ const headerSize = dataView.getUint16(94, true);
705
+ const globalEncoding = dataView.getUint16(6, true);
706
+ const vlrCount = dataView.getUint32(100, true);
707
+ const pointFormatByte = dataView.getUint8(104);
708
+ const pointsFormatId = pointFormatByte & POINT_FORMAT_MASK;
709
+ const isCompressed = Boolean(pointFormatByte & COMPRESSED_POINT_FORMAT_MASK);
710
+ const legacyPointCount = dataView.getUint32(107, true);
711
+ const extendedPointCount = versionMajor > 1 || versionMinor >= 4 ? readUint64(dataView, 247) : legacyPointCount;
712
+ const pointsCount = extendedPointCount || legacyPointCount;
713
+ const pointsOffset = dataView.getUint32(96, true);
714
+ const pointsStructSize = dataView.getUint16(105, true);
715
+ if (versionMinor === 5) {
716
+ if (headerSize < 393) {
717
+ throw new Error('LASLoader: LAS 1.5 header must be at least 393 bytes');
718
+ }
719
+ if (pointsFormatId < 6 || pointsFormatId > 10) {
720
+ throw new Error(`LASLoader: LAS 1.5 requires point data record formats 6-10; received ${pointsFormatId}`);
721
+ }
722
+ if ((globalEncoding & 0x10) === 0) {
723
+ throw new Error('LASLoader: LAS 1.5 requires the WKT global encoding flag');
724
+ }
725
+ if ((globalEncoding & 0xffa0) !== 0) {
726
+ throw new Error('LASLoader: LAS 1.5 Global Encoding contains reserved bits');
727
+ }
728
+ if ((globalEncoding & 0x40) !== 0 && (globalEncoding & 0x01) === 0) {
729
+ throw new Error('LASLoader: LAS 1.5 Time Offset Flag requires GPS Time Type');
730
+ }
731
+ const maxGpsTime = dataView.getFloat64(375, true);
732
+ const minGpsTime = dataView.getFloat64(383, true);
733
+ if (!Number.isFinite(maxGpsTime) ||
734
+ !Number.isFinite(minGpsTime) ||
735
+ (maxGpsTime !== 0 && minGpsTime !== 0 && maxGpsTime < minGpsTime)) {
736
+ throw new Error('LASLoader: LAS 1.5 GPS time range is invalid');
737
+ }
738
+ }
739
+ const scale = [
740
+ dataView.getFloat64(131, true),
741
+ dataView.getFloat64(139, true),
742
+ dataView.getFloat64(147, true)
743
+ ];
744
+ const offset = [
745
+ dataView.getFloat64(155, true),
746
+ dataView.getFloat64(163, true),
747
+ dataView.getFloat64(171, true)
748
+ ];
749
+ const maxs = [
750
+ dataView.getFloat64(179, true),
751
+ dataView.getFloat64(195, true),
752
+ dataView.getFloat64(211, true)
753
+ ];
754
+ const mins = [
755
+ dataView.getFloat64(187, true),
756
+ dataView.getFloat64(203, true),
757
+ dataView.getFloat64(219, true)
758
+ ];
759
+ const header = {
760
+ pointsOffset,
761
+ pointsFormatId,
762
+ pointsStructSize,
763
+ pointsCount,
764
+ scale,
765
+ offset,
766
+ maxs,
767
+ mins,
768
+ totalToRead: pointsCount,
769
+ totalRead: 0,
770
+ hasColor: hasPointColor(pointsFormatId),
771
+ versionAsString: `${versionMajor}.${versionMinor}`,
772
+ isCompressed,
773
+ headerSize,
774
+ vlrCount,
775
+ userHeaderData: versionMinor === 5 && headerSize > 393
776
+ ? new Uint8Array(arrayBuffer, 393, headerSize - 393).slice()
777
+ : undefined
778
+ };
779
+ if (hasCompleteLASMetadata(arrayBuffer, header)) {
780
+ header.metadata = parseLASMetadata(arrayBuffer, header);
781
+ }
782
+ return header;
783
+ }
784
+ function hasCompleteLASMetadata(arrayBuffer, header) {
785
+ const vlrEnd = header.headerSize + header.vlrCount * 54;
786
+ return arrayBuffer.byteLength >= Math.max(vlrEnd, header.pointsOffset);
787
+ }
788
+ function parseLASMetadata(arrayBuffer, header) {
789
+ const dataView = makeDataView(arrayBuffer);
790
+ const bytes = new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength);
791
+ const version = header.versionAsString || '1.0';
792
+ const versionParts = version.split('.').map(Number);
793
+ const isAtLeast14 = versionParts[0] > 1 || (versionParts[0] === 1 && versionParts[1] >= 4);
794
+ const vlrs = parseLASVariableLengthRecords(dataView, header);
795
+ const evlrOffset = isAtLeast14 ? readUint64(dataView, 235) : 0;
796
+ const evlrCount = isAtLeast14 ? dataView.getUint32(243, true) : 0;
797
+ const evlrs = parseLASExtendedVariableLengthRecords(dataView, evlrOffset, evlrCount);
798
+ const metadata = {
799
+ fileSourceId: dataView.getUint16(4, true),
800
+ globalEncoding: dataView.getUint16(6, true),
801
+ waveformDataOffset: versionParts[0] > 1 || (versionParts[0] === 1 && versionParts[1] >= 3)
802
+ ? dataView.getBigUint64(227, true)
803
+ : undefined,
804
+ projectId: formatLASProjectId(bytes.subarray(8, 24)),
805
+ systemIdentifier: readLASString(bytes, 26, 32),
806
+ generatingSoftware: readLASString(bytes, 58, 32),
807
+ creationDayOfYear: dataView.getUint16(90, true),
808
+ creationYear: dataView.getUint16(92, true),
809
+ headerSize: header.headerSize,
810
+ userHeaderData: header.userHeaderData,
811
+ vlrCount: header.vlrCount,
812
+ evlrOffset: evlrOffset || undefined,
813
+ evlrCount: evlrCount || undefined,
814
+ maxGpsTime: versionParts[1] === 5 ? dataView.getFloat64(375, true) : undefined,
815
+ minGpsTime: versionParts[1] === 5 ? dataView.getFloat64(383, true) : undefined,
816
+ timeOffset: versionParts[1] === 5 ? dataView.getUint16(391, true) : undefined,
817
+ pointsByReturn: parsePointsByReturn(dataView, isAtLeast14),
818
+ vlrs,
819
+ evlrs,
820
+ extraBytes: [],
821
+ waveformPacketDescriptors: []
822
+ };
823
+ for (const record of vlrs.concat(evlrs.map(evlr => ({ ...evlr, data: evlr.data || new Uint8Array() })))) {
824
+ parseTypedLASMetadataRecord(record, metadata);
825
+ }
826
+ metadata.compatibility = getLASCompatibilityLayout(header, metadata);
827
+ resolveLASGeoTIFFKeyDirectory(metadata);
828
+ return metadata;
829
+ }
830
+ function parseLASVariableLengthRecords(dataView, header) {
831
+ const bytes = new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength);
832
+ const records = [];
833
+ let offset = header.headerSize;
834
+ for (let index = 0; index < header.vlrCount; index++) {
835
+ if (offset + 54 > bytes.byteLength) {
836
+ break;
837
+ }
838
+ const dataLength = dataView.getUint16(offset + 20, true);
839
+ const dataOffset = offset + 54;
840
+ if (dataOffset + dataLength > bytes.byteLength) {
841
+ break;
842
+ }
843
+ records.push({
844
+ reserved: dataView.getUint16(offset, true),
845
+ userId: readLASString(bytes, offset + 2, 16),
846
+ recordId: dataView.getUint16(offset + 18, true),
847
+ description: readLASString(bytes, offset + 22, 32),
848
+ offset,
849
+ data: bytes.slice(dataOffset, dataOffset + dataLength)
850
+ });
851
+ offset = dataOffset + dataLength;
852
+ }
853
+ return records;
854
+ }
855
+ function parseLASExtendedVariableLengthRecords(dataView, evlrOffset, evlrCount) {
856
+ if (evlrOffset < 0 || !evlrCount || evlrOffset >= dataView.byteLength) {
857
+ return [];
858
+ }
859
+ const bytes = new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength);
860
+ const records = [];
861
+ let offset = evlrOffset;
862
+ for (let index = 0; index < evlrCount; index++) {
863
+ if (offset + 60 > bytes.byteLength) {
864
+ break;
865
+ }
866
+ const dataLength = readUint64(dataView, offset + 20);
867
+ const dataOffset = offset + 60;
868
+ if (dataLength > bytes.byteLength - dataOffset) {
869
+ break;
870
+ }
871
+ records.push({
872
+ reserved: dataView.getUint16(offset, true),
873
+ userId: readLASString(bytes, offset + 2, 16),
874
+ recordId: dataView.getUint16(offset + 18, true),
875
+ description: readLASString(bytes, offset + 28, 32),
876
+ offset,
877
+ data: bytes.slice(dataOffset, dataOffset + dataLength),
878
+ dataOffset,
879
+ dataLength
880
+ });
881
+ offset = dataOffset + dataLength;
882
+ }
883
+ return records;
884
+ }
885
+ function parseTypedLASMetadataRecord(record, metadata) {
886
+ const data = record.data;
887
+ if (!data) {
888
+ return;
889
+ }
890
+ if (record.userId === 'LASF_Spec' && record.recordId === 4) {
891
+ metadata.extraBytes.push(...parseLASExtraBytes(data));
892
+ }
893
+ else if (record.userId === 'LASF_Spec' && record.recordId >= 100 && record.recordId <= 354) {
894
+ metadata.waveformPacketDescriptors.push(parseLASWaveformDescriptor(record.recordId, data));
895
+ }
896
+ else if (record.userId === 'LASF_Projection' && record.recordId === 2111) {
897
+ metadata.wktMathTransform = decodeLASString(data);
898
+ }
899
+ else if (record.userId === 'LASF_Projection' && record.recordId === 2112) {
900
+ metadata.wkt = decodeLASString(data);
901
+ }
902
+ else if (record.userId === 'LASF_Projection' && record.recordId === 34735) {
903
+ metadata.geotiff = { ...metadata.geotiff, keys: readUint16Array(data) };
904
+ }
905
+ else if (record.userId === 'LASF_Projection' && record.recordId === 34736) {
906
+ metadata.geotiff = { ...metadata.geotiff, doubles: readFloat64Array(data) };
907
+ }
908
+ else if (record.userId === 'LASF_Projection' && record.recordId === 34737) {
909
+ metadata.geotiff = { ...metadata.geotiff, ascii: decodeLASString(data) };
910
+ }
911
+ }
912
+ /** Resolve GeoKey directory entries against their companion LAS GeoTIFF records. */
913
+ function resolveLASGeoTIFFKeyDirectory(metadata) {
914
+ const geotiff = metadata.geotiff;
915
+ const keys = geotiff?.keys;
916
+ if (!geotiff || !keys || keys.length < 4) {
917
+ return;
918
+ }
919
+ const declaredEntryCount = keys[3];
920
+ if (keys.length < 4 + declaredEntryCount * 4) {
921
+ return;
922
+ }
923
+ const entries = [];
924
+ for (let entryIndex = 0; entryIndex < declaredEntryCount; entryIndex++) {
925
+ const entryOffset = 4 + entryIndex * 4;
926
+ const entry = {
927
+ keyId: keys[entryOffset],
928
+ tiffTagLocation: keys[entryOffset + 1],
929
+ count: keys[entryOffset + 2],
930
+ valueOffset: keys[entryOffset + 3]
931
+ };
932
+ entry.value = resolveLASGeoTIFFKeyValue(entry, geotiff);
933
+ if (entry.value === undefined) {
934
+ delete entry.value;
935
+ }
936
+ entries.push(entry);
937
+ }
938
+ geotiff.keyDirectory = {
939
+ version: keys[0],
940
+ keyRevision: keys[1],
941
+ minorRevision: keys[2],
942
+ entries
943
+ };
944
+ }
945
+ /** Resolve one GeoKey value from its TIFF-tag location. */
946
+ function resolveLASGeoTIFFKeyValue(entry, geotiff) {
947
+ if (entry.tiffTagLocation === 0) {
948
+ return entry.valueOffset;
949
+ }
950
+ if (entry.tiffTagLocation === 34735 && geotiff.keys) {
951
+ const values = geotiff.keys.slice(entry.valueOffset, entry.valueOffset + entry.count);
952
+ return values.length === entry.count ? Array.from(values) : undefined;
953
+ }
954
+ if (entry.tiffTagLocation === 34736 && geotiff.doubles) {
955
+ const values = geotiff.doubles.slice(entry.valueOffset, entry.valueOffset + entry.count);
956
+ if (values.length !== entry.count) {
957
+ return undefined;
958
+ }
959
+ return entry.count === 1 ? values[0] : Array.from(values);
960
+ }
961
+ if (entry.tiffTagLocation === 34737 && geotiff.ascii !== undefined) {
962
+ const end = entry.valueOffset + entry.count;
963
+ if (end > geotiff.ascii.length) {
964
+ return undefined;
965
+ }
966
+ return geotiff.ascii.slice(entry.valueOffset, end).replace(/\|$/, '');
967
+ }
968
+ return undefined;
969
+ }
970
+ function parseLASWaveformDescriptor(recordId, data) {
971
+ if (data.byteLength < 28) {
972
+ throw new Error(`LASLoader: waveform descriptor VLR ${recordId} is truncated`);
973
+ }
974
+ const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
975
+ return {
976
+ recordId,
977
+ bitsPerSample: dataView.getUint8(2),
978
+ compressionType: dataView.getUint8(3),
979
+ numberOfSamples: dataView.getUint32(4, true),
980
+ temporalSampleSpacing: dataView.getUint32(8, true),
981
+ digitizerGain: dataView.getFloat64(12, true),
982
+ digitizerOffset: dataView.getFloat64(20, true)
983
+ };
984
+ }
985
+ function parsePointsByReturn(dataView, isAtLeast14) {
986
+ const count = isAtLeast14 ? 15 : 5;
987
+ const offset = isAtLeast14 ? 255 : 111;
988
+ const values = [];
989
+ for (let index = 0; index < count; index++) {
990
+ values.push(isAtLeast14
991
+ ? readUint64(dataView, offset + index * 8)
992
+ : dataView.getUint32(offset + index * 4, true));
993
+ }
994
+ return values;
995
+ }
996
+ function readLASString(bytes, offset, length) {
997
+ return decodeLASString(bytes.subarray(offset, offset + length));
998
+ }
999
+ function decodeLASString(bytes) {
1000
+ let end = bytes.length;
1001
+ while (end > 0 && bytes[end - 1] === 0) {
1002
+ end--;
1003
+ }
1004
+ return new TextDecoder().decode(bytes.subarray(0, end)).trim();
1005
+ }
1006
+ function formatLASProjectId(bytes) {
1007
+ const formatLittleEndian = (start, length) => Array.from(bytes.subarray(start, start + length), byte => byte.toString(16).padStart(2, '0'))
1008
+ .reverse()
1009
+ .join('');
1010
+ const formatBigEndian = (start, length) => Array.from(bytes.subarray(start, start + length), byte => byte.toString(16).padStart(2, '0')).join('');
1011
+ return `${formatLittleEndian(0, 4)}-${formatLittleEndian(4, 2)}-${formatLittleEndian(6, 2)}-${formatBigEndian(8, 2)}${formatBigEndian(10, 6)}`;
1012
+ }
1013
+ function readUint16Array(bytes) {
1014
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1015
+ const values = new Uint16Array(Math.floor(bytes.byteLength / 2));
1016
+ for (let index = 0; index < values.length; index++) {
1017
+ values[index] = dataView.getUint16(index * 2, true);
1018
+ }
1019
+ return values;
1020
+ }
1021
+ function readFloat64Array(bytes) {
1022
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1023
+ const values = new Float64Array(Math.floor(bytes.byteLength / 8));
1024
+ for (let index = 0; index < values.length; index++) {
1025
+ values[index] = dataView.getFloat64(index * 8, true);
1026
+ }
1027
+ return values;
1028
+ }
1029
+ /**
1030
+ * Parse raw LAS point records into a MeshArrowTable batch.
1031
+ * @param arrayBuffer Raw point record data or a complete LAS file buffer.
1032
+ * @param lasHeader LAS header describing the point record layout.
1033
+ * @param options LAS loader options.
1034
+ * @returns Arrow table batch populated directly from LAS point records.
1035
+ */
1036
+ function parseLASArrowTableBatch(arrayBuffer, lasHeader, options = {}) {
1037
+ const batchSize = lasHeader.pointsCount;
1038
+ const PositionsType = options.las?.fp64 ? Float64Array : Float32Array;
1039
+ const positions = new PositionsType(batchSize * 3);
1040
+ const selection = getLASColumnSelection(options);
1041
+ const colors = lasHeader.hasColor && selection.color ? new Uint8Array(batchSize * 4) : null;
1042
+ const intensities = selection.intensity ? new Uint16Array(batchSize) : null;
1043
+ const classifications = selection.classification ? new Uint8Array(batchSize) : null;
1044
+ const syntheticFlags = selection.synthetic ? new Uint8Array(batchSize) : null;
1045
+ const keyPointFlags = selection.keyPoint ? new Uint8Array(batchSize) : null;
1046
+ const withheldFlags = selection.withheld ? new Uint8Array(batchSize) : null;
1047
+ const overlapFlags = selection.overlap ? new Uint8Array(batchSize) : null;
1048
+ const gpsTimes = selection.gpsTime && getGpsTimeOffset(lasHeader.pointsFormatId) >= 0
1049
+ ? new Float64Array(batchSize)
1050
+ : null;
1051
+ const nir = selection.nir &&
1052
+ (getNirOffset(lasHeader.pointsFormatId) >= 0 ||
1053
+ (options.las?.compatibilityMode !== 'raw' &&
1054
+ lasHeader.metadata?.compatibility?.nirByteOffset !== undefined))
1055
+ ? new Uint16Array(batchSize)
1056
+ : null;
1057
+ const scanAngles = selection.scanAngle ? new Int16Array(batchSize) : null;
1058
+ const userData = selection.userData ? new Uint8Array(batchSize) : null;
1059
+ const pointSourceIds = selection.pointSourceId ? new Uint16Array(batchSize) : null;
1060
+ const returnNumbers = selection.returnNumber ? new Uint8Array(batchSize) : null;
1061
+ const numberOfReturns = selection.numberOfReturns ? new Uint8Array(batchSize) : null;
1062
+ const scannerChannels = selection.scannerChannel ? new Uint8Array(batchSize) : null;
1063
+ const scanDirectionFlags = selection.scanDirectionFlag ? new Uint8Array(batchSize) : null;
1064
+ const edgeOfFlightLines = selection.edgeOfFlightLine ? new Uint8Array(batchSize) : null;
1065
+ const waveforms = selection.waveform && getWaveformOffset(lasHeader.pointsFormatId) >= 0
1066
+ ? new Uint8Array(batchSize * 29)
1067
+ : null;
1068
+ const extraByteCount = Math.max(0, lasHeader.pointsStructSize - getLAZPointDataRecordBaseLength(lasHeader.pointsFormatId));
1069
+ const extraBytes = selection.extraBytes && options.las?.extraBytes !== 'typed' && extraByteCount
1070
+ ? new Uint8Array(batchSize * extraByteCount)
1071
+ : null;
1072
+ const typedExtraBytes = selection.extraBytes && options.las?.extraBytes === 'typed'
1073
+ ? createTypedExtraBytesAttributes(batchSize, lasHeader)
1074
+ : null;
1075
+ populateLASAttributesFromDataView(makeDataView(arrayBuffer), lasHeader, options, {
1076
+ positions,
1077
+ colors,
1078
+ intensities,
1079
+ classifications,
1080
+ syntheticFlags,
1081
+ keyPointFlags,
1082
+ withheldFlags,
1083
+ overlapFlags,
1084
+ gpsTimes,
1085
+ nir,
1086
+ scanAngles,
1087
+ userData,
1088
+ pointSourceIds,
1089
+ returnNumbers,
1090
+ numberOfReturns,
1091
+ scannerChannels,
1092
+ scanDirectionFlags,
1093
+ edgeOfFlightLines,
1094
+ waveforms,
1095
+ extraBytes,
1096
+ typedExtraBytes,
1097
+ pointOffset: 0,
1098
+ sourcePointIndex: 0,
1099
+ pointCount: batchSize
1100
+ });
1101
+ return makeLASArrowTableFromAttributes(lasHeader, positions, colors, intensities, classifications, syntheticFlags, keyPointFlags, withheldFlags, overlapFlags, gpsTimes, nir, scanAngles, userData, pointSourceIds, returnNumbers, numberOfReturns, scannerChannels, scanDirectionFlags, edgeOfFlightLines, waveforms, extraBytes, typedExtraBytes);
1102
+ }
1103
+ function makeLASArrowTableFromAttributes(lasHeader, positions, colors, intensities, classifications, syntheticFlags, keyPointFlags, withheldFlags, overlapFlags, gpsTimes, nir, scanAngles, userData, pointSourceIds, returnNumbers, numberOfReturns, scannerChannels, scanDirectionFlags, edgeOfFlightLines, waveforms, extraBytes, typedExtraBytes) {
1104
+ const attributes = {
1105
+ POSITION: { value: positions, size: 3 }
1106
+ };
1107
+ if (intensities) {
1108
+ attributes.intensity = { value: intensities, size: 1 };
1109
+ }
1110
+ if (classifications) {
1111
+ attributes.classification = { value: classifications, size: 1 };
1112
+ }
1113
+ if (syntheticFlags) {
1114
+ attributes.synthetic = { value: syntheticFlags, size: 1 };
1115
+ }
1116
+ if (keyPointFlags) {
1117
+ attributes.keyPoint = { value: keyPointFlags, size: 1 };
1118
+ }
1119
+ if (withheldFlags) {
1120
+ attributes.withheld = { value: withheldFlags, size: 1 };
1121
+ }
1122
+ if (overlapFlags) {
1123
+ attributes.overlap = { value: overlapFlags, size: 1 };
1124
+ }
1125
+ if (colors) {
1126
+ attributes.COLOR_0 = { value: colors, size: colors instanceof Uint16Array ? 3 : 4 };
1127
+ }
1128
+ if (gpsTimes) {
1129
+ attributes.GPS_TIME = { value: gpsTimes, size: 1 };
1130
+ }
1131
+ if (nir) {
1132
+ attributes.NIR = { value: nir, size: 1 };
1133
+ }
1134
+ if (scanAngles) {
1135
+ attributes.scanAngle = { value: scanAngles, size: 1 };
1136
+ }
1137
+ if (userData) {
1138
+ attributes.userData = { value: userData, size: 1 };
1139
+ }
1140
+ if (pointSourceIds) {
1141
+ attributes.pointSourceId = { value: pointSourceIds, size: 1 };
1142
+ }
1143
+ if (returnNumbers) {
1144
+ attributes.returnNumber = { value: returnNumbers, size: 1 };
1145
+ }
1146
+ if (numberOfReturns) {
1147
+ attributes.numberOfReturns = { value: numberOfReturns, size: 1 };
1148
+ }
1149
+ if (scannerChannels) {
1150
+ attributes.scannerChannel = { value: scannerChannels, size: 1 };
1151
+ }
1152
+ if (scanDirectionFlags) {
1153
+ attributes.scanDirectionFlag = { value: scanDirectionFlags, size: 1 };
1154
+ }
1155
+ if (edgeOfFlightLines) {
1156
+ attributes.edgeOfFlightLine = { value: edgeOfFlightLines, size: 1 };
1157
+ }
1158
+ if (waveforms) {
1159
+ attributes.WAVEFORM = { value: waveforms, size: 29 };
1160
+ }
1161
+ if (extraBytes) {
1162
+ attributes.EXTRA_BYTES = {
1163
+ value: extraBytes,
1164
+ size: extraBytes.length / Math.max(lasHeader.pointsCount, 1)
1165
+ };
1166
+ }
1167
+ if (typedExtraBytes) {
1168
+ for (const attribute of typedExtraBytes) {
1169
+ attributes[attribute.name] = { value: attribute.value, size: attribute.size };
1170
+ }
1171
+ }
1172
+ const numericAttributes = {};
1173
+ for (const [name, attribute] of Object.entries(attributes)) {
1174
+ if (!(attribute.value instanceof BigInt64Array) &&
1175
+ !(attribute.value instanceof BigUint64Array)) {
1176
+ numericAttributes[name] = { ...attribute, value: attribute.value };
1177
+ }
1178
+ }
1179
+ const schema = getLASSchema(lasHeader, numericAttributes);
1180
+ return {
1181
+ ...makeMeshArrowTable(attributes, {
1182
+ schema,
1183
+ topology: 'point-list',
1184
+ mode: 0,
1185
+ boundingBox: getHeaderBoundingBox(lasHeader)
1186
+ }),
1187
+ loader: 'las',
1188
+ loaderData: lasHeader,
1189
+ progress: lasHeader.totalRead / lasHeader.totalToRead
1190
+ };
1191
+ }
1192
+ /** Create a DataView with exact offsets for ArrayBufferView inputs. */
1193
+ function makeDataView(arrayBuffer) {
1194
+ if (ArrayBuffer.isView(arrayBuffer)) {
1195
+ return new DataView(arrayBuffer.buffer, arrayBuffer.byteOffset, arrayBuffer.byteLength);
1196
+ }
1197
+ return new DataView(arrayBuffer);
1198
+ }
1199
+ function populateLASAttributesFromDataView(dataView, lasHeader, options, target) {
1200
+ const { scale: [scaleX, scaleY, scaleZ], offset: [offsetX, offsetY, offsetZ] } = lasHeader;
1201
+ const pointsFormatId = lasHeader.pointsFormatId;
1202
+ const pointRecordLength = lasHeader.pointsStructSize;
1203
+ const colorOffset = target.colors ? getColorOffset(pointsFormatId) : -1;
1204
+ const twoByteColor = colorOffset >= 0
1205
+ ? detectTwoByteColors(dataView, lasHeader, target.sourcePointIndex, target.pointCount, options.las?.colorDepth)
1206
+ : false;
1207
+ const intensities = target.intensities;
1208
+ const classifications = target.classifications;
1209
+ const syntheticFlags = target.syntheticFlags;
1210
+ const keyPointFlags = target.keyPointFlags;
1211
+ const withheldFlags = target.withheldFlags;
1212
+ const overlapFlags = target.overlapFlags;
1213
+ const gpsTimes = target.gpsTimes;
1214
+ const nir = target.nir;
1215
+ const scanAngles = target.scanAngles;
1216
+ const userData = target.userData;
1217
+ const pointSourceIds = target.pointSourceIds;
1218
+ const returnNumbers = target.returnNumbers;
1219
+ const numberOfReturns = target.numberOfReturns;
1220
+ const scannerChannels = target.scannerChannels;
1221
+ const scanDirectionFlags = target.scanDirectionFlags;
1222
+ const edgeOfFlightLines = target.edgeOfFlightLines;
1223
+ const waveforms = target.waveforms;
1224
+ const extraBytes = target.extraBytes;
1225
+ const typedExtraBytes = target.typedExtraBytes;
1226
+ const gpsTimeOffset = gpsTimes ? getGpsTimeOffset(pointsFormatId) : -1;
1227
+ const nirOffset = nir ? getNirOffset(pointsFormatId) : -1;
1228
+ for (let pointIndex = 0; pointIndex < target.pointCount; pointIndex++) {
1229
+ const sourcePointIndex = target.sourcePointIndex + pointIndex;
1230
+ const pointOffset = lasHeader.pointsOffset + sourcePointIndex * pointRecordLength;
1231
+ const targetPointIndex = target.pointOffset + pointIndex;
1232
+ target.positions[targetPointIndex * 3] =
1233
+ dataView.getInt32(pointOffset, true) * scaleX + offsetX;
1234
+ target.positions[targetPointIndex * 3 + 1] =
1235
+ dataView.getInt32(pointOffset + 4, true) * scaleY + offsetY;
1236
+ target.positions[targetPointIndex * 3 + 2] =
1237
+ dataView.getInt32(pointOffset + 8, true) * scaleZ + offsetZ;
1238
+ if (intensities) {
1239
+ intensities[targetPointIndex] = dataView.getUint16(pointOffset + 12, true);
1240
+ }
1241
+ if (classifications) {
1242
+ classifications[targetPointIndex] = readClassification(dataView, pointOffset, pointsFormatId);
1243
+ }
1244
+ if (gpsTimes && gpsTimeOffset >= 0) {
1245
+ gpsTimes[targetPointIndex] = dataView.getFloat64(pointOffset + gpsTimeOffset, true);
1246
+ }
1247
+ if (nir && nirOffset >= 0) {
1248
+ nir[targetPointIndex] = dataView.getUint16(pointOffset + nirOffset, true);
1249
+ }
1250
+ if (scanAngles) {
1251
+ scanAngles[targetPointIndex] =
1252
+ pointsFormatId <= 5
1253
+ ? dataView.getInt8(pointOffset + 16)
1254
+ : dataView.getInt16(pointOffset + 18, true);
1255
+ }
1256
+ if (userData) {
1257
+ userData[targetPointIndex] = dataView.getUint8(pointOffset + 17);
1258
+ }
1259
+ if (pointSourceIds) {
1260
+ pointSourceIds[targetPointIndex] = dataView.getUint16(pointOffset + (pointsFormatId <= 5 ? 18 : 20), true);
1261
+ }
1262
+ const returnFlags = dataView.getUint8(pointOffset + 14);
1263
+ const scanFlags = dataView.getUint8(pointOffset + 15);
1264
+ const classificationFlags = pointsFormatId <= 5 ? scanFlags >> 5 : scanFlags;
1265
+ if (syntheticFlags) {
1266
+ syntheticFlags[targetPointIndex] = classificationFlags & 1;
1267
+ }
1268
+ if (keyPointFlags) {
1269
+ keyPointFlags[targetPointIndex] = (classificationFlags >> 1) & 1;
1270
+ }
1271
+ if (withheldFlags) {
1272
+ withheldFlags[targetPointIndex] = (classificationFlags >> 2) & 1;
1273
+ }
1274
+ if (overlapFlags) {
1275
+ overlapFlags[targetPointIndex] = pointsFormatId <= 5 ? 0 : (classificationFlags >> 3) & 1;
1276
+ }
1277
+ if (returnNumbers) {
1278
+ returnNumbers[targetPointIndex] =
1279
+ pointsFormatId <= 5 ? returnFlags & 0x07 : returnFlags & 0x0f;
1280
+ }
1281
+ if (numberOfReturns) {
1282
+ numberOfReturns[targetPointIndex] =
1283
+ pointsFormatId <= 5 ? (returnFlags >> 3) & 0x07 : returnFlags >> 4;
1284
+ }
1285
+ if (scannerChannels) {
1286
+ scannerChannels[targetPointIndex] = pointsFormatId <= 5 ? 0 : (scanFlags >> 4) & 0x03;
1287
+ }
1288
+ if (scanDirectionFlags) {
1289
+ scanDirectionFlags[targetPointIndex] =
1290
+ pointsFormatId <= 5 ? (returnFlags >> 6) & 1 : (scanFlags >> 6) & 1;
1291
+ }
1292
+ if (edgeOfFlightLines) {
1293
+ edgeOfFlightLines[targetPointIndex] =
1294
+ pointsFormatId <= 5 ? (returnFlags >> 7) & 1 : (scanFlags >> 7) & 1;
1295
+ }
1296
+ if (waveforms) {
1297
+ const waveformOffset = getWaveformOffset(pointsFormatId);
1298
+ if (waveformOffset >= 0) {
1299
+ const waveformBytes = new Uint8Array(dataView.buffer, dataView.byteOffset + pointOffset + waveformOffset, 29);
1300
+ waveforms.set(waveformBytes, targetPointIndex * 29);
1301
+ }
1302
+ }
1303
+ if (extraBytes) {
1304
+ const extraByteOffset = getLAZPointDataRecordBaseLength(pointsFormatId);
1305
+ const extraByteCount = lasHeader.pointsStructSize - extraByteOffset;
1306
+ if (extraByteCount > 0) {
1307
+ extraBytes.set(new Uint8Array(dataView.buffer, dataView.byteOffset + pointOffset + extraByteOffset, extraByteCount), targetPointIndex * extraByteCount);
1308
+ }
1309
+ }
1310
+ if (typedExtraBytes) {
1311
+ populateTypedExtraBytesFromDataView(dataView, pointOffset, pointsFormatId, targetPointIndex, typedExtraBytes);
1312
+ }
1313
+ if (options.las?.compatibilityMode !== 'raw' && lasHeader.metadata?.compatibility) {
1314
+ populateLASCompatibilityPoint(dataView, pointOffset + getLAZPointDataRecordBaseLength(pointsFormatId), targetPointIndex, lasHeader.metadata.compatibility, target);
1315
+ }
1316
+ if (colorOffset >= 0 && target.colors) {
1317
+ const red = dataView.getUint16(pointOffset + colorOffset, true);
1318
+ const green = dataView.getUint16(pointOffset + colorOffset + 2, true);
1319
+ const blue = dataView.getUint16(pointOffset + colorOffset + 4, true);
1320
+ target.colors[targetPointIndex * 4] = twoByteColor ? red / 256 : red;
1321
+ target.colors[targetPointIndex * 4 + 1] = twoByteColor ? green / 256 : green;
1322
+ target.colors[targetPointIndex * 4 + 2] = twoByteColor ? blue / 256 : blue;
1323
+ target.colors[targetPointIndex * 4 + 3] = 255;
1324
+ }
1325
+ }
1326
+ }
1327
+ function readClassification(dataView, pointOffset, pointsFormatId) {
1328
+ return pointsFormatId <= 5
1329
+ ? dataView.getUint8(pointOffset + 15) & 0x1f
1330
+ : dataView.getUint8(pointOffset + 16);
1331
+ }
1332
+ function hasPointColor(pointsFormatId) {
1333
+ return getColorOffset(pointsFormatId) >= 0;
1334
+ }
1335
+ function getColorOffset(pointsFormatId) {
1336
+ switch (pointsFormatId) {
1337
+ case 2:
1338
+ return 20;
1339
+ case 3:
1340
+ return 28;
1341
+ case 5:
1342
+ return 28;
1343
+ case 7:
1344
+ case 8:
1345
+ case 10:
1346
+ return 30;
1347
+ default:
1348
+ return -1;
1349
+ }
1350
+ }
1351
+ /** Return the fixed LAS waveform packet reference offset for a point format. */
1352
+ function getWaveformOffset(pointsFormatId) {
1353
+ switch (pointsFormatId) {
1354
+ case 4:
1355
+ return 28;
1356
+ case 5:
1357
+ return 34;
1358
+ case 9:
1359
+ return 30;
1360
+ case 10:
1361
+ return 38;
1362
+ default:
1363
+ return -1;
1364
+ }
1365
+ }
1366
+ function getGpsTimeOffset(pointsFormatId) {
1367
+ return pointsFormatId === 1 ||
1368
+ pointsFormatId === 3 ||
1369
+ pointsFormatId === 4 ||
1370
+ pointsFormatId === 5
1371
+ ? 20
1372
+ : pointsFormatId >= 6 && pointsFormatId <= 10
1373
+ ? 22
1374
+ : -1;
1375
+ }
1376
+ function getNirOffset(pointsFormatId) {
1377
+ return pointsFormatId === 8 || pointsFormatId === 10 ? 36 : -1;
1378
+ }
1379
+ function detectTwoByteColors(dataView, lasHeader, sourcePointIndex, batchSize, colorDepth) {
1380
+ if (colorDepth === 8) {
1381
+ return false;
1382
+ }
1383
+ if (colorDepth === 16) {
1384
+ return true;
1385
+ }
1386
+ if (colorDepth !== 'auto') {
1387
+ return false;
1388
+ }
1389
+ const colorOffset = getColorOffset(lasHeader.pointsFormatId);
1390
+ if (colorOffset < 0) {
1391
+ return false;
1392
+ }
1393
+ for (let pointIndex = 0; pointIndex < batchSize; pointIndex++) {
1394
+ const pointOffset = lasHeader.pointsOffset + (sourcePointIndex + pointIndex) * lasHeader.pointsStructSize;
1395
+ if (dataView.getUint16(pointOffset + colorOffset, true) > 255 ||
1396
+ dataView.getUint16(pointOffset + colorOffset + 2, true) > 255 ||
1397
+ dataView.getUint16(pointOffset + colorOffset + 4, true) > 255) {
1398
+ return true;
1399
+ }
1400
+ }
1401
+ return false;
1402
+ }
1403
+ function getBatchSize(options) {
1404
+ const batchSize = options.core?.batchSize;
1405
+ return typeof batchSize === 'number' ? batchSize : DEFAULT_BATCH_SIZE;
1406
+ }
1407
+ function parseLASZipVLR(bytes, header) {
1408
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1409
+ let offset = header.headerSize || LAS_14_HEADER_LENGTH;
1410
+ const vlrCount = header.vlrCount || 0;
1411
+ for (let index = 0; index < vlrCount; index++) {
1412
+ if (offset + 54 > bytes.byteLength) {
1413
+ throw new NeedsMoreData('LASLoader: incomplete VLR header');
1414
+ }
1415
+ const userId = readNullTerminatedAscii(bytes, offset + 2, 16);
1416
+ const recordId = dataView.getUint16(offset + 18, true);
1417
+ const recordLength = dataView.getUint16(offset + 20, true);
1418
+ const dataOffset = offset + 54;
1419
+ if (dataOffset + recordLength > bytes.byteLength) {
1420
+ throw new NeedsMoreData('LASLoader: incomplete VLR data');
1421
+ }
1422
+ if (userId === LASZIP_USER_ID && recordId === LASZIP_RECORD_ID) {
1423
+ if (recordLength < 34) {
1424
+ throw new Error('LASLoader: malformed LASzip VLR');
1425
+ }
1426
+ const chunkSize = dataView.getUint32(dataOffset + 12, true);
1427
+ const itemCount = dataView.getUint16(dataOffset + 32, true);
1428
+ const itemDataByteLength = 34 + itemCount * 6;
1429
+ if (itemDataByteLength > recordLength) {
1430
+ throw new Error('LASLoader: malformed LASzip VLR item table');
1431
+ }
1432
+ let point14ItemVersion = null;
1433
+ let rgb14ItemVersion = null;
1434
+ let wavePacket13ItemVersion = null;
1435
+ let wavePacketItemVersion = null;
1436
+ let byte14ItemVersion = null;
1437
+ const items = [];
1438
+ for (let itemIndex = 0; itemIndex < itemCount; itemIndex++) {
1439
+ const itemOffset = dataOffset + 34 + itemIndex * 6;
1440
+ const itemType = dataView.getUint16(itemOffset, true);
1441
+ const itemSize = dataView.getUint16(itemOffset + 2, true);
1442
+ const itemVersion = dataView.getUint16(itemOffset + 4, true);
1443
+ items.push({ type: itemType, size: itemSize, version: itemVersion });
1444
+ if ([0, 6, 7, 8].includes(itemType)) {
1445
+ if (itemVersion !== 1 && itemVersion !== 2) {
1446
+ throw new Error(`LASLoader: unsupported legacy LASzip item type ${itemType} version ${itemVersion}`);
1447
+ }
1448
+ }
1449
+ else if (itemType === 9) {
1450
+ if (itemVersion !== 1) {
1451
+ throw new Error(`LASLoader: unsupported WavePacket13 item version ${itemVersion}`);
1452
+ }
1453
+ wavePacket13ItemVersion = itemVersion;
1454
+ }
1455
+ else if ([10, 11, 12, 14].includes(itemType)) {
1456
+ if (itemVersion !== 2 && itemVersion !== 3 && itemVersion !== 4) {
1457
+ throw new Error(`LASLoader: unsupported LAS 1.4 item type ${itemType} version ${itemVersion}`);
1458
+ }
1459
+ if (itemType === 10) {
1460
+ point14ItemVersion = itemVersion;
1461
+ }
1462
+ else if (itemType === 11 || itemType === 12) {
1463
+ rgb14ItemVersion = itemVersion;
1464
+ }
1465
+ else {
1466
+ byte14ItemVersion = itemVersion;
1467
+ }
1468
+ }
1469
+ else if (itemType === 13) {
1470
+ if (itemVersion !== 3 && itemVersion !== 4) {
1471
+ throw new Error(`LASLoader: unsupported WavePacket14 item version ${itemVersion}`);
1472
+ }
1473
+ wavePacketItemVersion = itemVersion;
1474
+ }
1475
+ }
1476
+ return {
1477
+ compressor: dataView.getUint16(dataOffset, true),
1478
+ coder: dataView.getUint16(dataOffset + 2, true),
1479
+ chunkSize,
1480
+ variableChunks: dataView.getUint16(dataOffset, true) !== 1 &&
1481
+ (chunkSize === 0 || chunkSize === VARIABLE_CHUNK_SIZE),
1482
+ items,
1483
+ point14ItemVersion,
1484
+ rgb14ItemVersion,
1485
+ wavePacket13ItemVersion,
1486
+ wavePacketItemVersion,
1487
+ byte14ItemVersion
1488
+ };
1489
+ }
1490
+ offset = dataOffset + recordLength;
1491
+ }
1492
+ throw new Error('LASLoader: compressed LAZ file does not contain a LASzip VLR');
1493
+ }
1494
+ function validateTypeScriptLAZSupport(header, laszip) {
1495
+ if (![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].includes(header.pointsFormatId)) {
1496
+ throw new Error(`LASLoader: TypeScript LAZ streaming only supports point formats 0-10; received ${header.pointsFormatId}`);
1497
+ }
1498
+ if (header.pointsFormatId <= 5 && laszip.compressor !== 1 && laszip.compressor !== 2) {
1499
+ throw new Error(`LASLoader: legacy TypeScript LAZ decoding requires LASzip compressor 1 or 2; received ${laszip.compressor}`);
1500
+ }
1501
+ if (header.pointsFormatId >= 6 && laszip.compressor !== 3) {
1502
+ throw new Error(`LASLoader: LAS 1.4 TypeScript LAZ decoding requires LASzip compressor 3; received ${laszip.compressor}`);
1503
+ }
1504
+ if (laszip.coder !== 0) {
1505
+ throw new Error(`LASLoader: TypeScript LAZ decoding requires LASzip arithmetic coder 0; received ${laszip.coder}`);
1506
+ }
1507
+ validateLASZipItemLayout(header, laszip.items);
1508
+ if (header.pointsFormatId >= 6 && !laszip.point14ItemVersion) {
1509
+ throw new Error(`LASLoader: point format ${header.pointsFormatId} requires a Point14 LASzip item`);
1510
+ }
1511
+ if ((header.pointsFormatId === 4 || header.pointsFormatId === 5) &&
1512
+ !laszip.wavePacket13ItemVersion) {
1513
+ throw new Error(`LASLoader: point format ${header.pointsFormatId} requires a WavePacket13 LASzip item`);
1514
+ }
1515
+ if ((header.pointsFormatId === 7 || header.pointsFormatId === 8 || header.pointsFormatId === 10) &&
1516
+ !laszip.rgb14ItemVersion) {
1517
+ throw new Error(`LASLoader: point format ${header.pointsFormatId} requires an RGB14 or RGBNIR14 LASzip item`);
1518
+ }
1519
+ if (header.pointsFormatId >= 6 &&
1520
+ header.pointsStructSize > getLAZPointDataRecordBaseLength(header.pointsFormatId) &&
1521
+ !laszip.byte14ItemVersion) {
1522
+ throw new Error(`LASLoader: point format ${header.pointsFormatId} with Extra Bytes requires a Byte14 LASzip item`);
1523
+ }
1524
+ if ((header.pointsFormatId === 9 || header.pointsFormatId === 10) &&
1525
+ !laszip.wavePacketItemVersion) {
1526
+ throw new Error(`LASLoader: point format ${header.pointsFormatId} requires a WavePacket14 LASzip item`);
1527
+ }
1528
+ }
1529
+ /** Validate the LASzip item sequence assumed by the point-format decoder. */
1530
+ function validateLASZipItemLayout(header, actualItems) {
1531
+ const pointDataRecordFormat = header.pointsFormatId;
1532
+ const baseLength = getLAZPointDataRecordBaseLength(pointDataRecordFormat);
1533
+ const extraByteCount = header.pointsStructSize - baseLength;
1534
+ if (extraByteCount < 0) {
1535
+ throw new Error(`LASLoader: point format ${pointDataRecordFormat} record length ${header.pointsStructSize} is smaller than ${baseLength}`);
1536
+ }
1537
+ const expectedItems = [];
1538
+ switch (pointDataRecordFormat) {
1539
+ case 0:
1540
+ expectedItems.push({ type: 6, size: 20 });
1541
+ break;
1542
+ case 1:
1543
+ expectedItems.push({ type: 6, size: 20 }, { type: 7, size: 8 });
1544
+ break;
1545
+ case 2:
1546
+ expectedItems.push({ type: 6, size: 20 }, { type: 8, size: 6 });
1547
+ break;
1548
+ case 3:
1549
+ expectedItems.push({ type: 6, size: 20 }, { type: 7, size: 8 }, { type: 8, size: 6 });
1550
+ break;
1551
+ case 4:
1552
+ expectedItems.push({ type: 6, size: 20 }, { type: 7, size: 8 }, { type: 9, size: 29 });
1553
+ break;
1554
+ case 5:
1555
+ expectedItems.push({ type: 6, size: 20 }, { type: 7, size: 8 }, { type: 8, size: 6 }, { type: 9, size: 29 });
1556
+ break;
1557
+ case 6:
1558
+ expectedItems.push({ type: 10, size: 30 });
1559
+ break;
1560
+ case 7:
1561
+ expectedItems.push({ type: 10, size: 30 }, { type: 11, size: 6 });
1562
+ break;
1563
+ case 8:
1564
+ expectedItems.push({ type: 10, size: 30 }, { type: 12, size: 8 });
1565
+ break;
1566
+ case 9:
1567
+ expectedItems.push({ type: 10, size: 30 }, { type: 13, size: 29 });
1568
+ break;
1569
+ case 10:
1570
+ expectedItems.push({ type: 10, size: 30 }, { type: 12, size: 8 }, { type: 13, size: 29 });
1571
+ break;
1572
+ default:
1573
+ return;
1574
+ }
1575
+ if (extraByteCount > 0) {
1576
+ expectedItems.push({ type: pointDataRecordFormat <= 5 ? 0 : 14, size: extraByteCount });
1577
+ }
1578
+ if (actualItems.length !== expectedItems.length) {
1579
+ throw new Error(`LASLoader: point format ${pointDataRecordFormat} has ${actualItems.length} LASzip items; expected ${expectedItems.length}`);
1580
+ }
1581
+ for (let itemIndex = 0; itemIndex < expectedItems.length; itemIndex++) {
1582
+ const actualItem = actualItems[itemIndex];
1583
+ const expectedItem = expectedItems[itemIndex];
1584
+ if (actualItem.type !== expectedItem.type) {
1585
+ throw new Error(`LASLoader: LASzip item ${itemIndex} has type ${actualItem.type}; expected ${expectedItem.type} for point format ${pointDataRecordFormat}`);
1586
+ }
1587
+ if (actualItem.size !== expectedItem.size) {
1588
+ throw new Error(`LASLoader: LASzip item ${itemIndex} has size ${actualItem.size}; expected ${expectedItem.size} for point format ${pointDataRecordFormat}`);
1589
+ }
1590
+ }
1591
+ }
1592
+ /** Read enough input to parse the LASzip VLR without assuming VLR alignment. */
1593
+ async function readLASZipVLRFromInput(initialPending, inputIterator, header) {
1594
+ let pending = initialPending;
1595
+ while (true) {
1596
+ try {
1597
+ return { pending, laszip: parseLASZipVLR(pending, header) };
1598
+ }
1599
+ catch (error) {
1600
+ if (!(error instanceof NeedsMoreData)) {
1601
+ throw error;
1602
+ }
1603
+ }
1604
+ const next = await inputIterator.next();
1605
+ if (next.done) {
1606
+ throw new NeedsMoreData('LASLoader: incomplete LASzip VLR');
1607
+ }
1608
+ pending = concatenateUint8Arrays(pending, toUint8Array(next.value));
1609
+ }
1610
+ }
1611
+ /** Collect a forward-only input once when metadata at EOF is required before decoding. */
1612
+ async function collectRemainingInputBytes(initialPending, inputIterator) {
1613
+ const chunks = [initialPending];
1614
+ let totalByteLength = initialPending.byteLength;
1615
+ while (true) {
1616
+ const next = await inputIterator.next();
1617
+ if (next.done) {
1618
+ break;
1619
+ }
1620
+ const chunk = toUint8Array(next.value);
1621
+ chunks.push(chunk);
1622
+ totalByteLength += chunk.byteLength;
1623
+ }
1624
+ if (chunks.length === 1) {
1625
+ return initialPending;
1626
+ }
1627
+ const bytes = new Uint8Array(totalByteLength);
1628
+ let byteOffset = 0;
1629
+ for (const chunk of chunks) {
1630
+ bytes.set(chunk, byteOffset);
1631
+ byteOffset += chunk.byteLength;
1632
+ }
1633
+ return bytes;
1634
+ }
1635
+ /** Validate the trailing chunk table after progressively decoding a fixed-chunk LAZ file. */
1636
+ async function validateStreamedFixedLAZChunkTable(reader, inputIterator, header, laszip, chunkTableOffset, decodedChunkByteLengths, options) {
1637
+ if (options.las?.recoverMissingChunkTable && chunkTableOffset === header.pointsOffset) {
1638
+ if (laszip.variableChunks)
1639
+ throw new Error('LASLoader: cannot recover a missing variable-size LAZ chunk table');
1640
+ while (true) {
1641
+ if (reader.getAvailableByteLength())
1642
+ throw new Error('LASLoader: unexpected bytes after recovered LAZ chunks');
1643
+ const next = await inputIterator.next();
1644
+ if (next.done)
1645
+ return;
1646
+ reader.write(next.value);
1647
+ }
1648
+ }
1649
+ const decodedPointDataByteLength = decodedChunkByteLengths.reduce((total, byteLength) => total + byteLength, 0);
1650
+ const decodedPointDataEnd = header.pointsOffset + LAZ_CHUNK_TABLE_POINTER_LENGTH + decodedPointDataByteLength;
1651
+ const effectiveChunkTableOffset = chunkTableOffset === -1 ? decodedPointDataEnd : chunkTableOffset;
1652
+ if (!Number.isSafeInteger(effectiveChunkTableOffset) ||
1653
+ effectiveChunkTableOffset < decodedPointDataEnd) {
1654
+ throw new Error('LASLoader: LAZ chunk table byte lengths overlap the chunk table');
1655
+ }
1656
+ const gapByteLength = effectiveChunkTableOffset - decodedPointDataEnd;
1657
+ await readUntilAvailable(reader, inputIterator, gapByteLength, 'LASLoader: incomplete LAZ chunk table');
1658
+ reader.skip(gapByteLength);
1659
+ const chunks = await readStreamedLAZChunkTable(reader, inputIterator, header, laszip);
1660
+ if (chunks.length !== decodedChunkByteLengths.length) {
1661
+ throw new Error(`LASLoader: LAZ chunk table contains ${chunks.length} chunks; decoded ${decodedChunkByteLengths.length}`);
1662
+ }
1663
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
1664
+ if (chunks[chunkIndex].byteLength !== decodedChunkByteLengths[chunkIndex]) {
1665
+ throw new Error(`LASLoader: LAZ chunk ${chunkIndex} has ${chunks[chunkIndex].byteLength} table bytes; decoded ${decodedChunkByteLengths[chunkIndex]}`);
1666
+ }
1667
+ }
1668
+ if (chunkTableOffset === -1) {
1669
+ await validateStreamedLAZChunkTableFooter(reader, inputIterator, effectiveChunkTableOffset);
1670
+ }
1671
+ }
1672
+ /** Validate the trailing table pointer emitted by non-seekable LASzip writers. */
1673
+ async function validateStreamedLAZChunkTableFooter(reader, inputIterator, expectedChunkTableOffset) {
1674
+ const footer = new Uint8Array(LAZ_CHUNK_TABLE_POINTER_LENGTH);
1675
+ let byteLength = 0;
1676
+ while (true) {
1677
+ const availableByteLength = reader.getAvailableByteLength();
1678
+ if (availableByteLength > 0) {
1679
+ const bytes = reader.readBytes(Math.min(availableByteLength, 64 * 1024));
1680
+ appendTrailingBytes(footer, bytes);
1681
+ byteLength += bytes.byteLength;
1682
+ continue;
1683
+ }
1684
+ const next = await inputIterator.next();
1685
+ if (next.done) {
1686
+ break;
1687
+ }
1688
+ reader.write(next.value);
1689
+ }
1690
+ if (byteLength < LAZ_CHUNK_TABLE_POINTER_LENGTH) {
1691
+ throw new NeedsMoreData('LASLoader: incomplete non-seekable LAZ chunk-table footer');
1692
+ }
1693
+ const footerOffset = readUint64(new DataView(footer.buffer), 0);
1694
+ if (!Number.isSafeInteger(footerOffset) || footerOffset !== expectedChunkTableOffset) {
1695
+ throw new Error(`LASLoader: non-seekable LAZ footer points to ${footerOffset}; expected ${expectedChunkTableOffset}`);
1696
+ }
1697
+ }
1698
+ /** Retain only the final bytes from a forward-only input stream. */
1699
+ function appendTrailingBytes(target, bytes) {
1700
+ if (bytes.byteLength >= target.byteLength) {
1701
+ target.set(bytes.subarray(bytes.byteLength - target.byteLength));
1702
+ return;
1703
+ }
1704
+ target.copyWithin(0, bytes.byteLength);
1705
+ target.set(bytes, target.byteLength - bytes.byteLength);
1706
+ }
1707
+ /** Read only as much trailing input as the compressed chunk table requires. */
1708
+ async function readStreamedLAZChunkTable(reader, inputIterator, header, laszip) {
1709
+ let candidateByteLength = 64;
1710
+ while (true) {
1711
+ const availableByteLength = reader.getAvailableByteLength();
1712
+ if (availableByteLength >= 8) {
1713
+ const checkpoint = reader.checkpoint();
1714
+ try {
1715
+ const bytes = reader.readBytes(Math.min(availableByteLength, candidateByteLength));
1716
+ const chunks = decodeAndValidateLAZChunkTable(bytes, header, laszip);
1717
+ reader.restore(checkpoint);
1718
+ return chunks;
1719
+ }
1720
+ catch (error) {
1721
+ reader.restore(checkpoint);
1722
+ if (!(error instanceof NeedsMoreData)) {
1723
+ throw error;
1724
+ }
1725
+ candidateByteLength *= 2;
1726
+ if (candidateByteLength <= availableByteLength) {
1727
+ continue;
1728
+ }
1729
+ }
1730
+ }
1731
+ const next = await inputIterator.next();
1732
+ if (next.done) {
1733
+ throw new NeedsMoreData('LASLoader: incomplete LAZ chunk table');
1734
+ }
1735
+ reader.write(next.value);
1736
+ }
1737
+ }
1738
+ async function readUntilAvailable(reader, inputIterator, byteLength, errorMessage) {
1739
+ while (!reader.hasAvailableBytes(byteLength)) {
1740
+ const next = await inputIterator.next();
1741
+ if (next.done) {
1742
+ throw new NeedsMoreData(errorMessage);
1743
+ }
1744
+ reader.write(next.value);
1745
+ }
1746
+ }
1747
+ async function readLAZChunkByteLengthFromReader(reader, inputIterator, metadata) {
1748
+ while (true) {
1749
+ const checkpoint = reader.checkpoint();
1750
+ let chunkByteLength;
1751
+ try {
1752
+ chunkByteLength = tryReadLAZChunkByteLengthFromReader(reader, metadata);
1753
+ }
1754
+ finally {
1755
+ reader.restore(checkpoint);
1756
+ }
1757
+ if (chunkByteLength !== null) {
1758
+ await readUntilAvailable(reader, inputIterator, chunkByteLength, 'LASLoader: truncated LAZ chunk');
1759
+ return chunkByteLength;
1760
+ }
1761
+ const next = await inputIterator.next();
1762
+ if (next.done) {
1763
+ throw new NeedsMoreData('LASLoader: truncated LAZ chunk');
1764
+ }
1765
+ reader.write(next.value);
1766
+ }
1767
+ }
1768
+ function tryReadLAZChunkByteLengthFromReader(reader, metadata) {
1769
+ if (metadata.pointDataRecordFormat < 6 || metadata.pointDataRecordFormat > 10) {
1770
+ return null;
1771
+ }
1772
+ const extraByteCount = metadata.pointDataRecordLength -
1773
+ getLAZPointDataRecordBaseLength(metadata.pointDataRecordFormat);
1774
+ if (extraByteCount < 0) {
1775
+ throw new Error(`Invalid point record length ${metadata.pointDataRecordLength}`);
1776
+ }
1777
+ const sizeHeaderCount = getLAZChunkSizeHeaderBaseCount(metadata.pointDataRecordFormat) + extraByteCount;
1778
+ const sizeHeaderOffset = metadata.pointDataRecordLength + 4;
1779
+ const sizeHeaderByteLength = sizeHeaderCount * 4;
1780
+ const minimumByteLength = sizeHeaderOffset + sizeHeaderByteLength;
1781
+ if (!reader.hasAvailableBytes(minimumByteLength)) {
1782
+ return null;
1783
+ }
1784
+ reader.skip(sizeHeaderOffset);
1785
+ let chunkByteLength = minimumByteLength;
1786
+ for (let index = 0; index < sizeHeaderCount; index++) {
1787
+ chunkByteLength += reader.readUint32LE();
1788
+ }
1789
+ return chunkByteLength;
1790
+ }
1791
+ function getLAZPointDataRecordBaseLength(pointDataRecordFormat) {
1792
+ switch (pointDataRecordFormat) {
1793
+ case 0:
1794
+ return 20;
1795
+ case 1:
1796
+ return 28;
1797
+ case 2:
1798
+ return 26;
1799
+ case 3:
1800
+ return 34;
1801
+ case 4:
1802
+ return 57;
1803
+ case 5:
1804
+ return 63;
1805
+ case 6:
1806
+ return 30;
1807
+ case 7:
1808
+ return 36;
1809
+ case 8:
1810
+ return 38;
1811
+ case 9:
1812
+ return 59;
1813
+ case 10:
1814
+ return 67;
1815
+ default:
1816
+ throw new Error(`Unsupported LAS point format ${pointDataRecordFormat}`);
1817
+ }
1818
+ }
1819
+ function getLAZChunkSizeHeaderBaseCount(pointDataRecordFormat) {
1820
+ switch (pointDataRecordFormat) {
1821
+ case 6:
1822
+ return 9;
1823
+ case 7:
1824
+ return 10;
1825
+ case 8:
1826
+ return 11;
1827
+ case 9:
1828
+ return 10;
1829
+ case 10:
1830
+ return 12;
1831
+ default:
1832
+ throw new Error(`Unsupported LAS 1.4 point format ${pointDataRecordFormat}`);
1833
+ }
1834
+ }
1835
+ /** Describe a pointwise stream as one chunk, or read the compressed chunk table. */
1836
+ function readLAZFileChunks(bytes, header, laszip, options) {
1837
+ if (laszip.compressor === 1) {
1838
+ return [{ pointCount: header.pointsCount, byteLength: bytes.byteLength - header.pointsOffset }];
1839
+ }
1840
+ const chunkTableOffset = readLAZChunkTableOffset(new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), header.pointsOffset);
1841
+ return readLAZChunkTable(bytes, header, laszip, chunkTableOffset, options);
1842
+ }
1843
+ function readLAZChunkTable(bytes, header, laszip, chunkTableOffset, options) {
1844
+ if (options.las?.recoverMissingChunkTable && chunkTableOffset === header.pointsOffset) {
1845
+ return recoverFixedLAZChunkTable(bytes, header, laszip);
1846
+ }
1847
+ if (!Number.isSafeInteger(chunkTableOffset) ||
1848
+ chunkTableOffset < 0 ||
1849
+ chunkTableOffset + 8 > bytes.byteLength) {
1850
+ throw new NeedsMoreData('LASLoader: incomplete LAZ chunk table');
1851
+ }
1852
+ const chunks = decodeAndValidateLAZChunkTable(bytes.subarray(chunkTableOffset), header, laszip);
1853
+ const decodedByteLength = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
1854
+ if (header.pointsOffset + LAZ_CHUNK_TABLE_POINTER_LENGTH + decodedByteLength > chunkTableOffset) {
1855
+ throw new Error('LASLoader: LAZ chunk table byte lengths overlap the chunk table');
1856
+ }
1857
+ return chunks;
1858
+ }
1859
+ /** Recover fixed chunks only when LASzip explicitly marks an interrupted writer. */
1860
+ function recoverFixedLAZChunkTable(bytes, header, laszip) {
1861
+ if (laszip.variableChunks || !laszip.chunkSize) {
1862
+ throw new Error('LASLoader: cannot recover a missing variable-size LAZ chunk table');
1863
+ }
1864
+ const chunks = [];
1865
+ let byteOffset = header.pointsOffset + LAZ_CHUNK_TABLE_POINTER_LENGTH;
1866
+ let pointOffset = 0;
1867
+ while (pointOffset < header.pointsCount) {
1868
+ const pointCount = Math.min(laszip.chunkSize, header.pointsCount - pointOffset);
1869
+ const metadata = createLAZChunkMetadata(header, laszip, pointCount);
1870
+ const compressed = bytes.subarray(byteOffset);
1871
+ let byteLength;
1872
+ if (header.pointsFormatId <= 5) {
1873
+ const cursor = createLAZChunkDecoderCursor(compressed, metadata);
1874
+ const scratch = new Uint8Array(Math.min(pointCount, 256) * header.pointsStructSize);
1875
+ while (cursor.remainingPointCount) {
1876
+ cursor.decodeInto(scratch, 0, Math.min(cursor.remainingPointCount, 256));
1877
+ }
1878
+ byteLength = cursor.compressedByteOffset;
1879
+ }
1880
+ else {
1881
+ validateRecoveredLayeredPointCount(compressed, metadata);
1882
+ byteLength = getLAZChunkDeclaredByteLength(compressed, metadata);
1883
+ }
1884
+ if (!byteLength || byteOffset + byteLength > bytes.byteLength) {
1885
+ throw new NeedsMoreData('LASLoader: truncated LAZ chunk during table recovery');
1886
+ }
1887
+ chunks.push({ pointCount, byteLength });
1888
+ byteOffset += byteLength;
1889
+ pointOffset += pointCount;
1890
+ }
1891
+ if (byteOffset !== bytes.byteLength) {
1892
+ throw new Error('LASLoader: unexpected bytes after recovered LAZ chunks');
1893
+ }
1894
+ return chunks;
1895
+ }
1896
+ /** Require recovered layered chunks to agree with the inferred header point count. */
1897
+ function validateRecoveredLayeredPointCount(compressed, metadata) {
1898
+ if (compressed.byteLength < metadata.pointDataRecordLength + 4) {
1899
+ throw new NeedsMoreData('LASLoader: truncated recovered layered LAZ chunk header');
1900
+ }
1901
+ const view = new DataView(compressed.buffer, compressed.byteOffset, compressed.byteLength);
1902
+ const pointCount = view.getUint32(metadata.pointDataRecordLength, true);
1903
+ if (pointCount !== metadata.pointCount) {
1904
+ throw new Error(`LASLoader: recovered layered LAZ chunk contains ${pointCount} points; expected ${metadata.pointCount}`);
1905
+ }
1906
+ }
1907
+ /** Decode and validate a LAZ chunk table whose first byte is the table version. */
1908
+ function decodeAndValidateLAZChunkTable(chunkTableBytes, header, laszip) {
1909
+ if (chunkTableBytes.byteLength < 8) {
1910
+ throw new NeedsMoreData('LASLoader: incomplete LAZ chunk table');
1911
+ }
1912
+ const dataView = new DataView(chunkTableBytes.buffer, chunkTableBytes.byteOffset, chunkTableBytes.byteLength);
1913
+ const version = dataView.getUint32(0, true);
1914
+ const chunkCount = dataView.getUint32(4, true);
1915
+ if (version !== 0) {
1916
+ throw new Error(`LASLoader: unsupported LAZ chunk table version ${version}`);
1917
+ }
1918
+ if (chunkCount === 0) {
1919
+ if (header.pointsCount !== 0) {
1920
+ throw new Error('LASLoader: missing LAZ chunk table');
1921
+ }
1922
+ return [];
1923
+ }
1924
+ const chunks = decodeLAZChunkTable(chunkTableBytes.subarray(8), {
1925
+ chunkCount,
1926
+ pointCount: header.pointsCount,
1927
+ chunkSize: laszip.chunkSize,
1928
+ variable: laszip.variableChunks
1929
+ });
1930
+ let decodedPointCount = 0;
1931
+ for (const chunk of chunks) {
1932
+ if (chunk.pointCount === 0 || chunk.byteLength === 0) {
1933
+ throw new Error('LASLoader: invalid empty LAZ chunk-table entry');
1934
+ }
1935
+ decodedPointCount += chunk.pointCount;
1936
+ }
1937
+ if (decodedPointCount !== header.pointsCount) {
1938
+ throw new Error(`LASLoader: LAZ chunk table contains ${decodedPointCount} points; expected ${header.pointsCount}`);
1939
+ }
1940
+ return chunks;
1941
+ }
1942
+ function createRawPointBatchState(batchSize, pointRecordLength, stats) {
1943
+ if (stats) {
1944
+ stats.rawBatchAllocations++;
1945
+ }
1946
+ return {
1947
+ rawBatch: new Uint8Array(batchSize * pointRecordLength),
1948
+ batchPointCount: 0,
1949
+ totalRead: 0,
1950
+ rawBatchAllocations: 1,
1951
+ stats
1952
+ };
1953
+ }
1954
+ function createPointDataBatchState(batchSize, header, options) {
1955
+ const PositionsType = options.las?.fp64 ? Float64Array : Float32Array;
1956
+ const positions = new PositionsType(batchSize * 3);
1957
+ const selection = getLASColumnSelection(options);
1958
+ const useRawColors = header.hasColor &&
1959
+ selection.color &&
1960
+ (options.las?.colorDepth === 16 || options.las?.colorDepth === 'auto');
1961
+ const colors = header.hasColor && selection.color && !useRawColors ? new Uint8Array(batchSize * 4) : null;
1962
+ const rawColors = useRawColors ? new Uint16Array(batchSize * 3) : null;
1963
+ const intensities = selection.intensity ? new Uint16Array(batchSize) : null;
1964
+ const classifications = selection.classification ? new Uint8Array(batchSize) : null;
1965
+ const syntheticFlags = selection.synthetic ? new Uint8Array(batchSize) : null;
1966
+ const keyPointFlags = selection.keyPoint ? new Uint8Array(batchSize) : null;
1967
+ const withheldFlags = selection.withheld ? new Uint8Array(batchSize) : null;
1968
+ const overlapFlags = selection.overlap ? new Uint8Array(batchSize) : null;
1969
+ const gpsTimes = selection.gpsTime && getGpsTimeOffset(header.pointsFormatId) >= 0
1970
+ ? new Float64Array(batchSize)
1971
+ : null;
1972
+ const nir = selection.nir &&
1973
+ (getNirOffset(header.pointsFormatId) >= 0 ||
1974
+ (options.las?.compatibilityMode !== 'raw' &&
1975
+ header.metadata?.compatibility?.nirByteOffset !== undefined))
1976
+ ? new Uint16Array(batchSize)
1977
+ : null;
1978
+ const scanAngles = selection.scanAngle ? new Int16Array(batchSize) : null;
1979
+ const userData = selection.userData ? new Uint8Array(batchSize) : null;
1980
+ const pointSourceIds = selection.pointSourceId ? new Uint16Array(batchSize) : null;
1981
+ const returnNumbers = selection.returnNumber ? new Uint8Array(batchSize) : null;
1982
+ const numberOfReturns = selection.numberOfReturns ? new Uint8Array(batchSize) : null;
1983
+ const scannerChannels = selection.scannerChannel ? new Uint8Array(batchSize) : null;
1984
+ const scanDirectionFlags = selection.scanDirectionFlag ? new Uint8Array(batchSize) : null;
1985
+ const edgeOfFlightLines = selection.edgeOfFlightLine ? new Uint8Array(batchSize) : null;
1986
+ const waveforms = selection.waveform && getWaveformOffset(header.pointsFormatId) >= 0
1987
+ ? new Uint8Array(batchSize * 29)
1988
+ : null;
1989
+ const extraByteCount = Math.max(0, header.pointsStructSize - getLAZPointDataRecordBaseLength(header.pointsFormatId));
1990
+ const extraBytes = selection.extraBytes && options.las?.extraBytes !== 'typed' && extraByteCount
1991
+ ? new Uint8Array(batchSize * extraByteCount)
1992
+ : null;
1993
+ const typedExtraBytes = selection.extraBytes && options.las?.extraBytes === 'typed'
1994
+ ? createTypedExtraBytesAttributes(batchSize, header)
1995
+ : null;
1996
+ const typedExtraBytesSource = typedExtraBytes?.length && extraByteCount ? new Uint8Array(batchSize * extraByteCount) : null;
1997
+ const reconstructColumns = selection.classification ||
1998
+ selection.returnNumber ||
1999
+ selection.numberOfReturns ||
2000
+ selection.scanAngle ||
2001
+ selection.overlap ||
2002
+ selection.scannerChannel ||
2003
+ selection.nir;
2004
+ const compatibilityLayout = options.las?.compatibilityMode === 'raw' || !reconstructColumns
2005
+ ? null
2006
+ : header.metadata?.compatibility || null;
2007
+ const compatibilityExtraBytesSource = compatibilityLayout && !extraBytes && !typedExtraBytesSource
2008
+ ? new Uint8Array(batchSize * extraByteCount)
2009
+ : null;
2010
+ return {
2011
+ batchCapacity: batchSize,
2012
+ positions,
2013
+ colors,
2014
+ rawColors,
2015
+ intensities,
2016
+ classifications,
2017
+ syntheticFlags,
2018
+ keyPointFlags,
2019
+ withheldFlags,
2020
+ overlapFlags,
2021
+ gpsTimes,
2022
+ nir,
2023
+ scanAngles,
2024
+ userData,
2025
+ pointSourceIds,
2026
+ returnNumbers,
2027
+ numberOfReturns,
2028
+ scannerChannels,
2029
+ scanDirectionFlags,
2030
+ edgeOfFlightLines,
2031
+ waveforms,
2032
+ extraBytes,
2033
+ typedExtraBytes,
2034
+ typedExtraBytesSource,
2035
+ compatibilityLayout,
2036
+ compatibilityExtraBytesSource,
2037
+ target: {
2038
+ positions,
2039
+ intensities,
2040
+ classifications,
2041
+ syntheticFlags,
2042
+ keyPointFlags,
2043
+ withheldFlags,
2044
+ overlapFlags,
2045
+ gpsTimes,
2046
+ nir,
2047
+ scanAngles,
2048
+ userData,
2049
+ pointSourceIds,
2050
+ returnNumbers,
2051
+ numberOfReturns,
2052
+ scannerChannels,
2053
+ scanDirectionFlags,
2054
+ edgeOfFlightLines,
2055
+ waveforms,
2056
+ extraBytes: extraBytes || typedExtraBytesSource || compatibilityExtraBytesSource,
2057
+ colors,
2058
+ rawColors,
2059
+ pointOffset: 0,
2060
+ scale: header.scale,
2061
+ offset: header.offset
2062
+ },
2063
+ batchPointCount: 0,
2064
+ totalRead: 0
2065
+ };
2066
+ }
2067
+ /** Create typed Extra Bytes output buffers from the descriptors in a LAS header. */
2068
+ function createTypedExtraBytesAttributes(batchSize, header) {
2069
+ const extraByteCount = header.pointsStructSize - getLAZPointDataRecordBaseLength(header.pointsFormatId);
2070
+ return createLASTypedExtraBytesAttributes(batchSize, header.metadata?.extraBytes || [], extraByteCount);
2071
+ }
2072
+ /** Return the byte width of one scalar Extra Bytes value. */
2073
+ function getExtraBytesScalarByteLength(dataType) {
2074
+ if (dataType <= 2)
2075
+ return 1;
2076
+ if (dataType <= 4)
2077
+ return 2;
2078
+ if (dataType <= 6 || dataType === 9)
2079
+ return 4;
2080
+ if (dataType === 7 || dataType === 8 || dataType === 10)
2081
+ return 8;
2082
+ return 0;
2083
+ }
2084
+ /** Decode typed Extra Bytes directly from uncompressed LAS point records. */
2085
+ function populateTypedExtraBytesFromDataView(dataView, pointOffset, pointDataRecordFormat, targetPointIndex, attributes) {
2086
+ const extraByteBaseOffset = pointOffset + getLAZPointDataRecordBaseLength(pointDataRecordFormat);
2087
+ for (const attribute of attributes) {
2088
+ const targetOffset = targetPointIndex * attribute.size;
2089
+ const sourceOffset = extraByteBaseOffset + attribute.byteOffset;
2090
+ for (let componentIndex = 0; componentIndex < attribute.size; componentIndex++) {
2091
+ populateLASTypedExtraBytesComponent(dataView, sourceOffset + componentIndex * getExtraBytesScalarByteLength(attribute.scalarDataType), attribute, targetOffset + componentIndex, componentIndex);
2092
+ }
2093
+ }
2094
+ }
2095
+ /** Project packed direct-decoder Extra Bytes into descriptor-defined typed columns. */
2096
+ function populateTypedExtraBytesFromPacked(packedExtraBytes, extraByteCount, pointOffset, pointCount, attributes) {
2097
+ const dataView = new DataView(packedExtraBytes.buffer, packedExtraBytes.byteOffset, packedExtraBytes.byteLength);
2098
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex++) {
2099
+ const targetPointIndex = pointOffset + pointIndex;
2100
+ const sourceOffset = targetPointIndex * extraByteCount;
2101
+ for (const attribute of attributes) {
2102
+ const targetOffset = targetPointIndex * attribute.size;
2103
+ const scalarByteLength = getExtraBytesScalarByteLength(attribute.scalarDataType);
2104
+ for (let componentIndex = 0; componentIndex < attribute.size; componentIndex++) {
2105
+ populateLASTypedExtraBytesComponent(dataView, sourceOffset + attribute.byteOffset + componentIndex * scalarByteLength, attribute, targetOffset + componentIndex, componentIndex);
2106
+ }
2107
+ }
2108
+ }
2109
+ }
2110
+ /** Populate typed Extra Bytes produced by the direct LAZ target for newly decoded points. */
2111
+ function populateDecodedTypedExtraBytes(state, pointOffset, pointCount) {
2112
+ const compatibilityBytes = state.extraBytes || state.typedExtraBytesSource || state.compatibilityExtraBytesSource;
2113
+ if (state.compatibilityLayout && compatibilityBytes && pointCount) {
2114
+ const view = new DataView(compatibilityBytes.buffer, compatibilityBytes.byteOffset, compatibilityBytes.byteLength);
2115
+ const extraByteCount = compatibilityBytes.length / state.batchCapacity;
2116
+ for (let pointIndex = pointOffset; pointIndex < pointOffset + pointCount; pointIndex++) {
2117
+ populateLASCompatibilityPoint(view, pointIndex * extraByteCount, pointIndex, state.compatibilityLayout, state);
2118
+ }
2119
+ }
2120
+ if (!state.typedExtraBytesSource || !state.typedExtraBytes?.length || pointCount === 0) {
2121
+ return;
2122
+ }
2123
+ populateTypedExtraBytesFromPacked(state.typedExtraBytesSource, state.typedExtraBytesSource.length / state.batchCapacity, pointOffset, pointCount, state.typedExtraBytes);
2124
+ }
2125
+ function getLAZStreamingDecodeStats(options) {
2126
+ return options.las
2127
+ ?.lazStreamingStats;
2128
+ }
2129
+ function recordReadBytesStats(reader, byteLength, stats) {
2130
+ if (!stats) {
2131
+ return;
2132
+ }
2133
+ const bufferOffsets = reader.findBufferOffsets(byteLength);
2134
+ if (bufferOffsets && bufferOffsets.length > 1) {
2135
+ stats.copiedBytes += byteLength;
2136
+ stats.chunkConcatenations++;
2137
+ }
2138
+ }
2139
+ function recordDecodedChunkAllocation(byteLength, stats) {
2140
+ if (stats) {
2141
+ stats.copiedBytes += byteLength;
2142
+ stats.decodedChunkAllocations++;
2143
+ }
2144
+ }
2145
+ function* appendDecodedLAZChunk(compressedChunk, metadata, header, state) {
2146
+ const pointRecordLength = header.pointsStructSize;
2147
+ const decoder = createLAZChunkDecoderCursor(compressedChunk, metadata);
2148
+ while (decoder.remainingPointCount > 0) {
2149
+ const batchCapacity = state.rawBatch.byteLength / pointRecordLength;
2150
+ const batchRemainingPointCount = batchCapacity - state.batchPointCount;
2151
+ const pointsDecoded = decoder.decodeInto(state.rawBatch, state.batchPointCount * pointRecordLength, batchRemainingPointCount);
2152
+ state.batchPointCount += pointsDecoded;
2153
+ if (state.batchPointCount === batchCapacity) {
2154
+ const batch = flushRawPointBatch(header, state);
2155
+ if (batch) {
2156
+ yield batch;
2157
+ }
2158
+ }
2159
+ }
2160
+ }
2161
+ function* appendRawPointChunk(rawChunk, header, state) {
2162
+ const pointRecordLength = header.pointsStructSize;
2163
+ const chunkPointCount = rawChunk.byteLength / pointRecordLength;
2164
+ for (let chunkPointIndex = 0; chunkPointIndex < chunkPointCount; chunkPointIndex++) {
2165
+ const sourceOffset = chunkPointIndex * pointRecordLength;
2166
+ const targetOffset = state.batchPointCount * pointRecordLength;
2167
+ state.rawBatch.set(rawChunk.subarray(sourceOffset, sourceOffset + pointRecordLength), targetOffset);
2168
+ state.batchPointCount++;
2169
+ if (state.batchPointCount === state.rawBatch.byteLength / pointRecordLength) {
2170
+ const batch = flushRawPointBatch(header, state);
2171
+ if (batch) {
2172
+ yield batch;
2173
+ }
2174
+ }
2175
+ }
2176
+ }
2177
+ function flushRawPointBatch(header, state) {
2178
+ if (state.batchPointCount === 0) {
2179
+ return null;
2180
+ }
2181
+ const byteLength = state.batchPointCount * header.pointsStructSize;
2182
+ const fullBatch = byteLength === state.rawBatch.byteLength;
2183
+ const arrayBuffer = fullBatch
2184
+ ? state.rawBatch.buffer
2185
+ : state.rawBatch.buffer.slice(0, byteLength);
2186
+ state.totalRead += state.batchPointCount;
2187
+ const batchHeader = {
2188
+ ...header,
2189
+ pointsOffset: 0,
2190
+ pointsCount: state.batchPointCount,
2191
+ totalRead: state.totalRead
2192
+ };
2193
+ state.batchPointCount = 0;
2194
+ if (fullBatch) {
2195
+ state.rawBatch = new Uint8Array(state.rawBatch.byteLength);
2196
+ state.rawBatchAllocations++;
2197
+ if (state.stats) {
2198
+ state.stats.rawBatchAllocations++;
2199
+ }
2200
+ }
2201
+ return { arrayBuffer, header: batchHeader };
2202
+ }
2203
+ function flushPointDataBatch(header, state, options) {
2204
+ if (state.batchPointCount === 0) {
2205
+ return null;
2206
+ }
2207
+ state.totalRead += state.batchPointCount;
2208
+ const batchHeader = {
2209
+ ...header,
2210
+ pointsOffset: 0,
2211
+ pointsCount: state.batchPointCount,
2212
+ totalRead: state.totalRead
2213
+ };
2214
+ const batchPointCount = state.batchPointCount;
2215
+ const fullBatch = batchPointCount === state.batchCapacity;
2216
+ const positions = fullBatch ? state.positions : state.positions.subarray(0, batchPointCount * 3);
2217
+ const intensities = state.intensities
2218
+ ? fullBatch
2219
+ ? state.intensities
2220
+ : state.intensities.subarray(0, batchPointCount)
2221
+ : null;
2222
+ const classifications = state.classifications
2223
+ ? fullBatch
2224
+ ? state.classifications
2225
+ : state.classifications.subarray(0, batchPointCount)
2226
+ : null;
2227
+ const syntheticFlags = state.syntheticFlags
2228
+ ? fullBatch
2229
+ ? state.syntheticFlags
2230
+ : state.syntheticFlags.subarray(0, batchPointCount)
2231
+ : null;
2232
+ const keyPointFlags = state.keyPointFlags
2233
+ ? fullBatch
2234
+ ? state.keyPointFlags
2235
+ : state.keyPointFlags.subarray(0, batchPointCount)
2236
+ : null;
2237
+ const withheldFlags = state.withheldFlags
2238
+ ? fullBatch
2239
+ ? state.withheldFlags
2240
+ : state.withheldFlags.subarray(0, batchPointCount)
2241
+ : null;
2242
+ const overlapFlags = state.overlapFlags
2243
+ ? fullBatch
2244
+ ? state.overlapFlags
2245
+ : state.overlapFlags.subarray(0, batchPointCount)
2246
+ : null;
2247
+ const colors = state.colors
2248
+ ? fullBatch
2249
+ ? state.colors
2250
+ : state.colors.subarray(0, batchPointCount * 4)
2251
+ : state.rawColors
2252
+ ? convertRawColorsToUint8(state.rawColors, batchPointCount, options)
2253
+ : null;
2254
+ const gpsTimes = state.gpsTimes
2255
+ ? fullBatch
2256
+ ? state.gpsTimes
2257
+ : state.gpsTimes.subarray(0, batchPointCount)
2258
+ : null;
2259
+ const nir = state.nir ? (fullBatch ? state.nir : state.nir.subarray(0, batchPointCount)) : null;
2260
+ const scanAngles = state.scanAngles
2261
+ ? fullBatch
2262
+ ? state.scanAngles
2263
+ : state.scanAngles.subarray(0, batchPointCount)
2264
+ : null;
2265
+ const userData = state.userData
2266
+ ? fullBatch
2267
+ ? state.userData
2268
+ : state.userData.subarray(0, batchPointCount)
2269
+ : null;
2270
+ const pointSourceIds = state.pointSourceIds
2271
+ ? fullBatch
2272
+ ? state.pointSourceIds
2273
+ : state.pointSourceIds.subarray(0, batchPointCount)
2274
+ : null;
2275
+ const returnNumbers = state.returnNumbers
2276
+ ? fullBatch
2277
+ ? state.returnNumbers
2278
+ : state.returnNumbers.subarray(0, batchPointCount)
2279
+ : null;
2280
+ const numberOfReturns = state.numberOfReturns
2281
+ ? fullBatch
2282
+ ? state.numberOfReturns
2283
+ : state.numberOfReturns.subarray(0, batchPointCount)
2284
+ : null;
2285
+ const scannerChannels = state.scannerChannels
2286
+ ? fullBatch
2287
+ ? state.scannerChannels
2288
+ : state.scannerChannels.subarray(0, batchPointCount)
2289
+ : null;
2290
+ const scanDirectionFlags = state.scanDirectionFlags
2291
+ ? fullBatch
2292
+ ? state.scanDirectionFlags
2293
+ : state.scanDirectionFlags.subarray(0, batchPointCount)
2294
+ : null;
2295
+ const edgeOfFlightLines = state.edgeOfFlightLines
2296
+ ? fullBatch
2297
+ ? state.edgeOfFlightLines
2298
+ : state.edgeOfFlightLines.subarray(0, batchPointCount)
2299
+ : null;
2300
+ const waveforms = state.waveforms
2301
+ ? fullBatch
2302
+ ? state.waveforms
2303
+ : state.waveforms.subarray(0, batchPointCount * 29)
2304
+ : null;
2305
+ const extraByteCount = state.extraBytes ? state.extraBytes.length / state.batchCapacity : 0;
2306
+ const extraBytes = state.extraBytes
2307
+ ? fullBatch
2308
+ ? state.extraBytes
2309
+ : state.extraBytes.subarray(0, batchPointCount * extraByteCount)
2310
+ : null;
2311
+ const typedExtraBytes = state.typedExtraBytes
2312
+ ? state.typedExtraBytes.map(attribute => ({
2313
+ ...attribute,
2314
+ value: fullBatch
2315
+ ? attribute.value
2316
+ : attribute.value.subarray(0, batchPointCount * attribute.size)
2317
+ }))
2318
+ : null;
2319
+ const table = makeLASArrowTableFromAttributes(batchHeader, positions, colors, intensities, classifications, syntheticFlags, keyPointFlags, withheldFlags, overlapFlags, gpsTimes, nir, scanAngles, userData, pointSourceIds, returnNumbers, numberOfReturns, scannerChannels, scanDirectionFlags, edgeOfFlightLines, waveforms, extraBytes, typedExtraBytes);
2320
+ state.batchPointCount = 0;
2321
+ if (fullBatch) {
2322
+ const PositionsType = options.las?.fp64 ? Float64Array : Float32Array;
2323
+ state.positions = new PositionsType(state.positions.length);
2324
+ state.colors = state.colors ? new Uint8Array(state.colors.length) : null;
2325
+ state.rawColors = state.rawColors ? new Uint16Array(state.rawColors.length) : null;
2326
+ state.intensities = state.intensities ? new Uint16Array(state.intensities.length) : null;
2327
+ state.classifications = state.classifications
2328
+ ? new Uint8Array(state.classifications.length)
2329
+ : null;
2330
+ state.syntheticFlags = state.syntheticFlags
2331
+ ? new Uint8Array(state.syntheticFlags.length)
2332
+ : null;
2333
+ state.keyPointFlags = state.keyPointFlags ? new Uint8Array(state.keyPointFlags.length) : null;
2334
+ state.withheldFlags = state.withheldFlags ? new Uint8Array(state.withheldFlags.length) : null;
2335
+ state.overlapFlags = state.overlapFlags ? new Uint8Array(state.overlapFlags.length) : null;
2336
+ state.gpsTimes = state.gpsTimes ? new Float64Array(state.gpsTimes.length) : null;
2337
+ state.nir = state.nir ? new Uint16Array(state.nir.length) : null;
2338
+ state.scanAngles = state.scanAngles ? new Int16Array(state.scanAngles.length) : null;
2339
+ state.userData = state.userData ? new Uint8Array(state.userData.length) : null;
2340
+ state.pointSourceIds = state.pointSourceIds
2341
+ ? new Uint16Array(state.pointSourceIds.length)
2342
+ : null;
2343
+ state.returnNumbers = state.returnNumbers ? new Uint8Array(state.returnNumbers.length) : null;
2344
+ state.numberOfReturns = state.numberOfReturns
2345
+ ? new Uint8Array(state.numberOfReturns.length)
2346
+ : null;
2347
+ state.scannerChannels = state.scannerChannels
2348
+ ? new Uint8Array(state.scannerChannels.length)
2349
+ : null;
2350
+ state.scanDirectionFlags = state.scanDirectionFlags
2351
+ ? new Uint8Array(state.scanDirectionFlags.length)
2352
+ : null;
2353
+ state.edgeOfFlightLines = state.edgeOfFlightLines
2354
+ ? new Uint8Array(state.edgeOfFlightLines.length)
2355
+ : null;
2356
+ state.waveforms = state.waveforms ? new Uint8Array(state.waveforms.length) : null;
2357
+ state.extraBytes = state.extraBytes ? new Uint8Array(state.extraBytes.length) : null;
2358
+ state.typedExtraBytes = state.typedExtraBytes
2359
+ ? state.typedExtraBytes.map(attribute => ({
2360
+ ...attribute,
2361
+ value: createLASTypedExtraBytesValue(attribute.scalarDataType, attribute.value.length, attribute.outputFloat64)
2362
+ }))
2363
+ : null;
2364
+ state.typedExtraBytesSource = state.typedExtraBytesSource
2365
+ ? new Uint8Array(state.typedExtraBytesSource.length)
2366
+ : null;
2367
+ state.target.positions = state.positions;
2368
+ state.target.colors = state.colors;
2369
+ state.target.rawColors = state.rawColors;
2370
+ state.target.intensities = state.intensities;
2371
+ state.target.classifications = state.classifications;
2372
+ state.target.syntheticFlags = state.syntheticFlags;
2373
+ state.target.keyPointFlags = state.keyPointFlags;
2374
+ state.target.withheldFlags = state.withheldFlags;
2375
+ state.target.overlapFlags = state.overlapFlags;
2376
+ state.target.gpsTimes = state.gpsTimes;
2377
+ state.target.nir = state.nir;
2378
+ state.target.scanAngles = state.scanAngles;
2379
+ state.target.userData = state.userData;
2380
+ state.target.pointSourceIds = state.pointSourceIds;
2381
+ state.target.returnNumbers = state.returnNumbers;
2382
+ state.target.numberOfReturns = state.numberOfReturns;
2383
+ state.target.scannerChannels = state.scannerChannels;
2384
+ state.target.scanDirectionFlags = state.scanDirectionFlags;
2385
+ state.target.edgeOfFlightLines = state.edgeOfFlightLines;
2386
+ state.target.waveforms = state.waveforms;
2387
+ state.compatibilityExtraBytesSource = state.compatibilityExtraBytesSource
2388
+ ? new Uint8Array(state.compatibilityExtraBytesSource.length)
2389
+ : null;
2390
+ state.target.extraBytes =
2391
+ state.extraBytes || state.typedExtraBytesSource || state.compatibilityExtraBytesSource;
2392
+ }
2393
+ return table;
2394
+ }
2395
+ /** Resolve optional Arrow columns once before allocating or decoding a point batch. */
2396
+ function getLASColumnSelection(options) {
2397
+ const columns = options.las?.columns;
2398
+ if (!columns) {
2399
+ return {
2400
+ intensity: true,
2401
+ classification: true,
2402
+ synthetic: true,
2403
+ keyPoint: true,
2404
+ withheld: true,
2405
+ overlap: true,
2406
+ color: true,
2407
+ gpsTime: true,
2408
+ nir: true,
2409
+ scanAngle: true,
2410
+ userData: true,
2411
+ pointSourceId: true,
2412
+ returnNumber: true,
2413
+ numberOfReturns: true,
2414
+ scannerChannel: true,
2415
+ scanDirectionFlag: true,
2416
+ edgeOfFlightLine: true,
2417
+ waveform: true,
2418
+ extraBytes: options.las?.extraBytes === 'typed'
2419
+ };
2420
+ }
2421
+ let intensity = false;
2422
+ let classification = false;
2423
+ let synthetic = false;
2424
+ let keyPoint = false;
2425
+ let withheld = false;
2426
+ let overlap = false;
2427
+ let color = false;
2428
+ let gpsTime = false;
2429
+ let nir = false;
2430
+ let scanAngle = false;
2431
+ let userData = false;
2432
+ let pointSourceId = false;
2433
+ let returnNumber = false;
2434
+ let numberOfReturns = false;
2435
+ let scannerChannel = false;
2436
+ let scanDirectionFlag = false;
2437
+ let edgeOfFlightLine = false;
2438
+ let waveform = false;
2439
+ let extraBytes = false;
2440
+ for (const column of columns) {
2441
+ switch (column) {
2442
+ case 'POSITION':
2443
+ break;
2444
+ case 'intensity':
2445
+ intensity = true;
2446
+ break;
2447
+ case 'classification':
2448
+ classification = true;
2449
+ break;
2450
+ case 'synthetic':
2451
+ synthetic = true;
2452
+ break;
2453
+ case 'keyPoint':
2454
+ keyPoint = true;
2455
+ break;
2456
+ case 'withheld':
2457
+ withheld = true;
2458
+ break;
2459
+ case 'overlap':
2460
+ overlap = true;
2461
+ break;
2462
+ case 'COLOR_0':
2463
+ color = true;
2464
+ break;
2465
+ case 'GPS_TIME':
2466
+ gpsTime = true;
2467
+ break;
2468
+ case 'NIR':
2469
+ nir = true;
2470
+ break;
2471
+ case 'scanAngle':
2472
+ scanAngle = true;
2473
+ break;
2474
+ case 'userData':
2475
+ userData = true;
2476
+ break;
2477
+ case 'pointSourceId':
2478
+ pointSourceId = true;
2479
+ break;
2480
+ case 'returnNumber':
2481
+ returnNumber = true;
2482
+ break;
2483
+ case 'numberOfReturns':
2484
+ numberOfReturns = true;
2485
+ break;
2486
+ case 'scannerChannel':
2487
+ scannerChannel = true;
2488
+ break;
2489
+ case 'scanDirectionFlag':
2490
+ scanDirectionFlag = true;
2491
+ break;
2492
+ case 'edgeOfFlightLine':
2493
+ edgeOfFlightLine = true;
2494
+ break;
2495
+ case 'WAVEFORM':
2496
+ waveform = true;
2497
+ break;
2498
+ case 'EXTRA_BYTES':
2499
+ extraBytes = true;
2500
+ break;
2501
+ default:
2502
+ throw new Error(`LASLoader: unsupported column ${column}`);
2503
+ }
2504
+ }
2505
+ return {
2506
+ intensity,
2507
+ classification,
2508
+ synthetic,
2509
+ keyPoint,
2510
+ withheld,
2511
+ overlap,
2512
+ color,
2513
+ gpsTime,
2514
+ nir,
2515
+ scanAngle,
2516
+ userData,
2517
+ pointSourceId,
2518
+ returnNumber,
2519
+ numberOfReturns,
2520
+ scannerChannel,
2521
+ scanDirectionFlag,
2522
+ edgeOfFlightLine,
2523
+ waveform,
2524
+ extraBytes
2525
+ };
2526
+ }
2527
+ function convertRawColorsToUint8(rawColors, pointCount, options) {
2528
+ const colors = new Uint8Array(pointCount * 4);
2529
+ const packedColors = new Uint32Array(colors.buffer);
2530
+ const twoByteColor = detectTwoByteRawColors(rawColors, pointCount, options.las?.colorDepth);
2531
+ if (twoByteColor) {
2532
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex++) {
2533
+ const sourceOffset = pointIndex * 3;
2534
+ const red = (rawColors[sourceOffset] >> 8) & 0xff;
2535
+ const green = (rawColors[sourceOffset + 1] >> 8) & 0xff;
2536
+ const blue = (rawColors[sourceOffset + 2] >> 8) & 0xff;
2537
+ packedColors[pointIndex] = 0xff000000 | (blue << 16) | (green << 8) | red;
2538
+ }
2539
+ }
2540
+ else {
2541
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex++) {
2542
+ const sourceOffset = pointIndex * 3;
2543
+ const red = rawColors[sourceOffset] & 0xff;
2544
+ const green = rawColors[sourceOffset + 1] & 0xff;
2545
+ const blue = rawColors[sourceOffset + 2] & 0xff;
2546
+ packedColors[pointIndex] = 0xff000000 | (blue << 16) | (green << 8) | red;
2547
+ }
2548
+ }
2549
+ return colors;
2550
+ }
2551
+ function detectTwoByteRawColors(rawColors, pointCount, colorDepth) {
2552
+ if (colorDepth === 8) {
2553
+ return false;
2554
+ }
2555
+ if (colorDepth === 16) {
2556
+ return true;
2557
+ }
2558
+ if (colorDepth !== 'auto') {
2559
+ return false;
2560
+ }
2561
+ for (let index = 0; index < pointCount * 3; index++) {
2562
+ if (rawColors[index] > 255) {
2563
+ return true;
2564
+ }
2565
+ }
2566
+ return false;
2567
+ }
2568
+ function readNullTerminatedAscii(bytes, offset, length) {
2569
+ let end = offset;
2570
+ const maxEnd = offset + length;
2571
+ while (end < maxEnd && bytes[end] !== 0) {
2572
+ end++;
2573
+ }
2574
+ return String.fromCharCode(...bytes.subarray(offset, end));
2575
+ }
2576
+ function getHeaderBoundingBox(header) {
2577
+ const minimum = header.mins || [0, 0, 0];
2578
+ const maximum = header.maxs || [0, 0, 0];
2579
+ return [
2580
+ [minimum[0], minimum[1], minimum[2]],
2581
+ [maximum[0], maximum[1], maximum[2]]
2582
+ ];
2583
+ }
2584
+ function* readAvailablePointBatches(pending, absoluteOffset, header, sourcePointIndex, totalRead, batchSize) {
2585
+ while (sourcePointIndex < header.pointsCount && totalRead < header.totalToRead) {
2586
+ const batch = readAvailableContiguousPointBatch(pending, absoluteOffset, header, sourcePointIndex, totalRead, batchSize);
2587
+ if (!batch) {
2588
+ return;
2589
+ }
2590
+ pending = batch.pending;
2591
+ absoluteOffset = batch.absoluteOffset;
2592
+ sourcePointIndex = batch.sourcePointIndex;
2593
+ totalRead = batch.totalRead;
2594
+ yield batch;
2595
+ }
2596
+ }
2597
+ function readAvailableContiguousPointBatch(pending, absoluteOffset, header, sourcePointIndex, totalRead, batchSize) {
2598
+ const pointRecordLength = header.pointsStructSize;
2599
+ const pointAbsoluteOffset = header.pointsOffset + sourcePointIndex * pointRecordLength;
2600
+ const pointPendingOffset = pointAbsoluteOffset - absoluteOffset;
2601
+ if (pointPendingOffset < 0) {
2602
+ throw new Error('LASLoader: invalid streaming point offset');
2603
+ }
2604
+ if (pointPendingOffset + pointRecordLength > pending.byteLength) {
2605
+ return null;
2606
+ }
2607
+ const availablePointCount = Math.floor((pending.byteLength - pointPendingOffset) / pointRecordLength);
2608
+ const batchPointCount = Math.min(batchSize, availablePointCount, header.pointsCount - sourcePointIndex, header.totalToRead - totalRead);
2609
+ if (batchPointCount <= 0) {
2610
+ return null;
2611
+ }
2612
+ sourcePointIndex += batchPointCount;
2613
+ totalRead += batchPointCount;
2614
+ const batchHeader = {
2615
+ ...header,
2616
+ pointsOffset: pending.byteOffset + pointPendingOffset,
2617
+ pointsCount: batchPointCount,
2618
+ totalRead
2619
+ };
2620
+ const nextPointOffset = header.pointsOffset + sourcePointIndex * pointRecordLength;
2621
+ const discardByteCount = Math.max(0, Math.min(pending.byteLength, nextPointOffset - absoluteOffset));
2622
+ return {
2623
+ arrayBuffer: pending.buffer,
2624
+ header: batchHeader,
2625
+ pending: pending.subarray(discardByteCount),
2626
+ absoluteOffset: absoluteOffset + discardByteCount,
2627
+ sourcePointIndex,
2628
+ totalRead
2629
+ };
2630
+ }
2631
+ function tryParseHeader(pending, absoluteOffset) {
2632
+ if (absoluteOffset !== 0 || pending.byteLength < 227) {
2633
+ return null;
2634
+ }
2635
+ const dataView = new DataView(pending.buffer, pending.byteOffset, pending.byteLength);
2636
+ if (dataView.getUint32(0, true) !== LASF_SIGNATURE) {
2637
+ throw new Error('LASLoader: invalid LAS header');
2638
+ }
2639
+ const headerLength = dataView.getUint32(96, true);
2640
+ if (pending.byteLength < Math.max(headerLength, 255)) {
2641
+ return null;
2642
+ }
2643
+ const header = parseLASHeader(pending.buffer.slice(pending.byteOffset, pending.byteOffset + pending.byteLength));
2644
+ return header;
2645
+ }
2646
+ function concatenateUint8Arrays(first, second) {
2647
+ if (first.byteLength === 0) {
2648
+ return second;
2649
+ }
2650
+ const result = new Uint8Array(first.byteLength + second.byteLength);
2651
+ result.set(first);
2652
+ result.set(second, first.byteLength);
2653
+ return result;
2654
+ }
2655
+ function toUint8Array(data) {
2656
+ const source = ArrayBuffer.isView(data)
2657
+ ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
2658
+ : new Uint8Array(data);
2659
+ const copy = new Uint8Array(source.byteLength);
2660
+ copy.set(source);
2661
+ return copy;
2662
+ }
2663
+ function toAsyncIterator(data) {
2664
+ if (Symbol.asyncIterator in data) {
2665
+ return data[Symbol.asyncIterator]();
2666
+ }
2667
+ const iterator = data[Symbol.iterator]();
2668
+ return {
2669
+ async next() {
2670
+ return iterator.next();
2671
+ }
2672
+ };
2673
+ }
2674
+ /** Resolve the LAZ chunk-table offset, including the non-seekable-writer sentinel layout. */
2675
+ function readLAZChunkTableOffset(dataView, pointDataOffset) {
2676
+ if (isNegativeOneUint64(dataView, pointDataOffset)) {
2677
+ if (dataView.byteLength < LAZ_CHUNK_TABLE_POINTER_LENGTH) {
2678
+ return 0;
2679
+ }
2680
+ return readUint64(dataView, dataView.byteLength - LAZ_CHUNK_TABLE_POINTER_LENGTH);
2681
+ }
2682
+ return readUint64(dataView, pointDataOffset);
2683
+ }
2684
+ /** Read a streaming chunk-table pointer, returning -1 for the end-pointer sentinel. */
2685
+ function readStreamedLAZChunkTableOffset(dataView) {
2686
+ return isNegativeOneUint64(dataView, 0) ? -1 : readUint64(dataView, 0);
2687
+ }
2688
+ /** Return true when an eight-byte field contains signed little-endian -1. */
2689
+ function isNegativeOneUint64(dataView, byteOffset) {
2690
+ return (dataView.byteLength >= byteOffset + 8 &&
2691
+ dataView.getUint32(byteOffset, true) === 0xffffffff &&
2692
+ dataView.getUint32(byteOffset + 4, true) === 0xffffffff);
2693
+ }
2694
+ function readUint64(dataView, byteOffset) {
2695
+ if (dataView.byteLength < byteOffset + 8) {
2696
+ return 0;
2697
+ }
2698
+ const low = dataView.getUint32(byteOffset, true);
2699
+ const high = dataView.getUint32(byteOffset + 4, true);
2700
+ return high * 2 ** 32 + low;
2701
+ }
2702
+ export const LAS_1_4_HEADER_LENGTH = LAS_14_HEADER_LENGTH;
2703
+ //# sourceMappingURL=parse-las.js.map