Cesium源码解析四(metadata元数据拓展中行列号的分块规则解析)

快速导航

Cesium源码解析一(搭建开发环境)
Cesium源码解析二(terrain文件的加载、解析与渲染全过程梳理)
Cesium源码解析三(metadataAvailability的含义)
Cesium源码解析四(metadata元数据拓展中行列号的分块规则解析)
Cesium源码解析五(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)

1.前言

  上一篇我们讲到了 metadataAvailability 的含义,它的值是10,意味着每10级会进行一次四叉树的构建,因此第0级和第10级中才包含有元数据的信息。但是我们并没有讲元数据中行列号的分块规则是什么,这一篇,我们就来详细讲一讲。

2.layer.json中available参数意义

  我们先来看下 Cesium ion 官方提供的 layer.json 的结构:
在这里插入图片描述
  相信只要是用过 Cesium 的同学对这个结构应该都不陌生吧。那 available 参数什么意思呢?它自然是代表了每一级全部 terrain 瓦片的行列号。我们再来看看available 的结构:
在这里插入图片描述
  可以发现,其中包含了15个数组,也就是说 terrain 可以分为15级,每个数组中有一个或多个对象描述了行列号起始和结束号码。那我们的第一个问题就来了:为什么要分块? 要讲清楚这个问题,我们就有必要先来讲下切片的原理了。

3.EPSG:4626切片及terrain分块原理

  我们以 EPSG:4326 为例来讲下切片的规则,直接上图
在这里插入图片描述
  通过上图可以明显看出,每一级中列数都是行数的2倍。当我们把第0级,1行2列,画在图上,就变成了这样:
在这里插入图片描述
  那么我们怎么用 json 对象来描述它呢?自然是

{startX:0,endX:1,startY:0,endY:0}

  同理,当我们把第1级,2行4列,画在图上,就变成了这样:
在这里插入图片描述
  同样的,我们也拿一个 json 来描述它

{startX:0,endX:3,startY:0,endY:1}

  那么,第2级,4行8列,也就顺理成章了:
在这里插入图片描述

{startX:0,endX:7,startY:0,endY:3}

  第3级,8行16列:
在这里插入图片描述

{startX:0,endX:15,startY:0,endY:7}

  第4级,16行32列,由于网格太多,我们直接给出范围:
在这里插入图片描述

{startX:0,endX:31,startY:0,endY:15}

  第5级,32行64列,我们也同样给出范围:
在这里插入图片描述

{startX:0,endX:63,startY:0,endY:31}

  注意这里出现了不同,当们观察 Cesium 的数据时,发现并不是这样的,它是分块了,我们来看下:
在这里插入图片描述  很明显,这里分了6块,当我们把它表示成图时,变成了这样:
在这里插入图片描述
  细心的读者可能会问,明明分了6块,为什么这里只有5块?仔细看 Cesium 的分块规则,就会发现,第4块类似于一条线,紧挨着第五块。
  至此,我们就可以回答第一个问题了,肯定是由于性能问题,才将较大的块分为较小的块。但是我们又产生了第二个问题:Cesium的分块规则是什么?

4.Cesium的terrain分块规则

  说实话,这个规则 Cesium 并没有公布出来,在官方论坛上搜索相关内容,也只能拿到 The availability structure is not well documented 这样的答案。于是我产生了一个想法,我们能不能按自己的规则来分块呢?
  有了这个想法以后,我们就产生了第三个问题:按多少块来划分呢?

5.自定义terrain分块规则

  还是要回到我们切片规则那里,让我们仔细的回忆一下,跟我一起念,观自在菩萨,行深波若波罗密多时,照见五蕴皆空,度一切苦恶,是玄奘翻译的,咳咳咳,跑题了,赶紧回来:
第0级 1行2列 共2块
第1级 2行4列 共8块
第2级 4行8列 共32块
第3级 8行16列 共512块
第4级 16行32列 共2048块

512!!!这绝对是一个吉利的数字!!!,就它了,从第3级开始刚好是512块,以后每一级都是512的倍数,非常合适。
  那么 Cesium 是多少个网格分一块的呢?仔细看上面第5级的划分,有的是600块,有的是200块,很迷,难怪 Cesium 官网没给出文档。我们就按512来。
  因为第0是两个切片,当层级放大时,由这两个切片继续往下分,所以我们可以将所有切片分为两棵树,即左边的0/0/0.terrain所代表的左树和0/1/0.terrain所代表的右树。其实 Cesium 里也是两棵树,这一点我们是一样的。

