Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | 19x 11x 19x 18x 17x 17x 16x 24x 24x 24x 2x 22x 4x 18x 22x 22x 22x 2x 2x 40x 16x 24x 24x 22x 2x 2x 16x 16x 16x 16x 31x 31x 31x 31x 22x 22x 22x 31x 16x 16x 16x 13x 30x 30x 4x 26x 26x 22x 22x 22x 17x 17x 24x 24x 24x 4x 4x 17x 13x 4x 4x 6x 6x 18x 18x 2x 2x 2x | import Check from "./Check.js";
import defined from "./defined.js";
/**
* 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. This function needs the
* terrain level of detail as input, if you need to get the altitude of the terrain as precisely
* as possible (i.e. with maximum level of detail) use {@link sampleTerrainMostDetailed}.
*
* @function 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.
* @param {boolean} [rejectOnTileFail=false] If true, for any failed terrain tile requests, the promise will be rejected. If false, returned heights will be undefined.
* @returns {Promise<Cartographic[]>} A promise that resolves to the provided list of positions when terrain the query has completed.
*
* @see sampleTerrainMostDetailed
*
* @example
* // Query the terrain height of two Cartographic positions
* const terrainProvider = await Cesium.createWorldTerrainAsync();
* const positions = [
* Cesium.Cartographic.fromDegrees(86.925145, 27.988257),
* Cesium.Cartographic.fromDegrees(87.0, 28.0)
* ];
* const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 11, positions);
* // positions[0].height and positions[1].height have been updated.
* // updatedPositions is just a reference to positions.
*
* // To handle tile errors, pass true for the rejectOnTileFail parameter.
* try {
* const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 11, positions, true);
* } catch (error) {
* // A tile request error occurred.
* }
*/
async function sampleTerrain(
terrainProvider,
level,
positions,
rejectOnTileFail,
) {
if (!defined(rejectOnTileFail)) {
rejectOnTileFail = false;
}
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("terrainProvider", terrainProvider);
Check.typeOf.number("level", level);
Check.typeOf.bool("rejectOnTileFail", rejectOnTileFail);
Check.defined("positions", positions);
//>>includeEnd('debug');
return doSampling(terrainProvider, level, positions, rejectOnTileFail);
}
/**
* @param {object[]} tileRequests The mutated list of requests, the first one will be attempted
* @param {Array<Promise<void>>} results The list to put the result promises into
* @param {boolean} rejectOnTileFail If true, the promise will be rejected. If false, returned heights will be undefined.
* @returns {boolean} true if the request was made, and we are okay to attempt the next item immediately,
* or false if we were throttled and should wait awhile before retrying.
*
* @private
*/
function attemptConsumeNextQueueItem(tileRequests, results, rejectOnTileFail) {
const tileRequest = tileRequests[0];
const requestPromise = tileRequest.terrainProvider.requestTileGeometry(
tileRequest.x,
tileRequest.y,
tileRequest.level,
);
if (!requestPromise) {
// getting back undefined instead of a promise indicates we should retry a bit later
return false;
}
let promise;
if (rejectOnTileFail) {
promise = requestPromise.then(createInterpolateFunction(tileRequest));
} else {
promise = requestPromise
.then(createInterpolateFunction(tileRequest))
.catch(createMarkFailedFunction(tileRequest));
}
// remove the request we've just done from the queue
// and add its promise result to the result list
tileRequests.shift();
results.push(promise);
// indicate we should synchronously attempt the next request as well
return true;
}
/**
* Wrap window.setTimeout in a Promise
* @param {number} ms
* @private
*/
function delay(ms) {
return new Promise(function (res) {
setTimeout(res, ms);
});
}
/**
* Recursively consumes all the tileRequests until the list has been emptied
* and a Promise of each result has been put into the results list
* @param {object[]} tileRequests The list of requests desired to be made
* @param {Array<Promise<void>>} results The list to put all the result promises into
* @param {boolean} rejectOnTileFail If true, the promise will be rejected. If false, returned heights will be undefined.
* @returns {Promise<void>} A promise which resolves once all requests have been started
*
* @private
*/
function drainTileRequestQueue(tileRequests, results, rejectOnTileFail) {
// nothing left to do
if (!tileRequests.length) {
return Promise.resolve();
}
// consume an item from the queue, which will
// mutate the request and result lists, and return true if we should
// immediately attempt to consume the next item as well
const success = attemptConsumeNextQueueItem(
tileRequests,
results,
rejectOnTileFail,
);
if (success) {
return drainTileRequestQueue(tileRequests, results, rejectOnTileFail);
}
// wait a small fixed amount of time first, before retrying the same request again
return delay(100).then(() => {
return drainTileRequestQueue(tileRequests, results, rejectOnTileFail);
});
}
function doSampling(terrainProvider, level, positions, rejectOnTileFail) {
const tilingScheme = terrainProvider.tilingScheme;
let i;
// Sort points into a set of tiles
const tileRequests = []; // Result will be an Array as it's easier to work with
const tileRequestSet = {}; // A unique set
for (i = 0; i < positions.length; ++i) {
const xy = tilingScheme.positionToTileXY(positions[i], level);
Iif (!defined(xy)) {
continue;
}
const key = xy.toString();
if (!tileRequestSet.hasOwnProperty(key)) {
// When tile is requested for the first time
const 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]);
}
// create our list of result promises to be filled
const tilePromises = [];
return drainTileRequestQueue(
tileRequests,
tilePromises,
rejectOnTileFail,
).then(function () {
// now all the required requests have been started
// we just wait for them all to finish
return Promise.all(tilePromises).then(function () {
return positions;
});
});
}
/**
* Calls {@link TerrainData#interpolateHeight} on a given {@link TerrainData} for a given {@link Cartographic} and
* will assign the height property if the return value is not undefined.
*
* If the return value is false; it's suggesting that you should call {@link TerrainData#createMesh} first.
* @param {Cartographic} position The position to interpolate for and assign the height value to
* @param {TerrainData} terrainData
* @param {Rectangle} rectangle
* @returns {boolean} If the height was actually interpolated and assigned
* @private
*/
function interpolateAndAssignHeight(position, terrainData, rectangle) {
const height = terrainData.interpolateHeight(
rectangle,
position.longitude,
position.latitude,
);
if (height === undefined) {
// if height comes back as undefined, it may implicitly mean the terrain data
// requires us to call TerrainData.createMesh() first (ArcGIS requires this in particular)
// so we'll return false and do that next!
return false;
}
position.height = height;
return true;
}
function createInterpolateFunction(tileRequest) {
const tilePositions = tileRequest.positions;
const rectangle = tileRequest.tilingScheme.tileXYToRectangle(
tileRequest.x,
tileRequest.y,
tileRequest.level,
);
return function (terrainData) {
let isMeshRequired = false;
for (let i = 0; i < tilePositions.length; ++i) {
const position = tilePositions[i];
const isHeightAssigned = interpolateAndAssignHeight(
position,
terrainData,
rectangle,
);
// we've found a position which returned undefined - hinting to us
// that we probably need to create a mesh for this terrain data.
// so break out of this loop and create the mesh - then we'll interpolate all the heights again
if (!isHeightAssigned) {
isMeshRequired = true;
break;
}
}
if (!isMeshRequired) {
// all position heights were interpolated - we don't need the mesh
return Promise.resolve();
}
// create the mesh - and interpolate all the positions again
// note: terrain exaggeration is not passed in - we are only interested in the raw data
return terrainData
.createMesh({
tilingScheme: tileRequest.tilingScheme,
x: tileRequest.x,
y: tileRequest.y,
level: tileRequest.level,
// don't throttle this mesh creation because we've asked to sample these points;
// so sample them! We don't care how many tiles that is!
throttle: false,
})
.then(function () {
// mesh has been created - so go through every position (maybe again)
// and re-interpolate the heights - presumably using the mesh this time
for (let i = 0; i < tilePositions.length; ++i) {
const position = tilePositions[i];
// if it doesn't work this time - that's fine, we tried.
interpolateAndAssignHeight(position, terrainData, rectangle);
}
});
};
}
function createMarkFailedFunction(tileRequest) {
const tilePositions = tileRequest.positions;
return function () {
for (let i = 0; i < tilePositions.length; ++i) {
const position = tilePositions[i];
position.height = undefined;
}
};
}
export default sampleTerrain;
|