深入浅出学习切片LOD——ArcGIS server模拟缓存切片(影像快显)

一、第一次实践

原理

        免切片实现影像服务的模拟切片,主要原理是接收前端传过来的xyz(行列层级)以及切片方案,计算出该请求的切片的四至经纬度信息,通过mapserver的exportImage接口,传入每个模拟切片的四至经纬度信息得到图片返回。好似前端请求了http://xxx/mapserver/z/y/x,后台返回给前端一张图片(切片),让客户端无感知的就像是请求了缓存切片。

        而这问题的关键在于获取目标图片的四至经纬度信息。怎么计算?

        首先,咱们需要知道细节层次模型LOD。LOD技术即Levels of Detail的简称,意为多细节层次(百度解释的描述会越描越黑)。实际上就是世界地图展示的时候想看某个细节,那么就需要缩放(因为屏幕大小不会变),但缩放不能直接把整个图缩放,数据细节太大,放不下。那只需要把感兴趣的细节部分数据返回给客户拼接到一起,查看不同级别就返回不同级别的图片。

        这里模拟切片,第0级是一张图片,经度从-180到180,纬度从-90到90。流行方式是以左上角(-180,90)为坐标原点(origin),计算每个片的最小经纬度,最大经纬度(xmin,ymin,xmax,ymax)。

         如计算第1级的四张瓦片中的右上角,直观答案是经度0到180,纬度0到90。这个数据从公式中如何计算得来呢?如上图中x,y的计算方式:最小经度xmin(0)= 原点的经度(-180)+偏移量(偏移的瓦片数量(1)*第一级瓦片宽度(以角度为单位,也就是180)),这样就可以算出每个瓦片的最小最大经纬度。

        上图中用的resolution*256是什么意思,这是为了理解常说的切片方案中的参数,其中256是一张瓦片的宽度(256px像素),经过反算得出来的参数结果与天地图常用的切片方案一致。0级的resolution是0.703125,每个层级的resolution是上一层级的½。

lodinfos: [
          { 'level': 0, 'resolution': 0.703125, 'scale': 295497598.570834 },
          { 'level': 1, 'resolution': 0.3515625, 'scale': 147748799.285417 },
          { 'level': 2, 'resolution': 0.17578125, 'scale': 73874399.6427087 },
          { 'level': 3, 'resolution': 0.087890625, 'scale': 36937199.8213544 },
          { 'level': 4, 'resolution': 0.0439453125, 'scale': 18468599.9106772 },
          { 'level': 5, 'resolution': 0.02197265625, 'scale': 9234299.95533859 },
          { 'level': 6, 'resolution': 0.010986328125, 'scale': 4617149.97766929 },...
        ]

获得了这些参数信息,再输入/z/y/x,我们就获得了层级/行/列,通过公式便可计算出图片四至。这里有第一个坑,每个正方形瓦片的经度和纬度的边长是不一样的,宽(经度)是长(纬度)的两倍,可以结合第0级正方形理解,经度总共是360度,纬度总共是180度。

var lt_x = -180 + col * lodinfos[level].resolution * tilecols;
var lt_y = 90 - row * lodinfos[level].resolution * tilerows / 2;
var rb_x = -180  + (col + 1) * lodinfos[level].resolution * tilecols;
var rb_y = 90 - (row + 1) * lodinfos[level].resolution * tilerows / 2;

貌似大功告成,写了个本地服务测试两个切片结果,拉出来检验一下:用ArcGIS的切片服务看看

https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/2/1/2

         两个图片的最小经度和最大经度值是一样的,但纬度差异很大。一开始我以为是公式错误,或者切片方案的问题,实际上这里是投影的问题。从一开始我的计算方案就是以地理坐标的经纬度计算的,瓦片切分下来就是正轴墨卡托投影,而ArcGIS server使用的是横轴墨卡托投影(关于投影可以看看这个一文了解地图投影 - 知乎),所以两者并不对等,当细节级别更高时两者的结果就相差十万八千里了。

二、第二次实践

