ImageryLayer - 客户端图表

尝试一下在线预览

此示例演示如何在客户端为 ImageryLayer 生成图表。当用户在视图中单击或拖动指针时,应用将读取并处理距离指针位置一英里范围内的像素数据。“土地覆盖类型”图表会随着指针位置的更改而立即使用新数据进行更新。

此应用程序显示美国本土的国家土地覆盖数据库 (NLCD) 2001 年土地覆盖分类栅格。

工作原理

土地覆盖影像图层将使用 lerc格式进行初始化,该格式将返回所请求影像的原始像素值。

    
1
2
3
4
const imageryLayer = new ImageryLayer({
  url: "https://sampleserver6.geosceneonline.cn/arcgis/rest/services/NLCDLandCover2001/ImageServer",
  format: "lerc"
});

由于 lerc 格式返回图像的原始像素值,因此我们可以在浏览器中访问像素值。这些原始值可以通过 ImageryLayerView 的 pixelData 进行访问,每当用户缩放或平移视图时,它们都会更新。应用程序监视 LayerView 的更新属性以获取更新的像素值。然后,每当用户单击影像图层或将指针拖到影像图层上时,这些值都可用于创建土地覆盖类型图表。

                                     
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
view.whenLayerView(imageryLayer).then(layerLoaded);
function layerLoaded(layerView) {
  // watch for the imagery layer view's updating property
  // to get the updated pixel values
  layerView.watch("updating", (value) => {
    if (!value) {
      pixelData = layerView.pixelData;
    }
  });

  // when the layer loads, listen to the view's drag and click
  // events to update the land cover types chart to reflect an
  // area within 1 mile of the pointer location.
  removeChartEvents = view.on(["drag", "click"], (event) => {
    if (pixelData){
      event.stopPropagation();
      getLandCoverPixelInfo(event).then(updateLandCoverChart);
    }
  });
  // raster attributes table returns categorical mapping of pixel values such as class and group
  const attributesData = imageryLayer.serviceRasterInfo.attributeTable.features;

  // rasterAttributeFeatures will be used to add legend labels and colors for each
  // land use type
  for (let index in attributesData) {
    if (attributesData) {
      rasterAttributeFeatures[attributesData[index].attributes.Value] = {
        "Blue": attributesData[index].attributes.Blue,
        "ClassName": attributesData[index].attributes.ClassName,
        "Green": attributesData[index].attributes.Green,
        "Red": attributesData[index].attributes.Red
      };
    }
  }
  // initialize the land cover pie chart
  createLandCoverChart();
}

当用户单击视图或将指针拖到视图上时,将调用 getLandCoverPixelInfo() 函数。在此函数中,我们执行一个逻辑来读取和存储距离指针位置一英里以内的像素值。

                                                              
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
// This function is called as user drags the pointer over or clicks on the view.
// Here we figure out which pixels fall within one mile of the
// pointer location and update the chart accordingly
const getLandCoverPixelInfo = promiseUtils.debounce((event) => {

  const currentExtent = pixelData.extent;
  const pixelBlock = pixelData.pixelBlock;
  const height = pixelBlock.height;
  const width = pixelBlock.width;

  // map point for the pointer location.
  const point = view.toMap({
    x: event.x,
    y: event.y
  });
  // pointer x, y in pixels
  const reqX = Math.ceil(event.x);
  const reqY = Math.ceil(event.y);

  // calculate how many meters are represented by 1 pixel.
  const pixelSizeX = Math.abs(currentExtent.xmax - currentExtent.xmin) / width;

  // calculate how many pixels represent one mile
  const bufferDim = Math.ceil(1609 / pixelSizeX);

  // figure out 2 mile extent around the pointer location
  const xmin = (reqX - bufferDim < 0) ? 0 : reqX - bufferDim;
  const ymin = (reqY - bufferDim < 0) ? 0 : reqY - bufferDim;
  const startPixel = ymin * width + xmin;
  const bufferlength = bufferDim * 2;
  const pixels = pixelBlock.pixels[0];
  const radius2 = bufferDim * bufferDim;
  let oneMilePixelValues = [];

  // cover pixels within to 2 mile rectangle
  if (bufferlength) {
    for (let i = 0; i <= bufferlength; i++) {
      for (let j = 0; j <= bufferlength; j++) {
        // check if the given pixel location is in within one mile of the pointer
        // add its value to pixelValue.
        if ((Math.pow(i - bufferDim, 2) + Math.pow(j - bufferDim, 2)) <= radius2){
          const pixelValue = pixels[Math.floor(startPixel + i * width + j)];
        }
        if (pixelValue !== undefined) {
          oneMilePixelValues.push(pixelValue);
        }
      }
    }
  } else {
    oneMilePixelValues.push(pixels[startPixel]);
  }
  pixelValCount = {};
  // get the count of each land type returned within one mile radius
  for (let i = 0; i < oneMilePixelValues.length; i++) {
    pixelValCount[oneMilePixelValues[i]] = 1 + (pixelValCount[oneMilePixelValues[i]] || 0);
  }
  const circle = new Circle({
    center: point,
    radius: bufferDim * pixelSizeX
  });
  graphic.geometry = circle;
});

处理图表的原始像素值后,将调用 updateLandCovertChart() 并更新图表以反映新位置的土地覆盖类型。

                      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// This function is called once pixel values within one mile of the pointer
// location are processed and ready for the chart update.
function updateLandCoverChart() {
  ...
  // pixelValCount object contains land cover types and count of pixels
  // that represent that type in within one mile.
  for (let index in pixelValCount) {
    if (index == 0) {
      landCoverTypeColors.push("rgba(255,255,255,1");
      landCoverTypeLabels.push("NoData");
    } else {
      const color = 'rgba(' + rasterAttributeFeatures[index].Red + ', '
      + rasterAttributeFeatures[index].Green + ', ' + rasterAttributeFeatures[index].Blue + ', 1)';
      landCoverTypeColors.push(color);
      landCoverTypeLabels.push(rasterAttributeFeatures[index].ClassName);
    }
    landCoverChart.data.datasets[0].data.push(pixelValCount[index]);
  }
  landCoverChart.data.datasets[0].backgroundColor = landCoverTypeColors;
  landCoverChart.data.labels = landCoverTypeLabels;
  landCoverChart.update(0);
}

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