自定义 ElevationLayer - 作为高程的专题数据

尝试一下在线预览

此示例演示如何使用专题数据创建自定义 ElevationLayer在本例中,主题变量是海水温度。温度越高,海拔越高。陆地区域的高程代表平均海面温度(~17°C),因此在附近陆地上方达到峰值的伪海洋高程是温度高于 17°C 的区域,反之亦然。在这个海拔高度上覆盖了一个白色的图形,显示温度大于17。此图形可以通过滑动 Temperatures above ##° 滑块的手柄来移动。

SceneViewviewingMode 切换为 global以获得数据的不同视图。

custom-temp-layer-globe

创建自定义高程图层

要创建一个自定义的  ElevationLayer ,你必须在  BaseElevationLayer 上调用  createSubclass() 

由于此示例夸大了 ImageryLayer 的专题值,我们需要 ImageryLayer 并加载 ScientificData/SeaTemperature 影像服务作为自定义图层的依赖项。这是在 load() 方法中完成的,因为它是 可加载 资源。

 由于温度范围相对较小( -2°C  到 30°C ),需要将它们乘以一个很大的数字,才能在小尺度上看到高程之间的足够对比。我们将创建一个 factor 属性来指定这个值。

               
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Temperature3DLayer = BaseElevationLayer.createSubclass({
  properties: {
    // exaggerates the temps by 80000x so we can see
    // variation at large scales
    exaggeration: 80000
  },

  load: function() {
    // load ImageryLayer containing temperature values
    // See the code in the sandbox for details about
    // createTemperatureLayer()
    this._temperature = createTemperatureLayer(depth, tempDate, "lerc");
    this.addResolvingPromise(this._temperature.load());
  }
});

 createTemperatureLayer() 函数返回一个 ImageryLayer ,显示特定日期和海水深度的温度值。请求使用 lerc 格式的数据,因此每个像素的值代表一个温度值。

然后,必须操作  fetchTile()  方法,使其仅根据海水温度返回新的高程值。因此,我们希望看到覆盖海洋的像素的  z  值会发生变化,但陆地的值恒定的。

为了正确定位高程值,可以使用 getTileBounds()  方法返回的区段属性从 ImageryLayer 获取图像。一旦获取了覆盖高程块区域的图像,就可以操作每个像素的值,使其更接近于一个高程。该函数遍历每个像素并将其值乘以因子。如果遇到覆盖陆地的像素(noDataValues),那么将设置一个平均海洋温度乘以该因子的恒定海拔。fetchTile() 必须返回一个 promise ,该 promise 解析到  ElevationTileData 中指定的一个对象。

                                                
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
fetchTile: function(level, row, col, options) {
  const bounds = this.getTileBounds(level, row, col);
  // Add one because of overlapping vertices in neighboring tiles
  const tileSize = this.tileInfo.size[0] + 1;
  const extent = new Extent({
    xmin: bounds[0],
    ymin: bounds[1],
    xmax: bounds[2],
    ymax: bounds[3],
    spatialReference: this.spatialReference
  });
  const factor = this.factor;

  // fetch the image representing temperature for the extent of the tile.
  // this method returns the pixel data of the image for the extent
  // of the given elevation tile
  return this._temperature.fetchImage(extent, tileSize, tileSize, options)
    .then((data) => {

      const pixelBlock = data.pixelData.pixelBlock;
      // contains the temperature values of each pixel in the image
      const elevations = pixelBlock.pixels[0];
      const stats = pixelBlock.statistics[0];
      // pixels that don't contain any temperature values
      const noDataValue = stats.noDataValue;

      elevations.forEach((value, index, pixelData) => {
        if (value !== noDataValue) {
          // multiply temperatures by the given factor
          pixelData[index] = value * factor;
        }
        else {
          // areas with no temperature data (land)
          // will be assigned the average sea surface
          // temperature (17 degrees Celsius)
          pixelData[index] = 17*factor;
        }
      });

      // return the modified temperatures as elevations
      return {
        values: elevations,
        width: pixelBlock.width,
        height: pixelBlock.height,
        noDataValue: noDataValue
      };
    });
}

创建 Temperature3DLayer  图层后,您必须将其添加到  Map.ground  属性的图层并将地图添加到 SceneView 实例。我们还添加了一个自定义的 2D 切片图层覆盖在表面上,使高度数据更容易看到。

         
1
2
3
4
5
6
7
8
9
// Add the Temperature3DLayer as an elevation layer to the map
// with a 2D ImageryLayer representing elevation draped on top
const map = new Map({
  basemap: "oceans",
  ground: {
    layers: [new Temperature3DLayer()]
  },
  layers: [new Temperature2DLayer()]
});

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.