• Cesium关于Entity中的parent、isShowing、entityCollection和监听事件的探讨


    Entity在项目中使用的比较多,但是有几个属性值一直没有使用到,抱着好奇的心态去看了下源码。这些属性实际发挥着什么样的作用。

    definitionChanged

    我接触过的项目中,很少有使用到这个事件监听器。它的用法比较简单,直接entity.definitionChanged.addEventListener(方法,上下文);在官方文档的Event有案例。

    监听事件接收四个参数,第一个参数为当前的entity,第二个参数为更改属性的名称,比如position更改了就会是字符串形式的'position',第三个值为更改之后的新值newValue,第四个值为更改之前的旧值oldValue。

    源码中直接赋予监听事件的属性值有show,parent,isShowing这三个。

    1. // 203行 到 220行
    2. function updateShow(entity, children, isShowing) {
    3. const length = children.length;
    4. for (let i = 0; i < length; i++) {
    5. const child = children[i];
    6. const childShow = child._show;
    7. const oldValue = !isShowing && childShow;
    8. const newValue = isShowing && childShow;
    9. if (oldValue !== newValue) {
    10. updateShow(child, child._children, isShowing);
    11. }
    12. }
    13. entity._definitionChanged.raiseEvent(
    14. entity,
    15. "isShowing",
    16. isShowing,
    17. !isShowing
    18. );
    19. }
    20. // 268行 到 293行
    21. show: {
    22. get: function () {
    23. return this._show;
    24. },
    25. set: function (value) {
    26. //>>includeStart('debug', pragmas.debug);
    27. if (!defined(value)) {
    28. throw new DeveloperError("value is required.");
    29. }
    30. //>>includeEnd('debug');
    31. if (value === this._show) {
    32. return;
    33. }
    34. const wasShowing = this.isShowing;
    35. this._show = value;
    36. const isShowing = this.isShowing;
    37. if (wasShowing !== isShowing) {
    38. updateShow(this, this._children, isShowing);
    39. }
    40. this._definitionChanged.raiseEvent(this, "show", value, !value);
    41. },
    42. }
    43. // 314行 到 344行
    44. parent: {
    45. get: function () {
    46. return this._parent;
    47. },
    48. set: function (value) {
    49. const oldValue = this._parent;
    50. if (oldValue === value) {
    51. return;
    52. }
    53. const wasShowing = this.isShowing;
    54. if (defined(oldValue)) {
    55. const index = oldValue._children.indexOf(this);
    56. oldValue._children.splice(index, 1);
    57. }
    58. this._parent = value;
    59. if (defined(value)) {
    60. value._children.push(this);
    61. }
    62. const isShowing = this.isShowing;
    63. if (wasShowing !== isShowing) {
    64. updateShow(this, this._children, isShowing);
    65. }
    66. this._definitionChanged.raiseEvent(this, "parent", value, oldValue);
    67. },
    68. }

    其他属性的更改也是可以被监听到的,只不过并没有在Entity.js文件中直接触发监听。而是通过了两种方法。createPropertyTypeDescriptor和createPropertyDescriptor。

    虽然是两个,最后执行的都是createPropertyDescriptor方法,另一个只是做了检查而已。最终实现监听的是在createPropertyDescriptor.js 中的createProperty方法里。

    1. function createProperty(
    2. name,
    3. privateName,
    4. subscriptionName,
    5. configurable,
    6. createPropertyCallback
    7. ) {
    8. return {
    9. configurable: configurable,
    10. get: function () {
    11. return this[privateName];
    12. },
    13. set: function (value) {
    14. const oldValue = this[privateName];
    15. const subscription = this[subscriptionName];
    16. if (defined(subscription)) {
    17. subscription();
    18. this[subscriptionName] = undefined;
    19. }
    20. const hasValue = value !== undefined;
    21. if (
    22. hasValue &&
    23. (!defined(value) || !defined(value.getValue)) &&
    24. defined(createPropertyCallback)
    25. ) {
    26. value = createPropertyCallback(value);
    27. }
    28. if (oldValue !== value) {
    29. this[privateName] = value;
    30. this._definitionChanged.raiseEvent(this, name, value, oldValue);
    31. }
    32. if (defined(value) && defined(value.definitionChanged)) {
    33. this[subscriptionName] = value.definitionChanged.addEventListener(
    34. function () {
    35. this._definitionChanged.raiseEvent(this, name, value, value);
    36. },
    37. this
    38. );
    39. }
    40. },
    41. };
    42. }

    对entity设置监听,我个人的理解可以应用的场景在于一些无法预期下一个值的追踪上。

    比如,地图上有一个entity对象,这个对象并不会根据时间的推移而改变位置。而是通过接口或者某个定时行为来设置它的position。这时候就可以设置监听事件来针对position改变来实现代码功能。这种设置只针对于,无法知晓在哪里更新了position值,或者说使用的是其他人封装好的方法,没法在源码基础上添加代码;以及需要延时处理。因为,能直接设置entity的position值的地方,应该可以多放点代码进去。

    parent和isShowing

     按官方文档的意思,parent的值是一个entity,代表着它的父级entity。有父级就代表着有子级,可以通过entity._children来知道,有多少个entity儿子。

    重新设置parent的时候,如果设置的值与原有的值不相同。则会先把原先的_children中移除当前的entity,然后再加入到新设置的值的_children。这样就不会造成一个entity同时存在于另外两个entity的_children中,当然,人为添加的行为除外。

    _parent和_children最终会影响到的,就是当前entity的show属性是否有效。官方文档的解释中,如果你为entity的show设置为了true,那么当它的parent的显示也为true的时候才会显示。说人话就是如果你有parent,你的show并不由你决定了,你要看父级的脸色。

    1. // 300行 到 308行
    2. isShowing: {
    3. get: function () {
    4. return (
    5. this._show &&
    6. (!defined(this.entityCollection) || this.entityCollection.show) &&
    7. (!defined(this._parent) || this._parent.isShowing)
    8. );
    9. },
    10. }

    show的值决定是否显示,并不代表着一定会显示。如果需要知道一个entity是否在屏幕内显示,比较简单的判断方法就是进行两次判断。

    entity.entityCollection && entity.isShowing

     如果那种自定义的entityCollection或者CustomDataSource,判断就复杂一点。需要用对应的contains方法看是否被添加到viewer的entities或者dataSources里面。

    当使用new Cesium.Entity()方式创建一个entity的时候,它并没有添加到哪个entityCollection里面去显示,所以单纯的isShowing判断的并不准确。

    子级受父级的限制,那么父级的变化就会影响到子级。体现在了updateShow方法上,它会去遍历所有子级。

    1. function updateShow(entity, children, isShowing) {
    2. const length = children.length;
    3. for (let i = 0; i < length; i++) {
    4. const child = children[i];
    5. const childShow = child._show;
    6. const oldValue = !isShowing && childShow;
    7. const newValue = isShowing && childShow;
    8. if (oldValue !== newValue) {
    9. updateShow(child, child._children, isShowing);
    10. }
    11. }
    12. entity._definitionChanged.raiseEvent(
    13. entity,
    14. "isShowing",
    15. isShowing,
    16. !isShowing
    17. );
    18. }

    常见的用法就是多个entity有关联性,同时显示同时消失,将这些entity的parent都设置为同一个entity。这样控制显示的时候就不用去遍历所有的。也要看量,如果数量比较多,为什么不用entityCollection呢?

    实际项目上能用到的场景,比如,一个柱子cylinder和一个标签label。你想让label显示在柱子顶端,但是cylinder根据position定位,label也一样。柱子需要拔地而起,标签需要落在柱子上,那就只能创建两个entity,拥有相同的position定位,只不过label定位的高度是柱子的高度,柱子贴地就是了。

    entityCollection

    前面两个属性的探讨就是它全部的作用了。官方文档的解释说,通过这个值可以知道当前的entity属于哪个collection集合里面。言简意骇。

    在使用viewer.entities.add()方法将entity添加进去的时候,在add方法内部就将entity的entityCollection就指向了viewer.entities了。

  • 相关阅读:
    Java和JavaScript区别与联系
    解决<get-metadataDependencyResolutions>(...) must not be null的问题
    有方N58 HTTP POST 请求连接 TDengine
    代码越写越乱?那是因为你没用责任链
    git仓库的基本使用
    SpringMVC 资源状态转移RESTful
    使用Vite快速构建Vue3+ts+pinia脚手架
    MATLAB算法实战应用案例精讲-【智能优化算法】灰狼算法(GWO) (附MATLAB和Python源码)
    2022-8-6 集合容器
    从特斯拉林某某事件看Transformer
  • 原文地址:https://blog.csdn.net/GhostPaints/article/details/126010465