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页面(做测试的页面,有一些其他代码,可以忽略,留意本文需要的代码部分):

<!DOCTYPE html>
<html lang="en">
<head>
<!-- Use correct character set. -->
<meta charset="utf-8"/>
<!-- Tell IE to use the latest, best version. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<title>cesium加载影像和矢量数据</title>
<script src="../Build/CesiumUnminified/Cesium.js"></script>
<style>
@import url(../Build/CesiumUnminified/Widgets/widgets.css); html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
//天地图token
let TDT_tk = "b0df1f950b1fd6914abe9e17079c0345";
//Cesium token
let cesium_tk = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1NThjYTk0MC03YjQwLTQ3YWYtOTY5Yy04NDk3OTJmMmI4NDciLCJpZCI6NzAxOTMsImlhdCI6MTYzNDA4ODE1N30.TB1v9XXATQLUGE5GNki_fYFMHddIyQ9arXPIx65e09s";
//天地图影像
let TDT_IMG_C = "http://{s}.tianditu.gov.cn/img_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
"&LAYER=img&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
"&style=default&format=tiles&tk=" + TDT_tk; //标注
let TDT_CIA_C = "http://{s}.tianditu.gov.cn/cia_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
"&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
"&style=default&format=tiles&tk=" + TDT_tk; //初始页面加载
//Cesium.Ion.defaultAccessToken = cesium_tk;
var cgs2000Ellipsolid = Cesium.Ellipsoid.CGCS2000
var cgs2000GeographicProj = new Cesium.GeographicProjection(cgs2000Ellipsolid)
let viewer = new Cesium.Viewer('cesiumContainer', {
// baseLayerPicker: false,
timeline: true,
homeButton: true,
fullscreenButton: true,
infoBox: true,
animation: true,
shouldAnimate: true,
mapProjection: cgs2000GeographicProj
//imageryProvider: layer, //设置默认底图
});
let rightTilt = true;
if (rightTilt) {
viewer.scene.screenSpaceCameraController.tiltEventTypes = [
Cesium.CameraEventType.RIGHT_DRAG,
Cesium.CameraEventType.PINCH,
{
eventType: Cesium.CameraEventType.LEFT_DRAG,
modifier: Cesium.KeyboardEventModifier.CTRL
},
{
eventType: Cesium.CameraEventType.RIGHT_DRAG,
modifier: Cesium.KeyboardEventModifier.CTRL
}
]
viewer.scene.screenSpaceCameraController.zoomEventTypes = [
Cesium.CameraEventType.MIDDLE_DRAG,
Cesium.CameraEventType.WHEEL,
Cesium.CameraEventType.PINCH
]
}
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(evt) {
var cartesian=viewer.camera.pickEllipsoid(evt.position,viewer.scene.globe.ellipsoid);
var cartographic=Cesium.Cartographic.fromCartesian(cartesian);
var lng=Cesium.Math.toDegrees(cartographic.longitude);//经度值
var lat=Cesium.Math.toDegrees(cartographic.latitude);//纬度值
var mapPosition={x:lng,y:lat,z:cartographic.height};//cartographic.height的值始终为零。
alert("longitude:" + lng + ";latitude:" + lat );
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); viewer.imageryLayers.remove(viewer.imageryLayers.get(0))
//添加tms
let tms = {};
tms.url = "http://10.0.7.16:81/tms";
if (tms) {
const layerInfo = {
url: tms.url,
fileExtension: tms.fileExtension || 'jpg',
maximumLevel: tms.maxZoom || 7,
name: 'tms'
}
const tmsService = new Cesium.TileMapServiceImageryProvider(layerInfo)
tmsService.layerInfo = layerInfo
}
//添加地形
let terrain = {};
terrain.url = "http://data.marsgis.cn/terrain";
if (terrain) {
const terrainLayer = new Cesium.CesiumTerrainProvider({
url: terrain.url
})
viewer.terrainProvider = terrainLayer
} _matrixIds = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"]
//调用影响中文注记服务
/*viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: TDT_CIA_C,
layer: "tdtImg_c",
style: "default",
format: "tiles",
tileMatrixSetID: "c",
subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
tilingScheme: new Cesium.GeographicTilingScheme(),
tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
maximumLevel: 50,
show: false
}))*/ /*var myGeographicTilingScheme = new Cesium.GeographicTilingScheme({
ellipsoid: cgs2000Ellipsolid,
rectangle: Cesium.Rectangle.fromDegrees(-400, -399.9999999999998, 400, 399.9999999999998),
numberOfLevelZeroTilesX: 4,
numberOfLevelZeroTilesY: 4
})*/
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);
//viewer.imageryLayers.addImageryProvider(world);
//使用ArcGisMapServerImageryProvider加载影像没成功,改用WebMapServiceImageryProvider
//var world = new Cesium.ArcGisMapServerImageryProvider({
//url:'http://10.1.88.200:6080/arcgis/rest/services/test/globaltdt5/MapServer',
//});
//viewer.imageryLayers.addImageryProvider(world);
var arcgisyx = new Cesium.WebMapServiceImageryProvider({
url:'http://10.1.88.200:6080/arcgis/rest/services/test/globaltdt5/MapServer/tile/{z}/{y}/{x}',
layers:[0]
});
// viewer.imageryLayers.addImageryProvider(arcgisyx);
var china = new Cesium.ArcGisMapServerImageryProvider({
url:'http://10.1.88.200:6080/arcgis/rest/services/test/china4490/MapServer'
});
viewer.imageryLayers.addImageryProvider(china);
</script>
</body>
</html>

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

