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="https://www.cnblogs.com/kk8085/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
<本文完>
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/551224.html
標籤:GIS
上一篇:博客園silence主題修改記錄
下一篇:返回列表
