基于AcrGIS平台实现三维场景下的积水效果动态模拟

文章目录

  • 1.前言
  • 2.环境准备
  • 3.开发过程
    • 3.1 软件安装
    • 3.2 共享(发布)场景服务
    • 3.3 积水区三维可视化
    • 3.4 动态模拟
  • 4. 完整代码
  • 5. 效果图
  • 6. 在线演示

1.前言

现有基于暴雨洪水管理模型(SWMM)生成的多个时刻的积水区数据(json格式),要求在三维场景下依照时间动态展示积水的变化效果。在此记录开发过程中遇到的一些问题及注意事项。

2.环境准备

  1. ArcGIS Pro 2.3, ArcGIS Enterprise 10.6, ArcGIS JS API 4.12
  2. 高程数据(DEM,用于产生地形起伏效果), 影像数据(DOM,用于展示地形)

3.开发过程

3.1 软件安装

  • ArcGIS Enterprise安装及配置方法介绍(windows版)
  • ArcGIS Pro许可操作手册

ArcGIS Pro用于发布三维场景服务(包含Elevation Layer和Tile Layer),发布到Portal账户下。

3.2 共享(发布)场景服务

这部分需要注意在ArcGIS Pro中创建项目时是选择Global Scene 还是Local SceneGlobal Scene是个球,而Local Scene是平的,只是一块区域。

基于AcrGIS平台实现三维场景下的积水效果动态模拟_第1张图片

另外一个区别是Gloal Scene支持WGS84 or WebMercator坐标系(wkid: 4326 or wkid: 3857),而Local Scene支持任何投影坐标系统。

The SceneView supports following coordinate systems in a global scene:

  • WGS84 or WebMercator
  • Noncached layers with any spatial reference since they will be reprojected to the scene spatial reference

In a local scene the following coordinate systems are supported:

  • Any Projected Coordinate System
  • Noncached layers with any spatial reference since they will be reprojected to the scene spatial reference

发布服务的详细步骤参见: ArcGIS Pro发布三维场景服务

3.3 积水区三维可视化

积水数据通过GeoJson格式存储,每个时刻对应一个GeoJson文件。要实现对GeoJson数据的渲染有两种方式:

  • 使用类GeoJSONLayer,要求里面的坐标数据必须是WGS84坐标系;

The GeoJSON data must comply with the RFC 7946 specification which states that the coordinates are in SpatialReference.WGS84.

  • 解析GeoJson数据,获取其坐标数据,通过要素图层(FeatureLayer)、渲染器(Renderer)、视觉变量(VisualVariable)进行可视化。

另外还有一种方式,先解析GeoJson数据,通过其坐标数据创建graphic添加至GraphicsLayer,再进行符号化。不过这种方式下,在graphic数量较大时,页面会出现卡顿甚至崩溃(10000时出现崩溃情况)。主要还是由于GraphicsLayerFeatureLayer作用不太一致,前者可以承载多种类型的几何体(Point、Polyline、Polygon等),各个graphic之间没有统一的模式,每个graphic可以有自己的符号;而后者是有统一模式的,同样的几何类型,同样的属性字段,可以用同样的符号渲染(一个renderer即可),因此可视化效率更高。

Each graphic must have its own symbol since the GraphicsLayer cannot have an associated renderer. Graphics may also contain different attribute schema from one another.

It is generally preferred to construct a FeatureLayer with its source property when working with client-side graphics since the FeatureLayer has more capabilities than the GraphicsLayer, including rendering, querying, and labeling.

这里采用第二种方式,部分代码如下:

