地形渲染
使用高程服务叠加地形底图图层的场景
什么是地形渲染?
地形渲染是在 3D 场景中显示高程的过程。在场景中渲染地形需要数字高程模型。将此数据集发布为高程服务,并将其作为高程图层加载到应用程序中。您可以根据应用使用自己的高程模型。
高程图层在 3D 场景中不可见,但您可以渲染覆盖在高程图层之上的底图图层。数据图层叠加在高程图层上后,会显示出数据图层的高低起伏。当 3D 场景没有高程图层时,地形将渲染为以零高度(海平面)显示的平面。
如何渲染地形并将数据图层与地形对齐
您可以通过向场景中添加高程图层和底图图层来渲染地形。
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>
Terrain rendering
</title>
<link
rel="stylesheet"
href="https://js.geoscene.cn/4.23/geoscene/themes/light/main.css"
/>
<script src="https://js.geoscene.cn/4.23/"></script>
<style>
html,
body,
#viewDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script>
require([
"geoscene/config",
"geoscene/Map",
"geoscene/layers/FeatureLayer",
"geoscene/views/SceneView",
"geoscene/layers/ElevationLayer",
"geoscene/layers/support/LabelClass",
"geoscene/Basemap",
"geoscene/layers/TileLayer"
], function (geosceneConfig,Map, FeatureLayer, SceneView, ElevationLayer, LabelClass, Basemap, TileLayer) {
const basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "https://wtb.maptiles.geoscene.cn/arcgis/rest/services/World_Topo_Base/MapServer"
})
]
});
const map = new Map({
ground: {
layers: [ new ElevationLayer({
url: "https://links.geoscene.cn/geoscene/rest/services/elevation3d/WorldElevation3D/Terrain3D/ImageServer"
})]
},
basemap: basemap
});
const view = new SceneView({
container: "viewDiv",
map: map,
qualityProfile: "high",
camera: {
position: [
128.0522605138008,
41.847540077153155,
9260
],
heading: 0.51,
tilt: 65
}
});
const trails = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0",
elevationInfo: {
mode: "relative-to-ground",
offset: 3
},
renderer: {
type: "simple",
symbol: {
type: "line-3d",
symbolLayers: [{
type: "line",
material: { color: "#FF5500" },
size: "2px"
}]
}
}
});
const trailHeads = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
elevationInfo: {
mode: "relative-to-ground"
},
renderer: {
type: "simple",
symbol: {
type: "point-3d",
symbolLayers: [{
type: "icon",
resource: { primitive: "circle"},
material: { color: "#FF5500" },
outline: { color: "#FFFFFF", size: 1 },
size: "10px"
}],
verticalOffset: {
screenLength: 20,
maxWorldLength: 200,
minWorldLength: 20
},
callout: {
type: "line",
size: 1,
color: "#FFFFFF"
}
}
},
labelingInfo: [
new LabelClass({
labelExpressionInfo: { expression: "$feature.TRL_NAME"},
symbol: {
type: "label-3d",
symbolLayers: [{
type: "text",
material: {
color: "#FFFFFF"
},
halo: {
size: 1,
color: [0, 0, 0, 0.5]
},
font: {
size: 11,
family: "sans-serif"
}
}]
}
})
]
});
map.addMany([trails, trailHeads]);
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
在大多数应用程序中,需向场景添加其他数据图层。下图显示了用于垂直对齐数据图层的几个选项:
上例中的徒步路径是相对于地形进行渲染的,偏移量为 3 米:
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>
Terrain rendering
</title>
<link
rel="stylesheet"
href="https://js.geoscene.cn/4.23/geoscene/themes/light/main.css"
/>
<script src="https://js.geoscene.cn/4.23/"></script>
<style>
html,
body,
#viewDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script>
require([
"geoscene/config",
"geoscene/Map",
"geoscene/layers/FeatureLayer",
"geoscene/views/SceneView",
"geoscene/layers/ElevationLayer",
"geoscene/layers/support/LabelClass",
"geoscene/Basemap",
"geoscene/layers/TileLayer"
], function (geosceneConfig,Map, FeatureLayer, SceneView, ElevationLayer, LabelClass, Basemap, TileLayer) {
const basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "https://wtb.maptiles.geoscene.cn/arcgis/rest/services/World_Topo_Base/MapServer"
})
]
});
const map = new Map({
ground: {
layers: [ new ElevationLayer({
url: "https://elevation3d.geoscene.cn/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
})]
},
basemap: basemap
});
const view = new SceneView({
container: "viewDiv",
map: map,
qualityProfile: "high",
camera: {
position: [
128.0522605138008,
41.847540077153155,
9260
],
heading: 0.51,
tilt: 65
}
});
const trails = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0",
elevationInfo: {
mode: "relative-to-ground",
offset: 3
},
renderer: {
type: "simple",
symbol: {
type: "line-3d",
symbolLayers: [{
type: "line",
material: { color: "#FF5500" },
size: "2px"
}]
}
}
});
const trailHeads = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
elevationInfo: {
mode: "relative-to-ground"
},
renderer: {
type: "simple",
symbol: {
type: "point-3d",
symbolLayers: [{
type: "icon",
resource: { primitive: "circle"},
material: { color: "#FF5500" },
outline: { color: "#FFFFFF", size: 1 },
size: "10px"
}],
verticalOffset: {
screenLength: 20,
maxWorldLength: 200,
minWorldLength: 20
},
callout: {
type: "line",
size: 1,
color: "#FFFFFF"
}
}
},
labelingInfo: [
new LabelClass({
labelExpressionInfo: { expression: "$feature.TRL_NAME"},
symbol: {
type: "label-3d",
symbolLayers: [{
type: "text",
material: {
color: "#FFFFFF"
},
halo: {
size: 1,
color: [0, 0, 0, 0.5]
},
font: {
size: 11,
family: "sans-serif"
}
}]
}
})
]
});
map.addMany([trails, trailHeads]);
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
示例
显示具有高程的场景
您可以使用高程图层和底图图层显示一个简单的场景。底图图层将叠加在高程图层上以创建 3D 视图。如果您要显示其他数据,则可以添加数据图层或图形,然后使用 2D 或 3D 符号渲染要素。
步骤
- 创建场景并添加高程图层和底图图层。
- 将场景设置为场景视图。
- 设置照相机位置。
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>
Terrain rendering
</title>
<link
rel="stylesheet"
href="https://js.geoscene.cn/4.23/geoscene/themes/light/main.css"
/>
<script src="https://js.geoscene.cn/4.23/"></script>
<style>
html,
body,
#viewDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script>
require([
"geoscene/config",
"geoscene/Map",
"geoscene/views/SceneView"
], function (geosceneConfig,Map, SceneView) {
const map = new Map({
ground: "world-elevation",
basemap: "tianditu-image"
});
const view = new SceneView({
container: "viewDiv",
map: map,
qualityProfile: "high",
camera: {
position: [
128.0522605138008,
41.847540077153155,
9260
],
heading: 0.51,
tilt: 65
}
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
使用夸大显示地形
为了强调地形,有时您可能会发现使用夸张的高程值显示地形很有用。在此示例中,将显示地球上最高的山脉和最低的海洋区域。由于地球的半径太大,因此山脉高程在视觉上并不明显。在这里,将数据源夸大到这些高程值的 70 倍。
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>
Terrain exaggeration
</title>
<link
rel="stylesheet"
href="https://js.geoscene.cn/4.23/geoscene/themes/light/main.css"
/>
<script src="https://js.geoscene.cn/4.23/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
body {
background: radial-gradient(#12bff2, #0269a1);
}
#viewDiv canvas {
filter: saturate(1.2) drop-shadow(0 0 20px white);
}
.buttons {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
text-align: center;
}
.geoscene-button {
display: inline;
max-width: 200px;
}
</style>
<script>
require([
"geoscene/config",
"geoscene/Map",
"geoscene/views/SceneView",
"geoscene/layers/ElevationLayer",
"geoscene/layers/BaseElevationLayer",
"geoscene/Basemap",
"geoscene/layers/TileLayer"
], function (geosceneConfig,Map, SceneView, ElevationLayer, BaseElevationLayer, Basemap, TileLayer) {
const basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "https://tiles.geoscene.cn/tiles/nGt4QxSblgDfeJn9/arcgis/rest/services/terrain_with_heavy_bathymetry/MapServer",
copyright: "Bathymetry, topography and satellite imagery from <a href=\"https://visibleearth.nasa.gov/view_cat.php?categoryID=1484\" target=\"_blank\">NASA Visible Earth</a> | <a href=\"http://www.aag.org/global_ecosystems\" target=\"_blank\">World Ecological Land Units, AAG</a> | Oceans, glaciers and water bodies from <a href=\"https://www.naturalearthdata.com/\" target=\"_blank\">Natural Earth</a>"
})
]
});
const ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
properties: {
exaggeration: null
},
// 当图层在视图中渲染之前添加到地图时,会调用 load() 方法。
load: function () {
this._elevation = new ElevationLayer({
url:
"https://links.geoscene.cn/geoscene/rest/services/elevation3d/WorldElevation3D/Terrain3D/ImageServer"
});
// 解析 load() 方法之前等待高程图层加载
this.addResolvingPromise(this._elevation.load());
},
// 获取视图中可见的切片
fetchTile: function (level, row, col, options) {
// 在 elevationlayer 上为切片调用 fetchTile()
// 在视图中可见
return this._elevation.fetchTile(level, row, col, options).then(
function (data) {
var exaggeration = this.exaggeration;
// 数据是一个包含
//切片的宽度和高度(以像素为单位)
// 以及每个像素的值的对象
for (var i = 0; i < data.values.length; i++) {
// 将给定的像素值
// 乘以夸大值
data.values[i] = data.values[i] * exaggeration;
}
return data;
}.bind(this)
);
}
});
const elevationLayer = new ExaggeratedElevationLayer({ exaggeration: 70 });
const map = new Map({
basemap: basemap,
ground: {
layers: [elevationLayer]
}
});
const view = new SceneView({
container: "viewDiv",
map: map,
alphaCompositingEnabled: true,
qualityProfile: "high",
camera: {
position: [-55.03975781, 14.94826384, 19921223.30821],
heading: 2.03,
tilt: 0.13
},
environment: {
background: {
type: "color",
color: [255, 252, 244, 0]
},
starsEnabled: false,
atmosphereEnabled: false,
lighting: {
directShadowsEnabled: false,
date: "Sun Jun 23 2019 19:19:18 GMT+0200 (Central European Summer Time)"
}
},
constraints: {
altitude: {
min: 10000000,
max: 25000000
}
},
popup: {
dockEnabled: true,
dockOptions: {
position: "top-right",
breakpoint: false,
buttonEnabled: false
},
collapseEnabled: false
},
highlightOptions: {
color: [255, 255, 255],
haloOpacity: 0.5
}
});
let exaggerated = true;
document.getElementById("exaggerate").addEventListener("click", function() {
if (exaggerated) {
map.ground = "world-elevation";
this.innerHTML = "Enable exaggeration";
exaggerated = false;
} else {
map.ground = {
layers: [elevationLayer]
};
this.innerHTML = "Disable exaggeration";
exaggerated = true;
}
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div class="buttons">
<button
id="exaggerate"
class="geoscene-button"
>
Disable exaggeration
</button>
</div>
</body>
</html>