<本文完>

Cesium加载ArcGIS Server4490且orgin -400 400的切片服务的更多相关文章

  1. Cesium加载三维倾斜摄影数据

    具体技术来源自论文 基于Cesium的倾斜摄影三维模型Web加载与应用研究. 技术架构图 应用实例 利用一个实际实例来详细说明如何利用Cesium加载倾斜摄影数据,并进行可视化和交互操作. 首先,利用 ...

  2. maptalks 如何加载 ArcGIS 瓦片图层

    最近需要加载 ArcGIS 瓦片图层,运行官网加载 ArcGIS 瓦片图层的 demo 是没有问题的.如果把 ArcGIS 瓦片图层 URL 换成是自已发布的 ArcGIS 地图服务,发现加载不出来, ...

  3. cesium加载gltf模型

    cesium加载gltf模型 一.采用vue-cesium:在项目里加载依赖包.命令如下: npm i --save vue-cesium 在main.js中加入如下代码: https://www.n ...

  4. Leaflet,OpenLayers3加载ArcGIS切片(png格式,Exploded松散型)

    需求 做了一个简单的WebGIS应用,不想因为加载切片就安装一台GIS服务器.于是想直接访问图片的方式来加载地图. 需解决的问题 leafletjs目前是不能够直接加载ArcGIS服务切片的,但可以借 ...

  5. Cesium加载地形数据只显示半个地球

    Cesium第0级地形包括两个瓦片:0/0/0.terrain,0/1/0.terrain,分别为左半球和右半球(具体参考:https://blog.csdn.net/u013929284/artic ...

  6. cesium加载gltf模型点击以及列表点击定位弹窗

    前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 之 ...

  7. RequireJS加载ArcGIS API for JavaScript

    1.在main.js中配置ArcGIS API for JavaScript require.config({ paths : { //arcgisJS "esri": " ...

  8. geotrellis使用(三十五)Cesium加载geotrellis TMS瓦片

    前言 做任何事情都不是想象中的那么简单.好久没有更新技术博客了,跟最近瞎忙有很大关系,虽说是瞎忙也抽空研究了些技术. 主要是前端渲染,像原生的WebGL和Cesium.WebGL写了几篇博客,自我感觉 ...

  9. 不同网段无法加载ArcGIS Server发布服务解决方法

    问题描述: ArcGIS Server 10发布的服务, (1)在相同网段的Desktop9.3和Engine 9.3程序下可以正常显示, (2)在不同网段Desktop9.3和Engine 9.3程 ...

  10. cesium 加载倾斜摄影模型(这里有一坑)

    代码如下: // Construct the default list of terrain sources. var terrainModels = Cesium.createDefaultTerr ...

随机推荐

  1. 标题Ubuntu将默认的python3改为默认的python

    ryzen@cp7:~$ python Command 'python' not found, did you mean: command 'python3' from deb python3 roo ...

  2. cesium 3d tileset 问题总结

    Cesium 3d Tileset 中 i3dm 中存储的模型坐标为笛卡尔坐标,占四个字节,因为地球半径比较大,所以只有整数位和小数点后1位有效,因此会损失精度.对于要求精度比较高的模型,会发现位置偏 ...

  3. 助教工作总结(高级语言程序设计C语言)

    * { font-family: 宋体 } zw { font-size: 20px; line-height: 2em } 一.助教工作的具体职责和任务   在本学期担任陈欣老师的计科专业21级c语 ...

  4. Jmeter读取Csv文件,字段中有逗号分隔,读取不成功

    Jmeter读取Csv文件,字段中有逗号分隔,读取不成功

  5. LIS3DH三轴加速度计-实现欧拉角(俯仰角,横滚角)-转载

    1. LIS3DH管脚定义 PS:LIS3DH和mpu6050的X和Y方向是相反的, mpu6050如下图所示: 2.LIS3DH加速度计介绍 由于LIS3DH只可以得到XYZ加速度,无法获取角速度, ...

  6. SimpleITK和nibable读取nii文件

    import SimpleITK as sitk path = 'nii全路径' nii_obj = sitk.GetArrayFromImage(sitk.ReadImage(path)) #sha ...

  7. simpleini库的介绍和使用(面向业务编程-格式处理)

    simpleini库的介绍和使用(面向业务编程-格式处理) 介绍 simpleini是一个跨平台的ini格式处理库,提供了一些简单的API来读取和写入ini风格的配置文件.它支持ASCII.MBCS和 ...

  8. 设计师必备:免费素材管理工具Billfish v3.0更新了!

    ​​Billfish是专门为设计师打造的图片收藏管理工具,可以轻松管理您的各种素材文件.Billfish是一个免费的软件,支持对大量的图片素材进行管理,提供多种快速的检索筛选功能,如颜色,格式,方向, ...

  9. Oracle 函数整理

    一.字符控制函数 函数 结果 CONCAT('Hello','World') HelloWorld SUBSTR('HelloWorld',1,5) Hello LENGTH('HelloWorld' ...

  10. 鼎捷ERP二次开发教程 Tiptop GP开发资料大全 Tipto开发实战经验 鼎捷开发实战例子 Tiptop GP二次开发项目例子 4GL开发Demo 鼎捷二次开发完整例子 鼎捷ERP二次开发入门

    本人在ERP实施公司做顾问四五年,参与企业实施ERP十多个项目,非常熟悉企业ERP流程,在实施过程遇到众多问题,提出了不少根据企业具体情况的解决方案. 曾经参与鼎捷Tiptop GP实施十多个项目,具 ...