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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. import Cartographic from "../Core/Cartographic.js";
  2. import Check from "../Core/Check.js";
  3. import defined from "../Core/defined.js";
  4. import Ellipsoid from "../Core/Ellipsoid.js";
  5. import HeadingPitchRoll from "../Core/HeadingPitchRoll.js";
  6. import Matrix3 from "../Core/Matrix3.js";
  7. import Matrix4 from "../Core/Matrix4.js";
  8. import Resource from "../Core/Resource.js";
  9. import Quaternion from "../Core/Quaternion.js";
  10. import Transforms from "../Core/Transforms.js";
  11. import Cesium3DTile from "./Cesium3DTile.js";
  12. import I3SDataProvider from "./I3SDataProvider.js";
  13. import I3SDecoder from "./I3SDecoder.js";
  14. import I3SFeature from "./I3SFeature.js";
  15. import I3SField from "./I3SField.js";
  16. import I3SGeometry from "./I3SGeometry.js";
  17. /**
  18. * @typedef {object} I3SNode.AttributeFilter
  19. *
  20. * A filter given by an attribute name and values.
  21. * The 3D feature object should be hidden if its value for the attribute name is not specified in the collection of values.
  22. *
  23. * @property {string} name The name of the attribute
  24. * @property {string[]|number[]} values The collection of values
  25. */
  26. /**
  27. * This class implements an I3S Node. In CesiumJS each I3SNode creates a Cesium3DTile.
  28. * <p>
  29. * Do not construct this directly, instead access tiles through {@link I3SLayer}.
  30. * </p>
  31. * @alias I3SNode
  32. * @internalConstructor
  33. */
  34. function I3SNode(parent, ref, isRoot) {
  35. let level;
  36. let layer;
  37. let nodeIndex;
  38. let resource;
  39. if (isRoot) {
  40. level = 0;
  41. layer = parent;
  42. } else {
  43. level = parent._level + 1;
  44. layer = parent._layer;
  45. }
  46. if (typeof ref === "number") {
  47. nodeIndex = ref;
  48. } else {
  49. resource = parent.resource.getDerivedResource({
  50. url: `${ref}/`,
  51. });
  52. }
  53. this._parent = parent;
  54. this._dataProvider = parent._dataProvider;
  55. this._isRoot = isRoot;
  56. this._level = level;
  57. this._layer = layer;
  58. this._nodeIndex = nodeIndex;
  59. this._resource = resource;
  60. this._isLoading = false;
  61. this._tile = undefined;
  62. this._data = undefined;
  63. this._geometryData = [];
  64. this._featureData = [];
  65. this._fields = {};
  66. this._children = [];
  67. this._childrenReadyPromise = undefined;
  68. this._globalTransform = undefined;
  69. this._inverseGlobalTransform = undefined;
  70. this._inverseRotationMatrix = undefined;
  71. this._symbologyData = undefined;
  72. }
  73. Object.defineProperties(I3SNode.prototype, {
  74. /**
  75. * Gets the resource for the node.
  76. * @memberof I3SNode.prototype
  77. * @type {Resource}
  78. * @readonly
  79. */
  80. resource: {
  81. get: function () {
  82. return this._resource;
  83. },
  84. },
  85. /**
  86. * Gets the parent layer.
  87. * @memberof I3SNode.prototype
  88. * @type {I3SLayer}
  89. * @readonly
  90. */
  91. layer: {
  92. get: function () {
  93. return this._layer;
  94. },
  95. },
  96. /**
  97. * Gets the parent node.
  98. * @memberof I3SNode.prototype
  99. * @type {I3SNode|undefined}
  100. * @readonly
  101. */
  102. parent: {
  103. get: function () {
  104. return this._parent;
  105. },
  106. },
  107. /**
  108. * Gets the children nodes.
  109. * @memberof I3SNode.prototype
  110. * @type {I3SNode[]}
  111. * @readonly
  112. */
  113. children: {
  114. get: function () {
  115. return this._children;
  116. },
  117. },
  118. /**
  119. * Gets the collection of geometries.
  120. * @memberof I3SNode.prototype
  121. * @type {I3SGeometry[]}
  122. * @readonly
  123. */
  124. geometryData: {
  125. get: function () {
  126. return this._geometryData;
  127. },
  128. },
  129. /**
  130. * Gets the collection of features.
  131. * @memberof I3SNode.prototype
  132. * @type {I3SFeature[]}
  133. * @readonly
  134. */
  135. featureData: {
  136. get: function () {
  137. return this._featureData;
  138. },
  139. },
  140. /**
  141. * Gets the collection of fields.
  142. * @memberof I3SNode.prototype
  143. * @type {I3SField[]}
  144. * @readonly
  145. */
  146. fields: {
  147. get: function () {
  148. return this._fields;
  149. },
  150. },
  151. /**
  152. * Gets the Cesium3DTile for this node.
  153. * @memberof I3SNode.prototype
  154. * @type {Cesium3DTile}
  155. * @readonly
  156. */
  157. tile: {
  158. get: function () {
  159. return this._tile;
  160. },
  161. },
  162. /**
  163. * Gets the I3S data for this object.
  164. * @memberof I3SNode.prototype
  165. * @type {object}
  166. * @readonly
  167. */
  168. data: {
  169. get: function () {
  170. return this._data;
  171. },
  172. },
  173. });
  174. /**
  175. * @private
  176. */
  177. I3SNode.prototype.load = async function () {
  178. const that = this;
  179. function processData() {
  180. if (!that._isRoot) {
  181. // Create a new tile
  182. const tileDefinition = that._create3DTileDefinition();
  183. that._tile = new Cesium3DTile(
  184. that._layer._tileset,
  185. that._dataProvider.resource,
  186. tileDefinition,
  187. that._parent._tile,
  188. );
  189. that._tile._i3sNode = that;
  190. }
  191. }
  192. // If we don't have a nodepage index load from json
  193. if (!defined(this._nodeIndex)) {
  194. const data = await I3SDataProvider.loadJson(this._resource);
  195. that._data = data;
  196. processData();
  197. return;
  198. }
  199. const node = await this._layer._getNodeInNodePages(this._nodeIndex);
  200. that._data = node;
  201. let uri;
  202. if (that._isRoot) {
  203. uri = "nodes/root/";
  204. } else if (defined(node.mesh)) {
  205. const uriIndex = node.mesh.geometry.resource;
  206. uri = `../${uriIndex}/`;
  207. }
  208. if (defined(uri) && defined(that._parent.resource)) {
  209. that._resource = that._parent.resource.getDerivedResource({ url: uri });
  210. }
  211. processData();
  212. };
  213. function createAndLoadField(node, storageInfo) {
  214. const newField = new I3SField(node, storageInfo);
  215. node._fields[storageInfo.name] = newField;
  216. return newField.load();
  217. }
  218. /**
  219. * Loads the node fields.
  220. * @returns {Promise<void>} A promise that is resolved when the I3S Node fields are loaded
  221. */
  222. I3SNode.prototype.loadFields = function () {
  223. // Check if we must load fields
  224. const fields = this._layer._data.attributeStorageInfo;
  225. const promises = [];
  226. if (defined(fields)) {
  227. for (let i = 0; i < fields.length; i++) {
  228. const storageInfo = fields[i];
  229. const field = this._fields[storageInfo.name];
  230. if (defined(field)) {
  231. promises.push(field.load());
  232. } else {
  233. promises.push(createAndLoadField(this, storageInfo));
  234. }
  235. }
  236. }
  237. return Promise.all(promises);
  238. };
  239. /**
  240. * Loads the node field.
  241. * @param {string} name The field name
  242. * @returns {Promise<void>} A promise that is resolved when the I3S Node field is loaded
  243. */
  244. I3SNode.prototype.loadField = function (name) {
  245. //>>includeStart('debug', pragmas.debug);
  246. Check.defined("name", name);
  247. //>>includeEnd('debug');
  248. const field = this._fields[name];
  249. if (defined(field)) {
  250. return field.load();
  251. }
  252. const fields = this._layer._data.attributeStorageInfo;
  253. if (defined(fields)) {
  254. for (let i = 0; i < fields.length; i++) {
  255. const storageInfo = fields[i];
  256. if (storageInfo.name === name) {
  257. return createAndLoadField(this, storageInfo);
  258. }
  259. }
  260. }
  261. return Promise.resolve();
  262. };
  263. /**
  264. * Returns the fields for a given picked position
  265. * @param {Cartesian3} pickedPosition The picked position
  266. * @returns {object} Object containing field names and their values
  267. */
  268. I3SNode.prototype.getFieldsForPickedPosition = function (pickedPosition) {
  269. const geometry = this.geometryData[0];
  270. if (!defined(geometry.customAttributes.featureIndex)) {
  271. return {};
  272. }
  273. const location = geometry.getClosestPointIndexOnTriangle(
  274. pickedPosition.x,
  275. pickedPosition.y,
  276. pickedPosition.z,
  277. );
  278. if (
  279. location.index === -1 ||
  280. location.index > geometry.customAttributes.featureIndex.length
  281. ) {
  282. return {};
  283. }
  284. const featureIndex = geometry.customAttributes.featureIndex[location.index];
  285. return this.getFieldsForFeature(featureIndex);
  286. };
  287. /**
  288. * Returns the fields for a given feature
  289. * @param {number} featureIndex Index of the feature whose attributes we want to get
  290. * @returns {object} Object containing field names and their values
  291. */
  292. I3SNode.prototype.getFieldsForFeature = function (featureIndex) {
  293. const featureFields = {};
  294. for (const fieldName in this.fields) {
  295. if (this.fields.hasOwnProperty(fieldName)) {
  296. const field = this.fields[fieldName];
  297. if (featureIndex >= 0 && featureIndex < field.values.length) {
  298. featureFields[field.name] = field.values[featureIndex];
  299. }
  300. }
  301. }
  302. return featureFields;
  303. };
  304. /**
  305. * @private
  306. */
  307. I3SNode.prototype._loadChildren = function () {
  308. const that = this;
  309. // If the promise for loading the children was already created, just return it
  310. if (defined(this._childrenReadyPromise)) {
  311. return this._childrenReadyPromise;
  312. }
  313. const childPromises = [];
  314. if (defined(that._data.children)) {
  315. for (
  316. let childIndex = 0;
  317. childIndex < that._data.children.length;
  318. childIndex++
  319. ) {
  320. const child = that._data.children[childIndex];
  321. const newChild = new I3SNode(that, child.href ?? child, false);
  322. that._children.push(newChild);
  323. childPromises.push(newChild.load());
  324. }
  325. }
  326. this._childrenReadyPromise = Promise.all(childPromises).then(function () {
  327. for (let i = 0; i < that._children.length; i++) {
  328. that._tile.children.push(that._children[i]._tile);
  329. }
  330. });
  331. return this._childrenReadyPromise;
  332. };
  333. /**
  334. * @private
  335. */
  336. I3SNode.prototype._loadGeometryData = function () {
  337. const geometryPromises = [];
  338. // To debug decoding for a specific tile, add a condition
  339. // that wraps this if/else to match the tile uri
  340. if (defined(this._data.geometryData)) {
  341. for (
  342. let geomIndex = 0;
  343. geomIndex < this._data.geometryData.length;
  344. geomIndex++
  345. ) {
  346. const curGeometryData = new I3SGeometry(
  347. this,
  348. this._data.geometryData[geomIndex].href,
  349. );
  350. this._geometryData.push(curGeometryData);
  351. geometryPromises.push(curGeometryData.load());
  352. }
  353. } else if (defined(this._data.mesh)) {
  354. const geometryDefinition = this._layer._findBestGeometryBuffers(
  355. this._data.mesh.geometry.definition,
  356. ["position", "uv0"],
  357. );
  358. const geometryURI = `./geometries/${geometryDefinition.bufferIndex}/`;
  359. const newGeometryData = new I3SGeometry(this, geometryURI);
  360. newGeometryData._geometryDefinitions = geometryDefinition.definition;
  361. newGeometryData._geometryBufferInfo = geometryDefinition.geometryBufferInfo;
  362. this._geometryData.push(newGeometryData);
  363. geometryPromises.push(newGeometryData.load());
  364. }
  365. return Promise.all(geometryPromises);
  366. };
  367. /**
  368. * @private
  369. */
  370. I3SNode.prototype._loadFeatureData = function () {
  371. const featurePromises = [];
  372. // To debug decoding for a specific tile, add a condition
  373. // that wraps this if/else to match the tile uri
  374. if (defined(this._data.featureData)) {
  375. for (
  376. let featureIndex = 0;
  377. featureIndex < this._data.featureData.length;
  378. featureIndex++
  379. ) {
  380. const newFeatureData = new I3SFeature(
  381. this,
  382. this._data.featureData[featureIndex].href,
  383. );
  384. this._featureData.push(newFeatureData);
  385. featurePromises.push(newFeatureData.load());
  386. }
  387. }
  388. return Promise.all(featurePromises);
  389. };
  390. /**
  391. * @private
  392. */
  393. I3SNode.prototype._clearGeometryData = function () {
  394. this._geometryData = [];
  395. };
  396. /**
  397. * @private
  398. */
  399. I3SNode.prototype._create3DTileDefinition = function () {
  400. const obb = this._data.obb;
  401. const mbs = this._data.mbs;
  402. if (!defined(obb) && !defined(mbs)) {
  403. console.error("Failed to load I3S node. Bounding volume is required.");
  404. return undefined;
  405. }
  406. let geoPosition;
  407. if (defined(obb)) {
  408. geoPosition = Cartographic.fromDegrees(
  409. obb.center[0],
  410. obb.center[1],
  411. obb.center[2],
  412. );
  413. } else {
  414. geoPosition = Cartographic.fromDegrees(mbs[0], mbs[1], mbs[2]);
  415. }
  416. // Offset bounding box position if we have a geoid service defined
  417. if (defined(this._dataProvider._geoidDataList) && defined(geoPosition)) {
  418. for (let i = 0; i < this._dataProvider._geoidDataList.length; i++) {
  419. const tile = this._dataProvider._geoidDataList[i];
  420. const projectedPos = tile.projection.project(geoPosition);
  421. if (
  422. projectedPos.x > tile.nativeExtent.west &&
  423. projectedPos.x < tile.nativeExtent.east &&
  424. projectedPos.y > tile.nativeExtent.south &&
  425. projectedPos.y < tile.nativeExtent.north
  426. ) {
  427. geoPosition.height += sampleGeoid(projectedPos.x, projectedPos.y, tile);
  428. break;
  429. }
  430. }
  431. }
  432. let boundingVolume = {};
  433. let position;
  434. let span = 0;
  435. if (defined(obb)) {
  436. boundingVolume = {
  437. box: [
  438. 0,
  439. 0,
  440. 0,
  441. obb.halfSize[0],
  442. 0,
  443. 0,
  444. 0,
  445. obb.halfSize[1],
  446. 0,
  447. 0,
  448. 0,
  449. obb.halfSize[2],
  450. ],
  451. };
  452. span = Math.max(
  453. Math.max(this._data.obb.halfSize[0], this._data.obb.halfSize[1]),
  454. this._data.obb.halfSize[2],
  455. );
  456. position = Ellipsoid.WGS84.cartographicToCartesian(geoPosition);
  457. } else {
  458. boundingVolume = {
  459. sphere: [0, 0, 0, mbs[3]],
  460. };
  461. position = Ellipsoid.WGS84.cartographicToCartesian(geoPosition);
  462. span = this._data.mbs[3];
  463. }
  464. span *= 2;
  465. // Compute the geometric error
  466. let metersPerPixel = Infinity;
  467. // Get the meters/pixel density required to pop the next LOD
  468. if (defined(this._data.lodThreshold)) {
  469. if (
  470. this._layer._data.nodePages.lodSelectionMetricType ===
  471. "maxScreenThresholdSQ"
  472. ) {
  473. const maxScreenThreshold = Math.sqrt(
  474. this._data.lodThreshold / (Math.PI * 0.25),
  475. );
  476. metersPerPixel = span / maxScreenThreshold;
  477. } else if (
  478. this._layer._data.nodePages.lodSelectionMetricType ===
  479. "maxScreenThreshold"
  480. ) {
  481. const maxScreenThreshold = this._data.lodThreshold;
  482. metersPerPixel = span / maxScreenThreshold;
  483. } else {
  484. // Other LOD selection types can only be used for point cloud data
  485. console.error("Invalid lodSelectionMetricType in Layer");
  486. }
  487. } else if (defined(this._data.lodSelection)) {
  488. for (
  489. let lodIndex = 0;
  490. lodIndex < this._data.lodSelection.length;
  491. lodIndex++
  492. ) {
  493. if (
  494. this._data.lodSelection[lodIndex].metricType === "maxScreenThreshold"
  495. ) {
  496. metersPerPixel = span / this._data.lodSelection[lodIndex].maxError;
  497. }
  498. }
  499. }
  500. if (metersPerPixel === Infinity) {
  501. metersPerPixel = 100000;
  502. }
  503. // Calculate the length of 16 pixels in order to trigger the screen space error
  504. const geometricError = metersPerPixel * 16;
  505. // Transformations
  506. const hpr = new HeadingPitchRoll(0, 0, 0);
  507. let orientation = Transforms.headingPitchRollQuaternion(position, hpr);
  508. if (defined(this._data.obb)) {
  509. orientation = new Quaternion(
  510. this._data.obb.quaternion[0],
  511. this._data.obb.quaternion[1],
  512. this._data.obb.quaternion[2],
  513. this._data.obb.quaternion[3],
  514. );
  515. }
  516. const rotationMatrix = Matrix3.fromQuaternion(orientation);
  517. const inverseRotationMatrix = Matrix3.inverse(rotationMatrix, new Matrix3());
  518. const globalTransform = new Matrix4(
  519. rotationMatrix[0],
  520. rotationMatrix[1],
  521. rotationMatrix[2],
  522. 0,
  523. rotationMatrix[3],
  524. rotationMatrix[4],
  525. rotationMatrix[5],
  526. 0,
  527. rotationMatrix[6],
  528. rotationMatrix[7],
  529. rotationMatrix[8],
  530. 0,
  531. position.x,
  532. position.y,
  533. position.z,
  534. 1,
  535. );
  536. const inverseGlobalTransform = Matrix4.inverse(
  537. globalTransform,
  538. new Matrix4(),
  539. );
  540. const localTransform = Matrix4.clone(globalTransform);
  541. if (defined(this._parent._globalTransform)) {
  542. Matrix4.multiply(
  543. globalTransform,
  544. this._parent._inverseGlobalTransform,
  545. localTransform,
  546. );
  547. }
  548. this._globalTransform = globalTransform;
  549. this._inverseGlobalTransform = inverseGlobalTransform;
  550. this._inverseRotationMatrix = inverseRotationMatrix;
  551. // get children definition
  552. const childrenDefinition = [];
  553. for (let childIndex = 0; childIndex < this._children.length; childIndex++) {
  554. childrenDefinition.push(
  555. this._children[childIndex]._create3DTileDefinition(),
  556. );
  557. }
  558. // Create a tile set
  559. const inPlaceTileDefinition = {
  560. children: childrenDefinition,
  561. refine: "REPLACE",
  562. boundingVolume: boundingVolume,
  563. transform: [
  564. localTransform[0],
  565. localTransform[4],
  566. localTransform[8],
  567. localTransform[12],
  568. localTransform[1],
  569. localTransform[5],
  570. localTransform[9],
  571. localTransform[13],
  572. localTransform[2],
  573. localTransform[6],
  574. localTransform[10],
  575. localTransform[14],
  576. localTransform[3],
  577. localTransform[7],
  578. localTransform[11],
  579. localTransform[15],
  580. ],
  581. content: {
  582. uri: defined(this._resource) ? this._resource.url : undefined,
  583. },
  584. geometricError: geometricError,
  585. };
  586. return inPlaceTileDefinition;
  587. };
  588. /**
  589. * @private
  590. */
  591. I3SNode.prototype._loadSymbology = async function () {
  592. if (!defined(this._symbologyData) && defined(this._layer._symbology)) {
  593. this._symbologyData = await this._layer._symbology._getSymbology(this);
  594. }
  595. };
  596. /**
  597. * @private
  598. */
  599. I3SNode.prototype._createContentURL = async function () {
  600. let rawGltf = {
  601. scene: 0,
  602. scenes: [
  603. {
  604. nodes: [0],
  605. },
  606. ],
  607. nodes: [
  608. {
  609. name: "singleNode",
  610. },
  611. ],
  612. meshes: [],
  613. buffers: [],
  614. bufferViews: [],
  615. accessors: [],
  616. materials: [],
  617. textures: [],
  618. images: [],
  619. samplers: [],
  620. asset: {
  621. version: "2.0",
  622. },
  623. };
  624. // Load the geometry data
  625. const dataPromises = [this._loadGeometryData()];
  626. if (this._dataProvider.legacyVersion16) {
  627. dataPromises.push(this._loadFeatureData());
  628. }
  629. await Promise.all(dataPromises);
  630. // Binary glTF
  631. if (defined(this._geometryData) && this._geometryData.length > 0) {
  632. if (this._dataProvider._applySymbology) {
  633. await this._loadSymbology();
  634. }
  635. const url = this._geometryData[0].resource.url;
  636. const geometrySchema = this._layer._data.store.defaultGeometrySchema;
  637. const geometryData = this._geometryData[0];
  638. const result = await I3SDecoder.decode(
  639. url,
  640. geometrySchema,
  641. geometryData,
  642. this._featureData[0],
  643. this._symbologyData,
  644. );
  645. if (!defined(result)) {
  646. // Postponed
  647. return;
  648. }
  649. rawGltf = geometryData._generateGltf(
  650. result.meshData.nodesInScene,
  651. result.meshData.nodes,
  652. result.meshData.meshes,
  653. result.meshData.buffers,
  654. result.meshData.bufferViews,
  655. result.meshData.accessors,
  656. result.meshData.rootExtensions,
  657. result.meshData.extensionsUsed,
  658. );
  659. this._geometryData[0]._customAttributes = result.meshData._customAttributes;
  660. }
  661. const binaryGltfData = this._dataProvider._binarizeGltf(rawGltf);
  662. const glbDataBlob = new Blob([binaryGltfData], {
  663. type: "application/binary",
  664. });
  665. return URL.createObjectURL(glbDataBlob);
  666. };
  667. async function loadFilters(node) {
  668. const filters = node._layer._filters;
  669. const promises = [];
  670. for (let i = 0; i < filters.length; i++) {
  671. const promise = node.loadField(filters[i].name);
  672. promises.push(promise);
  673. }
  674. await Promise.all(promises);
  675. return filters;
  676. }
  677. function checkFeatureValue(featureIndex, field, filter) {
  678. if (!defined(filter.values) || filter.values.length === 0) {
  679. return false;
  680. }
  681. const fieldValues = defined(field) ? field.values : [];
  682. let featureValue;
  683. if (featureIndex < fieldValues.length) {
  684. featureValue = fieldValues[featureIndex];
  685. }
  686. let matches = false;
  687. for (let i = 0; i < filter.values.length; i++) {
  688. if (filter.values[i] === featureValue) {
  689. matches = true;
  690. break;
  691. }
  692. }
  693. return matches;
  694. }
  695. async function filterFeatures(node, contentModel) {
  696. const batchTable = node._tile.content.batchTable;
  697. if (defined(batchTable) && batchTable.featuresLength > 0) {
  698. batchTable.setAllShow(true);
  699. const filters = await loadFilters(node);
  700. if (filters.length > 0) {
  701. for (
  702. let featureIndex = 0;
  703. featureIndex < batchTable.featuresLength;
  704. featureIndex++
  705. ) {
  706. for (let filterIndex = 0; filterIndex < filters.length; filterIndex++) {
  707. const filter = filters[filterIndex];
  708. if (
  709. !checkFeatureValue(featureIndex, node._fields[filter.name], filter)
  710. ) {
  711. batchTable.setShow(featureIndex, false);
  712. break;
  713. }
  714. }
  715. }
  716. }
  717. }
  718. contentModel.show = true;
  719. }
  720. /**
  721. * @private
  722. */
  723. I3SNode.prototype._filterFeatures = function () {
  724. const promises = [];
  725. // Forced filtering is required for loaded nodes only
  726. for (let i = 0; i < this._children.length; i++) {
  727. const promise = this._children[i]._filterFeatures();
  728. promises.push(promise);
  729. }
  730. // Filters are applied for nodes with geometry data only
  731. const contentModel = this._tile?.content?._model;
  732. if (
  733. defined(this._geometryData) &&
  734. this._geometryData.length > 0 &&
  735. defined(contentModel) &&
  736. contentModel.ready
  737. ) {
  738. // The model needs to be hidden till the filters are applied
  739. contentModel.show = false;
  740. const promise = filterFeatures(this, contentModel);
  741. promises.push(promise);
  742. }
  743. return Promise.all(promises);
  744. };
  745. // Reimplement Cesium3DTile.prototype.requestContent so that
  746. // We get a chance to load our own gltf from I3S data
  747. Cesium3DTile.prototype._hookedRequestContent =
  748. Cesium3DTile.prototype.requestContent;
  749. /**
  750. * Requests the tile's content.
  751. * <p>
  752. * The request may not be made if the Cesium Request Scheduler can't prioritize it.
  753. * </p>
  754. *
  755. * @return {Promise<Cesium3DTileContent>|undefined} A promise that resolves when the request completes, or undefined if there is no request needed, or the request cannot be scheduled.
  756. * @private
  757. */
  758. Cesium3DTile.prototype.requestContent = function () {
  759. if (!this.tileset._isI3STileSet) {
  760. return this._hookedRequestContent();
  761. }
  762. if (!this._isLoading) {
  763. this._isLoading = true;
  764. const that = this;
  765. return this._i3sNode
  766. ._createContentURL()
  767. .then((url) => {
  768. if (!defined(url)) {
  769. that._isLoading = false;
  770. return;
  771. }
  772. that._contentResource = new Resource({ url: url });
  773. return that._hookedRequestContent();
  774. })
  775. .then((content) => {
  776. // Filters are applied for nodes with geometry data only
  777. const contentModel = content?._model;
  778. if (
  779. defined(that._i3sNode._geometryData) &&
  780. that._i3sNode._geometryData.length > 0 &&
  781. defined(contentModel)
  782. ) {
  783. // The model needs to be hidden till the filters are applied
  784. contentModel.show = false;
  785. contentModel.readyEvent.addEventListener(() => {
  786. filterFeatures(that._i3sNode, contentModel);
  787. });
  788. }
  789. that._isLoading = false;
  790. return content;
  791. });
  792. }
  793. };
  794. function bilinearInterpolate(tx, ty, h00, h10, h01, h11) {
  795. const a = h00 * (1 - tx) + h10 * tx;
  796. const b = h01 * (1 - tx) + h11 * tx;
  797. return a * (1 - ty) + b * ty;
  798. }
  799. function sampleMap(u, v, width, data) {
  800. const address = u + v * width;
  801. return data[address];
  802. }
  803. function sampleGeoid(sampleX, sampleY, geoidData) {
  804. const extent = geoidData.nativeExtent;
  805. let x =
  806. ((sampleX - extent.west) / (extent.east - extent.west)) *
  807. (geoidData.width - 1);
  808. let y =
  809. ((sampleY - extent.south) / (extent.north - extent.south)) *
  810. (geoidData.height - 1);
  811. const xi = Math.floor(x);
  812. let yi = Math.floor(y);
  813. x -= xi;
  814. y -= yi;
  815. const xNext = xi < geoidData.width ? xi + 1 : xi;
  816. let yNext = yi < geoidData.height ? yi + 1 : yi;
  817. yi = geoidData.height - 1 - yi;
  818. yNext = geoidData.height - 1 - yNext;
  819. const h00 = sampleMap(xi, yi, geoidData.width, geoidData.buffer);
  820. const h10 = sampleMap(xNext, yi, geoidData.width, geoidData.buffer);
  821. const h01 = sampleMap(xi, yNext, geoidData.width, geoidData.buffer);
  822. const h11 = sampleMap(xNext, yNext, geoidData.width, geoidData.buffer);
  823. let finalHeight = bilinearInterpolate(x, y, h00, h10, h01, h11);
  824. finalHeight = finalHeight * geoidData.scale + geoidData.offset;
  825. return finalHeight;
  826. }
  827. Object.defineProperties(Cesium3DTile.prototype, {
  828. /**
  829. * Gets the I3S Node for the tile.
  830. * @memberof Cesium3DTile.prototype
  831. * @type {string}
  832. */
  833. i3sNode: {
  834. get: function () {
  835. return this._i3sNode;
  836. },
  837. },
  838. });
  839. export default I3SNode;