6.实验

  既然思路有了,那我们就开始撸代码了,直接上源码。因为 DEM 数据精度不是很高,只算到了11级。

public void GetAvailable()throws IOException {
        long startx,endx,starty,endy;
        String json="C:\\Users\\zhangiser\\Desktop\\available.json";
        File file=new File(json);
        if(file.exists()){
            file.delete();
            file.createNewFile();
        }else {
            file.createNewFile();
        }
        FileWriter fw=new FileWriter(file);
        BufferedWriter bw=new BufferedWriter(fw);
        fw.write("[");
        //计算每一级的瓦片个数
        int max=11;
        for(int i=1;i<max;i++){
            long row = (long) Math.pow(2, i);
            long col=row*2;
            long total=row*col;
            fw.write("[");
            if(total<512){
                //如果瓦片个数没超过512,记录start和end信息
                startx=starty=0;
                endx=col-1;
                endy=row-1;
                fw.write(String.format("{\"startX\":%s,\"endX\":%s,\"startY\":%s,\"endY\":%s}",startx,endx,starty,endy));
            }else {
                //如果大于等于512,那么循环取512个网格为一个块
                //第4级 16行32列  共512个网格,但需要注意的是000和010的子节点会将这些网格平分
                //所以后续的分块方案是:
                //行按16为中断点,列按32位中断点
                long ysplit=row/16;
                long xsplit=col/32;
                for (int j = 0; j <ysplit; j++) {
                    for (int k = 0; k < xsplit; k++) {
                        startx=k*32;
                        endx=(k+1)*32-1;
                        starty=j*16;
                        endy=(j+1)*16-1;
                        if(j==ysplit-1 && k==xsplit-1){
                            fw.write(String.format("{\"startX\":%s,\"endX\":%s,\"startY\":%s,\"endY\":%s}",startx,endx,starty,endy));
                        }else{
                            fw.write(String.format("{\"startX\":%s,\"endX\":%s,\"startY\":%s,\"endY\":%s},",startx,endx,starty,endy));
                        }

                    }
                }

            }
            if(i<max-1) fw.write("],");
            else fw.write("]");
        }
        fw.write("]");
        fw.close();
        bw.close();
    }

这是生成layer.json中的available参数的方法,生成左树和右树的方法类似,就不列举了。

  最后,把我们的数据存到 sessionStorage 中,替换掉 Cesium 自己的,看看效果。
在这里插入图片描述
在这里插入图片描述

  完美,大功告成。

7.总结

  本篇博文中,我们深入的分析了 Cesiumterrainmetadata(元数据)这一拓展的组织形式和分块规则,并成功的修改了它的规则,用自己的修改好的规则和Cesium 的数据结合起来,也能实现数据的顺利加载。最后提一句,CesiumLabCesium 的规则也不一样,有兴趣可以自行对比。好了,本篇就讲到这里了,回见~

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 CZML ,一个实体的位置可以用一个时间序列来表示。每个时间节点都对应着一个位置信息。你可以通过解析 CZML 文件的每个时间节点的位置信息来获取每个时间点上实体的位置。 在具体实现上,你可以使用 Cesium 的 CZMLDataSource 类来加载 CZML 文件,并且可以使用 `entity.position.getValue(time)` 方法来获取指定时间点上的位置信息。该方法会返回一个 `Cartesian3` 类型的对象,表示三维坐标系的位置。在获取位置信息后,你可以将其存储到一个数组,以供后续处理使用。 以下是一个简单的代码示例,演示了如何获取 CZML 实体每200毫秒的位置信息: ```javascript // 加载 CZML 文件 const czmlDataSource = new Cesium.CzmlDataSource(); viewer.dataSources.add(czmlDataSource); czmlDataSource.load('path/to/czml/file.czml').then(() => { // 获取实体 const entity = czmlDataSource.entities.getById('myEntity'); // 获取时间范围 const startTime = entity.availability.start; const endTime = entity.availability.stop; const duration = Cesium.JulianDate.secondsDifference(endTime, startTime); // 获取每200毫秒的位置信息 const positions = []; for (let i = 0; i <= duration; i += 0.2) { const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate()); const position = entity.position.getValue(time); positions.push(position); } // 处理位置信息 // ... }); ``` 需要注意的是,以上示例的时间节点间隔为200毫秒,这个值可以根据实际需求进行调整。同时,由于 CZML 的时间是采用 JulianDate 格式表示的,因此需要使用 Cesium 提供的 JulianDate 类进行时间计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AIGIS.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值