All files / engine/Source/Scene Cesium3DTilesetHeatmap.js

96.55% Statements 56/58
91.3% Branches 21/23
100% Functions 6/6
96.55% Lines 56/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166                                      677x     677x 677x     677x 677x       677x 677x                 5x 2x   3x   5x                   1x         1x       1x             3x 3x 3x       3x       3x 3x 3x       1x                           1x 19x 19x         16x     3x 3x 3x   3x 2x       1x 1x             1x 1x 1x     1x 1x 1x 1x 1x     1x 1x 1x 1x 1x           1x   5997x 5997x 3x 3x 3x 3x 3x 3x 3x        
import Color from "../Core/Color.js";
import defined from "../Core/defined.js";
import JulianDate from "../Core/JulianDate.js";
import CesiumMath from "../Core/Math.js";
 
/**
 * A heatmap colorizer in a {@link Cesium3DTileset}. A tileset can colorize its visible tiles in a heatmap style.
 *
 * @alias Cesium3DTilesetHeatmap
 * @constructor
 * @private
 */
function Cesium3DTilesetHeatmap(tilePropertyName) {
  /**
   * The tile variable to track for heatmap colorization.
   * Tile's will be colorized relative to the other visible tile's values for this variable.
   *
   * @type {string}
   */
  this.tilePropertyName = tilePropertyName;
 
  // Members that are updated every time a tile is colorized
  this._minimum = Number.MAX_VALUE;
  this._maximum = -Number.MAX_VALUE;
 
  // Members that are updated once every frame
  this._previousMinimum = Number.MAX_VALUE;
  this._previousMaximum = -Number.MAX_VALUE;
 
  // If defined uses a reference minimum maximum to colorize by instead of using last frames minimum maximum of rendered tiles.
  // For example, the _loadTimestamp can get a better colorization using setReferenceMinimumMaximum in order to take accurate colored timing diffs of various scenes.
  this._referenceMinimum = {};
  this._referenceMaximum = {};
}
 
/**
 * Convert to a usable heatmap value (i.e. a number). Ensures that tile values that aren't stored as numbers can be used for colorization.
 * @private
 */
function getHeatmapValue(tileValue, tilePropertyName) {
  let value;
  if (tilePropertyName === "_loadTimestamp") {
    value = JulianDate.toDate(tileValue).getTime();
  } else {
    value = tileValue;
  }
  return value;
}
 
/**
 * Sets the reference minimum and maximum for the variable name. Converted to numbers before they are stored.
 *
 * @param {object} minimum The minimum reference value.
 * @param {object} maximum The maximum reference value.
 * @param {string} tilePropertyName The tile variable that will use these reference values when it is colorized.
 */
Cesium3DTilesetHeatmap.prototype.setReferenceMinimumMaximum = function (
  minimum,
  maximum,
  tilePropertyName,
) {
  this._referenceMinimum[tilePropertyName] = getHeatmapValue(
    minimum,
    tilePropertyName,
  );
  this._referenceMaximum[tilePropertyName] = getHeatmapValue(
    maximum,
    tilePropertyName,
  );
};
 
function getHeatmapValueAndUpdateMinimumMaximum(heatmap, tile) {
  const tilePropertyName = heatmap.tilePropertyName;
  Eif (defined(tilePropertyName)) {
    const heatmapValue = getHeatmapValue(
      tile[tilePropertyName],
      tilePropertyName,
    );
    Iif (!defined(heatmapValue)) {
      heatmap.tilePropertyName = undefined;
      return heatmapValue;
    }
    heatmap._maximum = Math.max(heatmapValue, heatmap._maximum);
    heatmap._minimum = Math.min(heatmapValue, heatmap._minimum);
    return heatmapValue;
  }
}
 
const heatmapColors = [
  new Color(0.1, 0.1, 0.1, 1), // Dark Gray
  new Color(0.153, 0.278, 0.878, 1), // Blue
  new Color(0.827, 0.231, 0.49, 1), // Pink
  new Color(0.827, 0.188, 0.22, 1), // Red
  new Color(1.0, 0.592, 0.259, 1), // Orange
  new Color(1.0, 0.843, 0.0, 1),
]; // Yellow
/**
 * Colorize the tile in heat map style based on where it lies within the minimum maximum window.
 * Heatmap colors are black, blue, pink, red, orange, yellow. 'Cold' or low numbers will be black and blue, 'Hot' or high numbers will be orange and yellow,
 * @param {Cesium3DTile} tile The tile to colorize relative to last frame's minimum and maximum values of all visible tiles.
 * @param {FrameState} frameState The frame state.
 */
Cesium3DTilesetHeatmap.prototype.colorize = function (tile, frameState) {
  const tilePropertyName = this.tilePropertyName;
  if (
    !defined(tilePropertyName) ||
    !tile.contentAvailable ||
    tile._selectedFrame !== frameState.frameNumber
  ) {
    return;
  }
 
  const heatmapValue = getHeatmapValueAndUpdateMinimumMaximum(this, tile);
  const minimum = this._previousMinimum;
  const maximum = this._previousMaximum;
 
  if (minimum === Number.MAX_VALUE || maximum === -Number.MAX_VALUE) {
    return;
  }
 
  // Shift the minimum maximum window down to 0
  const shiftedMax = maximum - minimum + CesiumMath.EPSILON7; // Prevent divide by 0
  const shiftedValue = CesiumMath.clamp(
    heatmapValue - minimum,
    0.0,
    shiftedMax,
  );
 
  // Get position between minimum and maximum and convert that to a position in the color array
  const zeroToOne = shiftedValue / shiftedMax;
  const lastIndex = heatmapColors.length - 1.0;
  const colorPosition = zeroToOne * lastIndex;
 
  // Take floor and ceil of the value to get the two colors to lerp between, lerp using the fractional portion
  const colorPositionFloor = Math.floor(colorPosition);
  const colorPositionCeil = Math.ceil(colorPosition);
  const t = colorPosition - colorPositionFloor;
  const colorZero = heatmapColors[colorPositionFloor];
  const colorOne = heatmapColors[colorPositionCeil];
 
  // Perform the lerp
  const finalColor = Color.clone(Color.WHITE);
  finalColor.red = CesiumMath.lerp(colorZero.red, colorOne.red, t);
  finalColor.green = CesiumMath.lerp(colorZero.green, colorOne.green, t);
  finalColor.blue = CesiumMath.lerp(colorZero.blue, colorOne.blue, t);
  tile._debugColor = finalColor;
};
 
/**
 * Resets the tracked minimum maximum values for heatmap colorization. Happens right before tileset traversal.
 */
Cesium3DTilesetHeatmap.prototype.resetMinimumMaximum = function () {
  // For heat map colorization
  const tilePropertyName = this.tilePropertyName;
  if (defined(tilePropertyName)) {
    const referenceMinimum = this._referenceMinimum[tilePropertyName];
    const referenceMaximum = this._referenceMaximum[tilePropertyName];
    const useReference = defined(referenceMinimum) && defined(referenceMaximum);
    this._previousMinimum = useReference ? referenceMinimum : this._minimum;
    this._previousMaximum = useReference ? referenceMaximum : this._maximum;
    this._minimum = Number.MAX_VALUE;
    this._maximum = -Number.MAX_VALUE;
  }
};
export default Cesium3DTilesetHeatmap;