| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323 | 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 defer from "./defer.js";import defined from "./defined.js";import DeveloperError from "./DeveloperError.js";import Event from "./Event.js";import GeographicTilingScheme from "./GeographicTilingScheme.js";import WebMercatorTilingScheme from "./WebMercatorTilingScheme.js";import getJsonFromTypedArray from "./getJsonFromTypedArray.js";import HeightmapTerrainData from "./HeightmapTerrainData.js";import IndexDatatype from "./IndexDatatype.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.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. * const 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._heightmapWidth = 65;  this._heightmapStructure = undefined;  this._hasWaterMask = false;  this._hasVertexNormals = false;  this._ellipsoid = options.ellipsoid;  /**   * 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();  let credit = options.credit;  if (typeof credit === "string") {    credit = new Credit(credit);  }  this._credit = credit;  this._availability = undefined;  const deferred = defer();  this._ready = false;  this._readyPromise = deferred;  this._tileCredits = undefined;  const that = this;  let lastResource;  let layerJsonResource;  let metadataError;  const layers = (this._layers = []);  let attribution = "";  const overallAvailability = [];  let overallMaxZoom = 0;  Promise.resolve(options.url)    .then(function (url) {      const 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();    })    .catch(function (e) {      deferred.reject(e);    });  function parseMetadataSuccess(data) {    let 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;    }    let hasVertexNormals = false;    let hasWaterMask = false;    let hasMetadata = false;    let littleEndianExtensionSize = true;    let 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;    }    const tileUrlTemplates = data.tiles;    const maxZoom = data.maxzoom;    overallMaxZoom = Math.max(overallMaxZoom, maxZoom);    // Keeps track of which of the availablity containing tiles have been loaded    if (!data.projection || data.projection === "EPSG:4326") {      that._tilingScheme = new GeographicTilingScheme({        numberOfLevelZeroTilesX: 2,        numberOfLevelZeroTilesY: 1,        ellipsoid: that._ellipsoid,      });    } else if (data.projection === "EPSG:3857") {      that._tilingScheme = new WebMercatorTilingScheme({        numberOfLevelZeroTilesX: 1,        numberOfLevelZeroTilesY: 1,        ellipsoid: that._ellipsoid,      });    } else {      message = `The projection "${data.projection}" is invalid or not supported.`;      metadataError = TileProviderError.handleError(        metadataError,        that,        that._errorEvent,        message,        undefined,        undefined,        undefined,        requestLayerJson      );      return;    }    that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(      that._tilingScheme.ellipsoid,      that._heightmapWidth,      that._tilingScheme.getNumberOfXTilesAtLevel(0)    );    if (!data.scheme || data.scheme === "tms" || data.scheme === "slippyMap") {      that._scheme = data.scheme;    } else {      message = `The scheme "${data.scheme}" is invalid or not supported.`;      metadataError = TileProviderError.handleError(        metadataError,        that,        that._errorEvent,        message,        undefined,        undefined,        undefined,        requestLayerJson      );      return;    }    let 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;    }    const availabilityLevels = data.metadataAvailability;    const availableTiles = data.available;    let availability;    if (defined(availableTiles) && !defined(availabilityLevels)) {      availability = new TileAvailability(        that._tilingScheme,        availableTiles.length      );      for (let level = 0; level < availableTiles.length; ++level) {        const rangesAtLevel = availableTiles[level];        const yTiles = that._tilingScheme.getNumberOfYTilesAtLevel(level);        if (!defined(overallAvailability[level])) {          overallAvailability[level] = [];        }        for (          let rangeIndex = 0;          rangeIndex < rangesAtLevel.length;          ++rangeIndex        ) {          const range = rangesAtLevel[rangeIndex];          const yStart = yTiles - range.endY - 1;          const 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,      })    );    const 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 Promise.resolve();      }      lastResource = lastResource.getDerivedResource({        url: parentUrl,      });      lastResource.appendForwardSlash(); // Terrain always expects a directory      layerJsonResource = lastResource.getDerivedResource({        url: "layer.json",      });      const parentMetadata = layerJsonResource.fetchJson();      return Promise.resolve(parentMetadata)        .then(parseMetadataSuccess)        .catch(parseMetadataFailure);    }    return Promise.resolve();  }  function parseMetadataFailure(data) {    const 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;      }      const length = overallAvailability.length;      if (length > 0) {        const availability = (that._availability = new TileAvailability(          that._tilingScheme,          overallMaxZoom        ));        for (let level = 0; level < length; ++level) {          const levelRanges = overallAvailability[level];          for (let i = 0; i < levelRanges.length; ++i) {            const range = levelRanges[i];            availability.addAvailableTileRange(              level,              range[0],              range[1],              range[2],              range[3]            );          }        }      }      if (attribution.length > 0) {        const 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() {    Promise.resolve(layerJsonResource.fetchJson())      .then(metadataSuccess)      .catch(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. * * @namespace QuantizedMeshExtensionIds * @see CesiumTerrainProvider * @private */const 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",    };  }  const 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) {  const 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, layer) {  const littleEndianExtensionSize = layer.littleEndianExtensionSize;  let pos = 0;  const cartesian3Elements = 3;  const boundingSphereElements = cartesian3Elements + 1;  const cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements;  const boundingSphereLength =    Float64Array.BYTES_PER_ELEMENT * boundingSphereElements;  const encodedVertexElements = 3;  const encodedVertexLength =    Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements;  const triangleElements = 3;  let bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;  let triangleLength = bytesPerIndex * triangleElements;  const view = new DataView(buffer);  const center = new Cartesian3(    view.getFloat64(pos, true),    view.getFloat64(pos + 8, true),    view.getFloat64(pos + 16, true)  );  pos += cartesian3Length;  const minimumHeight = view.getFloat32(pos, true);  pos += Float32Array.BYTES_PER_ELEMENT;  const maximumHeight = view.getFloat32(pos, true);  pos += Float32Array.BYTES_PER_ELEMENT;  const 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;  const horizonOcclusionPoint = new Cartesian3(    view.getFloat64(pos, true),    view.getFloat64(pos + 8, true),    view.getFloat64(pos + 16, true)  );  pos += cartesian3Length;  const vertexCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const 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.  const uBuffer = encodedVertexBuffer.subarray(0, vertexCount);  const vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount);  const 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);  }  const triangleCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const 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.  let highest = 0;  const length = indices.length;  for (let i = 0; i < length; ++i) {    const code = indices[i];    indices[i] = highest - code;    if (code === 0) {      ++highest;    }  }  const westVertexCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const westIndices = IndexDatatype.createTypedArrayFromArrayBuffer(    vertexCount,    buffer,    pos,    westVertexCount  );  pos += westVertexCount * bytesPerIndex;  const southVertexCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const southIndices = IndexDatatype.createTypedArrayFromArrayBuffer(    vertexCount,    buffer,    pos,    southVertexCount  );  pos += southVertexCount * bytesPerIndex;  const eastVertexCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const eastIndices = IndexDatatype.createTypedArrayFromArrayBuffer(    vertexCount,    buffer,    pos,    eastVertexCount  );  pos += eastVertexCount * bytesPerIndex;  const northVertexCount = view.getUint32(pos, true);  pos += Uint32Array.BYTES_PER_ELEMENT;  const northIndices = IndexDatatype.createTypedArrayFromArrayBuffer(    vertexCount,    buffer,    pos,    northVertexCount  );  pos += northVertexCount * bytesPerIndex;  let encodedNormalBuffer;  let waterMaskBuffer;  while (pos < view.byteLength) {    const extensionId = view.getUint8(pos, true);    pos += Uint8Array.BYTES_PER_ELEMENT;    const 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    ) {      const stringLength = view.getUint32(pos, true);      if (stringLength > 0) {        const metadata = getJsonFromTypedArray(          new Uint8Array(buffer),          pos + Uint32Array.BYTES_PER_ELEMENT,          stringLength        );        const availableTiles = metadata.available;        if (defined(availableTiles)) {          for (let offset = 0; offset < availableTiles.length; ++offset) {            const availableLevel = level + offset + 1;            const rangesAtLevel = availableTiles[offset];            const yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(              availableLevel            );            for (              let rangeIndex = 0;              rangeIndex < rangesAtLevel.length;              ++rangeIndex            ) {              const range = rangesAtLevel[rangeIndex];              const yStart = yTiles - range.endY - 1;              const 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;  }  const skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;  // 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`.  const rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);  const 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');  const layers = this._layers;  let layerToUse;  const layerCount = layers.length;  if (layerCount === 1) {    // Optimized path for single layers    layerToUse = layers[0];  } else {    for (let i = 0; i < layerCount; ++i) {      const 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 Promise.reject(new RuntimeError("Terrain tile doesn't exist"));  }  const urlTemplates = layerToUse.tileUrlTemplates;  if (urlTemplates.length === 0) {    return undefined;  }  // The TileMapService scheme counts from the bottom left  let terrainY;  if (!provider._scheme || provider._scheme === "tms") {    const yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(level);    terrainY = yTiles - y - 1;  } else {    terrainY = y;  }  const 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");  }  let headers;  let query;  const url = urlTemplates[(x + terrainY + level) % urlTemplates.length];  const 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);  }  const promise = resource    .getDerivedResource({      url: url,      templateValues: {        version: layerToUse.version,        z: level,        x: x,        y: terrainY,      },      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);    }    return createQuantizedMeshTerrainData(      provider,      buffer,      level,      x,      y,      layerToUse    );  });}Object.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}   * @readonly   */  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}   * @readonly   */  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}   * @readonly   */  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}   * @readonly   */  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}   * @readonly   * @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}   * @readonly   * @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}   * @readonly   * @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}   * @readonly   */  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}   * @readonly   */  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}   * @readonly   */  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}   * @readonly   */  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} 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;  }  const layers = this._layers;  const count = layers.length;  for (let i = 0; i < count; ++i) {    const 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<void>} 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;  }  const layers = this._layers;  const count = layers.length;  for (let i = 0; i < count; ++i) {    const 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;  }  const availabilityLevels = layer.availabilityLevels;  const parentLevel =    level % availabilityLevels === 0      ? level - availabilityLevels      : ((level / availabilityLevels) | 0) * availabilityLevels;  const divisor = 1 << (level - parentLevel);  const parentX = (x / divisor) | 0;  const 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,    };  }  let cacheKey;  const deleteFromCache = function () {    delete layer.availabilityPromiseCache[cacheKey];  };  const availabilityTilesLoaded = layer.availabilityTilesLoaded;  const availability = layer.availability;  let 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)    ) {      let 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.          const request = new Request({            throttle: false,            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 testingCesiumTerrainProvider._getAvailabilityTile = getAvailabilityTile;export default CesiumTerrainProvider;
 |