最近使用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 // 设置显隐
}
};
这样子就达到目的了。
模型显示:
模型隐藏: