Cesium3DTileFeature.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. import Color from "../Core/Color.js";
  2. import defined from "../Core/defined.js";
  3. /**
  4. * A feature of a {@link Cesium3DTileset}.
  5. * <p>
  6. * Provides access to a feature's properties stored in the tile's batch table, as well
  7. * as the ability to show/hide a feature and change its highlight color via
  8. * {@link Cesium3DTileFeature#show} and {@link Cesium3DTileFeature#color}, respectively.
  9. * </p>
  10. * <p>
  11. * Modifications to a <code>Cesium3DTileFeature</code> object have the lifetime of the tile's
  12. * content. If the tile's content is unloaded, e.g., due to it going out of view and needing
  13. * to free space in the cache for visible tiles, listen to the {@link Cesium3DTileset#tileUnload} event to save any
  14. * modifications. Also listen to the {@link Cesium3DTileset#tileVisible} event to reapply any modifications.
  15. * </p>
  16. * <p>
  17. * Do not construct this directly. Access it through {@link Cesium3DTileContent#getFeature}
  18. * or picking using {@link Scene#pick}.
  19. * </p>
  20. *
  21. * @alias Cesium3DTileFeature
  22. * @constructor
  23. *
  24. * @example
  25. * // On mouse over, display all the properties for a feature in the console log.
  26. * handler.setInputAction(function(movement) {
  27. * const feature = scene.pick(movement.endPosition);
  28. * if (feature instanceof Cesium.Cesium3DTileFeature) {
  29. * const propertyIds = feature.getPropertyIds();
  30. * const length = propertyIds.length;
  31. * for (let i = 0; i < length; ++i) {
  32. * const propertyId = propertyIds[i];
  33. * console.log(`{propertyId}: ${feature.getProperty(propertyId)}`);
  34. * }
  35. * }
  36. * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  37. */
  38. function Cesium3DTileFeature(content, batchId) {
  39. this._content = content;
  40. this._batchId = batchId;
  41. this._color = undefined; // for calling getColor
  42. }
  43. Object.defineProperties(Cesium3DTileFeature.prototype, {
  44. /**
  45. * Gets or sets if the feature will be shown. This is set for all features
  46. * when a style's show is evaluated.
  47. *
  48. * @memberof Cesium3DTileFeature.prototype
  49. *
  50. * @type {boolean}
  51. *
  52. * @default true
  53. */
  54. show: {
  55. get: function () {
  56. return this._content.batchTable.getShow(this._batchId);
  57. },
  58. set: function (value) {
  59. this._content.batchTable.setShow(this._batchId, value);
  60. },
  61. },
  62. /**
  63. * Gets or sets the highlight color multiplied with the feature's color. When
  64. * this is white, the feature's color is not changed. This is set for all features
  65. * when a style's color is evaluated.
  66. *
  67. * @memberof Cesium3DTileFeature.prototype
  68. *
  69. * @type {Color}
  70. *
  71. * @default {@link Color.WHITE}
  72. */
  73. color: {
  74. get: function () {
  75. if (!defined(this._color)) {
  76. this._color = new Color();
  77. }
  78. return this._content.batchTable.getColor(this._batchId, this._color);
  79. },
  80. set: function (value) {
  81. this._content.batchTable.setColor(this._batchId, value);
  82. },
  83. },
  84. /**
  85. * Gets a typed array containing the ECEF positions of the polyline.
  86. * Returns undefined if {@link Cesium3DTileset#vectorKeepDecodedPositions} is false
  87. * or the feature is not a polyline in a vector tile.
  88. *
  89. * @memberof Cesium3DTileFeature.prototype
  90. *
  91. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  92. *
  93. * @type {Float64Array}
  94. */
  95. polylinePositions: {
  96. get: function () {
  97. if (!defined(this._content.getPolylinePositions)) {
  98. return undefined;
  99. }
  100. return this._content.getPolylinePositions(this._batchId);
  101. },
  102. },
  103. /**
  104. * Gets the content of the tile containing the feature.
  105. *
  106. * @memberof Cesium3DTileFeature.prototype
  107. *
  108. * @type {Cesium3DTileContent}
  109. *
  110. * @readonly
  111. * @private
  112. */
  113. content: {
  114. get: function () {
  115. return this._content;
  116. },
  117. },
  118. /**
  119. * Gets the tileset containing the feature.
  120. *
  121. * @memberof Cesium3DTileFeature.prototype
  122. *
  123. * @type {Cesium3DTileset}
  124. *
  125. * @readonly
  126. */
  127. tileset: {
  128. get: function () {
  129. return this._content.tileset;
  130. },
  131. },
  132. /**
  133. * All objects returned by {@link Scene#pick} have a <code>primitive</code> property. This returns
  134. * the tileset containing the feature.
  135. *
  136. * @memberof Cesium3DTileFeature.prototype
  137. *
  138. * @type {Cesium3DTileset}
  139. *
  140. * @readonly
  141. */
  142. primitive: {
  143. get: function () {
  144. return this._content.tileset;
  145. },
  146. },
  147. /**
  148. * Get the feature ID associated with this feature. For 3D Tiles 1.0, the
  149. * batch ID is returned. For EXT_mesh_features, this is the feature ID from
  150. * the selected feature ID set.
  151. *
  152. * @memberof Cesium3DTileFeature.prototype
  153. *
  154. * @type {number}
  155. *
  156. * @readonly
  157. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  158. */
  159. featureId: {
  160. get: function () {
  161. return this._batchId;
  162. },
  163. },
  164. /**
  165. * @private
  166. */
  167. pickId: {
  168. get: function () {
  169. return this._content.batchTable.getPickColor(this._batchId);
  170. },
  171. },
  172. });
  173. /**
  174. * Returns whether the feature contains this property. This includes properties from this feature's
  175. * class and inherited classes when using a batch table hierarchy.
  176. *
  177. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_batch_table_hierarchy}
  178. *
  179. * @param {string} name The case-sensitive name of the property.
  180. * @returns {boolean} Whether the feature contains this property.
  181. */
  182. Cesium3DTileFeature.prototype.hasProperty = function (name) {
  183. return this._content.batchTable.hasProperty(this._batchId, name);
  184. };
  185. /**
  186. * Returns an array of property IDs for the feature. This includes properties from this feature's
  187. * class and inherited classes when using a batch table hierarchy.
  188. *
  189. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_batch_table_hierarchy}
  190. *
  191. * @param {string[]} [results] An array into which to store the results.
  192. * @returns {string[]} The IDs of the feature's properties.
  193. */
  194. Cesium3DTileFeature.prototype.getPropertyIds = function (results) {
  195. return this._content.batchTable.getPropertyIds(this._batchId, results);
  196. };
  197. /**
  198. * Returns a copy of the value of the feature's property with the given name. This includes properties from this feature's
  199. * class and inherited classes when using a batch table hierarchy.
  200. *
  201. * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_batch_table_hierarchy}
  202. *
  203. * @param {string} name The case-sensitive name of the property.
  204. * @returns {*} The value of the property or <code>undefined</code> if the feature does not have this property.
  205. *
  206. * @example
  207. * // Display all the properties for a feature in the console log.
  208. * const propertyIds = feature.getPropertyIds();
  209. * const length = propertyIds.length;
  210. * for (let i = 0; i < length; ++i) {
  211. * const propertyId = propertyIds[i];
  212. * console.log(`{propertyId}: ${feature.getProperty(propertyId)}`);
  213. * }
  214. */
  215. Cesium3DTileFeature.prototype.getProperty = function (name) {
  216. return this._content.batchTable.getProperty(this._batchId, name);
  217. };
  218. /**
  219. * Returns a copy of the feature's property with the given name, examining all
  220. * the metadata from 3D Tiles 1.0 formats, the EXT_structural_metadata and legacy
  221. * EXT_feature_metadata glTF extensions, and the metadata present either in the
  222. * tileset JSON (3D Tiles 1.1) or in the 3DTILES_metadata 3D Tiles extension.
  223. * Metadata is checked against name from most specific to most general and the
  224. * first match is returned. Metadata is checked in this order:
  225. *
  226. * <ol>
  227. * <li>Batch table (structural metadata) property by semantic</li>
  228. * <li>Batch table (structural metadata) property by property ID</li>
  229. * <li>Content metadata property by semantic</li>
  230. * <li>Content metadata property by property</li>
  231. * <li>Tile metadata property by semantic</li>
  232. * <li>Tile metadata property by property ID</li>
  233. * <li>Subtree metadata property by semantic</li>
  234. * <li>Subtree metadata property by property ID</li>
  235. * <li>Group metadata property by semantic</li>
  236. * <li>Group metadata property by property ID</li>
  237. * <li>Tileset metadata property by semantic</li>
  238. * <li>Tileset metadata property by property ID</li>
  239. * <li>Otherwise, return undefined</li>
  240. * </ol>
  241. * <p>
  242. * For 3D Tiles Next details, see the {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_metadata|3DTILES_metadata Extension}
  243. * for 3D Tiles, as well as the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata|EXT_structural_metadata Extension}
  244. * for glTF. For the legacy glTF extension, see {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata|EXT_feature_metadata Extension}
  245. * </p>
  246. *
  247. * @param {Cesium3DTileContent} content The content for accessing the metadata
  248. * @param {number} batchId The batch ID (or feature ID) of the feature to get a property for
  249. * @param {string} name The semantic or property ID of the feature. Semantics are checked before property IDs in each granularity of metadata.
  250. * @return {*} The value of the property or <code>undefined</code> if the feature does not have this property.
  251. *
  252. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  253. */
  254. Cesium3DTileFeature.getPropertyInherited = function (content, batchId, name) {
  255. const batchTable = content.batchTable;
  256. if (defined(batchTable)) {
  257. if (batchTable.hasPropertyBySemantic(batchId, name)) {
  258. return batchTable.getPropertyBySemantic(batchId, name);
  259. }
  260. if (batchTable.hasProperty(batchId, name)) {
  261. return batchTable.getProperty(batchId, name);
  262. }
  263. }
  264. const contentMetadata = content.metadata;
  265. if (defined(contentMetadata)) {
  266. if (contentMetadata.hasPropertyBySemantic(name)) {
  267. return contentMetadata.getPropertyBySemantic(name);
  268. }
  269. if (contentMetadata.hasProperty(name)) {
  270. return contentMetadata.getProperty(name);
  271. }
  272. }
  273. const tile = content.tile;
  274. const tileMetadata = tile.metadata;
  275. if (defined(tileMetadata)) {
  276. if (tileMetadata.hasPropertyBySemantic(name)) {
  277. return tileMetadata.getPropertyBySemantic(name);
  278. }
  279. if (tileMetadata.hasProperty(name)) {
  280. return tileMetadata.getProperty(name);
  281. }
  282. }
  283. let subtreeMetadata;
  284. if (defined(tile.implicitSubtree)) {
  285. subtreeMetadata = tile.implicitSubtree.metadata;
  286. }
  287. if (defined(subtreeMetadata)) {
  288. if (subtreeMetadata.hasPropertyBySemantic(name)) {
  289. return subtreeMetadata.getPropertyBySemantic(name);
  290. }
  291. if (subtreeMetadata.hasProperty(name)) {
  292. return subtreeMetadata.getProperty(name);
  293. }
  294. }
  295. const groupMetadata = defined(content.group)
  296. ? content.group.metadata
  297. : undefined;
  298. if (defined(groupMetadata)) {
  299. if (groupMetadata.hasPropertyBySemantic(name)) {
  300. return groupMetadata.getPropertyBySemantic(name);
  301. }
  302. if (groupMetadata.hasProperty(name)) {
  303. return groupMetadata.getProperty(name);
  304. }
  305. }
  306. const tilesetMetadata = content.tileset.metadata;
  307. if (defined(tilesetMetadata)) {
  308. if (tilesetMetadata.hasPropertyBySemantic(name)) {
  309. return tilesetMetadata.getPropertyBySemantic(name);
  310. }
  311. if (tilesetMetadata.hasProperty(name)) {
  312. return tilesetMetadata.getProperty(name);
  313. }
  314. }
  315. return undefined;
  316. };
  317. /**
  318. * Returns a copy of the value of the feature's property with the given name.
  319. * If the feature is contained within a tileset that has metadata (3D Tiles 1.1)
  320. * or uses the <code>3DTILES_metadata</code> extension, tileset, group and tile
  321. * metadata is inherited.
  322. * <p>
  323. * To resolve name conflicts, this method resolves names from most specific to
  324. * least specific by metadata granularity in the order: feature, tile, group,
  325. * tileset. Within each granularity, semantics are resolved first, then other
  326. * properties.
  327. * </p>
  328. * @param {string} name The case-sensitive name of the property.
  329. * @returns {*} The value of the property or <code>undefined</code> if the feature does not have this property.
  330. * @private
  331. */
  332. Cesium3DTileFeature.prototype.getPropertyInherited = function (name) {
  333. return Cesium3DTileFeature.getPropertyInherited(
  334. this._content,
  335. this._batchId,
  336. name
  337. );
  338. };
  339. /**
  340. * Sets the value of the feature's property with the given name.
  341. * <p>
  342. * If a property with the given name doesn't exist, it is created.
  343. * </p>
  344. *
  345. * @param {string} name The case-sensitive name of the property.
  346. * @param {*} value The value of the property that will be copied.
  347. *
  348. * @exception {DeveloperError} Inherited batch table hierarchy property is read only.
  349. *
  350. * @example
  351. * const height = feature.getProperty('Height'); // e.g., the height of a building
  352. *
  353. * @example
  354. * const name = 'clicked';
  355. * if (feature.getProperty(name)) {
  356. * console.log('already clicked');
  357. * } else {
  358. * feature.setProperty(name, true);
  359. * console.log('first click');
  360. * }
  361. */
  362. Cesium3DTileFeature.prototype.setProperty = function (name, value) {
  363. this._content.batchTable.setProperty(this._batchId, name, value);
  364. // PERFORMANCE_IDEA: Probably overkill, but maybe only mark the tile dirty if the
  365. // property is in one of the style's expressions or - if it can be done quickly -
  366. // if the new property value changed the result of an expression.
  367. this._content.featurePropertiesDirty = true;
  368. };
  369. /**
  370. * Returns whether the feature's class name equals <code>className</code>. Unlike {@link Cesium3DTileFeature#isClass}
  371. * this function only checks the feature's exact class and not inherited classes.
  372. * <p>
  373. * This function returns <code>false</code> if no batch table hierarchy is present.
  374. * </p>
  375. *
  376. * @param {string} className The name to check against.
  377. * @returns {boolean} Whether the feature's class name equals <code>className</code>
  378. *
  379. * @private
  380. */
  381. Cesium3DTileFeature.prototype.isExactClass = function (className) {
  382. return this._content.batchTable.isExactClass(this._batchId, className);
  383. };
  384. /**
  385. * Returns whether the feature's class or any inherited classes are named <code>className</code>.
  386. * <p>
  387. * This function returns <code>false</code> if no batch table hierarchy is present.
  388. * </p>
  389. *
  390. * @param {string} className The name to check against.
  391. * @returns {boolean} Whether the feature's class or inherited classes are named <code>className</code>
  392. *
  393. * @private
  394. */
  395. Cesium3DTileFeature.prototype.isClass = function (className) {
  396. return this._content.batchTable.isClass(this._batchId, className);
  397. };
  398. /**
  399. * Returns the feature's class name.
  400. * <p>
  401. * This function returns <code>undefined</code> if no batch table hierarchy is present.
  402. * </p>
  403. *
  404. * @returns {string} The feature's class name.
  405. *
  406. * @private
  407. */
  408. Cesium3DTileFeature.prototype.getExactClassName = function () {
  409. return this._content.batchTable.getExactClassName(this._batchId);
  410. };
  411. export default Cesium3DTileFeature;