// FeatureLayer的渲染器
                var renderer = {
                    type: "simple",  // autocasts as new SimpleRenderer()
                    symbol: {
                        type: "polygon-3d", // autocasts as new PolygonSymbol3D()
                        symbolLayers: [
                            {
                                type: "extrude", // autocasts as new ExtrudeSymbol3DLayer()
                                material: {
                                    color: "#4ec2cd"
                                }
                            }
                        ]
                    },
                    visualVariables: [ //视觉变量,elevation字段控制拉伸高度
                        {
                            type: "size",
                            field: "elevation",
                            valueUnit: "meters"
                        }
                    ]
                };
                let features = [];
                var layer = new FeatureLayer({
                    fields: [
                        {
                            name: "ObjectID",
                            alias: "ObjectID",
                            type: "oid"
                        }, {
                            name: "elevation",
                            alias: "elevation",
                            type: "double"
                        }],
                    objectIdField: "ObjectID",
                    // source: features,  // autocast as a Collection of new Graphic()
                    geometryType: "polygon",
                    spatialReference: { wkid: 3857 },
                    renderer: renderer,
                    outFields: ["*"]
                });

                // 先添加开始时刻的积水数据
                $.get('assets/data/2019-03-01-16-00.json', function (geoJson) {
                    var featureCollections = geoJson.features;//获取积水区域
                    featureCollections.forEach(function (element) {
                        let feature = {
                            geometry: {
                                type: "polygon",  //不可省略
                                rings: element.geometry.coordinates,
                                spatialReference: { wkid: 3857 },  //不可省略
                            },
                            attributes: {
                                ObjectID: element.id,
                                elevation: element.properties.elevation,
                            }
                        };
                        features.push(feature);
                    }, this);
                    layer.source = features;//设置featurelayer数据源
                    map.add(layer);
                });

具体可参考基于ArcGIS JS API 4.11实现对FeatureLayer的多变量渲染

3.4 动态模拟

要实现积水的动态变化效果的话,就要借助时间滑块TimeSlider,这里的主要思路就是通过监听时间滑块状态变化,去取该时刻对应的积水数据,解析GeoJson数据,创建FeatureLayer的数据源,重置数据源,刷新FeatureLayer。部分代码如下:

// watch for time slider timeExtent change
                timeSlider.watch("timeExtent", function (timeExtent) {
                    console.log("Time extent now starts at", timeExtent.start, "and finishes at:", timeExtent.end);
                    // 动态修改featureLayer数据源
                    var fileName = dateUtil(timeExtent.end);
                    $.get(`assets/data/${fileName}.json`, function (geoJson) {
                        features = [];//清空
                        var featureCollections = geoJson.features;
                        featureCollections.forEach(function (element) {
                            let feature = {
                                geometry: {
                                    type: "polygon",  //不可省略
                                    rings: element.geometry.coordinates,
                                    spatialReference: { wkid: 3857 },  //不可省略
                                },
                                attributes: {
                                    ObjectID: element.id,
                                    elevation: element.properties.elevation,
                                }
                            };
                            features.push(feature);
                        }, this);
                        layer.source = features; //重置FeatureLayer数据源
                        layer.refresh(); // 刷新FeatureLayer
                    });

                });

4. 完整代码

