基于ArcGIS JS API 4.11实现对FeatureLayer的多变量渲染

需求背景

有一个二维数组,里面包含几万个表示高度的值,现在要把这些高度值在地图上展示出来。可以通过小立方体的方式展现,长宽固定,高用实际值代替。

需求分析

  1. 数据量较大,需要考虑性能问题;
  2. 在三维场景中对点数据(二维数组中的单个值)进行三维符号化

开发过程

最开始的思路是创建类型为PointGraphic,通过PointSymbol3D进行符号化,在ObjectSymbol3DLayer中设置固定的长宽,高度根据数组里面的具体值对应,主要代码大概如下:

                    var elevArr = [];//模拟二维数组
                    for (let i=0;i<10;i++) {
                        let rowArr = [];
                        for (let j=0;j<10;j++) {
                            rowArr.push(Math.random() * 1000);
                        }
                        elevArr.push(rowArr);
                    }
                    
                    for (let row=0;row<elevArr.length;row++) {
                        for (let col=0;col<elevArr[row].length;col++) {
                            let locX = 17004630.030532643 + 500 + col*500;
                            let locY = 3875195.620791356 - 500 - row*500;
                            const objectSymbol = {
                                type: "point-3d",  // autocasts as new PointSymbol3D()
                                symbolLayers: [{
                                    type: "object",  // autocasts as new ObjectSymbol3DLayer()
                                    width: 1000,  // diameter of the object from east to west in meters
                                    height: elevArr[row][col],  // height of the object in meters
                                    depth: 1000,  // diameter of the object from north to south in meters
                                    resource: { primitive: "cube" },
                                    material: { color: "blue" }
                                }]
                            };
                            var point = {
                                type: "point", // autocasts as new Point()
                                x: locX,
                                y: locY,
                                z: 100,  
                                spatialReference: { wkid: 3857 }
                            };
                            const graphic = new Graphic({
                                geometry: point,
                                symbol: objectSymbol
                                });

                            graphicsLayer.add(graphic);
                        }
                    }  

经过测试发现1000个点的时候页面响应就有点慢了,增加到10000个点之后,页面崩溃,估计是和创建过多的Graphic , ObjectSymbol3DLayer等对象有关系。该方法在较大数据量的情况下不适用。
ArcGIS JS API里面有大数据集要素可视化的例子,通过FeatureLayerRenderer的方式进行可视化渲染,FeatureLayer可通过3种方式创建:

FeatureLayers may be created in one of three ways: from a service URL, an ArcGIS portal item ID, or from an array of client-side features.

这里采用第三种,直接在代码里面通过数组的方式,具体代码如下:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
  <title>实现FeatureLayer大数据集的多变量渲染</title>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <link rel="stylesheet" href="https://js.arcgis.com/4.11/esri/themes/light/main.css" />
  <script src="https://js.arcgis.com/4.11/"></script>
  <script>
    require(["esri/Map", "esri/views/SceneView", "esri/layers/FeatureLayer"], function (Map, SceneView, FeatureLayer) {
      var map = new Map({
        basemap: "streets",
        ground: "world-elevation"
      });
      var view = new SceneView({
        container: "viewDiv", // Reference to the scene div created in step 5
        map: map, // Reference to the map object created before the scene
        scale: 5000000, // Sets the initial scale to 1:50,000,000
        center: [-101.17, 21.78], // Sets the center point of view with lon/lat
        // viewingMode: "local"
      });
      view.on('click', (evt) => {
        console.log(evt.mapPoint);
      })
      var features = [];

      //模拟二维数组
      var elevArr = [];
      var fid = 0;
      for (let i = 0; i < 100; i++) {
        let rowArr = [];
        for (let j = 0; j < 600; j++) {
          rowArr.push(Math.random() * 1000);
        }
        elevArr.push(rowArr);
      }

      let leftCoorX, upCoorY, resolution;
      for (let row = 0; row < elevArr.length; row++) {
        for (let col = 0; col < elevArr[row].length; col++ , fid++) {
          let locX = -11807396.534464896 + 500 + col * 500;
          let locY = 2727548.99928753 - 500 - row * 500;
          let feature = {
            geometry: {
              type: "point",  //不可省略
              x: locX,
              y: locY,
              spatialReference: { wkid: 3857 },  //不可省略
            },
            attributes: {
              ObjectID: fid,
              elevation: elevArr[row][col],
              width: "100",
              depth: "100"
            }
          };
          features.push(feature);
        }
      }
      var uvRenderer = {
        type: "simple",  // autocasts as new SimpleRenderer()
        symbol: {
          type: "point-3d",  // autocasts as new PointSymbol3D()
          symbolLayers: [{
            type: "object",  // autocasts as new ObjectSymbol3DLayer()
            // width: 1000,  // diameter of the object from east to west in meters
            // height: 5000,  // height of the object in meters
            // depth: 1000,  // diameter of the object from north to south in meters
            resource: { primitive: "cube" },
            material: { color: "blue" }
          }]
        },
        visualVariables: [
          {
            type: "size",
            axis: "height", //在symbolLayers设置width、height、depth无效
            field: "elevation",
            valueUnit: "meters"
          },
          {
            type: "size",
            axis: "width",
            field: "width",
            valueUnit: "meters"
          },
          {
            type: "size",
            axis: "depth",
            field: "depth",
            valueUnit: "meters"
          }
        ]
      };

      var layer = new FeatureLayer({
        fields: [
          {
            name: "ObjectID",
            alias: "ObjectID",
            type: "oid"
          }, {
            name: "elevation",
            alias: "elevation",
            type: "double"
          }, {
            name: "width",
            alias: "width",
            type: "string"
          }, {
            name: "depth",
            alias: "depth",
            type: "string"
          }],
        objectIdField: "ObjectID",
        source: features,  // autocast as a Collection of new Graphic(),注意数组长度限制,尽量低于60000
        geometryType: "point",
        spatialReference: { wkid: 3857 },
        renderer: uvRenderer,
        // type: "feature",
        outFields: ["*"]
      });
      map.add(layer);
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>

通过60000个点进行测试,无卡顿情况,推测ArcGIS JS API内部有优化,如分块渲染、量化操作等(可参考链接1)

效果图

在这里插入图片描述

注意事项

  1. geometry里面属性typespatialReference不可忽略,否则图形加不到地图上;
  2. 创建FeatureLayersource有数组长度限制,经测试60000个点时正常,70000报错栈溢出,推测限制长度可能为65535,可以通过创建多个featurelayer的方式避免长度限制;
  3. 当geometry中包含z属性值时,符号化后高度是在z值之上叠加,如果不包含z属性,符号化后高度从该点所对应地形高程值之上累加;

参考链接

  1. Visualize large feature datasets in 3D with ArcGIS API for JavaScript: https://www.esri.com/arcgis-blog/products/js-api-arcgis/3d-gis/visualize-large-feature-datasets-in-3d-with-arcgis-api-for-javascript/?tdsourcetag=s_pcqq_aiomsg
  2. https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html

在线示例

示例地址

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值