草庐IT

Cesium之3DTileset实例管理

迦南giser 2024-01-03 原文

最近使用Cesium结合ts和react自己手动搭建了一个基本界面,加载3dTiles数据和geojson数据,动态控制图层的显隐。本来以为是非常简单的功能,但是实际操作中发现有一些地方值得注意。

搭建的效果就是这个样子,经典的顶部header+左侧布局,主视窗显示地图:

加载geojson数据源的方法很简单,Cesium.GeoJsonDataSource.load方法会返回Promise<Cesium.GeoJsonDataSource> 类型的Promise(resolve),给其指定一个固定的标识name。
代码:

  const shandongJson = Cesium.GeoJsonDataSource.load(
    '../mock/shandong.geojson',
    {
      clampToGround:true
    }
  )
  shandongJson.then((shandongJson)=>{
    shandongJson.name = '山东';
    viewRef.dataSources.add(shandongJson)
  })

因为有 Cesium.Viewer.dataSources.getByName() 方法,直接通过name属性获取对应的geojson对象。

// 涉及业务代码的部分就不放上了 重点是getByName方法
if (info.node.type === 'geojson' && viewer){
        viewer.dataSources.getByName(info.node.title as string)[0].show = info.checked;
      }

但是在加载3dTile数据时,就没这么方便了,当我向Viewer中同时添加json数据源和3dTile数据时,通过打印其存储位置,发现是这样的:

在Cesium官网上查阅没有一个可以直接获取Cesium3DTileset对象实例的方法,通过index获取获取实例显然不太可行,没办法,只能想一个其它的方法用来存储Cesium3DTileset对象。于是使用Mobx建立全局store,思路如下,Redux和Vuex也是同理,只是语法不同。

import { action, extendObservable, runInAction } from "mobx";
import * as Cesium from 'cesium'
interface tileSetListProp {
  [key: string]: Cesium.Cesium3DTileset
}
interface OBSERVABLE_PROP {
  tileSetList: tileSetListProp
}
// 可观察属性
const OBSERVABLE: OBSERVABLE_PROP = {
  tileSetList: {},
};
// 建立下面的数据结构存储
//  {
//   key1: Cesium3DTileset1,
//   key2: Cesium3DTileset2,
// }
class Tiles {
  tileSetList: tileSetListProp = {}

  constructor() {
    extendObservable(this, {
      ...OBSERVABLE
    });
  }
	// 向容器中添加新的Cesium3DTileset
  @action.bound addTileSet = (key: string, tileSet: Cesium.Cesium3DTileset) => {
    runInAction(() => {
      this.tileSetList[key] = tileSet;
    });

  }

  @action.bound update(data: any) {
    Object.assign(this, data);
  }

}

export default new Tiles();

页面加载时:引入全局变量,在数据加载时把它存进去:addTileSet(‘Building’, tileBuilding), useStores是一个工具函数,直接采用mobx的inject引用是一样的,全局store如何使用就不赘述了,网上资料很多。

const Pages: FC = () => {
  const [viewer, setViewer] = useState<null | Cesium.Viewer>(null)
  const {
    tiles: {
      tileSetList,
      addTileSet
    }
  } = useStores()

  // const viewerRef = useRef<null | Cesium.Viewer>(null);
  useEffect(() => {
    const view = initViewer();
    initTile(view)
    setViewer(view);
    const MP = new MousePosition(view);
  }, [])

  const initTile = (viewer:Cesium.Viewer) => {
    const tileBuilding = viewer!.scene.primitives.add(
      new Cesium.Cesium3DTileset({ // 3d titles
        url: '../mock/tileset.json',
      })
    )

    tileBuilding.readyPromise
      .then(function (tileBuilding: Cesium.Cesium3DTileset) {
        addTileSet('Building', tileBuilding)
      })
      .catch(function (error: any) {
        console.log(error);
      });

    const tileBIM = viewer!.scene.primitives.add(
      new Cesium.Cesium3DTileset({ // 3d titles
        url: Cesium.IonResource.fromAssetId(8564),
      })
    )

    tileBIM.readyPromise
      .then(function (tileBIM: Cesium.Cesium3DTileset) {
        addTileSet('BIM', tileBIM)
      })
      .catch(function (error: any) {
        console.log(error);
      });
  }

  const add3DTile = (tileObj: TileProp) => {
    if (!viewer) return message.error('图层未加载');      
      viewer.zoomTo(
        tileSetList[tileObj.title],
        new Cesium.HeadingPitchRange( // heading pitch roll
          0.5,
          -0.2,
          tileSetList[tileObj.title].boundingSphere.radius * 4.0
        )
      );
      return
    }
  return (
    <div className={styles.cesiumPage}>
      <MapHeader
        viewer={viewer}
        onRadioChange={add3DTile}
      />
      <div className={styles.cesiumPageContainer}>
        <LayerEdit
          viewer={viewer}
        />
        <div className={styles.baseMapWrapper}>
          <Titles />
        </div>
      </div>
    </div>
  );
}
export default Pages

同样的,在左侧组件中通过Antd的Tree勾选节点可以很方便控制显隐: tileSetList[key].show = info.checked

// 引入全局Store,只用到了tileSetList
const {
    tiles: {
      tileSetList,
      addTileSet
    }
  } = useStores()
  
 ···
 ··· // 省略无用代码
 
// Tree选中节点的触发的事件
 const onCheck = (checkedKeys:any, info:CheckInfo) => {

      const layers = getLayersById(info)
      console.log('onCheck', checkedKeys, info, layers);
      // @ts-ignore
      if (info.node.type === 'geojson' && viewer){
        viewer.dataSources.getByName(info.node.title as string)[0].show = info.checked;
      }
      // @ts-ignore
      if (info.node.type === '3dtiles' && viewer){
        const key = info.node.title as string
        console.log('3dtiles',tileSetList,tileSetList[key])
        // get方法通过索引获取数组元素,实践证明不大好用
        // viewer.scene.primitives.get(info.node.key as number).show = info.checked; 
        tileSetList[key].show = info.checked // 设置显隐
      }
    };

这样子就达到目的了。
模型显示:

模型隐藏:

有关Cesium之3DTileset实例管理的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  3. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  4. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  5. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  6. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  7. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  8. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  9. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

    我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

  10. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

随机推荐