Add existing to tracked
This commit is contained in:
+829
@@ -0,0 +1,829 @@
|
||||
import defined from "../Core/defined.js";
|
||||
import Cartesian2 from "../Core/Cartesian2.js";
|
||||
import Cartographic from "../Core/Cartographic.js";
|
||||
import destroyObject from "../Core/destroyObject.js";
|
||||
import DeveloperError from "../Core/DeveloperError.js";
|
||||
import Event from "../Core/Event.js";
|
||||
import GeographicProjection from "../Core/GeographicProjection.js";
|
||||
import CesiumMath from "../Core/Math.js";
|
||||
import Rectangle from "../Core/Rectangle.js";
|
||||
import ImageryLayer from "./ImageryLayer.js";
|
||||
import ImageryState from "./ImageryState.js";
|
||||
|
||||
/**
|
||||
* An ordered collection of imagery layers for rendering raster imagery on a {@link Globe} or {@link Cesium3DTileset}.
|
||||
*
|
||||
* @alias ImageryLayerCollection
|
||||
* @constructor
|
||||
* @see {@link Scene#imageryLayers} for manipulating imagery layers on the globe.
|
||||
* @see {@link Cesium3DTileset#imageryLayers} for manipulating imagery layers on a 3D tileset.
|
||||
* @demo {@link https://sandcastle.cesium.com/index.html?id=imagery-adjustment|Cesium Sandcastle Imagery Adjustment Demo}
|
||||
* @demo {@link https://sandcastle.cesium.com/index.html?id=imagery-layers-manipulation|Cesium Sandcastle Imagery Manipulation Demo}
|
||||
*/
|
||||
function ImageryLayerCollection() {
|
||||
this._layers = [];
|
||||
|
||||
/**
|
||||
* An event that is raised when a layer is added to the collection. Event handlers are passed the layer that
|
||||
* was added and the index at which it was added.
|
||||
* @type {Event}
|
||||
* @default Event()
|
||||
*/
|
||||
this.layerAdded = new Event();
|
||||
|
||||
/**
|
||||
* An event that is raised when a layer is removed from the collection. Event handlers are passed the layer that
|
||||
* was removed and the index from which it was removed.
|
||||
* @type {Event}
|
||||
* @default Event()
|
||||
*/
|
||||
this.layerRemoved = new Event();
|
||||
|
||||
/**
|
||||
* An event that is raised when a layer changes position in the collection. Event handlers are passed the layer that
|
||||
* was moved, its new index after the move, and its old index prior to the move.
|
||||
* @type {Event}
|
||||
* @default Event()
|
||||
*/
|
||||
this.layerMoved = new Event();
|
||||
|
||||
/**
|
||||
* An event that is raised when a layer is shown or hidden by setting the
|
||||
* {@link ImageryLayer#show} property. Event handlers are passed a reference to this layer,
|
||||
* the index of the layer in the collection, and a flag that is true if the layer is now
|
||||
* shown or false if it is now hidden.
|
||||
*
|
||||
* @type {Event}
|
||||
* @default Event()
|
||||
*/
|
||||
this.layerShownOrHidden = new Event();
|
||||
}
|
||||
|
||||
Object.defineProperties(ImageryLayerCollection.prototype, {
|
||||
/**
|
||||
* Gets the number of layers in this collection.
|
||||
* @memberof ImageryLayerCollection.prototype
|
||||
* @type {number}
|
||||
*/
|
||||
length: {
|
||||
get: function () {
|
||||
return this._layers.length;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Adds a layer to the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to add.
|
||||
* @param {number} [index] the index to add the layer at. If omitted, the layer will
|
||||
* be added on top of all existing layers.
|
||||
*
|
||||
* @exception {DeveloperError} index, if supplied, must be greater than or equal to zero and less than or equal to the number of the layers.
|
||||
*
|
||||
* @example
|
||||
* const imageryLayer = Cesium.ImageryLayer.fromWorldImagery();
|
||||
* scene.imageryLayers.add(imageryLayer);
|
||||
*
|
||||
* @example
|
||||
* const imageryLayer = Cesium.ImageryLayer.fromProviderAsync(Cesium.IonImageryProvider.fromAssetId(3812));
|
||||
* scene.imageryLayers.add(imageryLayer);
|
||||
*/
|
||||
ImageryLayerCollection.prototype.add = function (layer, index) {
|
||||
const hasIndex = defined(index);
|
||||
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
if (!defined(layer)) {
|
||||
throw new DeveloperError("layer is required.");
|
||||
}
|
||||
if (hasIndex) {
|
||||
if (index < 0) {
|
||||
throw new DeveloperError("index must be greater than or equal to zero.");
|
||||
} else if (index > this._layers.length) {
|
||||
throw new DeveloperError(
|
||||
"index must be less than or equal to the number of layers.",
|
||||
);
|
||||
}
|
||||
}
|
||||
//>>includeEnd('debug');
|
||||
|
||||
if (!hasIndex) {
|
||||
index = this._layers.length;
|
||||
this._layers.push(layer);
|
||||
} else {
|
||||
this._layers.splice(index, 0, layer);
|
||||
}
|
||||
|
||||
this._update();
|
||||
this.layerAdded.raiseEvent(layer, index);
|
||||
const removeReadyEventListener = layer.readyEvent.addEventListener(() => {
|
||||
this.layerShownOrHidden.raiseEvent(layer, layer._layerIndex, layer.show);
|
||||
removeReadyEventListener();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new layer using the given ImageryProvider and adds it to the collection.
|
||||
*
|
||||
* @param {ImageryProvider} imageryProvider the imagery provider to create a new layer for.
|
||||
* @param {number} [index] the index to add the layer at. If omitted, the layer will
|
||||
* added on top of all existing layers.
|
||||
* @returns {ImageryLayer} The newly created layer.
|
||||
*
|
||||
* @example
|
||||
* try {
|
||||
* const provider = await Cesium.IonImageryProvider.fromAssetId(3812);
|
||||
* scene.imageryLayers.addImageryProvider(provider);
|
||||
* } catch (error) {
|
||||
* console.log(`There was an error creating the imagery layer. ${error}`)
|
||||
* }
|
||||
*/
|
||||
ImageryLayerCollection.prototype.addImageryProvider = function (
|
||||
imageryProvider,
|
||||
index,
|
||||
) {
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
if (!defined(imageryProvider)) {
|
||||
throw new DeveloperError("imageryProvider is required.");
|
||||
}
|
||||
//>>includeEnd('debug');
|
||||
|
||||
const layer = new ImageryLayer(imageryProvider);
|
||||
this.add(layer, index);
|
||||
return layer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a layer from this collection, if present.
|
||||
*
|
||||
* @param {ImageryLayer} layer The layer to remove.
|
||||
* @param {boolean} [destroy=true] whether to destroy the layers in addition to removing them.
|
||||
* @returns {boolean} true if the layer was in the collection and was removed,
|
||||
* false if the layer was not in the collection.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.remove = function (layer, destroy) {
|
||||
destroy = destroy ?? true;
|
||||
|
||||
const index = this._layers.indexOf(layer);
|
||||
if (index !== -1) {
|
||||
this._layers.splice(index, 1);
|
||||
|
||||
this._update();
|
||||
|
||||
this.layerRemoved.raiseEvent(layer, index);
|
||||
|
||||
if (destroy) {
|
||||
layer.destroy();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all layers from this collection.
|
||||
*
|
||||
* @param {boolean} [destroy=true] whether to destroy the layers in addition to removing them.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.removeAll = function (destroy) {
|
||||
destroy = destroy ?? true;
|
||||
|
||||
const layers = this._layers;
|
||||
for (let i = 0, len = layers.length; i < len; i++) {
|
||||
const layer = layers[i];
|
||||
this.layerRemoved.raiseEvent(layer, i);
|
||||
|
||||
if (destroy) {
|
||||
layer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this._layers = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to see if the collection contains a given layer.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to check for.
|
||||
*
|
||||
* @returns {boolean} true if the collection contains the layer, false otherwise.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.contains = function (layer) {
|
||||
return this.indexOf(layer) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines the index of a given layer in the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer The layer to find the index of.
|
||||
*
|
||||
* @returns {number} The index of the layer in the collection, or -1 if the layer does not exist in the collection.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.indexOf = function (layer) {
|
||||
return this._layers.indexOf(layer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a layer by index from the collection.
|
||||
*
|
||||
* @param {number} index the index to retrieve.
|
||||
*
|
||||
* @returns {ImageryLayer} The imagery layer at the given index.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.get = function (index) {
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
if (!defined(index)) {
|
||||
throw new DeveloperError("index is required.", "index");
|
||||
}
|
||||
//>>includeEnd('debug');
|
||||
|
||||
return this._layers[index];
|
||||
};
|
||||
|
||||
function getLayerIndex(layers, layer) {
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
if (!defined(layer)) {
|
||||
throw new DeveloperError("layer is required.");
|
||||
}
|
||||
//>>includeEnd('debug');
|
||||
|
||||
const index = layers.indexOf(layer);
|
||||
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
if (index === -1) {
|
||||
throw new DeveloperError("layer is not in this collection.");
|
||||
}
|
||||
//>>includeEnd('debug');
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function swapLayers(collection, i, j) {
|
||||
const arr = collection._layers;
|
||||
i = CesiumMath.clamp(i, 0, arr.length - 1);
|
||||
j = CesiumMath.clamp(j, 0, arr.length - 1);
|
||||
|
||||
if (i === j) {
|
||||
return;
|
||||
}
|
||||
|
||||
const temp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = temp;
|
||||
|
||||
collection._update();
|
||||
|
||||
collection.layerMoved.raiseEvent(temp, j, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a layer up one position in the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to move.
|
||||
*
|
||||
* @exception {DeveloperError} layer is not in this collection.
|
||||
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.raise = function (layer) {
|
||||
const index = getLayerIndex(this._layers, layer);
|
||||
swapLayers(this, index, index + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lowers a layer down one position in the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to move.
|
||||
*
|
||||
* @exception {DeveloperError} layer is not in this collection.
|
||||
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.lower = function (layer) {
|
||||
const index = getLayerIndex(this._layers, layer);
|
||||
swapLayers(this, index, index - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Raises a layer to the top of the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to move.
|
||||
*
|
||||
* @exception {DeveloperError} layer is not in this collection.
|
||||
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.raiseToTop = function (layer) {
|
||||
const index = getLayerIndex(this._layers, layer);
|
||||
if (index === this._layers.length - 1) {
|
||||
return;
|
||||
}
|
||||
this._layers.splice(index, 1);
|
||||
this._layers.push(layer);
|
||||
|
||||
this._update();
|
||||
|
||||
this.layerMoved.raiseEvent(layer, this._layers.length - 1, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lowers a layer to the bottom of the collection.
|
||||
*
|
||||
* @param {ImageryLayer} layer the layer to move.
|
||||
*
|
||||
* @exception {DeveloperError} layer is not in this collection.
|
||||
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.lowerToBottom = function (layer) {
|
||||
const index = getLayerIndex(this._layers, layer);
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
this._layers.splice(index, 1);
|
||||
this._layers.splice(0, 0, layer);
|
||||
|
||||
this._update();
|
||||
|
||||
this.layerMoved.raiseEvent(layer, 0, index);
|
||||
};
|
||||
|
||||
const applicableRectangleScratch = new Rectangle();
|
||||
|
||||
function pickImageryHelper(scene, pickedLocation, pickFeatures, callback) {
|
||||
// Find the terrain tile containing the picked location.
|
||||
const tilesToRender = scene.globe._surface._tilesToRender;
|
||||
let pickedTile;
|
||||
|
||||
for (
|
||||
let textureIndex = 0;
|
||||
!defined(pickedTile) && textureIndex < tilesToRender.length;
|
||||
++textureIndex
|
||||
) {
|
||||
const tile = tilesToRender[textureIndex];
|
||||
if (Rectangle.contains(tile.rectangle, pickedLocation)) {
|
||||
pickedTile = tile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined(pickedTile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pick against all attached imagery tiles containing the pickedLocation.
|
||||
const imageryTiles = pickedTile.data.imagery;
|
||||
|
||||
for (let i = imageryTiles.length - 1; i >= 0; --i) {
|
||||
const terrainImagery = imageryTiles[i];
|
||||
const imagery = terrainImagery.readyImagery;
|
||||
if (!defined(imagery)) {
|
||||
continue;
|
||||
}
|
||||
if (!imagery.imageryLayer.ready) {
|
||||
continue;
|
||||
}
|
||||
const provider = imagery.imageryLayer.imageryProvider;
|
||||
if (pickFeatures && !defined(provider.pickFeatures)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Rectangle.contains(imagery.rectangle, pickedLocation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this imagery came from a parent, it may not be applicable to its entire rectangle.
|
||||
// Check the textureCoordinateRectangle.
|
||||
const applicableRectangle = applicableRectangleScratch;
|
||||
|
||||
const epsilon = 1 / 1024; // 1/4 of a pixel in a typical 256x256 tile.
|
||||
applicableRectangle.west = CesiumMath.lerp(
|
||||
pickedTile.rectangle.west,
|
||||
pickedTile.rectangle.east,
|
||||
terrainImagery.textureCoordinateRectangle.x - epsilon,
|
||||
);
|
||||
applicableRectangle.east = CesiumMath.lerp(
|
||||
pickedTile.rectangle.west,
|
||||
pickedTile.rectangle.east,
|
||||
terrainImagery.textureCoordinateRectangle.z + epsilon,
|
||||
);
|
||||
applicableRectangle.south = CesiumMath.lerp(
|
||||
pickedTile.rectangle.south,
|
||||
pickedTile.rectangle.north,
|
||||
terrainImagery.textureCoordinateRectangle.y - epsilon,
|
||||
);
|
||||
applicableRectangle.north = CesiumMath.lerp(
|
||||
pickedTile.rectangle.south,
|
||||
pickedTile.rectangle.north,
|
||||
terrainImagery.textureCoordinateRectangle.w + epsilon,
|
||||
);
|
||||
if (!Rectangle.contains(applicableRectangle, pickedLocation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
callback(imagery);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the imagery layers that are intersected by a pick ray. To compute a pick ray from a
|
||||
* location on the screen, use {@link Camera.getPickRay}.
|
||||
*
|
||||
* @param {Ray} ray The ray to test for intersection.
|
||||
* @param {Scene} scene The scene.
|
||||
* @return {ImageryLayer[]|undefined} An array that includes all of
|
||||
* the layers that are intersected by a given pick ray. Undefined if
|
||||
* no layers are selected.
|
||||
*
|
||||
*/
|
||||
ImageryLayerCollection.prototype.pickImageryLayers = function (ray, scene) {
|
||||
// Find the picked location on the globe.
|
||||
const pickedPosition = scene.globe.pick(ray, scene);
|
||||
if (!defined(pickedPosition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pickedLocation =
|
||||
scene.ellipsoid.cartesianToCartographic(pickedPosition);
|
||||
|
||||
const imageryLayers = [];
|
||||
|
||||
pickImageryHelper(scene, pickedLocation, false, function (imagery) {
|
||||
imageryLayers.push(imagery.imageryLayer);
|
||||
});
|
||||
|
||||
if (imageryLayers.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return imageryLayers;
|
||||
};
|
||||
|
||||
const nativeBoundingRectangleScratch = new Rectangle();
|
||||
const nativeApplicableRectangleScratch = new Rectangle();
|
||||
const southwestNativeScratch = new Cartesian2();
|
||||
const northeastNativeScratch = new Cartesian2();
|
||||
const southwestCartographicScratch = new Cartographic();
|
||||
const northeastCartographicScratch = new Cartographic();
|
||||
|
||||
/**
|
||||
* Maps a texture coordinate rectangle (UV space on the primitive) back to a
|
||||
* geographic rectangle, using the primitive cartographic bounds and the
|
||||
* imagery tiling scheme's native coordinates.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function computeApplicableGeographicRectangle(
|
||||
cartographicBoundingRectangle,
|
||||
textureCoordinateRectangle,
|
||||
imageryLayer,
|
||||
result,
|
||||
) {
|
||||
const tilingScheme = imageryLayer.imageryProvider.tilingScheme;
|
||||
const nativeBoundingRectangle = tilingScheme.rectangleToNativeRectangle(
|
||||
cartographicBoundingRectangle,
|
||||
nativeBoundingRectangleScratch,
|
||||
);
|
||||
|
||||
const epsilon = 1 / 1024; // 1/4 of a pixel in a typical 256x256 tile.
|
||||
const applicableNative = nativeApplicableRectangleScratch;
|
||||
applicableNative.west = CesiumMath.lerp(
|
||||
nativeBoundingRectangle.west,
|
||||
nativeBoundingRectangle.east,
|
||||
textureCoordinateRectangle.minX - epsilon,
|
||||
);
|
||||
applicableNative.east = CesiumMath.lerp(
|
||||
nativeBoundingRectangle.west,
|
||||
nativeBoundingRectangle.east,
|
||||
textureCoordinateRectangle.maxX + epsilon,
|
||||
);
|
||||
applicableNative.south = CesiumMath.lerp(
|
||||
nativeBoundingRectangle.south,
|
||||
nativeBoundingRectangle.north,
|
||||
textureCoordinateRectangle.minY - epsilon,
|
||||
);
|
||||
applicableNative.north = CesiumMath.lerp(
|
||||
nativeBoundingRectangle.south,
|
||||
nativeBoundingRectangle.north,
|
||||
textureCoordinateRectangle.maxY + epsilon,
|
||||
);
|
||||
|
||||
if (!defined(result)) {
|
||||
result = new Rectangle();
|
||||
}
|
||||
|
||||
const projection = tilingScheme.projection;
|
||||
if (projection instanceof GeographicProjection) {
|
||||
result.west = CesiumMath.toRadians(applicableNative.west);
|
||||
result.south = CesiumMath.toRadians(applicableNative.south);
|
||||
result.east = CesiumMath.toRadians(applicableNative.east);
|
||||
result.north = CesiumMath.toRadians(applicableNative.north);
|
||||
return result;
|
||||
}
|
||||
|
||||
southwestNativeScratch.x = applicableNative.west;
|
||||
southwestNativeScratch.y = applicableNative.south;
|
||||
northeastNativeScratch.x = applicableNative.east;
|
||||
northeastNativeScratch.y = applicableNative.north;
|
||||
|
||||
const southwest = projection.unproject(
|
||||
southwestNativeScratch,
|
||||
southwestCartographicScratch,
|
||||
);
|
||||
const northeast = projection.unproject(
|
||||
northeastNativeScratch,
|
||||
northeastCartographicScratch,
|
||||
);
|
||||
result.west = southwest.longitude;
|
||||
result.south = southwest.latitude;
|
||||
result.east = northeast.longitude;
|
||||
result.north = northeast.latitude;
|
||||
return result;
|
||||
}
|
||||
|
||||
function pickModelFeatures(model, pickedLocation, callback) {
|
||||
const modelImagery = model._modelImagery;
|
||||
|
||||
if (!defined(modelImagery)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modelPrimitiveImageries = modelImagery._modelPrimitiveImageries;
|
||||
|
||||
if (!defined(modelPrimitiveImageries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let j = 0; j < modelPrimitiveImageries.length; j++) {
|
||||
const modelPrimitiveImagery = modelPrimitiveImageries[j];
|
||||
const coveragesPerLayer = modelPrimitiveImagery._coveragesPerLayer;
|
||||
if (!defined(coveragesPerLayer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let k = coveragesPerLayer.length - 1; k >= 0; --k) {
|
||||
const coverages = coveragesPerLayer[k];
|
||||
|
||||
for (let l = 0; l < coverages.length; l++) {
|
||||
const coverage = coverages[l];
|
||||
const imagery = coverage.imagery;
|
||||
const imageryLayer = imagery.imageryLayer;
|
||||
const provider = imageryLayer.imageryProvider;
|
||||
|
||||
if (!defined(provider.pickFeatures)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!defined(imagery) || imagery.state !== ImageryState.READY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Rectangle.contains(imagery.rectangle, pickedLocation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mappedPositions =
|
||||
modelPrimitiveImagery.mappedPositionsForImageryLayer(imageryLayer);
|
||||
const cartographicBoundingRectangle =
|
||||
mappedPositions.cartographicBoundingRectangle;
|
||||
|
||||
const applicableRectangle = computeApplicableGeographicRectangle(
|
||||
cartographicBoundingRectangle,
|
||||
coverage.textureCoordinateRectangle,
|
||||
imageryLayer,
|
||||
applicableRectangleScratch,
|
||||
);
|
||||
|
||||
if (!Rectangle.contains(applicableRectangle, pickedLocation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
callback(imagery);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously determines the imagery layer features that are intersected by a pick ray. The intersected imagery
|
||||
* layer features are found by invoking {@link ImageryProvider#pickFeatures} for each imagery layer tile intersected
|
||||
* by the pick ray. To compute a pick ray from a location on the screen, use {@link Camera.getPickRay}.
|
||||
*
|
||||
* @param {Ray} ray The ray to test for intersection.
|
||||
* @param {Scene} scene The scene.
|
||||
* @return {Promise<ImageryLayerFeatureInfo[]>|undefined} A promise that resolves to an array of features intersected by the pick ray.
|
||||
* If it can be quickly determined that no features are intersected (for example,
|
||||
* because no active imagery providers support {@link ImageryProvider#pickFeatures}
|
||||
* or because the pick ray does not intersect the surface), this function will
|
||||
* return undefined.
|
||||
*
|
||||
* @example
|
||||
* const pickRay = viewer.camera.getPickRay(windowPosition);
|
||||
* const featuresPromise = viewer.imageryLayers.pickImageryLayerFeatures(pickRay, viewer.scene);
|
||||
* if (!Cesium.defined(featuresPromise)) {
|
||||
* console.log('No features picked.');
|
||||
* } else {
|
||||
* Promise.resolve(featuresPromise).then(function(features) {
|
||||
* // This function is called asynchronously when the list if picked features is available.
|
||||
* console.log(`Number of features: ${features.length}`);
|
||||
* if (features.length > 0) {
|
||||
* console.log(`First feature name: ${features[0].name}`);
|
||||
* }
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
ImageryLayerCollection.prototype.pickImageryLayerFeatures = function (
|
||||
ray,
|
||||
scene,
|
||||
) {
|
||||
// Find the picked location on the globe.
|
||||
let pickedPosition;
|
||||
|
||||
if (defined(scene.globe)) {
|
||||
pickedPosition = scene.globe.pick(ray, scene);
|
||||
}
|
||||
|
||||
let pickedModel;
|
||||
|
||||
if (!defined(pickedPosition)) {
|
||||
const result = scene.pickFromRay(ray);
|
||||
|
||||
if (!defined(result)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
pickedPosition = result.position;
|
||||
|
||||
if (!defined(pickedPosition)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (defined(result.object) && defined(result.object.detail)) {
|
||||
pickedModel = result.object.detail.model;
|
||||
}
|
||||
}
|
||||
|
||||
const pickedLocation =
|
||||
scene.ellipsoid.cartesianToCartographic(pickedPosition);
|
||||
|
||||
const promises = [];
|
||||
const imageryLayers = [];
|
||||
|
||||
function queuePickFeatures(imagery) {
|
||||
if (!imagery.imageryLayer.ready) {
|
||||
return undefined;
|
||||
}
|
||||
const provider = imagery.imageryLayer.imageryProvider;
|
||||
const promise = provider.pickFeatures(
|
||||
imagery.x,
|
||||
imagery.y,
|
||||
imagery.level,
|
||||
pickedLocation.longitude,
|
||||
pickedLocation.latitude,
|
||||
);
|
||||
if (defined(promise)) {
|
||||
promises.push(promise);
|
||||
imageryLayers.push(imagery.imageryLayer);
|
||||
}
|
||||
}
|
||||
|
||||
if (defined(scene.globe)) {
|
||||
pickImageryHelper(scene, pickedLocation, true, function (imagery) {
|
||||
queuePickFeatures(imagery);
|
||||
});
|
||||
}
|
||||
|
||||
if (defined(pickedModel)) {
|
||||
pickModelFeatures(pickedModel, pickedLocation, function (imagery) {
|
||||
queuePickFeatures(imagery);
|
||||
});
|
||||
}
|
||||
|
||||
if (promises.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(function (results) {
|
||||
const features = [];
|
||||
for (let resultIndex = 0; resultIndex < results.length; ++resultIndex) {
|
||||
const result = results[resultIndex];
|
||||
const image = imageryLayers[resultIndex];
|
||||
if (defined(result) && result.length > 0) {
|
||||
for (
|
||||
let featureIndex = 0;
|
||||
featureIndex < result.length;
|
||||
++featureIndex
|
||||
) {
|
||||
const feature = result[featureIndex];
|
||||
feature.imageryLayer = image;
|
||||
// For features without a position, use the picked location.
|
||||
if (!defined(feature.position)) {
|
||||
feature.position = pickedLocation;
|
||||
}
|
||||
features.push(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
return features;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates frame state to execute any queued texture re-projections.
|
||||
*
|
||||
* @ignore
|
||||
*
|
||||
* @param {FrameState} frameState The frameState.
|
||||
*/
|
||||
ImageryLayerCollection.prototype.queueReprojectionCommands = function (
|
||||
frameState,
|
||||
) {
|
||||
const layers = this._layers;
|
||||
for (let i = 0, len = layers.length; i < len; ++i) {
|
||||
layers[i].queueReprojectionCommands(frameState);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels re-projection commands queued for the next frame.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
ImageryLayerCollection.prototype.cancelReprojections = function () {
|
||||
const layers = this._layers;
|
||||
for (let i = 0, len = layers.length; i < len; ++i) {
|
||||
layers[i].cancelReprojections();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if this object was destroyed; otherwise, false.
|
||||
* <br /><br />
|
||||
* If this object was destroyed, it should not be used; calling any function other than
|
||||
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
|
||||
*
|
||||
* @returns {boolean} true if this object was destroyed; otherwise, false.
|
||||
*
|
||||
* @see ImageryLayerCollection#destroy
|
||||
*/
|
||||
ImageryLayerCollection.prototype.isDestroyed = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroys the WebGL resources held by all layers in this collection. Explicitly destroying this
|
||||
* object allows for deterministic release of WebGL resources, instead of relying on the garbage
|
||||
* collector.
|
||||
* <br /><br />
|
||||
* Once this object is destroyed, it should not be used; calling any function other than
|
||||
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
|
||||
* assign the return value (<code>undefined</code>) to the object as done in the example.
|
||||
*
|
||||
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* layerCollection = layerCollection && layerCollection.destroy();
|
||||
*
|
||||
* @see ImageryLayerCollection#isDestroyed
|
||||
*/
|
||||
ImageryLayerCollection.prototype.destroy = function () {
|
||||
this.removeAll(true);
|
||||
return destroyObject(this);
|
||||
};
|
||||
|
||||
ImageryLayerCollection.prototype._update = function () {
|
||||
let isBaseLayer = true;
|
||||
const layers = this._layers;
|
||||
let layersShownOrHidden;
|
||||
let layer;
|
||||
let i, len;
|
||||
for (i = 0, len = layers.length; i < len; ++i) {
|
||||
layer = layers[i];
|
||||
|
||||
layer._layerIndex = i;
|
||||
|
||||
if (layer.show) {
|
||||
layer._isBaseLayer = isBaseLayer;
|
||||
isBaseLayer = false;
|
||||
} else {
|
||||
layer._isBaseLayer = false;
|
||||
}
|
||||
|
||||
if (layer.show !== layer._show) {
|
||||
if (defined(layer._show)) {
|
||||
if (!defined(layersShownOrHidden)) {
|
||||
layersShownOrHidden = [];
|
||||
}
|
||||
layersShownOrHidden.push(layer);
|
||||
}
|
||||
layer._show = layer.show;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined(layersShownOrHidden)) {
|
||||
for (i = 0, len = layersShownOrHidden.length; i < len; ++i) {
|
||||
layer = layersShownOrHidden[i];
|
||||
this.layerShownOrHidden.raiseEvent(layer, layer._layerIndex, layer.show);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default ImageryLayerCollection;
|
||||
Reference in New Issue
Block a user