<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>三维场景下的积水效果动态模拟title>

    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }

        #timeSlider {
            position: absolute;
            left: 200px;
            right: 200px;
            bottom: 30px;
        }
    style>

    <link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/themes/dark/main.css" />
    <script src="https://js.arcgis.com/4.12/">script>
    <script src="assets/js/jquery-2.1.1.min.js">script>

    <script>
        require([
            "esri/Map",
            "esri/layers/GeoJSONLayer",
            "esri/layers/FeatureLayer",
            "esri/views/SceneView",
            "esri/views/MapView",
            "esri/WebScene",
            "esri/config",
            "esri/widgets/TimeSlider",
        ], function (Map,
            GeoJSONLayer,
            FeatureLayer,
            SceneView,
            MapView,
            WebScene,
            esriConfig,
            TimeSlider) {
                // 此处可使用ArcGIS Pro发布的场景服务(包含Ground Layer、2D Layer)
                // esriConfig.portalUrl = "https://myHostName.esri.com/arcgis";
                // var map = new WebScene({
                //     portalItem: { // autocasts as new PortalItem()
                //         id: "0614ea1f9dd043e9ba157b9c20d3c538"  // ID of the WebScene on the on-premise portal
                //     }
                // });

                var map = new Map({
                    basemap: "hybrid",
                    ground: "world-elevation" //使用ArcGIS自带高程图层
                });

                var view = new SceneView({
                    container: "viewDiv",
                    map: map,
                    camera: {
                        position: [
                            117.147, // lon
                            39.06, // lat
                            328  // elevation in meters
                        ],
                        tilt: 78, //倾斜角. 垂直地面为0
                        heading: 6 //方位角,正北为0 ,0-360
                    }
                });

                view.on("click", function (event) {
                    console.log(view);

                    console.log("click event: ", event.mapPoint.x + ',' + event.mapPoint.y + ',' + event.mapPoint.z);
                });

                const timeSlider = new TimeSlider({
                    container: "timeSlider",
                    mode: "instant",
                    stops: {
                        interval: {
                            value: 10,
                            unit: "minutes"
                        }
                    }
                });
                view.ui.add(timeSlider, "manual");

                // FeatureLayer的渲染器
                var renderer = {
                    type: "simple",  // autocasts as new SimpleRenderer()
                    symbol: {
                        type: "polygon-3d", // autocasts as new PolygonSymbol3D()
                        symbolLayers: [
                            {
                                type: "extrude", // autocasts as new ExtrudeSymbol3DLayer()
                                material: {
                                    color: "#4ec2cd"
                                }
                            }
                        ]
                    },
                    visualVariables: [ //视觉变量,elevation字段控制拉伸高度
                        {
                            type: "size",
                            field: "elevation",
                            valueUnit: "meters"
                        }
                    ]
                };
                let features = [];
                var layer = new FeatureLayer({
                    fields: [
                        {
                            name: "ObjectID",
                            alias: "ObjectID",
                            type: "oid"
                        }, {
                            name: "elevation",
                            alias: "elevation",
                            type: "double"
                        }],
                    objectIdField: "ObjectID",
                    // source: features,  // autocast as a Collection of new Graphic()
                    geometryType: "polygon",
                    spatialReference: { wkid: 3857 },
                    renderer: renderer,
                    outFields: ["*"]
                });
                //根据GeoJson文件名,设置时间滑块范围
                var dateString = "2019-03-01-16-00";
                var dateArr = dateString.split("-");
                dateArr = dateArr.map(date => {
                    return parseInt(date);
                });
                timeSlider.fullTimeExtent = {
                    start: new Date(dateArr[0], dateArr[1] - 1, dateArr[2], dateArr[3], dateArr[4]),
                    end: new Date(dateArr[0], dateArr[1] - 1, dateArr[2], dateArr[3], dateArr[4] + 50)
                };
                timeSlider.values = [timeSlider.fullTimeExtent.start];

                // 先添加开始时刻的积水数据
                $.get('assets/data/2019-03-01-16-00.json', function (geoJson) {
                    var featureCollections = geoJson.features;//获取积水区域
                    featureCollections.forEach(function (element) {
                        let feature = {
                            geometry: {
                                type: "polygon",  //不可省略
                                rings: element.geometry.coordinates,
                                spatialReference: { wkid: 3857 },  //不可省略
                            },
                            attributes: {
                                ObjectID: element.id,
                                elevation: element.properties.elevation,
                            }
                        };
                        features.push(feature);
                    }, this);
                    layer.source = features;//设置featurelayer数据源
                    map.add(layer);
                });


                // watch for time slider timeExtent change
                timeSlider.watch("timeExtent", function (timeExtent) {
                    console.log("Time extent now starts at", timeExtent.start, "and finishes at:", timeExtent.end);
                    // 动态修改featureLayer数据源
                    var fileName = dateUtil(timeExtent.end);
                    $.get(`assets/data/${fileName}.json`, function (geoJson) {
                        features = [];//清空
                        var featureCollections = geoJson.features;
                        featureCollections.forEach(function (element) {
                            let feature = {
                                geometry: {
                                    type: "polygon",  //不可省略
                                    rings: element.geometry.coordinates,
                                    spatialReference: { wkid: 3857 },  //不可省略
                                },
                                attributes: {
                                    ObjectID: element.id,
                                    elevation: element.properties.elevation,
                                }
                            };
                            features.push(feature);
                        }, this);
                        layer.source = features; //重置FeatureLayer数据源
                        layer.refresh(); // 刷新FeatureLayer
                    });

                });

            });
        // 时间对象转换成 YYYY-MM-DD-HH-mm 格式字符串
        function dateUtil(dateObject) {
            let year = dateObject.getFullYear();
            let month = dateObject.getMonth() + 1;
            let day = dateObject.getDate();
            let hour = dateObject.getHours();
            let minute = dateObject.getMinutes();
            let dateString = year + '-' + fillZero(month) + '-' + fillZero(day) + '-' + fillZero(hour) + '-' + fillZero(minute);
            return dateString;
        }
        // 为小于 10 的数补零
        function fillZero(num) {
            return num < 10 ? '0' + num : num;
        }
    script>
head>

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

html>

完整的代码文件及依赖的json数据等下载地址:https://download.csdn.net/download/wml00000/11357207

5. 效果图

6. 在线演示

示例地址

你可能感兴趣的:(ArcGIS,ArcGIS,Pro,WebScene,FeatureLayer,TimeSlider)