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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. import defined from "../Core/defined.js";
  2. import RuntimeError from "../Core/RuntimeError.js";
  3. /**
  4. * This class implements an I3S Field which is custom data attached
  5. * to nodes
  6. * @alias I3SField
  7. * @internalConstructor
  8. * @privateParam {I3SNode} parent The parent of that geometry
  9. * @privateParam {object} storageInfo The structure containing the storage info of the field
  10. */
  11. function I3SField(parent, storageInfo) {
  12. this._storageInfo = storageInfo;
  13. this._parent = parent;
  14. this._dataProvider = parent._dataProvider;
  15. this._loadPromise = undefined;
  16. const uri = `attributes/${storageInfo.key}/0`;
  17. if (defined(this._parent._nodeIndex)) {
  18. this._resource = this._parent._layer.resource.getDerivedResource({
  19. url: `nodes/${this._parent._data.mesh.attribute.resource}/${uri}`,
  20. });
  21. } else {
  22. this._resource = this._parent.resource.getDerivedResource({ url: uri });
  23. }
  24. }
  25. Object.defineProperties(I3SField.prototype, {
  26. /**
  27. * Gets the resource for the fields
  28. * @memberof I3SField.prototype
  29. * @type {Resource}
  30. * @readonly
  31. */
  32. resource: {
  33. get: function () {
  34. return this._resource;
  35. },
  36. },
  37. /**
  38. * Gets the header for this field.
  39. * @memberof I3SField.prototype
  40. * @type {object}
  41. * @readonly
  42. */
  43. header: {
  44. get: function () {
  45. return this._header;
  46. },
  47. },
  48. /**
  49. * Gets the values for this field.
  50. * @memberof I3SField.prototype
  51. * @type {object}
  52. * @readonly
  53. */
  54. values: {
  55. get: function () {
  56. if (defined(this._values)) {
  57. // attribute data can be stored either as values or as object identifiers
  58. if (defined(this._values.attributeValues)) {
  59. return this._values.attributeValues;
  60. }
  61. if (defined(this._values.objectIds)) {
  62. return this._values.objectIds;
  63. }
  64. }
  65. return [];
  66. },
  67. },
  68. /**
  69. * Gets the name for the field.
  70. * @memberof I3SField.prototype
  71. * @type {string}
  72. * @readonly
  73. */
  74. name: {
  75. get: function () {
  76. return this._storageInfo.name;
  77. },
  78. },
  79. });
  80. function getNumericTypeSize(type) {
  81. if (type === "UInt8" || type === "Int8") {
  82. return 1;
  83. } else if (type === "UInt16" || type === "Int16") {
  84. return 2;
  85. } else if (
  86. type === "UInt32" ||
  87. type === "Int32" ||
  88. type === "Oid32" ||
  89. type === "Float32"
  90. ) {
  91. return 4;
  92. } else if (type === "UInt64" || type === "Int64" || type === "Float64") {
  93. return 8;
  94. }
  95. // Not a numeric type
  96. return 0;
  97. }
  98. function getValueTypeSize(type) {
  99. if (type === "String") {
  100. return 1;
  101. }
  102. return getNumericTypeSize(type);
  103. }
  104. async function load(field) {
  105. const data = await field._dataProvider._loadBinary(field._resource);
  106. const dataView = new DataView(data);
  107. field._data = data;
  108. field._validateHeader(dataView);
  109. const headerSize = field._parseHeader(dataView);
  110. const offset = field._getBodyOffset(headerSize);
  111. field._validateBody(dataView, offset);
  112. field._parseBody(dataView, offset);
  113. }
  114. /**
  115. * Loads the content.
  116. * @returns {Promise<void>} A promise that is resolved when the field data is loaded
  117. */
  118. I3SField.prototype.load = function () {
  119. if (defined(this._loadPromise)) {
  120. return this._loadPromise;
  121. }
  122. this._loadPromise = load(this).catch(function (error) {
  123. console.error(error);
  124. });
  125. return this._loadPromise;
  126. };
  127. /**
  128. * @private
  129. */
  130. I3SField.prototype._parseValue = function (dataView, type, offset) {
  131. let value;
  132. if (type === "UInt8") {
  133. value = dataView.getUint8(offset);
  134. offset += 1;
  135. } else if (type === "Int8") {
  136. value = dataView.getInt8(offset);
  137. offset += 1;
  138. } else if (type === "UInt16") {
  139. value = dataView.getUint16(offset, true);
  140. offset += 2;
  141. } else if (type === "Int16") {
  142. value = dataView.getInt16(offset, true);
  143. offset += 2;
  144. } else if (type === "UInt32") {
  145. value = dataView.getUint32(offset, true);
  146. offset += 4;
  147. } else if (type === "Oid32") {
  148. value = dataView.getUint32(offset, true);
  149. offset += 4;
  150. } else if (type === "Int32") {
  151. value = dataView.getInt32(offset, true);
  152. offset += 4;
  153. } else if (type === "UInt64") {
  154. const left = dataView.getUint32(offset, true);
  155. const right = dataView.getUint32(offset + 4, true);
  156. value = left + Math.pow(2, 32) * right;
  157. offset += 8;
  158. } else if (type === "Int64") {
  159. const left = dataView.getUint32(offset, true);
  160. const right = dataView.getUint32(offset + 4, true);
  161. if (right < Math.pow(2, 31)) {
  162. // Positive number
  163. value = left + Math.pow(2, 32) * right;
  164. } else {
  165. // Negative
  166. value = left + Math.pow(2, 32) * (right - Math.pow(2, 32));
  167. }
  168. offset += 8;
  169. } else if (type === "Float32") {
  170. value = dataView.getFloat32(offset, true);
  171. offset += 4;
  172. } else if (type === "Float64") {
  173. value = dataView.getFloat64(offset, true);
  174. offset += 8;
  175. } else if (type === "String") {
  176. value = String.fromCharCode(dataView.getUint8(offset));
  177. offset += 1;
  178. }
  179. return {
  180. value: value,
  181. offset: offset,
  182. };
  183. };
  184. /**
  185. * @private
  186. */
  187. I3SField.prototype._parseHeader = function (dataView) {
  188. let offset = 0;
  189. this._header = {};
  190. for (
  191. let itemIndex = 0;
  192. itemIndex < this._storageInfo.header.length;
  193. itemIndex++
  194. ) {
  195. const item = this._storageInfo.header[itemIndex];
  196. const parsedValue = this._parseValue(dataView, item.valueType, offset);
  197. this._header[item.property] = parsedValue.value;
  198. offset = parsedValue.offset;
  199. }
  200. return offset;
  201. };
  202. /**
  203. * @private
  204. */
  205. I3SField.prototype._parseBody = function (dataView, offset) {
  206. this._values = {};
  207. for (
  208. let itemIndex = 0;
  209. itemIndex < this._storageInfo.ordering.length;
  210. itemIndex++
  211. ) {
  212. const orderingValue = this._storageInfo.ordering[itemIndex];
  213. // all strings in the ordering array correspond to the property name, except ObjectIds
  214. const item = orderingValue === "ObjectIds" ? "objectIds" : orderingValue;
  215. const desc = this._storageInfo[item];
  216. if (defined(desc)) {
  217. this._values[item] = [];
  218. for (let index = 0; index < this._header.count; ++index) {
  219. if (desc.valueType !== "String") {
  220. const parsedValue = this._parseValue(
  221. dataView,
  222. desc.valueType,
  223. offset,
  224. );
  225. this._values[item].push(parsedValue.value);
  226. offset = parsedValue.offset;
  227. } else {
  228. const stringLen = this._values.attributeByteCounts[index];
  229. let stringContent = "";
  230. for (let cIndex = 0; cIndex < stringLen; ++cIndex) {
  231. const curParsedValue = this._parseValue(
  232. dataView,
  233. desc.valueType,
  234. offset,
  235. );
  236. if (curParsedValue.value.charCodeAt(0) !== 0) {
  237. stringContent += curParsedValue.value;
  238. }
  239. offset = curParsedValue.offset;
  240. }
  241. // We skip the last character of the string since it's a null terminator
  242. this._values[item].push(stringContent);
  243. }
  244. }
  245. }
  246. }
  247. };
  248. /**
  249. * @private
  250. */
  251. I3SField.prototype._getBodyOffset = function (headerSize) {
  252. let valueSize = 0;
  253. if (defined(this._storageInfo.attributeValues)) {
  254. valueSize = getNumericTypeSize(this._storageInfo.attributeValues.valueType);
  255. } else if (defined(this._storageInfo.objectIds)) {
  256. valueSize = getNumericTypeSize(this._storageInfo.objectIds.valueType);
  257. }
  258. if (valueSize > 0) {
  259. // Values will be padded to align the addresses with the data size
  260. return Math.ceil(headerSize / valueSize) * valueSize;
  261. }
  262. return headerSize;
  263. };
  264. /**
  265. * @private
  266. */
  267. I3SField.prototype._validateHeader = function (dataView) {
  268. let headerSize = 0;
  269. for (
  270. let itemIndex = 0;
  271. itemIndex < this._storageInfo.header.length;
  272. itemIndex++
  273. ) {
  274. const item = this._storageInfo.header[itemIndex];
  275. headerSize += getValueTypeSize(item.valueType);
  276. }
  277. if (dataView.byteLength < headerSize) {
  278. throw new RuntimeError(
  279. `Invalid attribute buffer size (field: ${this.name}, header: ${headerSize}, actual: ${dataView.byteLength})`,
  280. );
  281. }
  282. };
  283. /**
  284. * @private
  285. */
  286. I3SField.prototype._validateBody = function (dataView, offset) {
  287. if (!defined(this._header.count)) {
  288. throw new RuntimeError(
  289. `Invalid attribute buffer (field: ${this.name}, count is missing)`,
  290. );
  291. }
  292. let attributeByteCountsOffset;
  293. for (
  294. let itemIndex = 0;
  295. itemIndex < this._storageInfo.ordering.length &&
  296. offset < dataView.byteLength;
  297. itemIndex++
  298. ) {
  299. const orderingValue = this._storageInfo.ordering[itemIndex];
  300. // all strings in the ordering array correspond to the property name, except ObjectIds
  301. const item = orderingValue === "ObjectIds" ? "objectIds" : orderingValue;
  302. const desc = this._storageInfo[item];
  303. if (defined(desc)) {
  304. if (desc.valueType !== "String") {
  305. if (item === "attributeByteCounts") {
  306. attributeByteCountsOffset = offset;
  307. }
  308. const valueSize = getNumericTypeSize(desc.valueType);
  309. offset += valueSize * this._header.count;
  310. } else {
  311. if (!defined(attributeByteCountsOffset)) {
  312. throw new RuntimeError(
  313. `Invalid attribute buffer (field: ${this.name}, attributeByteCounts is missing)`,
  314. );
  315. }
  316. for (
  317. let index = 0;
  318. index < this._header.count && offset < dataView.byteLength;
  319. ++index
  320. ) {
  321. const parsedValue = this._parseValue(
  322. dataView,
  323. this._storageInfo.attributeByteCounts.valueType,
  324. attributeByteCountsOffset,
  325. );
  326. offset += parsedValue.value;
  327. attributeByteCountsOffset = parsedValue.offset;
  328. }
  329. }
  330. } else {
  331. throw new RuntimeError(
  332. `Invalid attribute buffer (field: ${this.name}, ${item} is missing)`,
  333. );
  334. }
  335. }
  336. if (dataView.byteLength < offset) {
  337. throw new RuntimeError(
  338. `Invalid attribute buffer size (field: ${this.name}, expected: ${offset}, actual: ${dataView.byteLength})`,
  339. );
  340. }
  341. };
  342. export default I3SField;