/** * 地名注记数据提供者,通过改变LSGlobe实现地名注记数据的加载。 * * * @alias CesiumTerrainProvider * @constructor * * @param {LSGlobe} LSGlobe The result of LSGlobe.js * @param {Object} options The options include: * @param {String} options.url 地形服务的url。 * @param {Proxy} [options.proxy] 请求的代理。 * @param {String} [options.tileUrlTemplate] url模板。 * @param {Ellipsoid} [options.ellipsoid] 椭球体,默认为WGS84椭球体。 * @param {String} [options.metadataUrl] 元数据文件路径。 */ var VTPMarkerProvider = function (LSGlobe, options) { "use strict"; // 进入严格模式 if (typeof LSGlobe === 'undefined') { throw 'LSGlobe must be defined for this plugin to work.'; } if (typeof LSGlobe.defined === 'undefined' || !LSGlobe.defined(LSGlobe.GlobeSurfaceTile)) { throw 'Your version of LSGlobe is out of date, please upgrade LSGlobe.'; } this.LSGlobe = LSGlobe; // Provider自有的内容 if (!LSGlobe.defined(options) || !LSGlobe.defined(options.url)) { throw new LSGlobe.DeveloperError('options.url is required.'); } this.viewer = undefined; // 字体设置 this.fontSize = LSGlobe.defaultValue(options.fontSize, 16); this.fontFamily = LSGlobe.defaultValue(options.fontFamily, "Microsoft YaHei"); this._url = options.url; this._proxy = options.proxy; this.fatherGroup = options.fatherGroup; var tileUrlTemplate = this._url + '/ReadTerrainLrp?layer=' + options.layer + '&x={row}&y={col}&z={level}'; this._tileUrlTemplate = LSGlobe.defaultValue(options.tileUrlTemplate, tileUrlTemplate); this._tilingScheme = new LSGlobe.GeographicTilingScheme({ numberOfLevelZeroTilesX: 2, numberOfLevelZeroTilesY: 1, ellipsoid: options.ellipsoid }); this._errorEvent = new LSGlobe.Event(); this._availability = undefined; this._ready = false; this._readyPromise = LSGlobe.when.defer(); var metadataUrl = this._url + '/CreateTerrainLrc?layer=' + options.layer; if (LSGlobe.defined(this._proxy)) { metadataUrl = this._proxy.getURL(metadataUrl); } metadataUrl = LSGlobe.defaultValue(options.metadataUrl, metadataUrl); var metadataError; var that = this; /** * 元数据请求成功函数,用于解析元数据文件。 * @param {#document} xml 元数据文件 * @private */ function metadataSuccess(xml) { that._minimumLevel = parseInt(xml.getElementsByTagName('LevelBegin')[0].childNodes[0].nodeValue); that._maximumLevel = parseInt(xml.getElementsByTagName('LevelEnd')[0].childNodes[0].nodeValue); var west = LSGlobe.Math.toRadians(xml.getElementsByTagName('West')[0].childNodes[0].nodeValue); var south = LSGlobe.Math.toRadians(xml.getElementsByTagName('South')[0].childNodes[0].nodeValue); var east = LSGlobe.Math.toRadians(xml.getElementsByTagName('East')[0].childNodes[0].nodeValue); var north = LSGlobe.Math.toRadians(xml.getElementsByTagName('North')[0].childNodes[0].nodeValue); that._rectangle = new LSGlobe.Rectangle(west, south, east, north); that._ready = true; that._readyPromise.resolve(true); } /** * 元数据请求失败函数。 * @private */ function metadataFailure(data) { var message = 'An error occurred while accessing ' + metadataUrl + '.'; metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata); } /** * 元数据请求函数。 * @private */ function requestMetadata() { function getxmlDoc(xmlFile) { var xmlDoc; if (window.ActiveXObject) { xmlDoc = new ActiveXObject('Microsoft.XMLDOM'); //IE xmlDoc.async = false; xmlDoc.load(xmlFile); } else if (navigator.userAgent.indexOf("Firefox") > 0) { //火狐 try { xmlDoc = document.implementation.createDocument('', '', null); xmlDoc.load(xmlFile); } catch (e) { var xmlhttp = new window.XMLHttpRequest(); xmlhttp.open("GET", xmlFile, false); xmlhttp.send(null); if (xmlhttp.readyState == 4) { xmlDoc = xmlhttp.responseXML.documentElement; } } } else { //谷歌 var xmlhttp = new window.XMLHttpRequest(); xmlhttp.open("GET", xmlFile, false); xmlhttp.send(null); if (xmlhttp.readyState == 4) { xmlDoc = xmlhttp.responseXML.documentElement; } } return xmlDoc; } //加载XML var metadata = getxmlDoc(metadataUrl); //var metadata = LSGlobe.loadXML(metadataUrl); LSGlobe.when(metadata, metadataSuccess, metadataFailure); } // 请求元数据 requestMetadata(); /** * Modifying the passed in LSGlobe to allow for loading VTP markers... */ // 使用新的 processStateMachine 函数,增加对地名注记的处理 LSGlobe.GlobeSurfaceTile._oldProcessStateMachine = LSGlobe.GlobeSurfaceTile.processStateMachine; LSGlobe.GlobeSurfaceTile.processStateMachine = function (tile, frameState, terrainLayerCollection, imageryLayerCollection, vertexArraysToDestroy) { var priorityFunc = tile._priorityFunction; // _oldProcessStateMachine可能会改变tile._priorityFunction,提前备份。 LSGlobe.GlobeSurfaceTile._oldProcessStateMachine(tile, frameState, terrainLayerCollection, imageryLayerCollection, vertexArraysToDestroy); // 地名注记的处理 var surfaceTile = tile.data; var tileEntity = surfaceTile.tileEntity; if (!LSGlobe.defined(tileEntity)) { tileEntity = new TileEntity(that); surfaceTile.tileEntity = tileEntity; } // 地名注记没有加载完成,修改tile的状态不为QuadtreeTileLoadState.DONE var isDoneLoading = tileEntity.processStateMachine(frameState, that, tile.x, tile.y, tile.level, tile._priorityFunction); if (!isDoneLoading && tile.state === LSGlobe.QuadtreeTileLoadState.DONE) { tile.state = LSGlobe.QuadtreeTileLoadState.LOADING; tile._priorityFunction = priorityFunc; } }; // 使用新的 freeResources 函数,增加对 tileEntity 的释放。 LSGlobe.GlobeSurfaceTile.prototype._oldFreeResources = LSGlobe.GlobeSurfaceTile.prototype.freeResources; LSGlobe.GlobeSurfaceTile.prototype.freeResources = function () { this._oldFreeResources(); if (LSGlobe.defined(this.tileEntity)) { this.tileEntity.freeResources(); this.tileEntity = undefined; } }; // 是否需要隐藏当前没有渲染的tile对应的地标,待确认. LSGlobe.GlobeSurfaceTileProvider.prototype._oldEndUpdate = LSGlobe.GlobeSurfaceTileProvider.prototype.endUpdate; LSGlobe.GlobeSurfaceTileProvider.prototype.endUpdate = function (frameState) { // Call the original update this._oldEndUpdate(frameState); // 先把所有的设置为不可见,然后把所有要渲染的瓦片设置可见。 // 会晚一帧,因为给其添加command在这之前已经做了 // var entities = that.viewer.entities; // for (var i = 0, len = entities.length; i < len; ++i) { // entities[i]._show = false; // } // // var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount; // for (var textureCountIndex = 0, textureCountLength = tilesToRenderByTextureCount.length; textureCountIndex < textureCountLength; ++textureCountIndex) { // var tilesToRender = tilesToRenderByTextureCount[textureCountIndex]; // if (!LSGlobe.defined(tilesToRender)) { // continue; // } // // for (var tileIndex = 0, tileLength = tilesToRender.length; tileIndex < tileLength; ++tileIndex) { // var tileEntity = tilesToRender[tileIndex].data.tileEntity; // for (var i = 0, len = tileEntity.dataList.length; i < len; ++i) { // entities.getById(tileEntity.dataList[i])._show = true; // } // } // } }; }; /** * 为给定瓦片请求几何数据。 * * @param {Number} x x坐标。 * @param {Number} y y坐标。 * @param {Number} level tile等级。 * * @returns {Promise.|undefined} 地形数据对象。 * * @exception {DeveloperError} 函数必须在{@link DTPTerrainProvider#ready}为ture之后调用。 */ VTPMarkerProvider.prototype.requestEntityData = function (x, y, level, request) { if (!this._ready) { return undefined; //throw new DeveloperError('requestTileGeometry must not be called before the terrain provider is ready.'); } var urlTemplate = this._tileUrlTemplate; if (!isatCesium.defined(urlTemplate)) { return undefined; } if (!this.getTileDataAvailable(x, y, level)) { return undefined; } // 计算瓦片对应的地标块范围(一个瓦片可能对应多个地标块数据) var yTiles = this._tilingScheme.getNumberOfYTilesAtLevel(level) * 2; var minXY = new isatCesium.Cartesian2(-1, -1); var maxXY = new isatCesium.Cartesian2(-1, -1); calcMinMaxRowCol(this, x, y, level, minXY, maxXY); if (maxXY.x >= yTiles) { maxXY.x = yTiles - 1; } if (maxXY.y >= yTiles) { maxXY.y = yTiles - 1; } if (minXY.y >= yTiles || maxXY.x >= yTiles || // 无效范围 minXY.y < 0 || maxXY.y < 0 || minXY.x < 0 || maxXY.x < 0) { return undefined; } // 数据请求数组 var promiseArray = []; for (var i = minXY.y; i <= maxXY.y; ++i) { for (var j = minXY.x; j <= maxXY.x; ++j) { var url = buildUrl(this, j, yTiles - i - 1, level + 1); var promise = isatCesium.loadArrayBuffer(url); if (!isatCesium.defined(promise)) { return undefined; } promiseArray.push(promise); } } var that = this; return isatCesium.when.all(promiseArray, function (buffers) { return buffers; }, function () { return isatCesium.when.reject(); }); }; /** * 判断瓦片数据是否可以加载。 * * @param {Number} x 列号. * @param {Number} y 行号. * @param {Number} level 层号. * @returns {Boolean} Undefined if not supported, otherwise true or false. */ VTPMarkerProvider.prototype.getTileDataAvailable = function (x, y, level) { if (!this._ready) { return undefined; } if (level > this._maximumLevel - 1 || level < this._minimumLevel - 1) { return false; // 不在层级范围内 } var result = undefined; result = this._tilingScheme.tileXYToRectangle(x, y, level, result); if (Cesium.Rectangle.intersection(this._rectangle, result) == undefined) { return false; // 不在地理范围内 } return true; }; // 根据层号、行号和列号构建请求地址 function buildUrl(provider, x, y, level) { var url = provider._tileUrlTemplate.replace('{z}', level).replace('{x}', x).replace('{y}', y); var proxy = provider._proxy; if (isatCesium.defined(proxy)) { url = proxy.getURL(url); } return url; } // 根据tile的层号、行号和列号计算地标块索引范围 function calcMinMaxRowCol(provider, x, y, level, minXY, maxXY) { var tileRect = provider._tilingScheme.tileXYToRectangle(x, y, level); var tileSize = tileRect.width; var sample = 18; var sampleGap = tileSize / sample; var val = tileRect.west; while (val < tileRect.east) { adjustMinMaxRowCol(val, tileRect.south, tileSize, minXY, maxXY); adjustMinMaxRowCol(val, tileRect.north, tileSize, minXY, maxXY); val += sampleGap; } val = tileRect.south; while (val < tileRect.north) { adjustMinMaxRowCol(tileRect.west, val, tileSize, minXY, maxXY); adjustMinMaxRowCol(tileRect.east, val, tileSize, minXY, maxXY); val += sampleGap; } } // 根据经纬度及瓦片范围调整地标块索引范围 function adjustMinMaxRowCol(lon, lat, tileSize, minXY, maxXY) { var result = getRowCol(lon, lat, tileSize); var x = Math.floor(result.x); var y = Math.floor(result.y); if (x >= 0) { if (minXY.x < 0 || x < minXY.x) { minXY.x = x; } if (maxXY.x < 0 || x > maxXY.x) { maxXY.x = x; } } if (y >= 0) { if (minXY.y < 0 || y < minXY.y) { minXY.y = y; } if (maxXY.y < 0 || y > maxXY.y) { maxXY.y = y; } } } // 根据经纬度及瓦片范围计算对应的地标块索引 function getRowCol(lon, lat, tileSize, result) { if (!isatCesium.defined(result)) { result = new isatCesium.Cartesian2(); } result.x = (lon + Math.PI) / tileSize; var temp = Math.tan(Math.PI / 4.0 + lat / 2.0); if (temp > 0) { result.y = (Math.log(temp) + Math.PI) / tileSize; } else { result.y = -1; } return result; }