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. 005Java开发环境搭建

    005Java开发环境搭建 1.JDK下载与安装 (1)卸载 首先我们先来学习一下如何卸载JDK. 删除Java的安装目录(jdk整个包). 删除环境变量里的"JAVA_HOME" ...

  2. [代码片段] 获取分辨率DPI和像素、毫米、英寸互相转换

    private static float DEFAULT_DPI_X = 0; private static float DEFAULT_DPI_Y = 0; /// <summary>获 ...

  3. 配置git到码云

    一.安装完git之后,对项目文件点击右键选择Git Base Here #Git 全局设置用户名与邮箱 git config --global user.name "" git c ...

  4. eclipse console 控制台输出乱码解决办法

    一.console输出日志显示乱码 二.在类编辑处点击右键 Run As --> Run Configurations 三.在Common中设置字符集 gbk 四.restart 搜索 复制

  5. Keepalived+HAProxy基于ACL实现单IP多域名负载功能

    编译安装 HAProxy 新版 LTS 版本,编译安装 Keepalived 开启HAProxy多线程,线程数与CPU核心数保持一致,并绑定CPU核心 因业务较多避免配置文件误操作,需要按每业务一个配 ...

  6. meterpreter的使用

    meterpreter是metasploit中的一个杀手锏,通常在漏洞利用成功后,会返回给攻击者一个攻击通道,其中有很多自动化操作 场景布置 生成木马 首先,我们使用metasploit中的另一个后门 ...

  7. Spring--依赖注入:setter注入和构造器注入

    依赖注入:描述了在容器中建立Bean于Bean之间依赖关系的过程 setter注入 在本来已经在service里面引用了bean的相关方法的基础上,再引用之前已经写过的userDao的对象,即在ser ...

  8. badapple最后一步,讲黑白图转为字符图,然后输出就行了。

    from PIL import Image import os char_s = list(" .,-'`:!1+*abcdefghijklmnopqrstuvwxyz<>()\ ...

  9. scrcpy软件的使用

    一.scrcpy软件介绍: scrcpy是通过adb调试的方式来将手机屏幕投到电脑上,并可以通过电脑控制您的Android设备.它可以通过USB连接,也可以通过Wifi连接(类似于隔空投屏),而且不需 ...

  10. new做了哪些事情,手写一个new

    1)创建一个空对象,将构造函数中的this指向这个空对象 2)将空对象的__proto__指向构造函数的prototype原型 3)执行构造函数里面的代码,为这个空对象添加属性和方法 4)返回一个新的 ...