1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318 |
- import when from "../ThirdParty/when.js";
- import AttributeCompression from "./AttributeCompression.js";
- import BoundingSphere from "./BoundingSphere.js";
- import Cartesian3 from "./Cartesian3.js";
- import Credit from "./Credit.js";
- import defaultValue from "./defaultValue.js";
- import defined from "./defined.js";
- import DeveloperError from "./DeveloperError.js";
- import Event from "./Event.js";
- import GeographicTilingScheme from "./GeographicTilingScheme.js";
- import WebMercatorTilingScheme from "./WebMercatorTilingScheme.js";
- import getStringFromTypedArray from "./getStringFromTypedArray.js";
- import HeightmapTerrainData from "./HeightmapTerrainData.js";
- import IndexDatatype from "./IndexDatatype.js";
- import OrientedBoundingBox from "./OrientedBoundingBox.js";
- import QuantizedMeshTerrainData from "./QuantizedMeshTerrainData.js";
- import Request from "./Request.js";
- import RequestType from "./RequestType.js";
- import Resource from "./Resource.js";
- import RuntimeError from "./RuntimeError.js";
- import TerrainProvider from "./TerrainProvider.js";
- import TileAvailability from "./TileAvailability.js";
- import TileProviderError from "./TileProviderError.js";
- function LayerInformation(layer) {
- this.resource = layer.resource;
- this.version = layer.version;
- this.isHeightmap = layer.isHeightmap;
- this.tileUrlTemplates = layer.tileUrlTemplates;
- this.availability = layer.availability;
- this.hasVertexNormals = layer.hasVertexNormals;
- this.hasWaterMask = layer.hasWaterMask;
- this.hasMetadata = layer.hasMetadata;
- this.availabilityLevels = layer.availabilityLevels;
- this.availabilityTilesLoaded = layer.availabilityTilesLoaded;
- this.littleEndianExtensionSize = layer.littleEndianExtensionSize;
- this.availabilityTilesLoaded = layer.availabilityTilesLoaded;
- this.availabilityPromiseCache = {};
- }
- /**
- * A {@link TerrainProvider} that accesses terrain data in a Cesium terrain format.
- *
- * @alias CesiumTerrainProvider
- * @constructor
- *
- * @param {Object} options Object with the following properties:
- * @param {Resource|String|Promise<Resource>|Promise<String>} options.url The URL of the Cesium terrain server.
- * @param {Boolean} [options.requestVertexNormals=false] Flag that indicates if the client should request additional lighting information from the server, in the form of per vertex normals if available.
- * @param {Boolean} [options.requestWaterMask=false] Flag that indicates if the client should request per tile water masks from the server, if available.
- * @param {Boolean} [options.requestMetadata=true] Flag that indicates if the client should request per tile metadata from the server, if available.
- * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used.
- * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
- *
- *
- * @example
- * // Create Arctic DEM terrain with normals.
- * var viewer = new Cesium.Viewer('cesiumContainer', {
- * terrainProvider : new Cesium.CesiumTerrainProvider({
- * url : Cesium.IonResource.fromAssetId(3956),
- * requestVertexNormals : true
- * })
- * });
- *
- * @see createWorldTerrain
- * @see TerrainProvider
- */
- function CesiumTerrainProvider(options) {
- //>>includeStart('debug', pragmas.debug)
- if (!defined(options) || !defined(options.url)) {
- throw new DeveloperError("options.url is required.");
- }
- //>>includeEnd('debug');
- this._heightmapWidth = 65;
- this._heightmapStructure = undefined;
- this._hasWaterMask = false;
- this._hasVertexNormals = false;
- this._ellipsoid = options.ellipsoid;
- /**
- * Boolean flag that indicates if the client should request vertex normals from the server.
- * @type {Boolean}
- * @default false
- * @private
- */
- this._requestVertexNormals = defaultValue(
- options.requestVertexNormals,
- false
- );
- /**
- * Boolean flag that indicates if the client should request tile watermasks from the server.
- * @type {Boolean}
- * @default false
- * @private
- */
- this._requestWaterMask = defaultValue(options.requestWaterMask, false);
- /**
- * Boolean flag that indicates if the client should request tile metadata from the server.
- * @type {Boolean}
- * @default true
- * @private
- */
- this._requestMetadata = defaultValue(options.requestMetadata, true);
- this._errorEvent = new Event();
- var credit = options.credit;
- if (typeof credit === "string") {
- credit = new Credit(credit);
- }
- this._credit = credit;
- this._availability = undefined;
- var deferred = when.defer();
- this._ready = false;
- this._readyPromise = deferred;
- this._tileCredits = undefined;
- var that = this;
- var lastResource;
- var layerJsonResource;
- var metadataError;
- var layers = (this._layers = []);
- var attribution = "";
- var overallAvailability = [];
- var overallMaxZoom = 0;
- when(options.url)
- .then(function (url) {
- var resource = Resource.createIfNeeded(url);
- resource.appendForwardSlash();
- lastResource = resource;
- layerJsonResource = lastResource.getDerivedResource({
- url: "layer.json",
- });
- // ion resources have a credits property we can use for additional attribution.
- that._tileCredits = resource.credits;
- requestLayerJson();
- })
- .otherwise(function (e) {
- deferred.reject(e);
- });
- function parseMetadataSuccess(data) {
- var message;
- if (!data.format) {
- message = "The tile format is not specified in the layer.json file.";
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- return;
- }
- if (!data.tiles || data.tiles.length === 0) {
- message = "The layer.json file does not specify any tile URL templates.";
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- return;
- }
- var hasVertexNormals = false;
- var hasWaterMask = false;
- var hasMetadata = false;
- var littleEndianExtensionSize = true;
- var isHeightmap = false;
- if (data.format === "heightmap-1.0") {
- isHeightmap = true;
- if (!defined(that._heightmapStructure)) {
- that._heightmapStructure = {
- heightScale: 1.0 / 5.0,
- heightOffset: -1000.0,
- elementsPerHeight: 1,
- stride: 1,
- elementMultiplier: 256.0,
- isBigEndian: false,
- lowestEncodedHeight: 0,
- highestEncodedHeight: 256 * 256 - 1,
- };
- }
- hasWaterMask = true;
- that._requestWaterMask = true;
- } else if (data.format.indexOf("quantized-mesh-1.") !== 0) {
- message =
- 'The tile format "' + data.format + '" is invalid or not supported.';
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- return;
- }
- var tileUrlTemplates = data.tiles;
- var maxZoom = data.maxzoom;
- overallMaxZoom = Math.max(overallMaxZoom, maxZoom);
- // Keeps track of which of the availablity containing tiles have been loaded
- if (!data.projection || data.projection === "EPSG:4326") {
- that._tilingScheme = new GeographicTilingScheme({
- numberOfLevelZeroTilesX: 2,
- numberOfLevelZeroTilesY: 1,
- ellipsoid: that._ellipsoid,
- });
- } else if (data.projection === "EPSG:3857") {
- that._tilingScheme = new WebMercatorTilingScheme({
- numberOfLevelZeroTilesX: 1,
- numberOfLevelZeroTilesY: 1,
- ellipsoid: that._ellipsoid,
- });
- } else {
- message =
- 'The projection "' + data.projection + '" is invalid or not supported.';
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- return;
- }
- that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(
- that._tilingScheme.ellipsoid,
- that._heightmapWidth,
- that._tilingScheme.getNumberOfXTilesAtLevel(0)
- );
- if (!data.scheme || data.scheme === "tms" || data.scheme === "slippyMap") {
- that._scheme = data.scheme;
- } else {
- message = 'The scheme "' + data.scheme + '" is invalid or not supported.';
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- return;
- }
- var availabilityTilesLoaded;
- // The vertex normals defined in the 'octvertexnormals' extension is identical to the original
- // contents of the original 'vertexnormals' extension. 'vertexnormals' extension is now
- // deprecated, as the extensionLength for this extension was incorrectly using big endian.
- // We maintain backwards compatibility with the legacy 'vertexnormal' implementation
- // by setting the _littleEndianExtensionSize to false. Always prefer 'octvertexnormals'
- // over 'vertexnormals' if both extensions are supported by the server.
- if (
- defined(data.extensions) &&
- data.extensions.indexOf("octvertexnormals") !== -1
- ) {
- hasVertexNormals = true;
- } else if (
- defined(data.extensions) &&
- data.extensions.indexOf("vertexnormals") !== -1
- ) {
- hasVertexNormals = true;
- littleEndianExtensionSize = false;
- }
- if (
- defined(data.extensions) &&
- data.extensions.indexOf("watermask") !== -1
- ) {
- hasWaterMask = true;
- }
- if (
- defined(data.extensions) &&
- data.extensions.indexOf("metadata") !== -1
- ) {
- hasMetadata = true;
- }
- var availabilityLevels = data.metadataAvailability;
- var availableTiles = data.available;
- var availability;
- if (defined(availableTiles) && !defined(availabilityLevels)) {
- availability = new TileAvailability(
- that._tilingScheme,
- availableTiles.length
- );
- for (var level = 0; level < availableTiles.length; ++level) {
- var rangesAtLevel = availableTiles[level];
- var yTiles = that._tilingScheme.getNumberOfYTilesAtLevel(level);
- if (!defined(overallAvailability[level])) {
- overallAvailability[level] = [];
- }
- for (
- var rangeIndex = 0;
- rangeIndex < rangesAtLevel.length;
- ++rangeIndex
- ) {
- var range = rangesAtLevel[rangeIndex];
- var yStart = yTiles - range.endY - 1;
- var yEnd = yTiles - range.startY - 1;
- overallAvailability[level].push([
- range.startX,
- yStart,
- range.endX,
- yEnd,
- ]);
- availability.addAvailableTileRange(
- level,
- range.startX,
- yStart,
- range.endX,
- yEnd
- );
- }
- }
- } else if (defined(availabilityLevels)) {
- availabilityTilesLoaded = new TileAvailability(
- that._tilingScheme,
- maxZoom
- );
- availability = new TileAvailability(that._tilingScheme, maxZoom);
- overallAvailability[0] = [[0, 0, 1, 0]];
- availability.addAvailableTileRange(0, 0, 0, 1, 0);
- }
- that._hasWaterMask = that._hasWaterMask || hasWaterMask;
- that._hasVertexNormals = that._hasVertexNormals || hasVertexNormals;
- that._hasMetadata = that._hasMetadata || hasMetadata;
- if (defined(data.attribution)) {
- if (attribution.length > 0) {
- attribution += " ";
- }
- attribution += data.attribution;
- }
- layers.push(
- new LayerInformation({
- resource: lastResource,
- version: data.version,
- isHeightmap: isHeightmap,
- tileUrlTemplates: tileUrlTemplates,
- availability: availability,
- hasVertexNormals: hasVertexNormals,
- hasWaterMask: hasWaterMask,
- hasMetadata: hasMetadata,
- availabilityLevels: availabilityLevels,
- availabilityTilesLoaded: availabilityTilesLoaded,
- littleEndianExtensionSize: littleEndianExtensionSize,
- })
- );
- var parentUrl = data.parentUrl;
- if (defined(parentUrl)) {
- if (!defined(availability)) {
- console.log(
- "A layer.json can't have a parentUrl if it does't have an available array."
- );
- return when.resolve();
- }
- lastResource = lastResource.getDerivedResource({
- url: parentUrl,
- });
- lastResource.appendForwardSlash(); // Terrain always expects a directory
- layerJsonResource = lastResource.getDerivedResource({
- url: "layer.json",
- });
- var parentMetadata = layerJsonResource.fetchJson();
- return when(parentMetadata, parseMetadataSuccess, parseMetadataFailure);
- }
- return when.resolve();
- }
- function parseMetadataFailure(data) {
- var message =
- "An error occurred while accessing " + layerJsonResource.url + ".";
- metadataError = TileProviderError.handleError(
- metadataError,
- that,
- that._errorEvent,
- message,
- undefined,
- undefined,
- undefined,
- requestLayerJson
- );
- }
- function metadataSuccess(data) {
- parseMetadataSuccess(data).then(function () {
- if (defined(metadataError)) {
- return;
- }
- var length = overallAvailability.length;
- if (length > 0) {
- var availability = (that._availability = new TileAvailability(
- that._tilingScheme,
- overallMaxZoom
- ));
- for (var level = 0; level < length; ++level) {
- var levelRanges = overallAvailability[level];
- for (var i = 0; i < levelRanges.length; ++i) {
- var range = levelRanges[i];
- availability.addAvailableTileRange(
- level,
- range[0],
- range[1],
- range[2],
- range[3]
- );
- }
- }
- }
- if (attribution.length > 0) {
- var layerJsonCredit = new Credit(attribution);
- if (defined(that._tileCredits)) {
- that._tileCredits.push(layerJsonCredit);
- } else {
- that._tileCredits = [layerJsonCredit];
- }
- }
- that._ready = true;
- that._readyPromise.resolve(true);
- });
- }
- function metadataFailure(data) {
- // If the metadata is not found, assume this is a pre-metadata heightmap tileset.
- if (defined(data) && data.statusCode === 404) {
- metadataSuccess({
- tilejson: "2.1.0",
- format: "heightmap-1.0",
- version: "1.0.0",
- scheme: "tms",
- tiles: ["{z}/{x}/{y}.terrain?v={version}"],
- });
- return;
- }
- parseMetadataFailure(data);
- }
- function requestLayerJson() {
- when(layerJsonResource.fetchJson())
- .then(metadataSuccess)
- .otherwise(metadataFailure);
- }
- }
- /**
- * When using the Quantized-Mesh format, a tile may be returned that includes additional extensions, such as PerVertexNormals, watermask, etc.
- * This enumeration defines the unique identifiers for each type of extension data that has been appended to the standard mesh data.
- *
- * @namespace QuantizedMeshExtensionIds
- * @see CesiumTerrainProvider
- * @private
- */
- var QuantizedMeshExtensionIds = {
- /**
- * Oct-Encoded Per-Vertex Normals are included as an extension to the tile mesh
- *
- * @type {Number}
- * @constant
- * @default 1
- */
- OCT_VERTEX_NORMALS: 1,
- /**
- * A watermask is included as an extension to the tile mesh
- *
- * @type {Number}
- * @constant
- * @default 2
- */
- WATER_MASK: 2,
- /**
- * A json object contain metadata about the tile
- *
- * @type {Number}
- * @constant
- * @default 4
- */
- METADATA: 4,
- };
- function getRequestHeader(extensionsList) {
- if (!defined(extensionsList) || extensionsList.length === 0) {
- return {
- Accept:
- "application/vnd.quantized-mesh,application/octet-stream;q=0.9,*/*;q=0.01",
- };
- }
- var extensions = extensionsList.join("-");
- return {
- Accept:
- "application/vnd.quantized-mesh;extensions=" +
- extensions +
- ",application/octet-stream;q=0.9,*/*;q=0.01",
- };
- }
- function createHeightmapTerrainData(provider, buffer, level, x, y) {
- var heightBuffer = new Uint16Array(
- buffer,
- 0,
- provider._heightmapWidth * provider._heightmapWidth
- );
- return new HeightmapTerrainData({
- buffer: heightBuffer,
- childTileMask: new Uint8Array(buffer, heightBuffer.byteLength, 1)[0],
- waterMask: new Uint8Array(
- buffer,
- heightBuffer.byteLength + 1,
- buffer.byteLength - heightBuffer.byteLength - 1
- ),
- width: provider._heightmapWidth,
- height: provider._heightmapWidth,
- structure: provider._heightmapStructure,
- credits: provider._tileCredits,
- });
- }
- function createQuantizedMeshTerrainData(provider, buffer, level, x, y, layer) {
- var littleEndianExtensionSize = layer.littleEndianExtensionSize;
- var pos = 0;
- var cartesian3Elements = 3;
- var boundingSphereElements = cartesian3Elements + 1;
- var cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements;
- var boundingSphereLength =
- Float64Array.BYTES_PER_ELEMENT * boundingSphereElements;
- var encodedVertexElements = 3;
- var encodedVertexLength =
- Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements;
- var triangleElements = 3;
- var bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;
- var triangleLength = bytesPerIndex * triangleElements;
- var view = new DataView(buffer);
- var center = new Cartesian3(
- view.getFloat64(pos, true),
- view.getFloat64(pos + 8, true),
- view.getFloat64(pos + 16, true)
- );
- pos += cartesian3Length;
- var minimumHeight = view.getFloat32(pos, true);
- pos += Float32Array.BYTES_PER_ELEMENT;
- var maximumHeight = view.getFloat32(pos, true);
- pos += Float32Array.BYTES_PER_ELEMENT;
- var boundingSphere = new BoundingSphere(
- new Cartesian3(
- view.getFloat64(pos, true),
- view.getFloat64(pos + 8, true),
- view.getFloat64(pos + 16, true)
- ),
- view.getFloat64(pos + cartesian3Length, true)
- );
- pos += boundingSphereLength;
- var horizonOcclusionPoint = new Cartesian3(
- view.getFloat64(pos, true),
- view.getFloat64(pos + 8, true),
- view.getFloat64(pos + 16, true)
- );
- pos += cartesian3Length;
- var vertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3);
- pos += vertexCount * encodedVertexLength;
- if (vertexCount > 64 * 1024) {
- // More than 64k vertices, so indices are 32-bit.
- bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT;
- triangleLength = bytesPerIndex * triangleElements;
- }
- // Decode the vertex buffer.
- var uBuffer = encodedVertexBuffer.subarray(0, vertexCount);
- var vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount);
- var heightBuffer = encodedVertexBuffer.subarray(
- vertexCount * 2,
- 3 * vertexCount
- );
- AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
- // skip over any additional padding that was added for 2/4 byte alignment
- if (pos % bytesPerIndex !== 0) {
- pos += bytesPerIndex - (pos % bytesPerIndex);
- }
- var triangleCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var indices = IndexDatatype.createTypedArrayFromArrayBuffer(
- vertexCount,
- buffer,
- pos,
- triangleCount * triangleElements
- );
- pos += triangleCount * triangleLength;
- // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js.
- // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55
- // Copyright 2012 Google Inc., Apache 2.0 license.
- var highest = 0;
- var length = indices.length;
- for (var i = 0; i < length; ++i) {
- var code = indices[i];
- indices[i] = highest - code;
- if (code === 0) {
- ++highest;
- }
- }
- var westVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var westIndices = IndexDatatype.createTypedArrayFromArrayBuffer(
- vertexCount,
- buffer,
- pos,
- westVertexCount
- );
- pos += westVertexCount * bytesPerIndex;
- var southVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var southIndices = IndexDatatype.createTypedArrayFromArrayBuffer(
- vertexCount,
- buffer,
- pos,
- southVertexCount
- );
- pos += southVertexCount * bytesPerIndex;
- var eastVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var eastIndices = IndexDatatype.createTypedArrayFromArrayBuffer(
- vertexCount,
- buffer,
- pos,
- eastVertexCount
- );
- pos += eastVertexCount * bytesPerIndex;
- var northVertexCount = view.getUint32(pos, true);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- var northIndices = IndexDatatype.createTypedArrayFromArrayBuffer(
- vertexCount,
- buffer,
- pos,
- northVertexCount
- );
- pos += northVertexCount * bytesPerIndex;
- var encodedNormalBuffer;
- var waterMaskBuffer;
- while (pos < view.byteLength) {
- var extensionId = view.getUint8(pos, true);
- pos += Uint8Array.BYTES_PER_ELEMENT;
- var extensionLength = view.getUint32(pos, littleEndianExtensionSize);
- pos += Uint32Array.BYTES_PER_ELEMENT;
- if (
- extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS &&
- provider._requestVertexNormals
- ) {
- encodedNormalBuffer = new Uint8Array(buffer, pos, vertexCount * 2);
- } else if (
- extensionId === QuantizedMeshExtensionIds.WATER_MASK &&
- provider._requestWaterMask
- ) {
- waterMaskBuffer = new Uint8Array(buffer, pos, extensionLength);
- } else if (
- extensionId === QuantizedMeshExtensionIds.METADATA &&
- provider._requestMetadata
- ) {
- var stringLength = view.getUint32(pos, true);
- if (stringLength > 0) {
- var jsonString = getStringFromTypedArray(
- new Uint8Array(buffer),
- pos + Uint32Array.BYTES_PER_ELEMENT,
- stringLength
- );
- var metadata = JSON.parse(jsonString);
- var availableTiles = metadata.available;
- if (defined(availableTiles)) {
- for (var offset = 0; offset < availableTiles.length; ++offset) {
- var availableLevel = level + offset + 1;
- var rangesAtLevel = availableTiles[offset];
- var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(
- availableLevel
- );
- for (
- var rangeIndex = 0;
- rangeIndex < rangesAtLevel.length;
- ++rangeIndex
- ) {
- var range = rangesAtLevel[rangeIndex];
- var yStart = yTiles - range.endY - 1;
- var yEnd = yTiles - range.startY - 1;
- provider.availability.addAvailableTileRange(
- availableLevel,
- range.startX,
- yStart,
- range.endX,
- yEnd
- );
- layer.availability.addAvailableTileRange(
- availableLevel,
- range.startX,
- yStart,
- range.endX,
- yEnd
- );
- }
- }
- }
- }
- layer.availabilityTilesLoaded.addAvailableTileRange(level, x, y, x, y);
- }
- pos += extensionLength;
- }
- var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;
- // The skirt is not included in the OBB computation. If this ever
- // causes any rendering artifacts (cracks), they are expected to be
- // minor and in the corners of the screen. It's possible that this
- // might need to be changed - just change to `minimumHeight - skirtHeight`
- // A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
- var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);
- var orientedBoundingBox = OrientedBoundingBox.fromRectangle(
- rectangle,
- minimumHeight,
- maximumHeight,
- provider._tilingScheme.ellipsoid
- );
- return new QuantizedMeshTerrainData({
- center: center,
- minimumHeight: minimumHeight,
- maximumHeight: maximumHeight,
- boundingSphere: boundingSphere,
- orientedBoundingBox: orientedBoundingBox,
- horizonOcclusionPoint: horizonOcclusionPoint,
- quantizedVertices: encodedVertexBuffer,
- encodedNormals: encodedNormalBuffer,
- indices: indices,
- westIndices: westIndices,
- southIndices: southIndices,
- eastIndices: eastIndices,
- northIndices: northIndices,
- westSkirtHeight: skirtHeight,
- southSkirtHeight: skirtHeight,
- eastSkirtHeight: skirtHeight,
- northSkirtHeight: skirtHeight,
- childTileMask: provider.availability.computeChildMaskForTile(level, x, y),
- waterMask: waterMaskBuffer,
- credits: provider._tileCredits,
- });
- }
- /**
- * Requests the geometry for a given tile. This function should not be called before
- * {@link CesiumTerrainProvider#ready} returns true. The result must include terrain data and
- * may optionally include a water mask and an indication of which child tiles are available.
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @param {Request} [request] The request object. Intended for internal use only.
- *
- * @returns {Promise.<TerrainData>|undefined} A promise for the requested geometry. If this method
- * returns undefined instead of a promise, it is an indication that too many requests are already
- * pending and the request will be retried later.
- *
- * @exception {DeveloperError} This function must not be called before {@link CesiumTerrainProvider#ready}
- * returns true.
- */
- CesiumTerrainProvider.prototype.requestTileGeometry = function (
- x,
- y,
- level,
- request
- ) {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "requestTileGeometry must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- var layers = this._layers;
- var layerToUse;
- var layerCount = layers.length;
- if (layerCount === 1) {
- // Optimized path for single layers
- layerToUse = layers[0];
- } else {
- for (var i = 0; i < layerCount; ++i) {
- var layer = layers[i];
- if (
- !defined(layer.availability) ||
- layer.availability.isTileAvailable(level, x, y)
- ) {
- layerToUse = layer;
- break;
- }
- }
- }
- return requestTileGeometry(this, x, y, level, layerToUse, request);
- };
- function requestTileGeometry(provider, x, y, level, layerToUse, request) {
- if (!defined(layerToUse)) {
- return when.reject(new RuntimeError("Terrain tile doesn't exist"));
- }
- var urlTemplates = layerToUse.tileUrlTemplates;
- if (urlTemplates.length === 0) {
- return undefined;
- }
- // The TileMapService scheme counts from the bottom left
- var terrainY;
- if (!provider._scheme || provider._scheme === "tms") {
- var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(level);
- terrainY = yTiles - y - 1;
- } else {
- terrainY = y;
- }
- var extensionList = [];
- if (provider._requestVertexNormals && layerToUse.hasVertexNormals) {
- extensionList.push(
- layerToUse.littleEndianExtensionSize
- ? "octvertexnormals"
- : "vertexnormals"
- );
- }
- if (provider._requestWaterMask && layerToUse.hasWaterMask) {
- extensionList.push("watermask");
- }
- if (provider._requestMetadata && layerToUse.hasMetadata) {
- extensionList.push("metadata");
- }
- var headers;
- var query;
- var url = urlTemplates[(x + terrainY + level) % urlTemplates.length];
- var resource = layerToUse.resource;
- if (
- defined(resource._ionEndpoint) &&
- !defined(resource._ionEndpoint.externalType)
- ) {
- // ion uses query paremeters to request extensions
- if (extensionList.length !== 0) {
- query = { extensions: extensionList.join("-") };
- }
- headers = getRequestHeader(undefined);
- } else {
- //All other terrain servers
- headers = getRequestHeader(extensionList);
- }
- var promise = resource
- .getDerivedResource({
- url: url,
- templateValues: {
- version: layerToUse.version,
- z: level,
- x: x,
- y: terrainY,
- },
- queryParameters: query,
- headers: headers,
- request: request,
- })
- .fetchArrayBuffer();
- if (!defined(promise)) {
- return undefined;
- }
- return promise.then(function (buffer) {
- if (defined(provider._heightmapStructure)) {
- return createHeightmapTerrainData(provider, buffer, level, x, y);
- }
- return createQuantizedMeshTerrainData(
- provider,
- buffer,
- level,
- x,
- y,
- layerToUse
- );
- });
- }
- Object.defineProperties(CesiumTerrainProvider.prototype, {
- /**
- * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing
- * to the event, you will be notified of the error and can potentially recover from it. Event listeners
- * are passed an instance of {@link TileProviderError}.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Event}
- */
- errorEvent: {
- get: function () {
- return this._errorEvent;
- },
- },
- /**
- * Gets the credit to display when this terrain provider is active. Typically this is used to credit
- * the source of the terrain. This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Credit}
- */
- credit: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "credit must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- return this._credit;
- },
- },
- /**
- * Gets the tiling scheme used by this provider. This function should
- * not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {GeographicTilingScheme}
- */
- tilingScheme: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "tilingScheme must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- return this._tilingScheme;
- },
- },
- /**
- * Gets a value indicating whether or not the provider is ready for use.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- ready: {
- get: function () {
- return this._ready;
- },
- },
- /**
- * Gets a promise that resolves to true when the provider is ready for use.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Promise.<Boolean>}
- * @readonly
- */
- readyPromise: {
- get: function () {
- return this._readyPromise.promise;
- },
- },
- /**
- * Gets a value indicating whether or not the provider includes a water mask. The water mask
- * indicates which areas of the globe are water rather than land, so they can be rendered
- * as a reflective surface with animated waves. This function should not be
- * called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasWaterMask: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "hasWaterMask must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- return this._hasWaterMask && this._requestWaterMask;
- },
- },
- /**
- * Gets a value indicating whether or not the requested tiles include vertex normals.
- * This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasVertexNormals: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "hasVertexNormals must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- // returns true if we can request vertex normals from the server
- return this._hasVertexNormals && this._requestVertexNormals;
- },
- },
- /**
- * Gets a value indicating whether or not the requested tiles include metadata.
- * This function should not be called before {@link CesiumTerrainProvider#ready} returns true.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready}
- */
- hasMetadata: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "hasMetadata must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- // returns true if we can request metadata from the server
- return this._hasMetadata && this._requestMetadata;
- },
- },
- /**
- * Boolean flag that indicates if the client should request vertex normals from the server.
- * Vertex normals data is appended to the standard tile mesh data only if the client requests the vertex normals and
- * if the server provides vertex normals.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestVertexNormals: {
- get: function () {
- return this._requestVertexNormals;
- },
- },
- /**
- * Boolean flag that indicates if the client should request a watermask from the server.
- * Watermask data is appended to the standard tile mesh data only if the client requests the watermask and
- * if the server provides a watermask.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestWaterMask: {
- get: function () {
- return this._requestWaterMask;
- },
- },
- /**
- * Boolean flag that indicates if the client should request metadata from the server.
- * Metadata is appended to the standard tile mesh data only if the client requests the metadata and
- * if the server provides a metadata.
- * @memberof CesiumTerrainProvider.prototype
- * @type {Boolean}
- */
- requestMetadata: {
- get: function () {
- return this._requestMetadata;
- },
- },
- /**
- * Gets an object that can be used to determine availability of terrain from this provider, such as
- * at points and in rectangles. This function should not be called before
- * {@link CesiumTerrainProvider#ready} returns true. This property may be undefined if availability
- * information is not available. Note that this reflects tiles that are known to be available currently.
- * Additional tiles may be discovered to be available in the future, e.g. if availability information
- * exists deeper in the tree rather than it all being discoverable at the root. However, a tile that
- * is available now will not become unavailable in the future.
- * @memberof CesiumTerrainProvider.prototype
- * @type {TileAvailability}
- */
- availability: {
- get: function () {
- //>>includeStart('debug', pragmas.debug)
- if (!this._ready) {
- throw new DeveloperError(
- "availability must not be called before the terrain provider is ready."
- );
- }
- //>>includeEnd('debug');
- return this._availability;
- },
- },
- });
- /**
- * Gets the maximum geometric error allowed in a tile at a given level.
- *
- * @param {Number} level The tile level for which to get the maximum geometric error.
- * @returns {Number} The maximum geometric error.
- */
- CesiumTerrainProvider.prototype.getLevelMaximumGeometricError = function (
- level
- ) {
- return this._levelZeroMaximumGeometricError / (1 << level);
- };
- /**
- * Determines whether data for a tile is available to be loaded.
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @returns {Boolean} Undefined if not supported or availability is unknown, otherwise true or false.
- */
- CesiumTerrainProvider.prototype.getTileDataAvailable = function (x, y, level) {
- if (!defined(this._availability)) {
- return undefined;
- }
- if (level > this._availability._maximumLevel) {
- return false;
- }
- if (this._availability.isTileAvailable(level, x, y)) {
- // If the tile is listed as available, then we are done
- return true;
- }
- if (!this._hasMetadata) {
- // If we don't have any layers with the metadata extension then we don't have this tile
- return false;
- }
- var layers = this._layers;
- var count = layers.length;
- for (var i = 0; i < count; ++i) {
- var layerResult = checkLayer(this, x, y, level, layers[i], i === 0);
- if (layerResult.result) {
- // There is a layer that may or may not have the tile
- return undefined;
- }
- }
- return false;
- };
- /**
- * Makes sure we load availability data for a tile
- *
- * @param {Number} x The X coordinate of the tile for which to request geometry.
- * @param {Number} y The Y coordinate of the tile for which to request geometry.
- * @param {Number} level The level of the tile for which to request geometry.
- * @returns {undefined|Promise<void>} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded
- */
- CesiumTerrainProvider.prototype.loadTileDataAvailability = function (
- x,
- y,
- level
- ) {
- if (
- !defined(this._availability) ||
- level > this._availability._maximumLevel ||
- this._availability.isTileAvailable(level, x, y) ||
- !this._hasMetadata
- ) {
- // We know the tile is either available or not available so nothing to wait on
- return undefined;
- }
- var layers = this._layers;
- var count = layers.length;
- for (var i = 0; i < count; ++i) {
- var layerResult = checkLayer(this, x, y, level, layers[i], i === 0);
- if (defined(layerResult.promise)) {
- return layerResult.promise;
- }
- }
- };
- function getAvailabilityTile(layer, x, y, level) {
- if (level === 0) {
- return;
- }
- var availabilityLevels = layer.availabilityLevels;
- var parentLevel =
- level % availabilityLevels === 0
- ? level - availabilityLevels
- : ((level / availabilityLevels) | 0) * availabilityLevels;
- var divisor = 1 << (level - parentLevel);
- var parentX = (x / divisor) | 0;
- var parentY = (y / divisor) | 0;
- return {
- level: parentLevel,
- x: parentX,
- y: parentY,
- };
- }
- function checkLayer(provider, x, y, level, layer, topLayer) {
- if (!defined(layer.availabilityLevels)) {
- // It's definitely not in this layer
- return {
- result: false,
- };
- }
- var cacheKey;
- var deleteFromCache = function () {
- delete layer.availabilityPromiseCache[cacheKey];
- };
- var availabilityTilesLoaded = layer.availabilityTilesLoaded;
- var availability = layer.availability;
- var tile = getAvailabilityTile(layer, x, y, level);
- while (defined(tile)) {
- if (
- availability.isTileAvailable(tile.level, tile.x, tile.y) &&
- !availabilityTilesLoaded.isTileAvailable(tile.level, tile.x, tile.y)
- ) {
- var requestPromise;
- if (!topLayer) {
- cacheKey = tile.level + "-" + tile.x + "-" + tile.y;
- requestPromise = layer.availabilityPromiseCache[cacheKey];
- if (!defined(requestPromise)) {
- // For cutout terrain, if this isn't the top layer the availability tiles
- // may never get loaded, so request it here.
- var request = new Request({
- throttle: true,
- throttleByServer: true,
- type: RequestType.TERRAIN,
- });
- requestPromise = requestTileGeometry(
- provider,
- tile.x,
- tile.y,
- tile.level,
- layer,
- request
- );
- if (defined(requestPromise)) {
- layer.availabilityPromiseCache[cacheKey] = requestPromise;
- requestPromise.then(deleteFromCache);
- }
- }
- }
- // The availability tile is available, but not loaded, so there
- // is still a chance that it may become available at some point
- return {
- result: true,
- promise: requestPromise,
- };
- }
- tile = getAvailabilityTile(layer, tile.x, tile.y, tile.level);
- }
- return {
- result: false,
- };
- }
- // Used for testing
- CesiumTerrainProvider._getAvailabilityTile = getAvailabilityTile;
- export default CesiumTerrainProvider;
|