思路

        既然知道了原因,那么我们可以直接计算投影坐标下的四至,而不是经纬度四至。需要先获取0级的四至,即全球基准四至,然后每一级都可以通过上一级二分来计算。这里的基准四至可以从切片方案中获取也可以从ArcGIS服务中获取

      let deltX = (xmax -xmin)/Math.pow(2,level); //level级别的瓦片宽度 0级一张图,1级两张图
      let deltY = (ymax -ymin)/Math.pow(2,level); //level级别的瓦片高度 0级一张图,1级两张图
      let lt_x = xmin + col * deltX ;
      let lt_y = ymax - row * deltY ;
      let rb_x = xmin + (col + 1) * deltX ;
      let rb_y = ymax - (row + 1) * deltY ;

再拉出来看看结果

        怎么回事???为什么还是不一样???

        原来是取错数值了,我取原图的四至的时候使用了initial extent计算的,这个范围是超出了图本身的范围full extent,所以在计算的时候有偏差(initial不是正方形也有影响)。

        更新使用fullextent参数试试12层级的图片,大体上接近了,还有少许经度差异,可以再深入研究研究。

三、最终实践

正解

        第二次实践是自作聪明了。因为切片方案定义的就是以原点为起点,加偏移量去计算值。使用二分法必然不符合他原始的切片(尽管上面看起来只有微小差距,到了13级也是天差万别...)规则,纯属搞笑**行为。老老实实使用原点+切片方案计算偏移:

      const resolution = 156543.03392800014; //0级resolution(一张图)
      const originX = -2.0037508342787E7;
      const originY = 2.0037508342787E7;
      const delta = resolution*256/Math.pow(2,level);
      let lt_x = originX + col * delta ;
      let lt_y = originY - row * delta ;
      let rb_x = originX + (col + 1) * delta ;
      let rb_y = originY - (row + 1) * delta ;

检验:

鸣金收兵!

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在OSGEarth中,你可以使用LOD(Level of Detail)技术来实现分层级显示模型。LOD技术可以根据观察距离和需要显示的细节级别,选择合适的模型来渲染,以提高性能和效率。 以下是在OSGEarth中使用LOD分层级显示模型的基本步骤: 1. 创建多个细节级别的模型:根据你的需求,创建多个不同细节级别的模型,例如高细节级别的模型和低细节级别的模型。 2. 设置LOD节点:使用OSG的LOD节点(osg::LOD)来组织不同细节级别的模型。LOD节点是一个容器,可以根据距离来选择合适的子节点进行渲染。 3. 设置距离范围和细节级别:为LOD节点的每个子节点设置距离范围和对应的细节级别。例如,当相机距离LOD节点较远时,选择低细节级别的模型进行渲染;当相机距离LOD节点较近时,选择高细节级别的模型进行渲染。 4. 添加LOD节点到场景图中:将LOD节点添加到OSGEarth的场景图中,以便在渲染时自动根据相机距离选择合适的模型进行显示。 下面是一个简单的示例代码,演示如何在OSGEarth中使用LOD分层级显示模型: ```cpp osg::ref_ptr<osg::LOD> lodNode = new osg::LOD; // 添加不同细节级别的模型 osg::ref_ptr<osg::Node> highDetailModel = createHighDetailModel(); osg::ref_ptr<osg::Node> lowDetailModel = createLowDetailModel(); lodNode->addChild(highDetailModel, 0.0, 100.0); // 设置高细节级别模型的显示范围 lodNode->addChild(lowDetailModel, 100.0, FLT_MAX); // 设置低细节级别模型的显示范围 // 将LOD节点添加到场景图中 osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(lodNode); // 创建Viewer和ViewerBase对象,渲染场景图 osgViewer::Viewer viewer; viewer.setSceneData(root); viewer.run(); ``` 在上面的示例中,我们创建了一个LOD节点 `lodNode`,并向其添加了两个子节点,分别是高细节级别的模型和低细节级别的模型。通过设置子节点的距离范围,我们指定了在不同距离下显示不同细节级别的模型。最后,我们将LOD节点添加到场景图中,并使用OSG的Viewer对象进行渲染。 这样,你就可以在OSGEarth中使用LOD分层级显示模型了。根据相机距离的变化,OSGEarth会自动选择合适的细节级别的模型进行渲染,以实现分层级的显示效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值