123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044 |
- import when from '../ThirdParty/when.js';
- import AttributeCompression from './AttributeCompression.js';
- import BoundingSphere from './BoundingSphere.js';
- import Cartesian3 from './Cartesian3.js';
- import Credit from './Credit.js';
- import defaultValue from './defaultValue.js';
- import defined from './defined.js';
- import defineProperties from './defineProperties.js';
- import DeveloperError from './DeveloperError.js';
- import Event from './Event.js';
- import GeographicTilingScheme from './GeographicTilingScheme.js';
- import getStringFromTypedArray from './getStringFromTypedArray.js';
- import HeightmapTerrainData from './HeightmapTerrainData.js';
- import IndexDatatype from './IndexDatatype.js';
- import CesiumMath from './Math.js';
- import OrientedBoundingBox from './OrientedBoundingBox.js';
- import QuantizedMeshTerrainData from './QuantizedMeshTerrainData.js';
- import Request from './Request.js';
- import RequestType from './RequestType.js';
- import Resource from './Resource.js';
- import RuntimeError from './RuntimeError.js';
- import TerrainProvider from './TerrainProvider.js';
- import TileAvailability from './TileAvailability.js';
- import TileProviderError from './TileProviderError.js';
- function LayerInformation(layer) {
- this.resource = layer.resource;
- this.version = layer.version;
- this.isHeightmap = layer.isHeightmap;
- this.tileUrlTemplates = layer.tileUrlTemplates;
- this.availability = layer.availability;
- this.hasVertexNormals = layer.hasVertexNormals;
- this.hasWaterMask = layer.hasWaterMask;
- this.hasMetadata = layer.hasMetadata;
- this.availabilityLevels = layer.availabilityLevels;
- this.availabilityTilesLoaded = layer.availabilityTilesLoaded;
- this.littleEndianExtensionSize = layer.littleEndianExtensionSize;
- this.availabilityTilesLoaded = layer.availabilityTilesLoaded;
- this.availabilityPromiseCache = {};
- }
- /**
- * A {@link TerrainProvider} that accesses terrain data in a Cesium terrain format.
- *
- * @alias CesiumTerrainProvider
- * @constructor
- *
- * @param {Object} options Object with the following properties:
- * @param {Resource|String|Promise<Resource>|Promise<String>} options.url The URL of the Cesium terrain server.
- * @param {Boolean} [options.requestVertexNormals=false] Flag that indicates if the client should request additional lighting information from the server, in the form of per vertex normals if available.
- * @param {Boolean} [options.requestWaterMask=false] Flag that indicates if the client should request per tile water masks from the server, if available.
- * @param {Boolean} [options.requestMetadata=true] Flag that indicates if the client should request per tile metadata from the server, if available.
- * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used.
- * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
- *
- *
- * @example
- * // Create Arctic DEM terrain with normals.
- * var viewer = new Cesium.Viewer('cesiumContainer', {
- * terrainProvider : new Cesium.CesiumTerrainProvider({
- * url : Cesium.IonResource.fromAssetId(3956),
- * requestVertexNormals : true
- * })
- * });
- *
- * @see createWorldTerrain
- * @see TerrainProvider
- */
- function CesiumTerrainProvider(options) {
- //>>includeStart('debug', pragmas.debug)
- if (!defined(options) || !defined(options.url)) {
- throw new DeveloperError('options.url is required.');
- }
- //>>includeEnd('debug');
- this._tilingScheme = new GeographicTilingScheme({
- numberOfLevelZeroTilesX : 2,
- numberOfLevelZeroTilesY : 1,
- ellipsoid : options.ellipsoid
- });
- this._heightmapWidth = 65;
- this._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(this._tilingScheme.ellipsoid, this._heightmapWidth, this._tilingScheme.getNumberOfXTilesAtLevel(0));
- this._heightmapStructure = undefined;
- this._hasWaterMask = false;
- this._hasVertexNormals = false;
- /**
- * Boolean flag that indicates if the client should request vertex normals from the server.
- * @type {Boolean}
- * @default false
- * @private
- */
- this._requestVertexNormals = defaultValue(options.requestVertexNormals, false);
- /**
- * Boolean flag that indicates if the client should request tile watermasks from the server.
- * @type {Boolean}
- * @default false
- * @private
- */
- this._requestWaterMask = defaultValue(options.requestWaterMask, false);
- /**
- * Boolean flag that indicates if the client should request tile metadata from the server.
- * @type {Boolean}
- * @default true
- * @private
- */
- this._requestMetadata = defaultValue(options.requestMetadata, true);
- this._errorEvent = new Event();
- var credit = options.credit;
- if (typeof credit === 'string') {
- credit = new Credit(credit);
- }
- this._credit = credit;
- this._availability = undefined;
- var deferred = when.defer();
- this._ready = false;
- this._readyPromise = deferred;
- this._tileCredits = undefined;
- var that = this;
- var lastResource;
- var layerJsonResource;
- var metadataError;
- var layers = this._layers = [];
- var attribution = '';
- var overallAvailability = [];
- var overallMaxZoom = 0;
- when(options.url)
- .then(function(url) {
- var resource = Resource.createIfNeeded(url);
- resource.appendForwardSlash();
- lastResource = resource;
- layerJsonResource = lastResource.getDerivedResource({
- url: 'layer.json'
- });
- // ion resources have a credits property we can use for additional attribution.
- that._tileCredits = resource.credits;
- requestLayerJson();
- })
- .otherwise(function(e) {
- deferred.reject(e);
- });
- function parseMetadataSuccess(data) {
- var message;
- if (!data.format) {
- message = 'The tile format is not specified in the layer.json file.';
- metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson);
- return;
- }
- if (!data.tiles || data.tiles.length === 0) {
- message = 'The layer.json file does not specify any tile URL templates.';
- metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson);
- return;
- }
- var hasVertexNormals = false;
- var hasWaterMask = false;
- var hasMetadata = false;
- var littleEndianExtensionSize = true;
- var isHeightmap = false;
- if (data.format === 'heightmap-1.0') {
- isHeightmap = true;
- if (!defined(that._heightmapStructure)) {
- that._heightmapStructure = {
- heightScale : 1.0 / 5.0,
- heightOffset : -1000.0,
- elementsPerHeight : 1,
- stride : 1,
- elementMultiplier : 256.0,
- isBigEndian : false,
- lowestEncodedHeight : 0,
- highestEncodedHeight : 256 * 256 - 1
- };
- }
- hasWaterMask = true;
- that._requestWaterMask = true;
- } else if (data.format.indexOf('quantized-mesh-1.') !== 0) {
- message = 'The tile format "' + data.format + '" is invalid or not supported.';
- metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson);
- return;
- }
- var tileUrlTemplates = data.tiles;
- var maxZoom = data.maxzoom;
- overallMaxZoom = Math.max(overallMaxZoom, maxZoom);
- // Keeps track of which of the availablity containing tiles have been loaded
- var availabilityTilesLoaded;
- // The vertex normals defined in the 'octvertexnormals' extension is identical to the original
- // contents of the original 'vertexnormals' extension. 'vertexnormals' extension is now
- // deprecated, as the extensionLength for this extension was incorrectly using big endian.
- // We maintain backwards compatibility with the legacy 'vertexnormal' implementation
- // by setting the _littleEndianExtensionSize to false. Always prefer 'octvertexnormals'
- // over 'vertexnormals' if both extensions are supported by the server.
- if (defined(data.extensions) && data.extensions.indexOf('octvertexnormals') !== -1) {
- hasVertexNormals = true;
- } else if (defined(data.extensions) && data.extensions.indexOf('vertexnormals') !== -1) {
- hasVertexNormals = true;
- littleEndianExtensionSize = false;
- }
- if (defined(data.extensions) && data.extensions.indexOf('watermask') !== -1) {
- hasWaterMask = true;
- }
- if (defined(data.extensions) && data.extensions.indexOf('metadata') !== -1) {
- hasMetadata = true;
- }
- var availabilityLevels = data.metadataAvailability;
- var availableTiles = data.available;
- var availability;
- if (defined(availableTiles) && !defined(availabilityLevels)) {
- availability = new TileAvailability(that._tilingScheme, availableTiles.length);
- for (var level = 0; level < availableTiles.length; ++level) {
- var rangesAtLevel = availableTiles[level];
- var yTiles = that._tilingScheme.getNumberOfYTilesAtLevel(level);
- if (!defined(overallAvailability[level])) {
- overallAvailability[level] = [];
- }
- for (var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex) {
- var range = rangesAtLevel[rangeIndex];
- var yStart = yTiles - range.endY - 1;
- var yEnd = yTiles - range.startY - 1;
- overallAvailability[level].push([range.startX, yStart, range.endX, yEnd]);
- availability.addAvailableTileRange(level, range.startX, yStart, range.endX, yEnd);
- }
- }
- } else if (defined(availabilityLevels)) {
- availabilityTilesLoaded = new TileAvailability(that._tilingScheme, maxZoom);
- availability = new TileAvailability(that._tilingScheme, maxZoom);
- overallAvailability[0] = [
- [0, 0, 1, 0]
- ];
- availability.addAvailableTileRange(0, 0, 0, 1, 0);
- }
- that._hasWaterMask = that._hasWaterMask || hasWaterMask;
- that._hasVertexNormals = that._hasVertexNormals || hasVertexNormals;
- that._hasMetadata = that._hasMetadata || hasMetadata;
- if (defined(data.attribution)) {
- if (attribution.length > 0) {
- attribution += ' ';
- }
- attribution += data.attribution;
- }
- layers.push(new LayerInformation({
- resource: lastResource,
- version: data.version,
- isHeightmap: isHeightmap,
- tileUrlTemplates: tileUrlTemplates,
- availability: availability,
- hasVertexNormals: hasVertexNormals,
- hasWaterMask: hasWaterMask,
- hasMetadata: hasMetadata,
- availabilityLevels: availabilityLevels,
- availabilityTilesLoaded: availabilityTilesLoaded,
- littleEndianExtensionSize: littleEndianExtensionSize
- }));
- var parentUrl = data.parentUrl;
- if (defined(parentUrl)) {
- if (!defined(availability)) {
- console.log('A layer.json can\'t have a parentUrl if it does\'t have an available array.');
- return when.resolve();
- }
- lastResource = lastResource.getDerivedResource({
- url: parentUrl
- });
- lastResource.appendForwardSlash(); // Terrain always expects a directory
- layerJsonResource = lastResource.getDerivedResource({
- url: 'layer.json'
- });
- var parentMetadata = layerJsonResource.fetchJson();
- return when(parentMetadata, parseMetadataSuccess, parseMetadataFailure);
- }
- return when.resolve();
- }
- function parseMetadataFailure(data) {
- var message = 'An error occurred while accessing ' + layerJsonResource.url + '.';
- metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson);
- }
- function metadataSuccess(data) {
- parseMetadataSuccess(data)
- .then(function() {
- if (defined(metadataError)) {
- return;
- }
- var length = overallAvailability.length;
- if (length > 0) {
- var availability = that._availability = new TileAvailability(that._tilingScheme, overallMaxZoom);
- for (var level = 0; level < length; ++level) {
- var levelRanges = overallAvailability[level];
- for (var i = 0; i < levelRanges.length; ++i) {
- var range = levelRanges[i];
- availability.addAvailableTileRange(level, range[0], range[1], range[2], range[3]);
- }
- }
- }
- if (attribution.length > 0) {
- var layerJsonCredit = new Credit(attribution);
- if (defined(that._tileCredits)) {
- that._tileCredits.push(layerJsonCredit);
- } else {
- that._tileCredits = [layerJsonCredit];
- }
- }
- that._ready = true;
- that._readyPromise.resolve(true);
- });
- }
- function metadataFailure(data) {
- // If the metadata is not found, assume this is a pre-metadata heightmap tileset.
- if (defined(data) && data.statusCode === 404) {
- metadataSuccess({
- tilejson: '2.1.0',
- format : 'heightmap-1.0',
- version : '1.0.0',
- scheme : 'tms',
- tiles : [
- '{z}/{x}/{y}.terrain?v={version}'
- ]
- });
- return;
- }
- parseMetadataFailure(data);
- }
- function requestLayerJson() {
- when(layerJsonResource.fetchJson())
- .then(metadataSuccess)
- .otherwise(metadataFailure);
- }
- }
- /**
- * When using the Quantized-Mesh format, a tile may be returned that includes additional extensions, such as PerVertexNormals, watermask, etc.
- * This enumeration defines the unique identifiers for each type of extension data that has been appended to the standard mesh data.
- *
- * @exports QuantizedMeshExtensionIds
- * @see CesiumTerrainProvider
- * @private
- */
- var QuantizedMeshExtensionIds = {
- /**
- * Oct-Encoded Per-Vertex Normals are included as an extension to the tile mesh
- *
- * @type {Number}
- * @constant
- * @default 1
- */
- OCT_VERTEX_NORMALS: 1,
- /**
- * A watermask is included as an extension to the tile mesh
- *
- * @type {Number}
- * @constant
- * @default 2
- */
- WATER_MASK: 2,
- /**
- * A json object contain metadata about the tile
- *
- * @type {Number}
- * @constant
- * @default 4
- */
- METADATA: 4
- };
- function getRequestHeader(extensionsList) {
- if (!defined(extensionsList) || extensionsList.length === 0) {
- return {
- Accept : 'application/vnd.quantized-mesh,application/octet-stream;q=0.9,*/*;q=0.01'
- };
- }
- var extensions = extensionsList.join('-');
- return {
- Accept : 'application/vnd.quantized-mesh;extensions=' + extensions + ',application/octet-stream;q=0.9,*/*;q=0.01'
- };
- }
- function createHeightmapTerrainData(provider, buffer, level, x, y, tmsY) {
- var heightBuffer = new Uint16Array(buffer, 0, provider._heightmapWidth * provider._heightmapWidth);
- return new HeightmapTerrainData({
- buffer : heightBuffer,
- childTileMask : new Uint8Array(buffer, heightBuffer.byteLength, 1)[0],
- waterMask : new Uint8Array(buffer, heightBuffer.byteLength + 1, buffer.byteLength - heightBuffer.byteLength - 1),
- width : provider._heightmapWidth,
- height : provider._heightmapWidth,
- structure : provider._heightmapStructure,
- credits: provider._tileCredits
- });
- }
- function createQuantizedMeshTerrainData(provider, buffer, level, x, y, tmsY, layer) {
- var littleEndianExtensionSize = layer.littleEndianExtensionSize;
- var pos = 0;
- var cartesian3Elements = 3;
- var boundingSphereElements = cartesian3Elements + 1;
- var cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements;
- var boundingSphereLength = Float64Array.BYTES_PER_ELEMENT * boundingSphereElements;
- var encodedVertexElements = 3;
- var encodedVertexLength = Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements;
- var triangleElements = 3;
- var bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;
- var triangleLength = bytesPerIndex * triangleElements;
- var view = new DataView(buffer);
- var center = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
- pos += cartesian3Length;
- var minimumHeight = view.getFloat32(pos, true);
- pos += Float32Array.BYTES_PER_ELEMENT;
- var maximumHeight = view.getFloat32(pos, true);
- pos += Float32Array.BYTES_PER_ELEMENT;
- var boundingSphere = new BoundingSphere(
- new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true)),
- view.getFloat64(pos + cartesian3Length, true));
- pos += boundingSphereLength;
- var horizonOcclusionPoint = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
- pos += cartesian3Length;
- var vertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3);
- pos += vertexCount * encodedVertexLength;
- if (vertexCount > 64 * 1024) {
- // More than 64k vertices, so indices are 32-bit.
- bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT;
- triangleLength = bytesPerIndex * triangleElements;
- }
- // Decode the vertex buffer.
- var uBuffer = encodedVertexBuffer.subarray(0, vertexCount);
- var vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount);
- var heightBuffer = encodedVertexBuffer.subarray(vertexCount * 2, 3 * vertexCount);
- AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
- // skip over any additional padding that was added for 2/4 byte alignment
- if (pos % bytesPerIndex !== 0) {
- pos += (bytesPerIndex - (pos % bytesPerIndex));
- }
- var triangleCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var indices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, triangleCount * triangleElements);
- pos += triangleCount * triangleLength;
- // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js.
- // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55
- // Copyright 2012 Google Inc., Apache 2.0 license.
- var highest = 0;
- var length = indices.length;
- for (var i = 0; i < length; ++i) {
- var code = indices[i];
- indices[i] = highest - code;
- if (code === 0) {
- ++highest;
- }
- }
- var westVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var westIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, westVertexCount);
- pos += westVertexCount * bytesPerIndex;
- var southVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var southIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, southVertexCount);
- pos += southVertexCount * bytesPerIndex;
- var eastVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var eastIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, eastVertexCount);
- pos += eastVertexCount * bytesPerIndex;
- var northVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var northIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, northVertexCount);
- pos += northVertexCount * bytesPerIndex;
- var encodedNormalBuffer;
- var waterMaskBuffer;
- while (pos < view.byteLength) {
- var extensionId = view.getUint8(pos, true);
- pos += Uint8Array.BYTES_PER_ELEMENT;
- var extensionLength = view.getUint32(pos, littleEndianExtensionSize);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- if (extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS && provider._requestVertexNormals) {
- encodedNormalBuffer = new Uint8Array(buffer, pos, vertexCount * 2);
- } else if (extensionId === QuantizedMeshExtensionIds.WATER_MASK && provider._requestWaterMask) {
- waterMaskBuffer = new Uint8Array(buffer, pos, extensionLength);
- } else if (extensionId === QuantizedMeshExtensionIds.METADATA && provider._requestMetadata) {
- var stringLength = view.getUint32(pos, true);
- if (stringLength > 0) {
- var jsonString =
- getStringFromTypedArray(new Uint8Array(buffer), pos + Uint32Array.BYTES_PER_ELEMENT, stringLength);
- var metadata = JSON.parse(jsonString);
- var availableTiles = metadata.available;
- if (defined(availableTiles)) {
- for (var offset = 0; offset < availableTiles.length; ++offset) {
- var availableLevel = level + offset + 1;
- var rangesAtLevel = availableTiles[offset];
- var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(availableLevel);
- for (var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex) {
- var range = rangesAtLevel[rangeIndex];
- var yStart = yTiles - range.endY - 1;
- var yEnd = yTiles - range.startY - 1;
- provider.availability.addAvailableTileRange(availableLevel, range.startX, yStart, range.endX, yEnd);
- layer.availability.addAvailableTileRange(availableLevel, range.startX, yStart, range.endX, yEnd);
- }
- }
- }
- }
- layer.availabilityTilesLoaded.addAvailableTileRange(level, x, y, x, y);
- }
- pos += extensionLength;
- }
- var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;
- var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);
- var orientedBoundingBox;
- if (rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
- // Here, rectangle.width < pi/2, and rectangle.height < pi
- // (though it would still work with rectangle.width up to pi)
- // The skirt is not included in the OBB computation. If this ever
- // causes any rendering artifacts (cracks), they are expected to be
- // minor and in the corners of the screen. It's possible that this
- // might need to be changed - just change to `minimumHeight - skirtHeight`
- // A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
- orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid);
- }
- return new QuantizedMeshTerrainData({
- center : center,
- minimumHeight : minimumHeight,
- maximumHeight : maximumHeight,
- boundingSphere : boundingSphere,
- orientedBoundingBox : orientedBoundingBox,
- horizonOcclusionPoint : horizonOcclusionPoint,
- quantizedVertices : encodedVertexBuffer,
- encodedNormals : encodedNormalBuffer,
- indices : indices,
- westIndices : westIndices,
- southIndices : southIndices,
- eastIndices : eastIndices,
- northIndices : northIndices,
- westSkirtHeight : skirtHeight,
- southSkirtHeight : skirtHeight,
- eastSkirtHeight : skirtHeight,
- northSkirtHeight : skirtHeight,
- childTileMask: provider.availability.computeChildMaskForTile(level, x, y),
- waterMask: waterMaskBuffer,
- credits: provider._tileCredits
- });
- }
- /**
- * Requests the geometry for a given tile. This function should not be called before
- * {@link CesiumTerrainProvider#ready} returns true. The result must include terrain data and
- * may optionally include a water mask and an indication of which child tiles are available.
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @param {Request} [request] The request object. Intended for internal use only.
- *
- * @returns {Promise.<TerrainData>|undefined} A promise for the requested geometry. If this method
- * returns undefined instead of a promise, it is an indication that too many requests are already
- * pending and the request will be retried later.
- *
- * @exception {DeveloperError} This function must not be called before {@link CesiumTerrainProvider#ready}
- * returns true.
- */
- CesiumTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('requestTileGeometry must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- var layers = this._layers;
- var layerToUse;
- var layerCount = layers.length;
- if (layerCount === 1) { // Optimized path for single layers
- layerToUse = layers[0];
- } else {
- for (var i = 0; i < layerCount; ++i) {
- var layer = layers[i];
- if (!defined(layer.availability) || layer.availability.isTileAvailable(level, x, y)) {
- layerToUse = layer;
- break;
- }
- }
- }
- return requestTileGeometry(this, x, y, level, layerToUse, request);
- };
- function requestTileGeometry(provider, x, y, level, layerToUse, request) {
- if (!defined(layerToUse)) {
- return when.reject(new RuntimeError('Terrain tile doesn\'t exist'));
- }
- var urlTemplates = layerToUse.tileUrlTemplates;
- if (urlTemplates.length === 0) {
- return undefined;
- }
- var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(level);
- var tmsY = (yTiles - y - 1);
- var extensionList = [];
- if (provider._requestVertexNormals && layerToUse.hasVertexNormals) {
- extensionList.push(layerToUse.littleEndianExtensionSize ? 'octvertexnormals' : 'vertexnormals');
- }
- if (provider._requestWaterMask && layerToUse.hasWaterMask) {
- extensionList.push('watermask');
- }
- if (provider._requestMetadata && layerToUse.hasMetadata) {
- extensionList.push('metadata');
- }
- var headers;
- var query;
- var url = urlTemplates[(x + tmsY + level) % urlTemplates.length];
- var resource = layerToUse.resource;
- if (defined(resource._ionEndpoint) && !defined(resource._ionEndpoint.externalType)) {
- // ion uses query paremeters to request extensions
- if (extensionList.length !== 0) {
- query = { extensions: extensionList.join('-') };
- }
- headers = getRequestHeader(undefined);
- } else {
- //All other terrain servers
- headers = getRequestHeader(extensionList);
- }
- var promise = resource.getDerivedResource({
- url: url,
- templateValues: {
- version: layerToUse.version,
- z: level,
- x: x,
- y: tmsY
- },
- queryParameters: query,
- headers: headers,
- request: request
- }).fetchArrayBuffer();
- if (!defined(promise)) {
- return undefined;
- }
- return promise.then(function (buffer) {
- if (defined(provider._heightmapStructure)) {
- return createHeightmapTerrainData(provider, buffer, level, x, y, tmsY);
- }
- return createQuantizedMeshTerrainData(provider, buffer, level, x, y, tmsY, layerToUse);
- });
- }
- defineProperties(CesiumTerrainProvider.prototype, {
- /**
- * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing
- * to the event, you will be notified of the error and can potentially recover from it. Event listeners
- * are passed an instance of {@link TileProviderError}.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Event}
- */
- errorEvent : {
- get : function() {
- return this._errorEvent;
- }
- },
- /**
- * Gets the credit to display when this terrain provider is active. Typically this is used to credit
- * the source of the terrain. This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Credit}
- */
- credit : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('credit must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- return this._credit;
- }
- },
- /**
- * Gets the tiling scheme used by this provider. This function should
- * not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {GeographicTilingScheme}
- */
- tilingScheme : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('tilingScheme must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- return this._tilingScheme;
- }
- },
- /**
- * Gets a value indicating whether or not the provider is ready for use.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- ready : {
- get : function() {
- return this._ready;
- }
- },
- /**
- * Gets a promise that resolves to true when the provider is ready for use.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Promise.<Boolean>}
- * @readonly
- */
- readyPromise : {
- get : function() {
- return this._readyPromise.promise;
- }
- },
- /**
- * Gets a value indicating whether or not the provider includes a water mask. The water mask
- * indicates which areas of the globe are water rather than land, so they can be rendered
- * as a reflective surface with animated waves. This function should not be
- * called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasWaterMask : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('hasWaterMask must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- return this._hasWaterMask && this._requestWaterMask;
- }
- },
- /**
- * Gets a value indicating whether or not the requested tiles include vertex normals.
- * This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasVertexNormals : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('hasVertexNormals must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- // returns true if we can request vertex normals from the server
- return this._hasVertexNormals && this._requestVertexNormals;
- }
- },
- /**
- * Gets a value indicating whether or not the requested tiles include metadata.
- * This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasMetadata : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('hasMetadata must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- // returns true if we can request metadata from the server
- return this._hasMetadata && this._requestMetadata;
- }
- },
- /**
- * Boolean flag that indicates if the client should request vertex normals from the server.
- * Vertex normals data is appended to the standard tile mesh data only if the client requests the vertex normals and
- * if the server provides vertex normals.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestVertexNormals : {
- get : function() {
- return this._requestVertexNormals;
- }
- },
- /**
- * Boolean flag that indicates if the client should request a watermask from the server.
- * Watermask data is appended to the standard tile mesh data only if the client requests the watermask and
- * if the server provides a watermask.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestWaterMask : {
- get : function() {
- return this._requestWaterMask;
- }
- },
- /**
- * Boolean flag that indicates if the client should request metadata from the server.
- * Metadata is appended to the standard tile mesh data only if the client requests the metadata and
- * if the server provides a metadata.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestMetadata : {
- get : function() {
- return this._requestMetadata;
- }
- },
- /**
- * Gets an object that can be used to determine availability of terrain from this provider, such as
- * at points and in rectangles. This function should not be called before
- * {@link CesiumTerrainProvider#ready} returns true. This property may be undefined if availability
- * information is not available. Note that this reflects tiles that are known to be available currently.
- * Additional tiles may be discovered to be available in the future, e.g. if availability information
- * exists deeper in the tree rather than it all being discoverable at the root. However, a tile that
- * is available now will not become unavailable in the future.
- * @memberof CesiumTerrainProvider.prototype
- * @type {TileAvailability}
- */
- availability : {
- get : function() {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError('availability must not be called before the terrain provider is ready.');
- }
- //>>includeEnd('debug');
- return this._availability;
- }
- }
- });
- /**
- * Gets the maximum geometric error allowed in a tile at a given level.
- *
- * @param {Number} level The tile level for which to get the maximum geometric error.
- * @returns {Number} The maximum geometric error.
- */
- CesiumTerrainProvider.prototype.getLevelMaximumGeometricError = function(level) {
- return this._levelZeroMaximumGeometricError / (1 << level);
- };
- /**
- * Determines whether data for a tile is available to be loaded.
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @returns {Boolean} Undefined if not supported or availability is unknown, otherwise true or false.
- */
- CesiumTerrainProvider.prototype.getTileDataAvailable = function(x, y, level) {
- if (!defined(this._availability)) {
- return undefined;
- }
- if (level > this._availability._maximumLevel) {
- return false;
- }
- if (this._availability.isTileAvailable(level, x, y)) {
- // If the tile is listed as available, then we are done
- return true;
- }
- if (!this._hasMetadata) {
- // If we don't have any layers with the metadata extension then we don't have this tile
- return false;
- }
- var layers = this._layers;
- var count = layers.length;
- for (var i = 0; i < count; ++i) {
- var layerResult = checkLayer(this, x, y, level, layers[i], (i===0));
- if (layerResult.result) {
- // There is a layer that may or may not have the tile
- return undefined;
- }
- }
- return false;
- };
- /**
- * Makes sure we load availability data for a tile
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded
- */
- CesiumTerrainProvider.prototype.loadTileDataAvailability = function(x, y, level) {
- if (!defined(this._availability) || (level > this._availability._maximumLevel) ||
- (this._availability.isTileAvailable(level, x, y) || (!this._hasMetadata))) {
- // We know the tile is either available or not available so nothing to wait on
- return undefined;
- }
- var layers = this._layers;
- var count = layers.length;
- for (var i = 0; i < count; ++i) {
- var layerResult = checkLayer(this, x, y, level, layers[i], (i===0));
- if (defined(layerResult.promise)) {
- return layerResult.promise;
- }
- }
- };
- function getAvailabilityTile(layer, x, y, level) {
- if (level === 0) {
- return;
- }
- var availabilityLevels = layer.availabilityLevels;
- var parentLevel = (level % availabilityLevels === 0) ?
- (level - availabilityLevels) : ((level / availabilityLevels) | 0) * availabilityLevels;
- var divisor = 1 << (level - parentLevel);
- var parentX = (x / divisor) | 0;
- var parentY = (y / divisor) | 0;
- return {
- level: parentLevel,
- x: parentX,
- y: parentY
- };
- }
- function checkLayer(provider, x, y, level, layer, topLayer) {
- if (!defined(layer.availabilityLevels)) {
- // It's definitely not in this layer
- return {
- result: false
- };
- }
- var cacheKey;
- var deleteFromCache = function () {
- delete layer.availabilityPromiseCache[cacheKey];
- };
- var availabilityTilesLoaded = layer.availabilityTilesLoaded;
- var availability = layer.availability;
- var tile = getAvailabilityTile(layer, x, y, level);
- while(defined(tile)) {
- if (availability.isTileAvailable(tile.level, tile.x, tile.y) &&
- !availabilityTilesLoaded.isTileAvailable(tile.level, tile.x, tile.y))
- {
- var requestPromise;
- if (!topLayer) {
- cacheKey = tile.level + '-' + tile.x + '-' + tile.y;
- requestPromise = layer.availabilityPromiseCache[cacheKey];
- if (!defined(requestPromise)) {
- // For cutout terrain, if this isn't the top layer the availability tiles
- // may never get loaded, so request it here.
- var request = new Request({
- throttle: true,
- throttleByServer: true,
- type: RequestType.TERRAIN
- });
- requestPromise = requestTileGeometry(provider, tile.x, tile.y, tile.level, layer, request);
- if (defined(requestPromise)) {
- layer.availabilityPromiseCache[cacheKey] = requestPromise;
- requestPromise.then(deleteFromCache);
- }
- }
- }
- // The availability tile is available, but not loaded, so there
- // is still a chance that it may become available at some point
- return {
- result: true,
- promise: requestPromise
- };
- }
- tile = getAvailabilityTile(layer, tile.x, tile.y, tile.level);
- }
- return {
- result: false
- };
- }
- // Used for testing
- CesiumTerrainProvider._getAvailabilityTile = getAvailabilityTile;
- export default CesiumTerrainProvider;
|