/
sampleTerrain.js
142 lines (127 loc) · 5.63 KB
/
sampleTerrain.js
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
/*global define*/
define([
'../ThirdParty/when',
'./defined',
'./DeveloperError'
], function(
when,
defined,
DeveloperError) {
'use strict';
/**
* Initiates a terrain height query for an array of {@link Cartographic} positions by
* requesting tiles from a terrain provider, sampling, and interpolating. The interpolation
* matches the triangles used to render the terrain at the specified level. The query
* happens asynchronously, so this function returns a promise that is resolved when
* the query completes. Each point height is modified in place. If a height can not be
* determined because no terrain data is available for the specified level at that location,
* or another error occurs, the height is set to undefined. As is typical of the
* {@link Cartographic} type, the supplied height is a height above the reference ellipsoid
* (such as {@link Ellipsoid.WGS84}) rather than an altitude above mean sea level. In other
* words, it will not necessarily be 0.0 if sampled in the ocean.
*
* @exports sampleTerrain
*
* @param {TerrainProvider} terrainProvider The terrain provider from which to query heights.
* @param {Number} level The terrain level-of-detail from which to query terrain heights.
* @param {Cartographic[]} positions The positions to update with terrain heights.
* @returns {Promise.<Cartographic[]>} A promise that resolves to the provided list of positions when terrain the query has completed.
*
* @example
* // Query the terrain height of two Cartographic positions
* var terrainProvider = new Cesium.CesiumTerrainProvider({
* url : 'https://assets.agi.com/stk-terrain/world'
* });
* var positions = [
* Cesium.Cartographic.fromDegrees(86.925145, 27.988257),
* Cesium.Cartographic.fromDegrees(87.0, 28.0)
* ];
* var promise = Cesium.sampleTerrain(terrainProvider, 11, positions);
* Cesium.when(promise, function(updatedPositions) {
* // positions[0].height and positions[1].height have been updated.
* // updatedPositions is just a reference to positions.
* });
*/
function sampleTerrain(terrainProvider, level, positions) {
//>>includeStart('debug', pragmas.debug);
if (!defined(terrainProvider)) {
throw new DeveloperError('terrainProvider is required.');
}
if (!defined(level)) {
throw new DeveloperError('level is required.');
}
if (!defined(positions)) {
throw new DeveloperError('positions is required.');
}
//>>includeEnd('debug');
var deferred = when.defer();
function doSamplingWhenReady() {
if (terrainProvider.ready) {
when(doSampling(terrainProvider, level, positions), function(updatedPositions) {
deferred.resolve(updatedPositions);
});
} else {
setTimeout(doSamplingWhenReady, 10);
}
}
doSamplingWhenReady();
return deferred.promise;
}
function doSampling(terrainProvider, level, positions) {
var tilingScheme = terrainProvider.tilingScheme;
var i;
// Sort points into a set of tiles
var tileRequests = []; // Result will be an Array as it's easier to work with
var tileRequestSet = {}; // A unique set
for (i = 0; i < positions.length; ++i) {
var xy = tilingScheme.positionToTileXY(positions[i], level);
var key = xy.toString();
if (!tileRequestSet.hasOwnProperty(key)) {
// When tile is requested for the first time
var value = {
x : xy.x,
y : xy.y,
level : level,
tilingScheme : tilingScheme,
terrainProvider : terrainProvider,
positions : []
};
tileRequestSet[key] = value;
tileRequests.push(value);
}
// Now append to array of points for the tile
tileRequestSet[key].positions.push(positions[i]);
}
// Send request for each required tile
var tilePromises = [];
for (i = 0; i < tileRequests.length; ++i) {
var tileRequest = tileRequests[i];
var requestPromise = tileRequest.terrainProvider.requestTileGeometry(tileRequest.x, tileRequest.y, tileRequest.level, false);
var tilePromise = when(requestPromise, createInterpolateFunction(tileRequest), createMarkFailedFunction(tileRequest));
tilePromises.push(tilePromise);
}
return when.all(tilePromises, function() {
return positions;
});
}
function createInterpolateFunction(tileRequest) {
var tilePositions = tileRequest.positions;
var rectangle = tileRequest.tilingScheme.tileXYToRectangle(tileRequest.x, tileRequest.y, tileRequest.level);
return function(terrainData) {
for (var i = 0; i < tilePositions.length; ++i) {
var position = tilePositions[i];
position.height = terrainData.interpolateHeight(rectangle, position.longitude, position.latitude);
}
};
}
function createMarkFailedFunction(tileRequest) {
var tilePositions = tileRequest.positions;
return function() {
for (var i = 0; i < tilePositions.length; ++i) {
var position = tilePositions[i];
position.height = undefined;
}
};
}
return sampleTerrain;
});