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

模型隐藏:

我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如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个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我正在处理旧代码的一部分。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_
我安装了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
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作: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作为该等式的第二部分,但这仍然是主要问题。
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认
我想用这两种语言中的任何一种(最好是ruby)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生