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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. import Cartesian4 from "../Core/Cartesian4.js";
  2. import clone from "../Core/clone.js";
  3. import Color from "../Core/Color.js";
  4. import defined from "../Core/defined.js";
  5. import Rectangle from "../Core/Rectangle.js";
  6. import Resource from "../Core/Resource.js";
  7. import RuntimeError from "../Core/RuntimeError.js";
  8. import Cesium3DTileset from "./Cesium3DTileset.js";
  9. import I3SNode from "./I3SNode.js";
  10. import I3SSymbology from "./I3SSymbology.js";
  11. /**
  12. * This class implements an I3S layer. In CesiumJS each I3SLayer creates a Cesium3DTileset.
  13. * <p>
  14. * Do not construct this directly, instead access layers through {@link I3SDataProvider}.
  15. * </p>
  16. * @alias I3SLayer
  17. * @internalConstructor
  18. * @privateParam {I3SDataProvider} dataProvider The i3s data provider
  19. * @privateParam {object} layerData The layer data that is loaded from the scene layer
  20. * @privateParam {I3SDataProvider|I3SSublayer} parent The parent of that layer
  21. */
  22. function I3SLayer(dataProvider, layerData, parent) {
  23. this._dataProvider = dataProvider;
  24. this._parent = parent;
  25. if (!defined(layerData.href) && defined(layerData.id)) {
  26. // assign a default layer
  27. layerData.href = `layers/${layerData.id}`;
  28. }
  29. const parentUrl = this._parent.resource.getUrlComponent();
  30. let tilesetUrl = "";
  31. if (parentUrl.match(/layers\/\d/)) {
  32. tilesetUrl = `${parentUrl}`.replace(/\/+$/, "");
  33. } else {
  34. // Add '/' to url if needed + `${layerData.href}` if tilesetUrl not already in ../layers/[id] format
  35. tilesetUrl = `${parentUrl}`
  36. .replace(/\/?$/, "/")
  37. .concat(`${layerData.href}`);
  38. }
  39. this._version = layerData.store.version;
  40. const splitVersion = this._version.split(".");
  41. this._majorVersion = parseInt(splitVersion[0]);
  42. this._minorVersion = splitVersion.length > 1 ? parseInt(splitVersion[1]) : 0;
  43. this._resource = new Resource({ url: tilesetUrl });
  44. this._resource.setQueryParameters(
  45. this._dataProvider.resource.queryParameters,
  46. );
  47. this._resource.appendForwardSlash();
  48. this._data = layerData;
  49. this._rootNode = undefined;
  50. this._nodePages = {};
  51. this._nodePageFetches = {};
  52. this._extent = undefined;
  53. this._tileset = undefined;
  54. this._geometryDefinitions = undefined;
  55. this._filters = [];
  56. this._symbology = undefined;
  57. this._computeGeometryDefinitions(true);
  58. this._computeExtent();
  59. }
  60. Object.defineProperties(I3SLayer.prototype, {
  61. /**
  62. * Gets the resource for the layer.
  63. * @memberof I3SLayer.prototype
  64. * @type {Resource}
  65. * @readonly
  66. */
  67. resource: {
  68. get: function () {
  69. return this._resource;
  70. },
  71. },
  72. /**
  73. * Gets the root node of this layer.
  74. * @memberof I3SLayer.prototype
  75. * @type {I3SNode}
  76. * @readonly
  77. */
  78. rootNode: {
  79. get: function () {
  80. return this._rootNode;
  81. },
  82. },
  83. /**
  84. * Gets the Cesium3DTileset for this layer.
  85. * @memberof I3SLayer.prototype
  86. * @type {Cesium3DTileset|undefined}
  87. * @readonly
  88. */
  89. tileset: {
  90. get: function () {
  91. return this._tileset;
  92. },
  93. },
  94. /**
  95. * Gets the I3S data for this object.
  96. * @memberof I3SLayer.prototype
  97. * @type {object}
  98. * @readonly
  99. */
  100. data: {
  101. get: function () {
  102. return this._data;
  103. },
  104. },
  105. /**
  106. * The version string of the loaded I3S dataset
  107. * @memberof I3SLayer.prototype
  108. * @type {string}
  109. * @readonly
  110. */
  111. version: {
  112. get: function () {
  113. return this._version;
  114. },
  115. },
  116. /**
  117. * The major version number of the loaded I3S dataset
  118. * @memberof I3SLayer.prototype
  119. * @type {number}
  120. * @readonly
  121. */
  122. majorVersion: {
  123. get: function () {
  124. return this._majorVersion;
  125. },
  126. },
  127. /**
  128. * The minor version number of the loaded I3S dataset
  129. * @memberof I3SLayer.prototype
  130. * @type {number}
  131. * @readonly
  132. */
  133. minorVersion: {
  134. get: function () {
  135. return this._minorVersion;
  136. },
  137. },
  138. /**
  139. * When <code>true</code>, when the loaded I3S version is 1.6 or older
  140. * @memberof I3SLayer.prototype
  141. * @type {boolean}
  142. * @readonly
  143. */
  144. legacyVersion16: {
  145. get: function () {
  146. if (!defined(this.version)) {
  147. return undefined;
  148. }
  149. if (
  150. this.majorVersion < 1 ||
  151. (this.majorVersion === 1 && this.minorVersion <= 6)
  152. ) {
  153. return true;
  154. }
  155. return false;
  156. },
  157. },
  158. });
  159. /**
  160. * Loads the content, including the root node definition and its children
  161. * @param {Cesium3DTileset.ConstructorOptions} [cesium3dTilesetOptions] options for Cesium3dTileset constructor
  162. * @returns {Promise<void>} A promise that is resolved when the layer data is loaded
  163. * @private
  164. */
  165. I3SLayer.prototype.load = async function (cesium3dTilesetOptions) {
  166. if (this._data.spatialReference.wkid !== 4326) {
  167. throw new RuntimeError(
  168. `Unsupported spatial reference: ${this._data.spatialReference.wkid}`,
  169. );
  170. }
  171. if (this._dataProvider.applySymbology) {
  172. this._symbology = new I3SSymbology(this);
  173. }
  174. await this._dataProvider.loadGeoidData();
  175. await this._loadRootNode(cesium3dTilesetOptions);
  176. await this._create3DTileset(cesium3dTilesetOptions);
  177. this._rootNode._tile = this._tileset._root;
  178. this._tileset._root._i3sNode = this._rootNode;
  179. if (this.legacyVersion16) {
  180. return this._rootNode._loadChildren();
  181. }
  182. };
  183. /**
  184. * @private
  185. */
  186. I3SLayer.prototype._computeGeometryDefinitions = function (useCompression) {
  187. // create a table of all geometry buffers based on
  188. // the number of attributes and whether they are
  189. // compressed or not, sort them by priority
  190. this._geometryDefinitions = [];
  191. if (defined(this._data.geometryDefinitions)) {
  192. for (
  193. let defIndex = 0;
  194. defIndex < this._data.geometryDefinitions.length;
  195. defIndex++
  196. ) {
  197. const geometryBuffersInfo = [];
  198. const geometryBuffers =
  199. this._data.geometryDefinitions[defIndex].geometryBuffers;
  200. for (let bufIndex = 0; bufIndex < geometryBuffers.length; bufIndex++) {
  201. const geometryBuffer = geometryBuffers[bufIndex];
  202. const collectedAttributes = [];
  203. let compressed = false;
  204. if (defined(geometryBuffer.compressedAttributes) && useCompression) {
  205. // check if compressed
  206. compressed = true;
  207. const attributes = geometryBuffer.compressedAttributes.attributes;
  208. for (let i = 0; i < attributes.length; i++) {
  209. collectedAttributes.push(attributes[i]);
  210. }
  211. } else {
  212. // uncompressed attributes
  213. for (const attribute in geometryBuffer) {
  214. if (attribute !== "offset") {
  215. collectedAttributes.push(attribute);
  216. }
  217. }
  218. }
  219. geometryBuffersInfo.push({
  220. compressed: compressed,
  221. attributes: collectedAttributes,
  222. index: geometryBuffers.indexOf(geometryBuffer),
  223. });
  224. }
  225. // rank the buffer info
  226. geometryBuffersInfo.sort(function (a, b) {
  227. if (a.compressed && !b.compressed) {
  228. return -1;
  229. } else if (!a.compressed && b.compressed) {
  230. return 1;
  231. }
  232. return a.attributes.length - b.attributes.length;
  233. });
  234. this._geometryDefinitions.push(geometryBuffersInfo);
  235. }
  236. }
  237. };
  238. /**
  239. * @private
  240. */
  241. I3SLayer.prototype._findBestGeometryBuffers = function (
  242. definition,
  243. attributes,
  244. ) {
  245. // find the most appropriate geometry definition
  246. // based on the required attributes, and by favouring
  247. // compression to improve bandwidth requirements
  248. const geometryDefinition = this._geometryDefinitions[definition];
  249. if (defined(geometryDefinition)) {
  250. for (let index = 0; index < geometryDefinition.length; ++index) {
  251. const geometryBufferInfo = geometryDefinition[index];
  252. let missed = false;
  253. const geometryAttributes = geometryBufferInfo.attributes;
  254. for (let attrIndex = 0; attrIndex < attributes.length; attrIndex++) {
  255. if (!geometryAttributes.includes(attributes[attrIndex])) {
  256. missed = true;
  257. break;
  258. }
  259. }
  260. if (!missed) {
  261. return {
  262. bufferIndex: geometryBufferInfo.index,
  263. definition: geometryDefinition,
  264. geometryBufferInfo: geometryBufferInfo,
  265. };
  266. }
  267. }
  268. // If no match found return first geometryBuffer
  269. if (defined(geometryDefinition[0])) {
  270. return {
  271. bufferIndex: 0,
  272. definition: geometryDefinition,
  273. geometryBufferInfo: geometryDefinition[0],
  274. };
  275. }
  276. }
  277. };
  278. /**
  279. * @private
  280. */
  281. I3SLayer.prototype._loadRootNode = function (cesium3dTilesetOptions) {
  282. if (defined(this._data.nodePages)) {
  283. let rootIndex = 0;
  284. if (defined(this._data.nodePages.rootIndex)) {
  285. rootIndex = this._data.nodePages.rootIndex;
  286. }
  287. this._rootNode = new I3SNode(this, rootIndex, true);
  288. } else {
  289. this._rootNode = new I3SNode(this, this._data.store.rootNode, true);
  290. }
  291. return this._rootNode.load(cesium3dTilesetOptions);
  292. };
  293. /**
  294. * @private
  295. */
  296. I3SLayer.prototype._getNodeInNodePages = function (nodeIndex) {
  297. const index = Math.floor(nodeIndex / this._data.nodePages.nodesPerPage);
  298. const offsetInPage = nodeIndex % this._data.nodePages.nodesPerPage;
  299. return this._loadNodePage(index).then(function (data) {
  300. return data.nodes[offsetInPage];
  301. });
  302. };
  303. /**
  304. * @private
  305. */
  306. I3SLayer._fetchJson = function (resource) {
  307. return resource.fetchJson();
  308. };
  309. /**
  310. * @private
  311. */
  312. I3SLayer.prototype._loadNodePage = function (page) {
  313. const that = this;
  314. // If node page was already requested return the same promise
  315. if (!defined(this._nodePageFetches[page])) {
  316. const nodePageResource = this.resource.getDerivedResource({
  317. url: `nodepages/${page}/`,
  318. });
  319. const fetchPromise = I3SLayer._fetchJson(nodePageResource).then(
  320. function (data) {
  321. if (defined(data.error) && data.error.code !== 200) {
  322. return Promise.reject(data.error);
  323. }
  324. that._nodePages[page] = data.nodes;
  325. return data;
  326. },
  327. );
  328. this._nodePageFetches[page] = fetchPromise;
  329. }
  330. return this._nodePageFetches[page];
  331. };
  332. /**
  333. * @private
  334. */
  335. I3SLayer.prototype._computeExtent = function () {
  336. if (defined(this._data.fullExtent)) {
  337. this._extent = Rectangle.fromDegrees(
  338. this._data.fullExtent.xmin,
  339. this._data.fullExtent.ymin,
  340. this._data.fullExtent.xmax,
  341. this._data.fullExtent.ymax,
  342. );
  343. } else if (defined(this._data.store.extent)) {
  344. this._extent = Rectangle.fromDegrees(
  345. this._data.store.extent[0],
  346. this._data.store.extent[1],
  347. this._data.store.extent[2],
  348. this._data.store.extent[3],
  349. );
  350. }
  351. };
  352. /**
  353. * @private
  354. */
  355. I3SLayer.prototype._create3DTileset = async function (cesium3dTilesetOptions) {
  356. const inPlaceTileset = {
  357. asset: {
  358. version: "1.0",
  359. },
  360. geometricError: Number.MAX_VALUE,
  361. root: this._rootNode._create3DTileDefinition(),
  362. };
  363. const tilesetBlob = new Blob([JSON.stringify(inPlaceTileset)], {
  364. type: "application/json",
  365. });
  366. const tilesetUrl = URL.createObjectURL(tilesetBlob);
  367. const outlineColor = this._symbology?.defaultSymbology?.edges?.color;
  368. if (defined(outlineColor) && !defined(cesium3dTilesetOptions?.outlineColor)) {
  369. cesium3dTilesetOptions = defined(cesium3dTilesetOptions)
  370. ? clone(cesium3dTilesetOptions)
  371. : {};
  372. cesium3dTilesetOptions.outlineColor = Color.fromCartesian4(
  373. Cartesian4.fromArray(outlineColor),
  374. );
  375. }
  376. this._tileset = await Cesium3DTileset.fromUrl(
  377. tilesetUrl,
  378. cesium3dTilesetOptions,
  379. );
  380. this._tileset.show = this._parent.show;
  381. this._tileset._isI3STileSet = true;
  382. this._tileset.tileUnload.addEventListener(function (tile) {
  383. tile._i3sNode._clearGeometryData();
  384. URL.revokeObjectURL(tile._contentResource._url);
  385. tile._contentResource = tile._i3sNode.resource;
  386. });
  387. this._tileset.tileVisible.addEventListener(function (tile) {
  388. if (defined(tile._i3sNode)) {
  389. tile._i3sNode._loadChildren();
  390. }
  391. });
  392. };
  393. /**
  394. * @private
  395. */
  396. I3SLayer.prototype._updateVisibility = function () {
  397. if (defined(this._tileset)) {
  398. this._tileset.show = this._parent.show;
  399. }
  400. };
  401. /**
  402. * Filters the drawn elements of a layer to specific attribute names and values
  403. * @param {I3SNode.AttributeFilter[]} [filters=[]] The collection of attribute filters
  404. * @returns {Promise<void>} A promise that is resolved when the filter is applied
  405. */
  406. I3SLayer.prototype.filterByAttributes = function (filters) {
  407. // Filters are applied for each node in the layer when the node model is loaded
  408. this._filters = defined(filters) ? clone(filters, true) : [];
  409. // Forced filtering is required for loaded nodes only
  410. const rootNode = this._rootNode;
  411. if (defined(rootNode)) {
  412. return rootNode._filterFeatures();
  413. }
  414. return Promise.resolve();
  415. };
  416. export default I3SLayer;