import { Cartesian3 } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { Event } from "../../Source/Cesium.js";
import { GeographicTilingScheme } from "../../Source/Cesium.js";
import { Matrix4 } from "../../Source/Cesium.js";
import { Ray } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { WebMercatorProjection } from "../../Source/Cesium.js";
import { WebMercatorTilingScheme } from "../../Source/Cesium.js";
import { Globe } from "../../Source/Cesium.js";
import { ImageryLayer } from "../../Source/Cesium.js";
import { ImageryLayerCollection } from "../../Source/Cesium.js";
import { ImageryLayerFeatureInfo } from "../../Source/Cesium.js";
import { ImageryProvider } from "../../Source/Cesium.js";
import createScene from "../createScene.js";
import pollToPromise from "../pollToPromise.js";
import { when } from "../../Source/Cesium.js";
describe(
"Scene/ImageryLayerCollection",
function () {
var fakeProvider = {
isReady: function () {
return false;
},
};
it("tracks the base layer on add", function () {
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
var layer3 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
expect(layer1.isBaseLayer()).toEqual(false);
collection.add(layer1);
expect(layer1.isBaseLayer()).toEqual(true);
collection.add(layer2);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
collection.add(layer3, 0);
expect(layer1.isBaseLayer()).toEqual(false);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(true);
});
it("tracks the base layer on remove", function () {
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
var layer3 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
collection.add(layer1);
collection.add(layer2);
collection.add(layer3);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
collection.remove(layer1);
expect(layer2.isBaseLayer()).toEqual(true);
expect(layer3.isBaseLayer()).toEqual(false);
collection.remove(layer3);
expect(layer2.isBaseLayer()).toEqual(true);
});
it("updates isBaseLayer on re-add", function () {
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
layer1._isBaseLayer = true;
layer2._isBaseLayer = true;
collection.add(layer1);
collection.add(layer2);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
});
it("does not crash when raising and lowering a single layer.", function () {
var layer1 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
collection.add(layer1);
collection.raise(layer1);
collection.lower(layer1);
collection.raiseToTop(layer1);
collection.lowerToBottom(layer1);
});
it("tracks the base layer on raise and lower", function () {
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
var layer3 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
collection.add(layer1);
collection.add(layer2);
collection.add(layer3);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
collection.lower(layer1);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
collection.raise(layer1);
expect(layer1.isBaseLayer()).toEqual(false);
expect(layer2.isBaseLayer()).toEqual(true);
expect(layer3.isBaseLayer()).toEqual(false);
collection.lower(layer1);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
});
it("tracks the base layer on raiseToTop to lowerToBottom", function () {
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
var layer3 = new ImageryLayer(fakeProvider);
var collection = new ImageryLayerCollection();
collection.add(layer1);
collection.add(layer2);
collection.add(layer3);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
collection.raiseToTop(layer1);
expect(layer1.isBaseLayer()).toEqual(false);
expect(layer2.isBaseLayer()).toEqual(true);
expect(layer3.isBaseLayer()).toEqual(false);
collection.lowerToBottom(layer1);
expect(layer1.isBaseLayer()).toEqual(true);
expect(layer2.isBaseLayer()).toEqual(false);
expect(layer3.isBaseLayer()).toEqual(false);
});
it("add throws when layer is undefined", function () {
var collection = new ImageryLayerCollection();
expect(function () {
collection.add(undefined);
}).toThrowDeveloperError();
});
it("addImageryProvider throws when imageryProvider is undefined", function () {
var collection = new ImageryLayerCollection();
expect(function () {
collection.addImageryProvider(undefined);
}).toThrowDeveloperError();
});
it("add throws when index is outside valid range", function () {
var collection = new ImageryLayerCollection();
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
expect(function () {
collection.add(layer1, 1);
}).toThrowDeveloperError();
expect(function () {
collection.add(layer1, -1);
}).toThrowDeveloperError();
collection.add(layer1, 0);
expect(function () {
collection.add(layer2, -1);
}).toThrowDeveloperError();
expect(function () {
collection.add(layer2, 2);
}).toThrowDeveloperError();
collection.add(layer2, 0);
});
it("remove ignores request to remove a layer that does not exist in the collection", function () {
var collection = new ImageryLayerCollection();
var layer1 = new ImageryLayer(fakeProvider);
expect(collection.remove(layer1)).toBe(false);
});
it("contains works as expected", function () {
var collection = new ImageryLayerCollection();
var layer1 = new ImageryLayer(fakeProvider);
var layer2 = new ImageryLayer(fakeProvider);
expect(collection.contains(layer1)).toEqual(false);
expect(collection.contains(layer2)).toEqual(false);
collection.add(layer1);
expect(collection.contains(layer1)).toEqual(true);
expect(collection.contains(layer2)).toEqual(false);
collection.add(layer2);
expect(collection.contains(layer1)).toEqual(true);
expect(collection.contains(layer2)).toEqual(true);
collection.remove(layer1);
expect(collection.contains(layer1)).toEqual(false);
expect(collection.contains(layer2)).toEqual(true);
collection.remove(layer2);
expect(collection.contains(layer1)).toEqual(false);
expect(collection.contains(layer2)).toEqual(false);
});
it("get throws if index is not provided", function () {
var collection = new ImageryLayerCollection();
expect(function () {
collection.get();
}).toThrowDeveloperError();
});
it("throws when raising an undefined layer", function () {
var collection = new ImageryLayerCollection();
expect(function () {
collection.raise(undefined);
}).toThrowDeveloperError();
});
it("throws when raising a layer not in the collection", function () {
var collection = new ImageryLayerCollection();
var layer1 = new ImageryLayer(fakeProvider);
expect(function () {
collection.raise(layer1);
}).toThrowDeveloperError();
});
it("reports whether or not it is destroyed", function () {
var collection = new ImageryLayerCollection();
expect(collection.isDestroyed()).toEqual(false);
collection.destroy();
expect(collection.isDestroyed()).toEqual(true);
});
describe("pickImageryLayerFeatures", function () {
var scene;
var globe;
var camera;
beforeAll(function () {
scene = createScene();
globe = scene.globe = new Globe();
camera = scene.camera;
scene.frameState.passes.render = true;
});
afterAll(function () {
scene.destroyForSpecs();
});
beforeEach(function () {
globe.imageryLayers.removeAll();
});
/**
* Repeatedly calls update until the load queue is empty. Returns a promise that resolves
* once the load queue is empty.
*/
function updateUntilDone(globe) {
// update until the load queue is empty.
return pollToPromise(function () {
globe._surface._debug.enableDebugOutput = true;
scene.render();
return (
globe._surface.tileProvider.ready &&
globe._surface._tileLoadQueueHigh.length === 0 &&
globe._surface._tileLoadQueueMedium.length === 0 &&
globe._surface._tileLoadQueueLow.length === 0 &&
globe._surface._debug.tilesWaitingForChildren === 0
);
});
}
it("returns undefined when pick ray does not intersect surface", function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
var ray = new Ray(
camera.position,
Cartesian3.negate(camera.direction, new Cartesian3())
);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeUndefined();
});
it("returns undefined when globe has no pickable layers", function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeUndefined();
});
it("returns undefined when ImageryProvider does not implement pickFeatures", function () {
var provider = {
ready: true,
rectangle: Rectangle.MAX_VALUE,
tileWidth: 256,
tileHeight: 256,
maximumLevel: 0,
minimumLevel: 0,
tilingScheme: new GeographicTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
requestImage: function (x, y, level) {
return ImageryProvider.loadImage(this, "Data/Images/Blue.png");
},
};
globe.imageryLayers.addImageryProvider(provider);
return updateUntilDone(globe).then(function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeUndefined();
});
});
it("returns undefined when ImageryProvider.pickFeatures returns undefined", function () {
var provider = {
ready: true,
rectangle: Rectangle.MAX_VALUE,
tileWidth: 256,
tileHeight: 256,
maximumLevel: 0,
minimumLevel: 0,
tilingScheme: new GeographicTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
pickFeatures: function (x, y, level, longitude, latitude) {
return undefined;
},
requestImage: function (x, y, level) {
return ImageryProvider.loadImage(this, "Data/Images/Blue.png");
},
};
globe.imageryLayers.addImageryProvider(provider);
return updateUntilDone(globe).then(function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeUndefined();
});
});
it("returns features from one layer", function () {
var provider = {
ready: true,
rectangle: Rectangle.MAX_VALUE,
tileWidth: 256,
tileHeight: 256,
maximumLevel: 0,
minimumLevel: 0,
tilingScheme: new GeographicTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
pickFeatures: function (x, y, level, longitude, latitude) {
var deferred = when.defer();
setTimeout(function () {
var featureInfo = new ImageryLayerFeatureInfo();
featureInfo.name = "Foo";
featureInfo.description = "Foo!";
deferred.resolve([featureInfo]);
}, 1);
return deferred.promise;
},
requestImage: function (x, y, level) {
return ImageryProvider.loadImage(this, "Data/Images/Blue.png");
},
};
var currentLayer = globe.imageryLayers.addImageryProvider(provider);
return updateUntilDone(globe).then(function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
camera.lookAtTransform(Matrix4.IDENTITY);
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeDefined();
return featuresPromise.then(function (features) {
expect(features.length).toBe(1);
expect(features[0].name).toEqual("Foo");
expect(features[0].description).toContain("Foo!");
expect(features[0].imageryLayer).toBe(currentLayer);
});
});
});
it("returns features from two layers", function () {
var provider1 = {
ready: true,
rectangle: Rectangle.MAX_VALUE,
tileWidth: 256,
tileHeight: 256,
maximumLevel: 0,
minimumLevel: 0,
tilingScheme: new GeographicTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
pickFeatures: function (x, y, level, longitude, latitude) {
var deferred = when.defer();
setTimeout(function () {
var featureInfo = new ImageryLayerFeatureInfo();
featureInfo.name = "Foo";
featureInfo.description = "Foo!";
deferred.resolve([featureInfo]);
}, 1);
return deferred.promise;
},
requestImage: function (x, y, level) {
return ImageryProvider.loadImage(this, "Data/Images/Blue.png");
},
};
var currentLayer1 = globe.imageryLayers.addImageryProvider(provider1);
var provider2 = {
ready: true,
rectangle: Rectangle.MAX_VALUE,
tileWidth: 256,
tileHeight: 256,
maximumLevel: 0,
minimumLevel: 0,
tilingScheme: new GeographicTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
pickFeatures: function (x, y, level, longitude, latitude) {
var deferred = when.defer();
setTimeout(function () {
var featureInfo = new ImageryLayerFeatureInfo();
featureInfo.name = "Bar";
featureInfo.description = "Bar!";
deferred.resolve([featureInfo]);
}, 1);
return deferred.promise;
},
requestImage: function (x, y, level) {
return ImageryProvider.loadImage(this, "Data/Images/Green.png");
},
};
var currentLayer2 = globe.imageryLayers.addImageryProvider(provider2);
return updateUntilDone(globe).then(function () {
var ellipsoid = Ellipsoid.WGS84;
camera.lookAt(
new Cartesian3(ellipsoid.maximumRadius, 0.0, 0.0),
new Cartesian3(0.0, 0.0, 100.0)
);
camera.lookAtTransform(Matrix4.IDENTITY);
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeDefined();
return featuresPromise.then(function (features) {
expect(features.length).toBe(2);
expect(features[0].name).toEqual("Bar");
expect(features[0].description).toContain("Bar!");
expect(features[0].imageryLayer).toBe(currentLayer2);
expect(features[1].name).toEqual("Foo");
expect(features[1].description).toContain("Foo!");
expect(features[1].imageryLayer).toBe(currentLayer1);
});
});
});
it("correctly picks from a terrain tile that is partially covered by correct-level imagery and partially covered by imagery from an ancestor level", function () {
var provider = {
ready: true,
rectangle: new Rectangle(
-Math.PI,
-WebMercatorProjection.MaximumLatitude,
Math.PI,
WebMercatorProjection.MaximumLatitude
),
tileWidth: 256,
tileHeight: 256,
maximumLevel: 1,
minimumLevel: 1,
tilingScheme: new WebMercatorTilingScheme(),
errorEvent: new Event(),
hasAlphaChannel: true,
pickFeatures: function (x, y, level, longitude, latitude) {
var deferred = when.defer();
setTimeout(function () {
var featureInfo = new ImageryLayerFeatureInfo();
featureInfo.name = "L" + level + "X" + x + "Y" + y;
deferred.resolve([featureInfo]);
}, 1);
return deferred.promise;
},
requestImage: function (x, y, level) {
// At level 1, only the northwest quadrant has a valid tile.
if (level !== 1 || (x === 0 && y === 0)) {
return ImageryProvider.loadImage(this, "Data/Images/Blue.png");
}
return when.reject();
},
};
globe.imageryLayers.addImageryProvider(provider);
camera.setView({
destination: Rectangle.fromDegrees(-180.0, 0, 0, 90),
});
return updateUntilDone(globe).then(function () {
var ray = new Ray(camera.position, camera.direction);
var featuresPromise = scene.imageryLayers.pickImageryLayerFeatures(
ray,
scene
);
expect(featuresPromise).toBeDefined();
return featuresPromise.then(function (features) {
// Verify that we don't end up picking from imagery level 0.
expect(features.length).toBe(1);
expect(features[0].name).toEqual("L1X0Y0");
});
});
});
});
},
"WebGL"
);