• Cesium加载ArcGIS Server4490且orgin -400 400的切片服务


    Cesium在使用加载Cesium.ArcGisMapServerImageryProvider加载切片服务时,默认只支持wgs84的4326坐标系,不支持CGCS2000的4490坐标系。

    如果是ArcGIS发布的4490坐标系的切片服务,如果原点在orgin X: -180.0Y: 90.0的情况下,我们可以通过WebMapTileServiceImageryProvider按照WMTS的方式加载(需符合OGC标准的WMTS类型)。

    但是对于ArcGIS发布4490坐标系的切片服务,如果原点在orgin X: -400.0Y: 400.0的情况下,我们无法实现加载,本文通过示例演示实现Cesium加载ArcGIS Server4490且orgin -400 400的切片服务。

    本文使用:

    Cesium源码版本:1.94

    源码打包测试参考另一篇文档:cesium源码编译调试及调用全过程

    另外,本文的一些解释需要对切片原理有一定了解(想进一步了解的话可以去看看相关文档说明,不想了解的话按步骤修改就行了)。

    为了能够调试源码,打包的时候使用命令:npm run combine

    一、通过修改源码实现ArcGIS的切片服务,需要修改的源码文件包括:

    • ArcGisMapServerImageryProvider
    • GeographicTilingScheme
    • Ellipsoid

    1、修改ArcGisMapServerImageryProvider类

    通过查看ArcGisMapServerImageryProvider(\Source\Scene\ArcGisMapServerImageryProvider.js)源码,我们发现它不支持CGCS2000的4490坐标系(仅支持wgs84的4326坐标系):

    找到metadataSuccess方法,进行以下修改:

    (1)读取切片元数据时增加支持wkid 4490坐标系的判断,同时将切片信息也传入,目的是为了后面在获取行列号xy时,可以通过读取切片信息,使用自定义方法改写行列号的获取方式。

    复制代码
    else if (data.tileInfo.spatialReference.wkid === 4490) {
            that._tilingScheme = new GeographicTilingScheme({
              ellipsoid: options.ellipsoid,
              tileInfo: data.tileInfo,
              rectangle: that._rectangle,
              numberOfLevelZeroTilesX: options.numberOfLevelZeroTilesX,
              numberOfLevelZeroTilesY: options.numberOfLevelZeroTilesY
            });
            
            that._tilingScheme._tileInfo = data.tileInfo;//附加自定义属性
          }
    复制代码

     具体位置如图所示:

    (2)fullExtent范围增加wkid 4490坐标系判断。

    复制代码
    else if (data.fullExtent.spatialReference.wkid === 4326 || data.fullExtent.spatialReference.wkid === 4490) {
                that._rectangle = Rectangle.fromDegrees(
                  data.fullExtent.xmin,
                  data.fullExtent.ymin,
                  data.fullExtent.xmax,
                  data.fullExtent.ymax
                );
              }
    复制代码

     代码位置:

     2、修改GeographicTilingScheme类

    GeographicTilingScheme类的位置是:\Source\Core\GeographicTilingScheme.js

    通过增加4490坐标系的椭球、矩阵范围等定义,4490坐标系默认椭球为CGCS2000,矩阵范围为(-180,-90,180,90),开放矩阵范围的目的就是为了支持自定义的origin原点。

    复制代码
    if (defined(options.tileInfo)
        && defined(options.tileInfo.spatialReference)
        && defined(options.tileInfo.spatialReference.wkid)
        && options.tileInfo.spatialReference.wkid == 4490) {
        this._tileInfo = options.tileInfo;
        this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.CGCS2000);
        this._rectangle = defaultValue(options.rectangle, Rectangle.fromDegrees(-180, -90, 180, 90));
        this._numberOfLevelZeroTilesX = defaultValue(options.numberOfLevelZeroTilesX, 4);
        this._numberOfLevelZeroTilesY = defaultValue(options.numberOfLevelZeroTilesY, 2);
      }
      else {
        this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
        this._rectangle = defaultValue(options.rectangle, Rectangle.MAX_VALUE);
        this._numberOfLevelZeroTilesX = defaultValue(options.numberOfLevelZeroTilesX, 2);
        this._numberOfLevelZeroTilesY = defaultValue(options.numberOfLevelZeroTilesY, 1);
      }
      this._projection = new GeographicProjection(this._ellipsoid);
    复制代码

    代码位置: 

     (2)修改切片矩阵计算获取行列号数量xy值的原型方法getNumberOfXTilesAtLevel和getNumberOfYTilesAtLeve

    复制代码
    /**
     * Gets the total number of tiles in the X direction at a specified level-of-detail.
     *
     * @param {Number} level The level-of-detail.
     * @returns {Number} The number of tiles in the X direction at the given level.
     */
    GeographicTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) {
      // return this._numberOfLevelZeroTilesX << level;
      if (!defined(this._tileInfo)) {
        return this._numberOfLevelZeroTilesX << level
      } else { // 使用切片矩阵计算
        var currentMatrix = this._tileInfo.lods.filter(function (item) {
          return item.level === level
        })
        var currentResolution = currentMatrix[0].resolution
        // return Math.round(360 / (this._tileInfo.rows * currentResolution))
        return Math.round(CesiumMath.toDegrees(CesiumMath.TWO_PI * 2) / (this._tileInfo.rows * currentResolution));
      }
    };
    
    /**
     * Gets the total number of tiles in the Y direction at a specified level-of-detail.
     *
     * @param {Number} level The level-of-detail.
     * @returns {Number} The number of tiles in the Y direction at the given level.
     */
    GeographicTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) {
      // return this._numberOfLevelZeroTilesY << level;
      if (!defined(this._tileInfo)) {
        return this._numberOfLevelZeroTilesY << level
      } else { // 使用切片矩阵计算
        var currentMatrix = this._tileInfo.lods.filter(function (item) {
          return item.level === level
        })
        var currentResolution = currentMatrix[0].resolution
        // return Math.round(180 / (this._tileInfo.cols * currentResolution))
        return Math.round(CesiumMath.toDegrees(CesiumMath.TWO_PI * 2) / (this._tileInfo.cols * currentResolution));
      }
    };
    复制代码

    代码位置:

    这段代码和参考文章的代码存在一定出入,在文末会做详细说明。

    3、修改Ellipsoid类,定义2000椭球参数

    Ellipsoid类位置:\Source\Core\Ion.js

     定义2000椭球参数:

    复制代码
    /**
     * An Ellipsoid instance initialized to the CGCS2000 standard.
     *
     * @type {Ellipsoid}
     * @constant
     */
    Ellipsoid.CGCS2000 = Object.freeze(
      new Ellipsoid(6378137.0, 6378137.0, 6356752.31414035585)
    );
    复制代码

     代码位置:

    二、代码调用

    源码修改后,为了能够调试源码,使用npm run combine打包下代码,并在调用地方修改下引用,

    测试用html页面(做测试的页面,有一些其他代码,可以忽略,留意本文需要的代码部分):

    复制代码
    
    
    
        
        
        
        
        
        <meta
                name="viewport"
                content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
        />
        cesium加载影像和矢量数据
        
        
    
    
    
    复制代码

    1、定义椭球体部分:

     var cgs2000Ellipsolid = Cesium.Ellipsoid.CGCS2000
        var cgs2000GeographicProj = new Cesium.GeographicProjection(cgs2000Ellipsolid)

    2、初始化Cesium.Viewer时,增加2000的mapProjection

    复制代码
    let viewer = new Cesium.Viewer('cesiumContainer', {
            // baseLayerPicker: false,
            timeline: true,
            homeButton: true,
            fullscreenButton: true,
            infoBox: true,
            animation: true,
            shouldAnimate: true,
            mapProjection: cgs2000GeographicProj
            //imageryProvider: layer, //设置默认底图
        });
    复制代码

    3、调用关键代码,加载图层

    复制代码
    var world4490 = new Cesium.ArcGisMapServerImageryProvider({
            url: 'http://10.1.88.200:6080/arcgis/rest/services/test/global4490ori400/MapServer',
            //tilingScheme: myGeographicTilingScheme,
            rectangle: Cesium.Rectangle.fromDegrees(-400, -320, 320, 400),
            ellipsoid:cgs2000Ellipsolid,
            numberOfLevelZeroTilesX: 4,
            numberOfLevelZeroTilesY: 4
        });
        viewer.imageryLayers.addImageryProvider(world4490);
    复制代码

    说明:

    (1)服务说明,发布了一个全球影像4490坐标系-400,400起点的数据(切片方案保证其他参数与-180,90一致,可从本文文末获取切片方案xml文件用来切片测试)

     (2)在新建ArcGisMapServerImageryProvider时,可以不设置tilingScheme。发现ArcGisMapServerImageryProvider里有新建tilingScheme,如果设置了tilingScheme,发现这里的行列数参数没有起作用,故直接在新建ArcGisMapServerImageryProvider传入行列数参数,并在类中新建切片方案的时候读取。

    rectangle切片范围:发现用(-400, -400, 400, 400)带入整个地图偏移了:

    对切片的原理进一步了解后:

    针对-400,400起点切片,为了保证和-180,90按照0级两列一行的大小,如上图,按照格网一样90°大小,划分成4行4列,切片范围应该是(-400, -320, 320, 400),请求的切片应该6块,行列是(行在前列在后):(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)

    按照这样设置,此时再次运行后,能够正常加载-400,400起点切片了:

    以上是展开的效果,球体的效果:

     

    注:

    本文参考文章:https://blog.csdn.net/wokao253615105/article/details/123462643

    关于getNumberOfXTilesAtLevel和getNumberOfYTilesAtLeve这段代码和上述参考的文档存在一定出入做进一步说明。

    但是因为直接拷贝后发现不能正常加载,通过切片原理判断得出,计算的行列式数量不对,改成了2π*2:

     

    这里0级的话,-400,400起点切片获取的xy切片数量应该是4行4列,通过反推应该是4π,如果是-180,90起点的话,是2行1列,则x应该用2π,y应该用π,原文章应该是针对-180,90起点的计算方式。

    这段代码的写法只支持-400,400起点,因为只是为了测试能够把-400,400起点的切片数据,偷懒直接这么写了。如果要同时支持两种起点,这里的写法应该要改成公式:(右顶点X-左顶点X)/(256*分辨率);(上顶点Y-下顶点Y)/(256*分辨率)

    如:

    -180,90起点:[180-(-180)]/(256*分辨率)      范围右顶点经度-左顶点经度,256是因为切片大小是256*256,当0级时候,分辨率为0.7031250000026057

    -400,400起点:[320-(-400)]/(256*分辨率)      范围右顶点经度-左顶点经度,256是因为切片大小是256*256,当0级时候,分辨率为0.7031250000026057

     

    本文使用的数据说明及测试下载:

    影像数据:用来切4490起点-400,400的切片数据,全球影像,天地图5级数据合成的。

    链接:https://pan.baidu.com/s/14SrGonqHG9gL6ixixIFVUw

    矢量数据:用来验证与影像数据叠加是否大致吻合,全国行政区数据,包括省会,国界、省界

    链接:https://pan.baidu.com/s/1FdEaoD8rs5ogLZ2Pwa3Xxw

    切片方案:起点-400,400的切片方案

    链接:https://pan.baidu.com/s/1vTunkbMMaV3wFyXF1YwbGA

    测试页面:cesiumlayer.html

    链接:https://pan.baidu.com/s/1uNHBg-dY2xIgyyTuzllaPw

    <本文完>

     

  • 相关阅读:
    文心一言 VS 讯飞星火 VS chatgpt (97)-- 算法导论9.3 3题
    快手116购物节第四年,已成为电商领域大IP
    Effective java 总结11 - 序列化
    使用 Docker Compose V2 快速搭建日志分析平台 ELK (Elasticsearch、Logstash 和 Kibana)
    Vue_Bug npm install报错 code:128
    万字指针超详细总结
    SU-03T语音模块使用简介
    GoLong的学习之路(八)语法之Map
    Linux线程调度策略与优先级
    1355C - Count Triangles,2021CCPC桂林 C,D
  • 原文地址:https://www.cnblogs.com/kk8085/p/17350615.html