智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import AttributeType from "./AttributeType.js";
  2. import Check from "../Core/Check.js";
  3. import clone from "../Core/clone.js";
  4. import combine from "../Core/combine.js";
  5. import ComponentDatatype from "../Core/ComponentDatatype.js";
  6. import defined from "../Core/defined.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import getBinaryAccessor from "./getBinaryAccessor.js";
  9. import Cesium3DTileBatchTable from "./Cesium3DTileBatchTable.js";
  10. /**
  11. * Object for handling the <code>3DTILES_batch_table_hierarchy</code> extension
  12. *
  13. * @param {object} options Object with the following properties:
  14. * @param {object} options.extension The <code>3DTILES_batch_table_hierarchy</code> extension object.
  15. * @param {Uint8Array} [options.binaryBody] The binary body of the batch table
  16. *
  17. * @alias BatchTableHierarchy
  18. * @constructor
  19. *
  20. * @private
  21. */
  22. function BatchTableHierarchy(options) {
  23. this._classes = undefined;
  24. this._classIds = undefined;
  25. this._classIndexes = undefined;
  26. this._parentCounts = undefined;
  27. this._parentIndexes = undefined;
  28. this._parentIds = undefined;
  29. // Total memory used by the typed arrays
  30. this._byteLength = 0;
  31. //>>includeStart('debug', pragmas.debug);
  32. Check.typeOf.object("options.extension", options.extension);
  33. //>>includeEnd('debug');
  34. initialize(this, options.extension, options.binaryBody);
  35. //>>includeStart('debug', pragmas.debug);
  36. validateHierarchy(this);
  37. //>>includeEnd('debug');
  38. }
  39. Object.defineProperties(BatchTableHierarchy.prototype, {
  40. byteLength: {
  41. get: function () {
  42. return this._byteLength;
  43. },
  44. },
  45. });
  46. /**
  47. * Parse the batch table hierarchy from the
  48. * <code>3DTILES_batch_table_hierarchy</code> extension.
  49. *
  50. * @param {BatchTableHierarchy} hierarchy The hierarchy instance
  51. * @param {object} hierarchyJson The JSON of the extension
  52. * @param {Uint8Array} [binaryBody] The binary body of the batch table for accessing binary properties
  53. * @private
  54. */
  55. function initialize(hierarchy, hierarchyJson, binaryBody) {
  56. let i;
  57. let classId;
  58. let binaryAccessor;
  59. const instancesLength = hierarchyJson.instancesLength;
  60. const classes = hierarchyJson.classes;
  61. let classIds = hierarchyJson.classIds;
  62. let parentCounts = hierarchyJson.parentCounts;
  63. let parentIds = hierarchyJson.parentIds;
  64. let parentIdsLength = instancesLength;
  65. let byteLength = 0;
  66. if (defined(classIds.byteOffset)) {
  67. classIds.componentType =
  68. classIds.componentType ?? ComponentDatatype.UNSIGNED_SHORT;
  69. classIds.type = AttributeType.SCALAR;
  70. binaryAccessor = getBinaryAccessor(classIds);
  71. classIds = binaryAccessor.createArrayBufferView(
  72. binaryBody.buffer,
  73. binaryBody.byteOffset + classIds.byteOffset,
  74. instancesLength,
  75. );
  76. byteLength += classIds.byteLength;
  77. }
  78. let parentIndexes;
  79. if (defined(parentCounts)) {
  80. if (defined(parentCounts.byteOffset)) {
  81. parentCounts.componentType =
  82. parentCounts.componentType ?? ComponentDatatype.UNSIGNED_SHORT;
  83. parentCounts.type = AttributeType.SCALAR;
  84. binaryAccessor = getBinaryAccessor(parentCounts);
  85. parentCounts = binaryAccessor.createArrayBufferView(
  86. binaryBody.buffer,
  87. binaryBody.byteOffset + parentCounts.byteOffset,
  88. instancesLength,
  89. );
  90. byteLength += parentCounts.byteLength;
  91. }
  92. parentIndexes = new Uint16Array(instancesLength);
  93. parentIdsLength = 0;
  94. for (i = 0; i < instancesLength; ++i) {
  95. parentIndexes[i] = parentIdsLength;
  96. parentIdsLength += parentCounts[i];
  97. }
  98. byteLength += parentIndexes.byteLength;
  99. }
  100. if (defined(parentIds) && defined(parentIds.byteOffset)) {
  101. parentIds.componentType =
  102. parentIds.componentType ?? ComponentDatatype.UNSIGNED_SHORT;
  103. parentIds.type = AttributeType.SCALAR;
  104. binaryAccessor = getBinaryAccessor(parentIds);
  105. parentIds = binaryAccessor.createArrayBufferView(
  106. binaryBody.buffer,
  107. binaryBody.byteOffset + parentIds.byteOffset,
  108. parentIdsLength,
  109. );
  110. byteLength += parentIds.byteLength;
  111. }
  112. const classesLength = classes.length;
  113. for (i = 0; i < classesLength; ++i) {
  114. const classInstancesLength = classes[i].length;
  115. const properties = classes[i].instances;
  116. const binaryProperties = Cesium3DTileBatchTable.getBinaryProperties(
  117. classInstancesLength,
  118. properties,
  119. binaryBody,
  120. );
  121. byteLength += countBinaryPropertyMemory(binaryProperties);
  122. classes[i].instances = combine(binaryProperties, properties);
  123. }
  124. const classCounts = new Array(classesLength).fill(0);
  125. const classIndexes = new Uint16Array(instancesLength);
  126. for (i = 0; i < instancesLength; ++i) {
  127. classId = classIds[i];
  128. classIndexes[i] = classCounts[classId];
  129. ++classCounts[classId];
  130. }
  131. byteLength += classIndexes.byteLength;
  132. hierarchy._classes = classes;
  133. hierarchy._classIds = classIds;
  134. hierarchy._classIndexes = classIndexes;
  135. hierarchy._parentCounts = parentCounts;
  136. hierarchy._parentIndexes = parentIndexes;
  137. hierarchy._parentIds = parentIds;
  138. hierarchy._byteLength = byteLength;
  139. }
  140. function countBinaryPropertyMemory(binaryProperties) {
  141. let byteLength = 0;
  142. for (const name in binaryProperties) {
  143. if (binaryProperties.hasOwnProperty(name)) {
  144. byteLength += binaryProperties[name].typedArray.byteLength;
  145. }
  146. }
  147. return byteLength;
  148. }
  149. //>>includeStart('debug', pragmas.debug);
  150. const scratchValidateStack = [];
  151. function validateHierarchy(hierarchy) {
  152. const stack = scratchValidateStack;
  153. stack.length = 0;
  154. const classIds = hierarchy._classIds;
  155. const instancesLength = classIds.length;
  156. for (let i = 0; i < instancesLength; ++i) {
  157. validateInstance(hierarchy, i, stack);
  158. }
  159. }
  160. function validateInstance(hierarchy, instanceIndex, stack) {
  161. const parentCounts = hierarchy._parentCounts;
  162. const parentIds = hierarchy._parentIds;
  163. const parentIndexes = hierarchy._parentIndexes;
  164. const classIds = hierarchy._classIds;
  165. const instancesLength = classIds.length;
  166. if (!defined(parentIds)) {
  167. // No need to validate if there are no parents
  168. return;
  169. }
  170. if (instanceIndex >= instancesLength) {
  171. throw new DeveloperError(
  172. `Parent index ${instanceIndex} exceeds the total number of instances: ${instancesLength}`,
  173. );
  174. }
  175. if (stack.indexOf(instanceIndex) > -1) {
  176. throw new DeveloperError(
  177. "Circular dependency detected in the batch table hierarchy.",
  178. );
  179. }
  180. stack.push(instanceIndex);
  181. const parentCount = defined(parentCounts) ? parentCounts[instanceIndex] : 1;
  182. const parentIndex = defined(parentCounts)
  183. ? parentIndexes[instanceIndex]
  184. : instanceIndex;
  185. for (let i = 0; i < parentCount; ++i) {
  186. const parentId = parentIds[parentIndex + i];
  187. // Stop the traversal when the instance has no parent (its parentId equals itself), else continue the traversal.
  188. if (parentId !== instanceIndex) {
  189. validateInstance(hierarchy, parentId, stack);
  190. }
  191. }
  192. stack.pop(instanceIndex);
  193. }
  194. //>>includeEnd('debug');
  195. // The size of this array equals the maximum instance count among all loaded tiles, which has the potential to be large.
  196. const scratchVisited = [];
  197. const scratchStack = [];
  198. let marker = 0;
  199. function traverseHierarchyMultipleParents(
  200. hierarchy,
  201. instanceIndex,
  202. endConditionCallback,
  203. ) {
  204. const classIds = hierarchy._classIds;
  205. const parentCounts = hierarchy._parentCounts;
  206. const parentIds = hierarchy._parentIds;
  207. const parentIndexes = hierarchy._parentIndexes;
  208. const instancesLength = classIds.length;
  209. // Ignore instances that have already been visited. This occurs in diamond inheritance situations.
  210. // Use a marker value to indicate that an instance has been visited, which increments with each run.
  211. // This is more efficient than clearing the visited array every time.
  212. const visited = scratchVisited;
  213. visited.length = Math.max(visited.length, instancesLength);
  214. const visitedMarker = ++marker;
  215. const stack = scratchStack;
  216. stack.length = 0;
  217. stack.push(instanceIndex);
  218. while (stack.length > 0) {
  219. instanceIndex = stack.pop();
  220. if (visited[instanceIndex] === visitedMarker) {
  221. // This instance has already been visited, stop traversal
  222. continue;
  223. }
  224. visited[instanceIndex] = visitedMarker;
  225. const result = endConditionCallback(hierarchy, instanceIndex);
  226. if (defined(result)) {
  227. // The end condition was met, stop the traversal and return the result
  228. return result;
  229. }
  230. const parentCount = parentCounts[instanceIndex];
  231. const parentIndex = parentIndexes[instanceIndex];
  232. for (let i = 0; i < parentCount; ++i) {
  233. const parentId = parentIds[parentIndex + i];
  234. // Stop the traversal when the instance has no parent (its parentId equals itself)
  235. // else add the parent to the stack to continue the traversal.
  236. if (parentId !== instanceIndex) {
  237. stack.push(parentId);
  238. }
  239. }
  240. }
  241. }
  242. function traverseHierarchySingleParent(
  243. hierarchy,
  244. instanceIndex,
  245. endConditionCallback,
  246. ) {
  247. let hasParent = true;
  248. while (hasParent) {
  249. const result = endConditionCallback(hierarchy, instanceIndex);
  250. if (defined(result)) {
  251. // The end condition was met, stop the traversal and return the result
  252. return result;
  253. }
  254. const parentId = hierarchy._parentIds[instanceIndex];
  255. hasParent = parentId !== instanceIndex;
  256. instanceIndex = parentId;
  257. }
  258. }
  259. function traverseHierarchy(hierarchy, instanceIndex, endConditionCallback) {
  260. // Traverse over the hierarchy and process each instance with the endConditionCallback.
  261. // When the endConditionCallback returns a value, the traversal stops and that value is returned.
  262. const parentCounts = hierarchy._parentCounts;
  263. const parentIds = hierarchy._parentIds;
  264. if (!defined(parentIds)) {
  265. return endConditionCallback(hierarchy, instanceIndex);
  266. } else if (defined(parentCounts)) {
  267. return traverseHierarchyMultipleParents(
  268. hierarchy,
  269. instanceIndex,
  270. endConditionCallback,
  271. );
  272. }
  273. return traverseHierarchySingleParent(
  274. hierarchy,
  275. instanceIndex,
  276. endConditionCallback,
  277. );
  278. }
  279. /**
  280. * Returns whether the feature has this property.
  281. *
  282. * @param {number} batchId the batch ID of the feature
  283. * @param {string} propertyId The case-sensitive ID of the property.
  284. * @returns {boolean} Whether the feature has this property.
  285. * @private
  286. */
  287. BatchTableHierarchy.prototype.hasProperty = function (batchId, propertyId) {
  288. const result = traverseHierarchy(
  289. this,
  290. batchId,
  291. function (hierarchy, instanceIndex) {
  292. const classId = hierarchy._classIds[instanceIndex];
  293. const instances = hierarchy._classes[classId].instances;
  294. if (defined(instances[propertyId])) {
  295. return true;
  296. }
  297. },
  298. );
  299. return defined(result);
  300. };
  301. /**
  302. * Returns whether any feature has this property.
  303. *
  304. * @param {string} propertyId The case-sensitive ID of the property.
  305. * @returns {boolean} Whether any feature has this property.
  306. * @private
  307. */
  308. BatchTableHierarchy.prototype.propertyExists = function (propertyId) {
  309. const classes = this._classes;
  310. const classesLength = classes.length;
  311. for (let i = 0; i < classesLength; ++i) {
  312. const instances = classes[i].instances;
  313. if (defined(instances[propertyId])) {
  314. return true;
  315. }
  316. }
  317. return false;
  318. };
  319. /**
  320. * Returns an array of property IDs.
  321. *
  322. * @param {number} batchId the batch ID of the feature
  323. * @param {number} index The index of the entity.
  324. * @param {string[]} [results] An array into which to store the results.
  325. * @returns {string[]} The property IDs.
  326. * @private
  327. */
  328. BatchTableHierarchy.prototype.getPropertyIds = function (batchId, results) {
  329. results = defined(results) ? results : [];
  330. results.length = 0;
  331. traverseHierarchy(this, batchId, function (hierarchy, instanceIndex) {
  332. const classId = hierarchy._classIds[instanceIndex];
  333. const instances = hierarchy._classes[classId].instances;
  334. for (const name in instances) {
  335. if (instances.hasOwnProperty(name)) {
  336. if (results.indexOf(name) === -1) {
  337. results.push(name);
  338. }
  339. }
  340. }
  341. });
  342. return results;
  343. };
  344. /**
  345. * Returns a copy of the value of the property with the given ID.
  346. *
  347. * @param {number} batchId the batch ID of the feature
  348. * @param {string} propertyId The case-sensitive ID of the property.
  349. * @returns {*} The value of the property or <code>undefined</code> if the feature does not have this property.
  350. * @private
  351. */
  352. BatchTableHierarchy.prototype.getProperty = function (batchId, propertyId) {
  353. return traverseHierarchy(this, batchId, function (hierarchy, instanceIndex) {
  354. const classId = hierarchy._classIds[instanceIndex];
  355. const instanceClass = hierarchy._classes[classId];
  356. const indexInClass = hierarchy._classIndexes[instanceIndex];
  357. const propertyValues = instanceClass.instances[propertyId];
  358. if (defined(propertyValues)) {
  359. if (defined(propertyValues.typedArray)) {
  360. return getBinaryProperty(propertyValues, indexInClass);
  361. }
  362. return clone(propertyValues[indexInClass], true);
  363. }
  364. });
  365. };
  366. function getBinaryProperty(binaryProperty, index) {
  367. const typedArray = binaryProperty.typedArray;
  368. const componentCount = binaryProperty.componentCount;
  369. if (componentCount === 1) {
  370. return typedArray[index];
  371. }
  372. return binaryProperty.type.unpack(typedArray, index * componentCount);
  373. }
  374. /**
  375. * Sets the value of the property with the given ID. Only properties of the
  376. * instance may be set; parent properties may not be set.
  377. *
  378. * @param {number} batchId The batchId of the feature
  379. * @param {string} propertyId The case-sensitive ID of the property.
  380. * @param {*} value The value of the property that will be copied.
  381. * @returns {boolean} <code>true</code> if the property was set, <code>false</code> otherwise.
  382. *
  383. * @exception {DeveloperError} when setting an inherited property
  384. * @private
  385. */
  386. BatchTableHierarchy.prototype.setProperty = function (
  387. batchId,
  388. propertyId,
  389. value,
  390. ) {
  391. const result = traverseHierarchy(
  392. this,
  393. batchId,
  394. function (hierarchy, instanceIndex) {
  395. const classId = hierarchy._classIds[instanceIndex];
  396. const instanceClass = hierarchy._classes[classId];
  397. const indexInClass = hierarchy._classIndexes[instanceIndex];
  398. const propertyValues = instanceClass.instances[propertyId];
  399. if (defined(propertyValues)) {
  400. //>>includeStart('debug', pragmas.debug);
  401. if (instanceIndex !== batchId) {
  402. throw new DeveloperError(
  403. `Inherited property "${propertyId}" is read-only.`,
  404. );
  405. }
  406. //>>includeEnd('debug');
  407. if (defined(propertyValues.typedArray)) {
  408. setBinaryProperty(propertyValues, indexInClass, value);
  409. } else {
  410. propertyValues[indexInClass] = clone(value, true);
  411. }
  412. return true;
  413. }
  414. },
  415. );
  416. return defined(result);
  417. };
  418. function setBinaryProperty(binaryProperty, index, value) {
  419. const typedArray = binaryProperty.typedArray;
  420. const componentCount = binaryProperty.componentCount;
  421. if (componentCount === 1) {
  422. typedArray[index] = value;
  423. } else {
  424. binaryProperty.type.pack(value, typedArray, index * componentCount);
  425. }
  426. }
  427. /**
  428. * Check if a feature belongs to a class with the given name
  429. *
  430. * @param {number} batchId The batch ID of the feature
  431. * @param {string} className The name of the class
  432. * @return {boolean} <code>true</code> if the feature belongs to the class given by className, or <code>false</code> otherwise
  433. * @private
  434. */
  435. BatchTableHierarchy.prototype.isClass = function (batchId, className) {
  436. // PERFORMANCE_IDEA : cache results in the ancestor classes to speed up this check if this area becomes a hotspot
  437. // PERFORMANCE_IDEA : treat class names as integers for faster comparisons
  438. const result = traverseHierarchy(
  439. this,
  440. batchId,
  441. function (hierarchy, instanceIndex) {
  442. const classId = hierarchy._classIds[instanceIndex];
  443. const instanceClass = hierarchy._classes[classId];
  444. if (instanceClass.name === className) {
  445. return true;
  446. }
  447. },
  448. );
  449. return defined(result);
  450. };
  451. /**
  452. * Get the name of the class a given feature belongs to
  453. *
  454. * @param {number} batchId The batch ID of the feature
  455. * @return {string} The name of the class this feature belongs to
  456. */
  457. BatchTableHierarchy.prototype.getClassName = function (batchId) {
  458. const classId = this._classIds[batchId];
  459. const instanceClass = this._classes[classId];
  460. return instanceClass.name;
  461. };
  462. export default BatchTableHierarchy;