text: import Tile2DHeader from './tile-2d-header';
  import { getTileIndices, tileToBoundingBox } from './utils';
  import { RequestScheduler } from '@loaders.gl/loader-utils';
  const TILE_STATE_UNKNOWN = 0;
  const TILE_STATE_VISIBLE = 1;
  const TILE_STATE_PLACEHOLDER = 3;
  const TILE_STATE_HIDDEN = 4;
  const TILE_STATE_SELECTED = 5;
  export const STRATEGY_NEVER = 'never';
  export const STRATEGY_REPLACE = 'no-overlap';
  export const STRATEGY_DEFAULT = 'best-available';
  const DEFAULT_CACHE_SCALE = 5;
  export default class Tileset2D {
    constructor(opts) {
      this.opts = opts;
      this._getTileData = opts.getTileData;
      this.onTileError = opts.onTileError;
  
      this.onTileLoad = tile => {
        opts.onTileLoad(tile);
  
        if (this.opts.maxCacheByteSize) {
          this._cacheByteSize += tile.byteLength;
  
          this._resizeCache();
        }
      };
  
      this._requestScheduler = new RequestScheduler({
        maxRequests: opts.maxRequests,
        throttleRequests: opts.maxRequests > 0
      });
      this._cache = new Map();
      this._tiles = [];
      this._dirty = false;
      this._cacheByteSize = 0;
      this._viewport = null;
      this._selectedTiles = null;
      this._frameNumber = 0;
      this.setOptions(opts);
    }
  
    get tiles() {
      return this._tiles;
    }
  
    get selectedTiles() {
      return this._selectedTiles;
    }
  
    get isLoaded() {
      return this._selectedTiles.every(tile => tile.isLoaded);
    }
  
    setOptions(opts) {
      Object.assign(this.opts, opts);
  
      if (Number.isFinite(opts.maxZoom)) {
        this._maxZoom = Math.floor(opts.maxZoom);
      }
  
      if (Number.isFinite(opts.minZoom)) {
        this._minZoom = Math.ceil(opts.minZoom);
      }
    }
  
    update(viewport, {
      zRange
    } = {}) {
      if (!viewport.equals(this._viewport)) {
        this._viewport = viewport;
        const tileIndices = this.getTileIndices({
          viewport,
          maxZoom: this._maxZoom,
          minZoom: this._minZoom,
          zRange
        });
        this._selectedTiles = tileIndices.map(index => this._getTile(index, true));
  
        if (this._dirty) {
          this._rebuildTree();
        }
      }
  
      const changed = this.updateTileStates();
  
      if (this._dirty) {
        this._resizeCache();
      }
  
      if (changed) {
        this._frameNumber++;
      }
  
      return this._frameNumber;
    }
  
    getTileIndices({
      viewport,
      maxZoom,
      minZoom,
      zRange
    }) {
      const {
        tileSize,
        extent
      } = this.opts;
      return getTileIndices({
        viewport,
        maxZoom,
        minZoom,
        zRange,
        tileSize,
        extent
      });
    }
  
    getTileMetadata({
      x,
      y,
      z
    }) {
      return {
        bbox: tileToBoundingBox(this._viewport, x, y, z)
      };
    }
  
    getParentIndex(tileIndex) {
      tileIndex.x = Math.floor(tileIndex.x / 2);
      tileIndex.y = Math.floor(tileIndex.y / 2);
      tileIndex.z -= 1;
      return tileIndex;
    }
  
    updateTileStates() {
      this._updateTileStates(this.selectedTiles);
  
      let changed = false;
  
      for (const tile of this._cache.values()) {
        const isVisible = Boolean(tile.state & TILE_STATE_VISIBLE);
  
        if (tile.isVisible !== isVisible) {
          changed = true;
          tile.isVisible = isVisible;
        }
  
        tile.isSelected = tile.state === TILE_STATE_SELECTED;
      }
  
      return changed;
    }
  
    _rebuildTree() {
      const {
        _cache
      } = this;
  
      for (const tile of _cache.values()) {
        tile.parent = null;
        tile.children.length = 0;
      }
  
      for (const tile of _cache.values()) {
        const parent = this._getNearestAncestor(tile.x, tile.y, tile.z);
  
        tile.parent = parent;
  
        if (parent) {
          parent.children.push(tile);
        }
      }
    }
  
    _updateTileStates(selectedTiles) {
      const {
        _cache
      } = this;
      const refinementStrategy = this.opts.refinementStrategy || STRATEGY_DEFAULT;
  
      for (const tile of _cache.values()) {
        tile.state = TILE_STATE_UNKNOWN;
      }
  
      for (const tile of selectedTiles) {
        tile.state = TILE_STATE_SELECTED;
      }
  
      if (refinementStrategy === STRATEGY_NEVER) {
        return;
      }
  
      for (const tile of selectedTiles) {
        getPlaceholderInAncestors(tile, refinementStrategy);
      }
  
      for (const tile of selectedTiles) {
        if (needsPlaceholder(tile)) {
          getPlaceholderInChildren(tile);
        }
      }
    }
  
    _resizeCache() {
      const {
        _cache,
        opts
      } = this;
      const maxCacheSize = opts.maxCacheSize || (opts.maxCacheByteSize ? Infinity : DEFAULT_CACHE_SCALE * this.selectedTiles.length);
      const maxCacheByteSize = opts.maxCacheByteSize || Infinity;
      const overflown = _cache.size > maxCacheSize || this._cacheByteSize > maxCacheByteSize;
  
      if (overflown) {
        for (const [tileId, tile] of _cache) {
          if (!tile.isVisible) {
            this._cacheByteSize -= opts.maxCacheByteSize ? tile.byteLength : 0;
  
            _cache.delete(tileId);
          }
  
          if (_cache.size <= maxCacheSize && this._cacheByteSize <= maxCacheByteSize) {
            break;
          }
        }
  
        this._rebuildTree();
  
        this._dirty = true;
      }
  
      if (this._dirty) {
        this._tiles = Array.from(this._cache.values()).sort((t1, t2) => t1.z - t2.z);
        this._dirty = false;
      }
    }
  
    _getTile({
      x,
      y,
      z
    }, create) {
      const tileId = "".concat(x, ",").concat(y, ",").concat(z);
  
      let tile = this._cache.get(tileId);
  
      if (!tile && create) {
        tile = new Tile2DHeader({
          x,
          y,
          z,
          onTileLoad: this.onTileLoad,
          onTileError: this.onTileError
        });
        Object.assign(tile, this.getTileMetadata(tile));
        tile.loadData(this._getTileData, this._requestScheduler);
  
        this._cache.set(tileId, tile);
  
        this._dirty = true;
      } else if (tile && tile.isCancelled) {
        tile.loadData(this._getTileData, this._requestScheduler);
      }
  
      return tile;
    }
  
    _getNearestAncestor(x, y, z) {
      const {
        _minZoom = 0
      } = this;
      let index = {
        x,
        y,
        z
      };
  
      while (index.z > _minZoom) {
        index = this.getParentIndex(index);
  
        const parent = this._getTile(index);
  
        if (parent) {
          return parent;
        }
      }
  
      return null;
    }
  
  }
  
  function needsPlaceholder(tile) {
    let t = tile;
  
    while (t) {
      if (t.state & TILE_STATE_VISIBLE === 0) {
        return true;
      }
  
      if (t.isLoaded) {
        return false;
      }
  
      t = t.parent;
    }
  
    return true;
  }
  
  function getPlaceholderInAncestors(tile, refinementStrategy) {
    let parent;
    let state = TILE_STATE_PLACEHOLDER;
  
    while (parent = tile.parent) {
      if (tile.isLoaded) {
        state = TILE_STATE_HIDDEN;
  
        if (refinementStrategy === STRATEGY_DEFAULT) {
          return;
        }
      }
  
      parent.state = Math.max(parent.state, state);
      tile = parent;
    }
  }
  
  function getPlaceholderInChildren(tile) {
    for (const child of tile.children) {
      child.state = Math.max(child.state, TILE_STATE_PLACEHOLDER);
  
      if (!child.isLoaded) {
        getPlaceholderInChildren(child);
      }
    }
  }
  //# sourceMappingURL=tileset-2d.js.map