Skip to content

Commit 2862c99

Browse files
authored
Merge pull request #5864 from AnalyticalGraphicsInc/terrain-ancestor-layers
Added support of parentUrl property in layer.json
2 parents 7e96f5f + e6e45b5 commit 2862c99

File tree

5 files changed

+260
-41
lines changed

5 files changed

+260
-41
lines changed

CHANGES.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
Change Log
22
==========
3+
### 1.39 - 2017-11-01
4+
* Added support for the layer.json `parentUrl` property in `CesiumTerrainProvider` to allow for compositing of tilesets.
5+
36
### 1.38 - 2017-10-02
47

58
* Breaking changes

Source/Core/CesiumTerrainProvider.js

+146-41
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ define([
2020
'./QuantizedMeshTerrainData',
2121
'./Request',
2222
'./RequestType',
23+
'./RuntimeError',
2324
'./TerrainProvider',
2425
'./TileAvailability',
2526
'./TileProviderError'
@@ -45,11 +46,21 @@ define([
4546
QuantizedMeshTerrainData,
4647
Request,
4748
RequestType,
49+
RuntimeError,
4850
TerrainProvider,
4951
TileAvailability,
5052
TileProviderError) {
5153
'use strict';
5254

55+
function LayerInformation(layer) {
56+
this.isHeightmap = layer.isHeightmap;
57+
this.tileUrlTemplates = layer.tileUrlTemplates;
58+
this.availability = layer.availability;
59+
this.hasVertexNormals = layer.hasVertexNormals;
60+
this.hasWaterMask = layer.hasWaterMask;
61+
this.littleEndianExtensionSize = layer.littleEndianExtensionSize;
62+
}
63+
5364
/**
5465
* A {@link TerrainProvider} that accesses terrain data in a Cesium terrain format.
5566
* The format is described on the
@@ -114,22 +125,16 @@ define([
114125

115126
this._heightmapStructure = undefined;
116127
this._hasWaterMask = false;
117-
118-
/**
119-
* Boolean flag that indicates if the Terrain Server can provide vertex normals.
120-
* @type {Boolean}
121-
* @default false
122-
* @private
123-
*/
124128
this._hasVertexNormals = false;
129+
125130
/**
126131
* Boolean flag that indicates if the client should request vertex normals from the server.
127132
* @type {Boolean}
128133
* @default false
129134
* @private
130135
*/
131136
this._requestVertexNormals = defaultValue(options.requestVertexNormals, false);
132-
this._littleEndianExtensionSize = true;
137+
133138
/**
134139
* Boolean flag that indicates if the client should request tile watermasks from the server.
135140
* @type {Boolean}
@@ -139,17 +144,19 @@ define([
139144
this._requestWaterMask = defaultValue(options.requestWaterMask, false);
140145

141146
this._errorEvent = new Event();
142-
this._availability = undefined;
143147

144148
var credit = options.credit;
145149
if (typeof credit === 'string') {
146150
credit = new Credit(credit);
147151
}
148152
this._credit = credit;
149153

154+
this._availability = undefined;
155+
150156
this._ready = false;
151157
this._readyPromise = when.defer();
152158

159+
var lastUrl = this._url;
153160
var metadataUrl = joinUrls(this._url, 'layer.json');
154161
if (defined(this._proxy)) {
155162
metadataUrl = this._proxy.getURL(metadataUrl);
@@ -158,7 +165,11 @@ define([
158165
var that = this;
159166
var metadataError;
160167

161-
function metadataSuccess(data) {
168+
var layers = this._layers = [];
169+
var attribution = '';
170+
var overallAvailability = [];
171+
172+
function parseMetadataSuccess(data) {
162173
var message;
163174

164175
if (!data.format) {
@@ -173,8 +184,14 @@ define([
173184
return;
174185
}
175186

187+
var hasVertexNormals = false;
188+
var hasWaterMask = false;
189+
var littleEndianExtensionSize = true;
190+
var isHeightmap = false;
176191
if (data.format === 'heightmap-1.0') {
177-
that._heightmapStructure = {
192+
isHeightmap = true;
193+
if (!defined(that._heightmapStructure)) {
194+
that._heightmapStructure = {
178195
heightScale : 1.0 / 5.0,
179196
heightOffset : -1000.0,
180197
elementsPerHeight : 1,
@@ -184,63 +201,131 @@ define([
184201
lowestEncodedHeight : 0,
185202
highestEncodedHeight : 256 * 256 - 1
186203
};
187-
that._hasWaterMask = true;
204+
}
205+
hasWaterMask = true;
188206
that._requestWaterMask = true;
189207
} else if (data.format.indexOf('quantized-mesh-1.') !== 0) {
190208
message = 'The tile format "' + data.format + '" is invalid or not supported.';
191209
metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
192210
return;
193211
}
194212

195-
that._tileUrlTemplates = data.tiles;
196-
for (var i = 0; i < that._tileUrlTemplates.length; ++i) {
197-
var template = new Uri(that._tileUrlTemplates[i]);
198-
var baseUri = new Uri(that._url);
213+
var tileUrlTemplates = data.tiles;
214+
for (var i = 0; i < tileUrlTemplates.length; ++i) {
215+
var template = new Uri(tileUrlTemplates[i]);
216+
var baseUri = new Uri(lastUrl);
199217
if (template.authority && !baseUri.authority) {
200218
baseUri.authority = template.authority;
201219
baseUri.scheme = template.scheme;
202220
}
203-
that._tileUrlTemplates[i] = joinUrls(baseUri, template).toString().replace('{version}', data.version);
221+
tileUrlTemplates[i] = joinUrls(baseUri, template).toString().replace('{version}', data.version);
204222
}
205223

206224
var availableTiles = data.available;
207-
225+
var availability;
208226
if (defined(availableTiles)) {
209-
that._availability = new TileAvailability(that._tilingScheme, availableTiles.length);
227+
availability = new TileAvailability(that._tilingScheme, availableTiles.length);
210228

211229
for (var level = 0; level < availableTiles.length; ++level) {
212230
var rangesAtLevel = availableTiles[level];
213231
var yTiles = that._tilingScheme.getNumberOfYTilesAtLevel(level);
232+
if (!defined(overallAvailability[level])) {
233+
overallAvailability[level] = [];
234+
}
214235

215236
for (var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex) {
216237
var range = rangesAtLevel[rangeIndex];
217-
that._availability.addAvailableTileRange(level, range.startX, yTiles - range.endY - 1, range.endX, yTiles - range.startY - 1);
238+
var yStart = yTiles - range.endY - 1;
239+
var yEnd = yTiles - range.startY - 1;
240+
overallAvailability[level].push([range.startX, yStart, range.endX, yEnd]);
241+
availability.addAvailableTileRange(level, range.startX, yStart, range.endX, yEnd);
218242
}
219243
}
220244
}
221245

222-
if (!defined(that._credit) && defined(data.attribution) && data.attribution !== null) {
223-
that._credit = new Credit(data.attribution);
224-
}
225-
226246
// The vertex normals defined in the 'octvertexnormals' extension is identical to the original
227247
// contents of the original 'vertexnormals' extension. 'vertexnormals' extension is now
228248
// deprecated, as the extensionLength for this extension was incorrectly using big endian.
229249
// We maintain backwards compatibility with the legacy 'vertexnormal' implementation
230250
// by setting the _littleEndianExtensionSize to false. Always prefer 'octvertexnormals'
231251
// over 'vertexnormals' if both extensions are supported by the server.
232252
if (defined(data.extensions) && data.extensions.indexOf('octvertexnormals') !== -1) {
233-
that._hasVertexNormals = true;
253+
hasVertexNormals = true;
234254
} else if (defined(data.extensions) && data.extensions.indexOf('vertexnormals') !== -1) {
235-
that._hasVertexNormals = true;
236-
that._littleEndianExtensionSize = false;
255+
hasVertexNormals = true;
256+
littleEndianExtensionSize = false;
237257
}
238258
if (defined(data.extensions) && data.extensions.indexOf('watermask') !== -1) {
239-
that._hasWaterMask = true;
259+
hasWaterMask = true;
260+
}
261+
262+
that._hasWaterMask = that._hasWaterMask || hasWaterMask;
263+
that._hasVertexNormals = that._hasVertexNormals || hasVertexNormals;
264+
if (defined(data.attribution)) {
265+
if (attribution.length > 0) {
266+
attribution += ' ';
267+
}
268+
attribution += data.attribution;
269+
}
270+
271+
layers.push(new LayerInformation({
272+
isHeightmap: isHeightmap,
273+
tileUrlTemplates: tileUrlTemplates,
274+
availability: availability,
275+
hasVertexNormals: hasVertexNormals,
276+
hasWaterMask: hasWaterMask,
277+
littleEndianExtensionSize: littleEndianExtensionSize
278+
}));
279+
280+
var parentUrl = data.parentUrl;
281+
if (defined(parentUrl)) {
282+
if (!defined(availability)) {
283+
console.log('A layer.json can\'t have a parentUrl if it does\'t have an available array.');
284+
return when.resolve();
285+
}
286+
lastUrl = joinUrls(lastUrl, parentUrl);
287+
metadataUrl = joinUrls(lastUrl, 'layer.json');
288+
if (defined(that._proxy)) {
289+
metadataUrl = that._proxy.getURL(metadataUrl);
290+
}
291+
var parentMetadata = loadJson(metadataUrl);
292+
return when(parentMetadata, parseMetadataSuccess, parseMetadataFailure);
240293
}
241294

242-
that._ready = true;
243-
that._readyPromise.resolve(true);
295+
return when.resolve();
296+
}
297+
298+
function parseMetadataFailure(data) {
299+
var message = 'An error occurred while accessing ' + metadataUrl + '.';
300+
metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
301+
}
302+
303+
function metadataSuccess(data) {
304+
parseMetadataSuccess(data)
305+
.then(function() {
306+
if (defined(metadataError)) {
307+
return;
308+
}
309+
310+
var length = overallAvailability.length;
311+
if (length > 0) {
312+
var availability = that._availability = new TileAvailability(that._tilingScheme, length);
313+
for (var level = 0; level < length; ++level) {
314+
var levelRanges = overallAvailability[level];
315+
for (var i = 0; i < levelRanges.length; ++i) {
316+
var range = levelRanges[i];
317+
availability.addAvailableTileRange(level, range[0], range[1], range[2], range[3]);
318+
}
319+
}
320+
}
321+
322+
if (!defined(that._credit) && attribution.length > 0) {
323+
that._credit = new Credit(attribution);
324+
}
325+
326+
that._ready = true;
327+
that._readyPromise.resolve(true);
328+
});
244329
}
245330

246331
function metadataFailure(data) {
@@ -257,8 +342,7 @@ define([
257342
});
258343
return;
259344
}
260-
var message = 'An error occurred while accessing ' + metadataUrl + '.';
261-
metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
345+
parseMetadataFailure(data);
262346
}
263347

264348
function requestMetadata() {
@@ -320,7 +404,7 @@ define([
320404
});
321405
}
322406

323-
function createQuantizedMeshTerrainData(provider, buffer, level, x, y, tmsY) {
407+
function createQuantizedMeshTerrainData(provider, buffer, level, x, y, tmsY, littleEndianExtensionSize) {
324408
var pos = 0;
325409
var cartesian3Elements = 3;
326410
var boundingSphereElements = cartesian3Elements + 1;
@@ -431,7 +515,7 @@ define([
431515
while (pos < view.byteLength) {
432516
var extensionId = view.getUint8(pos, true);
433517
pos += Uint8Array.BYTES_PER_ELEMENT;
434-
var extensionLength = view.getUint32(pos, provider._littleEndianExtensionSize);
518+
var extensionLength = view.getUint32(pos, littleEndianExtensionSize);
435519
pos += Uint32Array.BYTES_PER_ELEMENT;
436520

437521
if (extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS && provider._requestVertexNormals) {
@@ -505,7 +589,27 @@ define([
505589
}
506590
//>>includeEnd('debug');
507591

508-
var urlTemplates = this._tileUrlTemplates;
592+
var layers = this._layers;
593+
var layerToUse;
594+
var layerCount = layers.length;
595+
596+
if (layerCount === 1) { // Optimized path for single layers
597+
layerToUse = layers[0];
598+
} else {
599+
for (var i = 0; i < layerCount; ++i) {
600+
var layer = layers[i];
601+
if (!defined(layer.availability) || layer.availability.isTileAvailable(level, x, y)) {
602+
layerToUse = layer;
603+
break;
604+
}
605+
}
606+
}
607+
608+
if (!defined(layerToUse)) {
609+
return when.reject(new RuntimeError('Terrain tile doesn\'t exist'));
610+
}
611+
612+
var urlTemplates = layerToUse.tileUrlTemplates;
509613
if (urlTemplates.length === 0) {
510614
return undefined;
511615
}
@@ -522,10 +626,10 @@ define([
522626
}
523627

524628
var extensionList = [];
525-
if (this._requestVertexNormals && this._hasVertexNormals) {
526-
extensionList.push(this._littleEndianExtensionSize ? 'octvertexnormals' : 'vertexnormals');
629+
if (this._requestVertexNormals && layerToUse.hasVertexNormals) {
630+
extensionList.push(layerToUse.littleEndianExtensionSize ? 'octvertexnormals' : 'vertexnormals');
527631
}
528-
if (this._requestWaterMask && this._hasWaterMask) {
632+
if (this._requestWaterMask && layerToUse.hasWaterMask) {
529633
extensionList.push('watermask');
530634
}
531635

@@ -540,7 +644,7 @@ define([
540644
if (defined(that._heightmapStructure)) {
541645
return createHeightmapTerrainData(that, buffer, level, x, y, tmsY);
542646
}
543-
return createQuantizedMeshTerrainData(that, buffer, level, x, y, tmsY);
647+
return createQuantizedMeshTerrainData(that, buffer, level, x, y, tmsY, layerToUse.littleEndianExtensionSize);
544648
});
545649
};
546650

@@ -723,10 +827,11 @@ define([
723827
* @returns {Boolean} Undefined if not supported, otherwise true or false.
724828
*/
725829
CesiumTerrainProvider.prototype.getTileDataAvailable = function(x, y, level) {
726-
if (!defined(this.availability)) {
830+
if (!defined(this._availability)) {
727831
return undefined;
728832
}
729-
return this.availability.isTileAvailable(level, x, y);
833+
834+
return this._availability.isTileAvailable(level, x, y);
730835
};
731836

732837
return CesiumTerrainProvider;

0 commit comments

Comments
 (0)