text: import BoundingSphere from '../Core/BoundingSphere.js';
  import Cartesian3 from '../Core/Cartesian3.js';
  import Cartesian4 from '../Core/Cartesian4.js';
  import defined from '../Core/defined.js';
  import IndexDatatype from '../Core/IndexDatatype.js';
  import IntersectionTests from '../Core/IntersectionTests.js';
  import OrientedBoundingBox from '../Core/OrientedBoundingBox.js';
  import PixelFormat from '../Core/PixelFormat.js';
  import Ray from '../Core/Ray.js';
  import Request from '../Core/Request.js';
  import RequestState from '../Core/RequestState.js';
  import RequestType from '../Core/RequestType.js';
  import TileProviderError from '../Core/TileProviderError.js';
  import Buffer from '../Renderer/Buffer.js';
  import BufferUsage from '../Renderer/BufferUsage.js';
  import PixelDatatype from '../Renderer/PixelDatatype.js';
  import Sampler from '../Renderer/Sampler.js';
  import Texture from '../Renderer/Texture.js';
  import TextureMagnificationFilter from '../Renderer/TextureMagnificationFilter.js';
  import TextureMinificationFilter from '../Renderer/TextureMinificationFilter.js';
  import TextureWrap from '../Renderer/TextureWrap.js';
  import VertexArray from '../Renderer/VertexArray.js';
  import when from '../ThirdParty/when.js';
  import ImageryState from './ImageryState.js';
  import QuadtreeTileLoadState from './QuadtreeTileLoadState.js';
  import SceneMode from './SceneMode.js';
  import TerrainState from './TerrainState.js';
  
      /**
       * Contains additional information about a {@link QuadtreeTile} of the globe's surface, and
       * encapsulates state transition logic for loading tiles.
       *
       * @constructor
       * @alias GlobeSurfaceTile
       * @private
       */
      function GlobeSurfaceTile() {
          /**
           * The {@link TileImagery} attached to this tile.
           * @type {TileImagery[]}
           * @default []
           */
          this.imagery = [];
  
          this.waterMaskTexture = undefined;
          this.waterMaskTranslationAndScale = new Cartesian4(0.0, 0.0, 1.0, 1.0);
  
          this.terrainData = undefined;
          this.vertexArray = undefined;
          this.orientedBoundingBox = undefined;
          this.boundingVolumeSourceTile = undefined;
  
          /**
           * A bounding region used to estimate distance to the tile. The horizontal bounds are always tight-fitting,
           * but the `minimumHeight` and `maximumHeight` properties may be derived from the min/max of an ancestor tile
           * and be quite loose-fitting and thus very poor for estimating distance. The {@link TileBoundingRegion#boundingVolume}
           * and {@link TileBoundingRegion#boundingSphere} will always be undefined; tiles store these separately.
           * @type {TileBoundingRegion}
           */
          this.tileBoundingRegion = undefined;
          this.occludeePointInScaledSpace = new Cartesian3();
  
          this.terrainState = TerrainState.UNLOADED;
          this.mesh = undefined;
          this.fill = undefined;
  
          this.pickBoundingSphere = new BoundingSphere();
  
          this.surfaceShader = undefined;
          this.isClipped = true;
  
          this.clippedByBoundaries = false;
      }
  
      Object.defineProperties(GlobeSurfaceTile.prototype, {
          /**
           * Gets a value indicating whether or not this tile is eligible to be unloaded.
           * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
           * such as a request for data, is in progress on it.  A tile will never be
           * unloaded while it is needed for rendering, regardless of the value of this
           * property.
           * @memberof GlobeSurfaceTile.prototype
           * @type {Boolean}
           */
          eligibleForUnloading : {
              get : function() {
                  // Do not remove tiles that are transitioning or that have
                  // imagery that is transitioning.
                  var terrainState = this.terrainState;
                  var loadingIsTransitioning = terrainState === TerrainState.RECEIVING || terrainState === TerrainState.TRANSFORMING;
  
                  var shouldRemoveTile = !loadingIsTransitioning;
  
                  var imagery = this.imagery;
                  for (var i = 0, len = imagery.length; shouldRemoveTile && i < len; ++i) {
                      var tileImagery = imagery[i];
                      shouldRemoveTile = !defined(tileImagery.loadingImagery) || tileImagery.loadingImagery.state !== ImageryState.TRANSITIONING;
                  }
  
                  return shouldRemoveTile;
              }
          },
  
          /**
           * Gets the {@link TerrainMesh} that is used for rendering this tile, if any.
           * Returns the value of the {@link GlobeSurfaceTile#mesh} property if
           * {@link GlobeSurfaceTile#vertexArray} is defined. Otherwise, It returns the
           * {@link TerrainFillMesh#mesh} property of the {@link GlobeSurfaceTile#fill}.
           * If there is no fill, it returns undefined.
           *
           * @memberof GlobeSurfaceTile.prototype
           * @type {TerrainMesh}
           */
          renderedMesh : {
              get : function() {
                  if (defined(this.vertexArray)) {
                      return this.mesh;
                  } else if (defined(this.fill)) {
                      return this.fill.mesh;
                  }
                  return undefined;
              }
          }
      });
  
      function getPosition(encoding, mode, projection, vertices, index, result) {
          encoding.decodePosition(vertices, index, result);
  
          if (defined(mode) && mode !== SceneMode.SCENE3D) {
              var ellipsoid = projection.ellipsoid;
              var positionCart = ellipsoid.cartesianToCartographic(result);
              projection.project(positionCart, result);
              Cartesian3.fromElements(result.z, result.x, result.y, result);
          }
  
          return result;
      }
  
      var scratchV0 = new Cartesian3();
      var scratchV1 = new Cartesian3();
      var scratchV2 = new Cartesian3();
  
      GlobeSurfaceTile.prototype.pick = function(ray, mode, projection, cullBackFaces, result) {
          var mesh = this.renderedMesh;
          if (!defined(mesh)) {
              return undefined;
          }
  
          var vertices = mesh.vertices;
          var indices = mesh.indices;
          var encoding = mesh.encoding;
          var indicesLength = indices.length;
  
          var minT = Number.MAX_VALUE;
  
          for (var i = 0; i < indicesLength; i += 3) {
              var i0 = indices[i];
              var i1 = indices[i + 1];
              var i2 = indices[i + 2];
  
              var v0 = getPosition(encoding, mode, projection, vertices, i0, scratchV0);
              var v1 = getPosition(encoding, mode, projection, vertices, i1, scratchV1);
              var v2 = getPosition(encoding, mode, projection, vertices, i2, scratchV2);
  
              var t = IntersectionTests.rayTriangleParametric(ray, v0, v1, v2, cullBackFaces);
              if (defined(t) && t < minT && t >= 0.0) {
                  minT = t;
              }
          }
  
          return minT !== Number.MAX_VALUE ? Ray.getPoint(ray, minT, result) : undefined;
      };
  
      GlobeSurfaceTile.prototype.freeResources = function() {
          if (defined(this.waterMaskTexture)) {
              --this.waterMaskTexture.referenceCount;
              if (this.waterMaskTexture.referenceCount === 0) {
                  this.waterMaskTexture.destroy();
              }
              this.waterMaskTexture = undefined;
          }
  
          this.terrainData = undefined;
  
          this.terrainState = TerrainState.UNLOADED;
          this.mesh = undefined;
          this.fill = this.fill && this.fill.destroy();
  
          var imageryList = this.imagery;
          for (var i = 0, len = imageryList.length; i < len; ++i) {
              imageryList[i].freeResources();
          }
          this.imagery.length = 0;
  
          this.freeVertexArray();
      };
  
      GlobeSurfaceTile.prototype.freeVertexArray = function() {
          GlobeSurfaceTile._freeVertexArray(this.vertexArray);
          this.vertexArray = undefined;
          GlobeSurfaceTile._freeVertexArray(this.wireframeVertexArray);
          this.wireframeVertexArray = undefined;
      };
  
      GlobeSurfaceTile.initialize = function(tile, terrainProvider, imageryLayerCollection) {
          var surfaceTile = tile.data;
          if (!defined(surfaceTile)) {
              surfaceTile = tile.data = new GlobeSurfaceTile();
          }
  
          if (tile.state === QuadtreeTileLoadState.START) {
              prepareNewTile(tile, terrainProvider, imageryLayerCollection);
              tile.state = QuadtreeTileLoadState.LOADING;
          }
      };
  
      GlobeSurfaceTile.processStateMachine = function(tile, frameState, terrainProvider, imageryLayerCollection, vertexArraysToDestroy, terrainOnly) {
          GlobeSurfaceTile.initialize(tile, terrainProvider, imageryLayerCollection);
  
          var surfaceTile = tile.data;
  
          if (tile.state === QuadtreeTileLoadState.LOADING) {
              processTerrainStateMachine(tile, frameState, terrainProvider, imageryLayerCollection, vertexArraysToDestroy);
          }
  
          // From here down we're loading imagery, not terrain. We don't want to load imagery until
          // we're certain that the terrain tiles are actually visible, though. We'll load terrainOnly
          // in these scenarios:
          //   * our bounding volume isn't accurate so we're not certain this tile is really visible (see GlobeSurfaceTileProvider#loadTile).
          //   * we want to upsample from this tile but don't plan to render it (see processTerrainStateMachine).
          if (terrainOnly) {
              return;
          }
  
          var wasAlreadyRenderable = tile.renderable;
  
          // The terrain is renderable as soon as we have a valid vertex array.
          tile.renderable = defined(surfaceTile.vertexArray);
  
          // But it's not done loading until it's in the READY state.
          var isTerrainDoneLoading = surfaceTile.terrainState === TerrainState.READY;
  
          // If this tile's terrain and imagery are just upsampled from its parent, mark the tile as
          // upsampled only.  We won't refine a tile if its four children are upsampled only.
          tile.upsampledFromParent = defined(surfaceTile.terrainData) && surfaceTile.terrainData.wasCreatedByUpsampling();
  
          var isImageryDoneLoading = surfaceTile.processImagery(tile, terrainProvider, frameState);
  
          if (isTerrainDoneLoading && isImageryDoneLoading) {
              var callbacks = tile._loadedCallbacks;
              var newCallbacks = {};
              for(var layerId in callbacks) {
                  if (callbacks.hasOwnProperty(layerId)) {
                      if(!callbacks[layerId](tile)) {
                          newCallbacks[layerId] = callbacks[layerId];
                      }
                  }
              }
              tile._loadedCallbacks = newCallbacks;
  
              tile.state = QuadtreeTileLoadState.DONE;
          }
  
          // Once a tile is renderable, it stays renderable, because doing otherwise would
          // cause detail (or maybe even the entire globe) to vanish when adding a new
          // imagery layer. `GlobeSurfaceTileProvider._onLayerAdded` sets renderable to
          // false for all affected tiles that are not currently being rendered.
          if (wasAlreadyRenderable) {
              tile.renderable = true;
          }
      };
  
      GlobeSurfaceTile.prototype.processImagery = function(tile, terrainProvider, frameState, skipLoading) {
          var surfaceTile = tile.data;
          var isUpsampledOnly = tile.upsampledFromParent;
          var isAnyTileLoaded = false;
          var isDoneLoading = true;
  
          // Transition imagery states
          var tileImageryCollection = surfaceTile.imagery;
          var i, len;
          for (i = 0, len = tileImageryCollection.length; i < len; ++i) {
              var tileImagery = tileImageryCollection[i];
              if (!defined(tileImagery.loadingImagery)) {
                  isUpsampledOnly = false;
                  continue;
              }
  
              if (tileImagery.loadingImagery.state === ImageryState.PLACEHOLDER) {
                  var imageryLayer = tileImagery.loadingImagery.imageryLayer;
                  if (imageryLayer.imageryProvider.ready) {
                      // Remove the placeholder and add the actual skeletons (if any)
                      // at the same position.  Then continue the loop at the same index.
                      tileImagery.freeResources();
                      tileImageryCollection.splice(i, 1);
                      imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i);
                      --i;
                      len = tileImageryCollection.length;
                      continue;
                  } else {
                      isUpsampledOnly = false;
                  }
              }
  
              var thisTileDoneLoading = tileImagery.processStateMachine(tile, frameState, skipLoading);
              isDoneLoading = isDoneLoading && thisTileDoneLoading;
  
              // The imagery is renderable as soon as we have any renderable imagery for this region.
              isAnyTileLoaded = isAnyTileLoaded || (thisTileDoneLoading || defined(tileImagery.readyImagery));
  
              isUpsampledOnly = isUpsampledOnly && defined(tileImagery.loadingImagery) &&
                                (tileImagery.loadingImagery.state === ImageryState.FAILED || tileImagery.loadingImagery.state === ImageryState.INVALID);
          }
  
          tile.upsampledFromParent = isUpsampledOnly;
  
          // Allow rendering if any available layers are loaded
          tile.renderable = tile.renderable && (isAnyTileLoaded || isDoneLoading);
  
          return isDoneLoading;
      };
  
      function prepareNewTile(tile, terrainProvider, imageryLayerCollection) {
          var available = terrainProvider.getTileDataAvailable(tile.x, tile.y, tile.level);
  
          if (!defined(available) && defined(tile.parent)) {
              // Provider doesn't know if this tile is available. Does the parent tile know?
              var parent = tile.parent;
              var parentSurfaceTile = parent.data;
              if (defined(parentSurfaceTile) && defined(parentSurfaceTile.terrainData)) {
                  available = parentSurfaceTile.terrainData.isChildAvailable(parent.x, parent.y, tile.x, tile.y);
              }
          }
  
          if (available === false) {
              // This tile is not available, so mark it failed so we start upsampling right away.
              tile.data.terrainState = TerrainState.FAILED;
          }
  
          // Map imagery tiles to this terrain tile
          for (var i = 0, len = imageryLayerCollection.length; i < len; ++i) {
              var layer = imageryLayerCollection.get(i);
              if (layer.show) {
                  layer._createTileImagerySkeletons(tile, terrainProvider);
              }
          }
      }
  
      function processTerrainStateMachine(tile, frameState, terrainProvider, imageryLayerCollection, vertexArraysToDestroy) {
          var surfaceTile = tile.data;
  
          // If this tile is FAILED, we'll need to upsample from the parent. If the parent isn't
          // ready for that, let's push it along.
          var parent = tile.parent;
          if (surfaceTile.terrainState === TerrainState.FAILED && parent !== undefined) {
              var parentReady = parent.data !== undefined && parent.data.terrainData !== undefined && parent.data.terrainData.canUpsample !== false;
              if (!parentReady) {
                  GlobeSurfaceTile.processStateMachine(parent, frameState, terrainProvider, imageryLayerCollection, true);
              }
          }
  
          if (surfaceTile.terrainState === TerrainState.FAILED) {
              upsample(surfaceTile, tile, frameState, terrainProvider, tile.x, tile.y, tile.level);
          }
  
          if (surfaceTile.terrainState === TerrainState.UNLOADED) {
              requestTileGeometry(surfaceTile, terrainProvider, tile.x, tile.y, tile.level);
          }
  
          if (surfaceTile.terrainState === TerrainState.RECEIVED) {
              transform(surfaceTile, frameState, terrainProvider, tile.x, tile.y, tile.level);
          }
  
          if (surfaceTile.terrainState === TerrainState.TRANSFORMED) {
              createResources(surfaceTile, frameState.context, terrainProvider, tile.x, tile.y, tile.level, vertexArraysToDestroy);
          }
  
          if (surfaceTile.terrainState >= TerrainState.RECEIVED && surfaceTile.waterMaskTexture === undefined && terrainProvider.hasWaterMask) {
              var terrainData = surfaceTile.terrainData;
              if (terrainData.waterMask !== undefined) {
                  createWaterMaskTextureIfNeeded(frameState.context, surfaceTile);
              } else {
                  var sourceTile = surfaceTile._findAncestorTileWithTerrainData(tile);
                  if (defined(sourceTile) && defined(sourceTile.data.waterMaskTexture)) {
                      surfaceTile.waterMaskTexture = sourceTile.data.waterMaskTexture;
                      ++surfaceTile.waterMaskTexture.referenceCount;
                      surfaceTile._computeWaterMaskTranslationAndScale(tile, sourceTile, surfaceTile.waterMaskTranslationAndScale);
                  }
              }
          }
      }
  
      function upsample(surfaceTile, tile, frameState, terrainProvider, x, y, level) {
          var parent = tile.parent;
          if (!parent) {
              // Trying to upsample from a root tile. No can do. This tile is a failure.
              tile.state = QuadtreeTileLoadState.FAILED;
              return;
          }
  
          var sourceData = parent.data.terrainData;
          var sourceX = parent.x;
          var sourceY = parent.y;
          var sourceLevel = parent.level;
  
          if (!defined(sourceData)) {
              // Parent is not available, so we can't upsample this tile yet.
              return;
          }
  
          var terrainDataPromise = sourceData.upsample(terrainProvider.tilingScheme, sourceX, sourceY, sourceLevel, x, y, level);
          if (!defined(terrainDataPromise)) {
              // The upsample request has been deferred - try again later.
              return;
          }
  
          surfaceTile.terrainState = TerrainState.RECEIVING;
  
          when(terrainDataPromise, function(terrainData) {
              surfaceTile.terrainData = terrainData;
              surfaceTile.terrainState = TerrainState.RECEIVED;
          }, function() {
              surfaceTile.terrainState = TerrainState.FAILED;
          });
      }
  
      function requestTileGeometry(surfaceTile, terrainProvider, x, y, level) {
          function success(terrainData) {
              surfaceTile.terrainData = terrainData;
              surfaceTile.terrainState = TerrainState.RECEIVED;
              surfaceTile.request = undefined;
          }
  
          function failure() {
              if (surfaceTile.request.state === RequestState.CANCELLED) {
                  // Cancelled due to low priority - try again later.
                  surfaceTile.terrainData = undefined;
                  surfaceTile.terrainState = TerrainState.UNLOADED;
                  surfaceTile.request = undefined;
                  return;
              }
  
              // Initially assume failure.  handleError may retry, in which case the state will
              // change to RECEIVING or UNLOADED.
              surfaceTile.terrainState = TerrainState.FAILED;
              surfaceTile.request = undefined;
  
              var message = 'Failed to obtain terrain tile X: ' + x + ' Y: ' + y + ' Level: ' + level + '.';
              terrainProvider._requestError = TileProviderError.handleError(
                  terrainProvider._requestError,
                  terrainProvider,
                  terrainProvider.errorEvent,
                  message,
                  x, y, level,
                  doRequest);
          }
  
          function doRequest() {
              // Request the terrain from the terrain provider.
              var request = new Request({
                  throttle : false,
                  throttleByServer : true,
                  type : RequestType.TERRAIN
              });
              surfaceTile.request = request;
              var requestPromise = terrainProvider.requestTileGeometry(x, y, level, request);
  
              // If the request method returns undefined (instead of a promise), the request
              // has been deferred.
              if (defined(requestPromise)) {
                  surfaceTile.terrainState = TerrainState.RECEIVING;
                  when(requestPromise, success, failure);
              } else {
                  // Deferred - try again later.
                  surfaceTile.terrainState = TerrainState.UNLOADED;
                  surfaceTile.request = undefined;
              }
          }
  
          doRequest();
      }
  
      function transform(surfaceTile, frameState, terrainProvider, x, y, level) {
          var tilingScheme = terrainProvider.tilingScheme;
  
          var terrainData = surfaceTile.terrainData;
          var meshPromise = terrainData.createMesh(tilingScheme, x, y, level, frameState.terrainExaggeration);
  
          if (!defined(meshPromise)) {
              // Postponed.
              return;
          }
  
          surfaceTile.terrainState = TerrainState.TRANSFORMING;
  
          when(meshPromise, function(mesh) {
              surfaceTile.mesh = mesh;
              surfaceTile.orientedBoundingBox = OrientedBoundingBox.clone(mesh.orientedBoundingBox, surfaceTile.orientedBoundingBox);
              surfaceTile.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace);
              surfaceTile.terrainState = TerrainState.TRANSFORMED;
          }, function() {
              surfaceTile.terrainState = TerrainState.FAILED;
          });
      }
  
      GlobeSurfaceTile._createVertexArrayForMesh = function(context, mesh) {
          var typedArray = mesh.vertices;
          var buffer = Buffer.createVertexBuffer({
              context : context,
              typedArray : typedArray,
              usage : BufferUsage.STATIC_DRAW
          });
          var attributes = mesh.encoding.getAttributes(buffer);
  
          var indexBuffers = mesh.indices.indexBuffers || {};
          var indexBuffer = indexBuffers[context.id];
          if (!defined(indexBuffer) || indexBuffer.isDestroyed()) {
              var indices = mesh.indices;
              indexBuffer = Buffer.createIndexBuffer({
                  context : context,
                  typedArray : indices,
                  usage : BufferUsage.STATIC_DRAW,
                  indexDatatype : IndexDatatype.fromSizeInBytes(indices.BYTES_PER_ELEMENT)
              });
              indexBuffer.vertexArrayDestroyable = false;
              indexBuffer.referenceCount = 1;
              indexBuffers[context.id] = indexBuffer;
              mesh.indices.indexBuffers = indexBuffers;
          } else {
              ++indexBuffer.referenceCount;
          }
  
          return new VertexArray({
              context : context,
              attributes : attributes,
              indexBuffer : indexBuffer
          });
      };
  
      GlobeSurfaceTile._freeVertexArray = function(vertexArray) {
          if (defined(vertexArray)) {
              var indexBuffer = vertexArray.indexBuffer;
  
              vertexArray.destroy();
  
              if (defined(indexBuffer) && !indexBuffer.isDestroyed() && defined(indexBuffer.referenceCount)) {
                  --indexBuffer.referenceCount;
                  if (indexBuffer.referenceCount === 0) {
                      indexBuffer.destroy();
                  }
              }
          }
  
      };
  
      function createResources(surfaceTile, context, terrainProvider, x, y, level, vertexArraysToDestroy) {
          surfaceTile.vertexArray = GlobeSurfaceTile._createVertexArrayForMesh(context, surfaceTile.mesh);
          surfaceTile.terrainState = TerrainState.READY;
          surfaceTile.fill = surfaceTile.fill && surfaceTile.fill.destroy(vertexArraysToDestroy);
      }
  
      function getContextWaterMaskData(context) {
          var data = context.cache.tile_waterMaskData;
  
          if (!defined(data)) {
              var allWaterTexture = Texture.create({
                  context : context,
                  pixelFormat : PixelFormat.LUMINANCE,
                  pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
                  source : {
                      arrayBufferView : new Uint8Array([255]),
                      width : 1,
                      height : 1
                  }
              });
              allWaterTexture.referenceCount = 1;
  
              var sampler = new Sampler({
                  wrapS : TextureWrap.CLAMP_TO_EDGE,
                  wrapT : TextureWrap.CLAMP_TO_EDGE,
                  minificationFilter : TextureMinificationFilter.LINEAR,
                  magnificationFilter : TextureMagnificationFilter.LINEAR
              });
  
              data = {
                  allWaterTexture : allWaterTexture,
                  sampler : sampler,
                  destroy : function() {
                      this.allWaterTexture.destroy();
                  }
              };
  
              context.cache.tile_waterMaskData = data;
          }
  
          return data;
      }
  
      function createWaterMaskTextureIfNeeded(context, surfaceTile) {
          var waterMask = surfaceTile.terrainData.waterMask;
          var waterMaskData = getContextWaterMaskData(context);
          var texture;
  
          var waterMaskLength = waterMask.length;
          if (waterMaskLength === 1) {
              // Length 1 means the tile is entirely land or entirely water.
              // A value of 0 indicates entirely land, a value of 1 indicates entirely water.
              if (waterMask[0] !== 0) {
                  texture = waterMaskData.allWaterTexture;
              } else {
                  // Leave the texture undefined if the tile is entirely land.
                  return;
              }
          } else {
              var textureSize = Math.sqrt(waterMaskLength);
              texture = Texture.create({
                  context : context,
                  pixelFormat : PixelFormat.LUMINANCE,
                  pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
                  source : {
                      width : textureSize,
                      height : textureSize,
                      arrayBufferView : waterMask
                  },
                  sampler : waterMaskData.sampler,
                  flipY : false
              });
  
              texture.referenceCount = 0;
          }
  
          ++texture.referenceCount;
          surfaceTile.waterMaskTexture = texture;
  
          Cartesian4.fromElements(0.0, 0.0, 1.0, 1.0, surfaceTile.waterMaskTranslationAndScale);
      }
  
      GlobeSurfaceTile.prototype._findAncestorTileWithTerrainData = function(tile) {
          var sourceTile = tile.parent;
  
          while (defined(sourceTile) && (!defined(sourceTile.data) || !defined(sourceTile.data.terrainData) || sourceTile.data.terrainData.wasCreatedByUpsampling())) {
              sourceTile = sourceTile.parent;
          }
  
          return sourceTile;
      };
  
      GlobeSurfaceTile.prototype._computeWaterMaskTranslationAndScale = function(tile, sourceTile, result) {
          var sourceTileRectangle = sourceTile.rectangle;
          var tileRectangle = tile.rectangle;
          var tileWidth = tileRectangle.width;
          var tileHeight = tileRectangle.height;
  
          var scaleX = tileWidth / sourceTileRectangle.width;
          var scaleY = tileHeight / sourceTileRectangle.height;
          result.x = scaleX * (tileRectangle.west - sourceTileRectangle.west) / tileWidth;
          result.y = scaleY * (tileRectangle.south - sourceTileRectangle.south) / tileHeight;
          result.z = scaleX;
          result.w = scaleY;
  
          return result;
      };
  export default GlobeSurfaceTile;
  