diff --git a/packages/engine/Source/Core/Rectangle.js b/packages/engine/Source/Core/Rectangle.js
index eedaa97df6c5..46e4a589cc7f 100644
--- a/packages/engine/Source/Core/Rectangle.js
+++ b/packages/engine/Source/Core/Rectangle.js
@@ -86,6 +86,42 @@ Object.defineProperties(Rectangle.prototype, {
*/
Rectangle.packedLength = 4;
+/**
+ * XXX_DRAPING
+ *
+ * Print the message and the rectangle for debugging, assuming that it stores values
+ * that are given in radians, and converting them to degrees for printing.
+ *
+ * Note that a rectangle may contain arbitrary values in arbitrary units (degrees, meters,
+ * furlongs, who knows). If the printed values do not make sense, try debugPrintDirectly.
+ *
+ * @param {string} message The message
+ * @param {Rectangle} rectangle The rectangle
+ */
+Rectangle.debugPrintRadiansAsDegrees = function (message, rectangle) {
+ console.log(message, " (converted from radians to degrees)");
+ console.log(" E ", CesiumMath.toDegrees(rectangle.east));
+ console.log(" S ", CesiumMath.toDegrees(rectangle.south));
+ console.log(" W ", CesiumMath.toDegrees(rectangle.west));
+ console.log(" N ", CesiumMath.toDegrees(rectangle.north));
+};
+/**
+ * XXX_DRAPING
+ *
+ * Print the message and the rectangle for debugging, directly, without assuming
+ * that the input is actually storing values in radians.
+ *
+ * @param {string} message The message
+ * @param {Rectangle} rectangle The rectangle
+ */
+Rectangle.debugPrintDirectly = function (message, rectangle) {
+ console.log(message, " (printed directly)");
+ console.log(" W ", rectangle.west);
+ console.log(" S ", rectangle.south);
+ console.log(" E ", rectangle.east);
+ console.log(" N ", rectangle.north);
+};
+
/**
* Stores the provided instance into the provided array.
*
diff --git a/packages/engine/Source/Scene/Model/ImageryCoverage.js b/packages/engine/Source/Scene/Model/ImageryCoverage.js
index 776371ba1a0e..a3f67bb47186 100644
--- a/packages/engine/Source/Scene/Model/ImageryCoverage.js
+++ b/packages/engine/Source/Scene/Model/ImageryCoverage.js
@@ -1,4 +1,5 @@
import defined from "../../Core/defined.js";
+import CesiumMath from "../../Core/Math.js";
import Rectangle from "../../Core/Rectangle.js";
import CartesianRectangle from "./CartesianRectangle.js";
@@ -7,7 +8,7 @@ const imageryBoundsScratch = new Rectangle();
const overlappedRectangleScratch = new Rectangle();
const clippedRectangleScratch = new Rectangle();
const nativeInputRectangleScratch = new Rectangle();
-const nativeImageryBoundsScratch = new Rectangle();
+//const nativeImageryBoundsScratch = new Rectangle(); XXX_DRAPING unused
const nativeClippedImageryBoundsScratch = new Rectangle();
/**
@@ -31,7 +32,8 @@ const nativeClippedImageryBoundsScratch = new Rectangle();
* ImageryLayer.prototype._createTileImagerySkeletons
* See https://github.com/CesiumGS/cesium/blob/5eaa2280f495d8f300d9e1f0497118c97aec54c8/packages/engine/Source/Scene/ImageryLayer.js#L700
* An instance of this class roughly corresponds to the TileImagery
- * that is created there.
+ * that is created there. Questions about the implementation here should
+ * be addressed to the original author.
*
* @private
*/
@@ -162,11 +164,15 @@ class ImageryCoverage {
inputRectangle,
nativeInputRectangle,
);
+
+ // XXX_DRAPING Was unused?
+ /*
const nativeImageryBounds = nativeImageryBoundsScratch;
imageryTilingScheme.rectangleToNativeRectangle(
imageryBounds,
nativeImageryBounds,
);
+ */
// A function that returns an imagery rectangle, based on (x, y, level),
// clipped to the imagery bounds (or undefined if there is no intersection
@@ -236,7 +242,12 @@ class ImageryCoverage {
* the X/Y coordinates of the imagery that is overlapped by the given
* input rectangle, based on the given imagery rectangle.
*
- * Extracted from _createTileImagerySkeletons.
+ * This was largely extracted from _createTileImagerySkeletons.
+ *
+ * Note that the resulting rectangle will always obey the `minX<=maxX`
+ * constraint, but `maxX` may be larger than the number of tiles along
+ * x in the given imagery level. The receiver is responsible for
+ * wrapping the results into the valid range.
*
* @param {Rectangle} inputRectangle The input rectangle
* @param {Rectangle} imageryBounds The imagery bounds
@@ -254,12 +265,22 @@ class ImageryCoverage {
inputRectangle,
imageryBounds,
);
+
+ // XXX_DRAPING: The wrapping that is happening here has to be
+ // verified. The "computeOverlappedRectangle" is doing some
+ // obscure "clamping" under certain conditions, as extracted
+ // from "_createTileImagerySkeletons", which may cause results
+ // that do not make sense here.
+ const nw = Rectangle.northwest(overlappedRectangle);
+ nw.longitude = CesiumMath.convertLongitudeRange(nw.longitude);
const northwestTileCoordinates = imageryTilingScheme.positionToTileXY(
- Rectangle.northwest(overlappedRectangle),
+ nw,
imageryLevel,
);
+ const se = Rectangle.southeast(overlappedRectangle);
+ se.longitude = CesiumMath.convertLongitudeRange(se.longitude);
const southeastTileCoordinates = imageryTilingScheme.positionToTileXY(
- Rectangle.southeast(overlappedRectangle),
+ se,
imageryLevel,
);
@@ -269,6 +290,7 @@ class ImageryCoverage {
result.maxX = southeastTileCoordinates.x;
result.maxY = southeastTileCoordinates.y;
+ /*/ XXX_DRAPING We have to get rid of this...
// As extracted from _createTileImagerySkeletons:
// If the southeast corner of the rectangle lies very close to the north or west side
// of the southeast tile, we don't actually need the southernmost or easternmost
@@ -314,6 +336,24 @@ class ImageryCoverage {
if (deltaEast < veryCloseX && result.maxX > result.minX) {
--result.maxX;
}
+ //*/
+
+ // For the case that the inputRectangle crosses the antimeridian,
+ // "west" (the westernmost side) may be 179° and "east" (the
+ // easternmost side) may be -179°.
+ // The "imageryTilingScheme.positionToTileXY" call returns coordinates
+ // that are wrapped into the valid range individually (!). This
+ // means that the tile x-coordinate for "west" (minX) may be larger
+ // than the tile x-coordinate for "east" (maxX). In this case,
+ // wrap the "maxX" around, based on the number of tiles along x.
+ if (result.minX > result.maxX) {
+ const numTilesX =
+ imageryTilingScheme.getNumberOfXTilesAtLevel(imageryLevel);
+ result.maxX += numTilesX;
+ }
+
+ // XXX_DRAPING
+ console.log("ImageryCoverage._computeImageryRange: result ", result);
return result;
}
@@ -383,6 +423,10 @@ class ImageryCoverage {
* the texture coordinates that are contained in the given range of
* imagery tile coordinates, referring to the given input rectangle.
*
+ * The given imageryRange may contain x-coordinates that are larger than the
+ * number of tiles along x for the given imagery level. This method will
+ * wrap the coordinates to be in a valid range.
+ *
* @param {ImageryLayer} imageryLayer The imagery layer
* @param {CartesianRectangle} imageryRange The range of imagery tile coordinates
* @param {number} imageryLevel The imagery level
@@ -402,11 +446,25 @@ class ImageryCoverage {
nativeInputRectangle,
computeClippedImageryRectangle,
) {
+ const imageryProvider = imageryLayer.imageryProvider;
+ const imageryTilingScheme = imageryProvider.tilingScheme;
+ const numTilesX =
+ imageryTilingScheme.getNumberOfXTilesAtLevel(imageryLevel);
+
const imageryCoverages = [];
- for (let i = imageryRange.minX; i <= imageryRange.maxX; i++) {
+ for (let rawX = imageryRange.minX; rawX <= imageryRange.maxX; rawX++) {
+ let x = rawX;
+ if (rawX >= numTilesX) {
+ x = rawX % numTilesX;
+ /// XXX_DRAPING Debug log
+ console.log(
+ `ImageryCoverage._computeImageryCoverages: Wrapping ${rawX} to ${x} for ${numTilesX} tiles on level ${imageryLevel}`,
+ );
+ }
+
const clippedImageryRectangleU = computeClippedImageryRectangle(
- i,
+ x,
imageryRange.maxY,
imageryLevel,
);
@@ -415,10 +473,10 @@ class ImageryCoverage {
continue;
}
- for (let j = imageryRange.minY; j <= imageryRange.maxY; j++) {
+ for (let y = imageryRange.minY; y <= imageryRange.maxY; y++) {
const clippedImageryRectangleV = computeClippedImageryRectangle(
- i,
- j,
+ x,
+ y,
imageryLevel,
);
@@ -433,16 +491,30 @@ class ImageryCoverage {
undefined,
);
+ // XXX_DRAPING Debug log...
+ Rectangle.debugPrintDirectly(
+ "ImageryCoverage._computeImageryCoverages: clippedImageryRectangleV",
+ clippedImageryRectangleV,
+ );
+ Rectangle.debugPrintDirectly(
+ "ImageryCoverage._computeImageryCoverages: nativeInputRectangle",
+ nativeInputRectangle,
+ );
+ console.log(
+ "ImageryCoverage._computeImageryCoverages: textureCoordinateRectangle",
+ textureCoordinateRectangle,
+ );
+
// Note: The getImageryFromCache function will create the whole "chain"
// of ancestor imageries, up to the root, and increases the reference
// counter for each of them, even though it is not called
// getImageryFromCacheAndCreateAllAncestorsAndAddReferences.
// There is currently no way to have a single imagery, because
// somewhere in TileImagery, the parent is assumed to be present.
- const imagery = imageryLayer.getImageryFromCache(i, j, imageryLevel);
+ const imagery = imageryLayer.getImageryFromCache(x, y, imageryLevel);
const imageryCoverage = new ImageryCoverage(
- i,
- j,
+ x,
+ y,
imageryLevel,
textureCoordinateRectangle,
imagery,
@@ -454,6 +526,10 @@ class ImageryCoverage {
}
/**
+ * XXX_DRAPING This function does not work for rectangles that have
+ * been converted to the "native" representation, because
+ * Rectangle.computeWidth is broken for these rectangles
+ *
* Compute the coordinates of the first rectangle relative to the
* second rectangle.
*
@@ -483,6 +559,96 @@ class ImageryCoverage {
result.maxY = (rectangleA.north - rectangleB.south) * invY;
return result;
}
+
+ /**
+ * XXX_DRAPING This function should replace _localizeToCartesianRectangle,
+ * but operates on Rectangles that are proper Cartographic rectangles,
+ * which is often not the case for the imagery-related computations that
+ * have been extracted from _createTileImagerySkeletons
+ *
+ * Compute the coordinates of the first rectangle relative to the
+ * second rectangle.
+ *
+ * The result will describe the bounds of the first rectangle
+ * in coordinates that are relative to the (west, south) and
+ * (width, height) of the second rectangle, wrapping the
+ * longitude at the antimeridian. This is suitable for
+ * describing the texture coordinates of the first
+ * rectangle within the second one.
+ *
+ * The result will be stored in the given result parameter, or
+ * in a new rectangle if the result was undefined.
+ *
+ * @param {Rectangle} rectangleA The first rectangle
+ * @param {Rectangle} rectangleB The second rectangle
+ * @param {CartesianRectangle} [result] The result
+ * @returns {CartesianRectangle} The result
+ */
+ static _localizeCartographicRectanglesToCartesianRectangle(
+ rectangleA,
+ rectangleB,
+ result,
+ ) {
+ if (!defined(result)) {
+ result = new CartesianRectangle();
+ }
+ const invX = 1.0 / rectangleB.width;
+ const invY = 1.0 / rectangleB.height;
+
+ const wa = CesiumMath.zeroToTwoPi(rectangleA.west);
+ const ea = CesiumMath.zeroToTwoPi(rectangleA.east);
+ const wb = CesiumMath.zeroToTwoPi(rectangleB.west);
+
+ const rawMinX = ImageryCoverage.wrappedDifference(
+ wa,
+ wb,
+ CesiumMath.TWO_PI,
+ );
+ const rawMaxX = ImageryCoverage.wrappedDifference(
+ ea,
+ wb,
+ CesiumMath.TWO_PI,
+ );
+
+ result.minX = rawMinX * invX;
+ result.minY = (rectangleA.south - rectangleB.south) * invY;
+ result.maxX = rawMaxX * invX;
+ result.maxY = (rectangleA.north - rectangleB.south) * invY;
+ return result;
+ }
+
+ /**
+ * Computes the difference between the given values, wrapped to
+ * the given wrapping value.
+ *
+ * The values will be brought into the range [0, wrap]. The
+ * result will be the signed (!) difference between both values,
+ * considering the wrapping of the values.
+ *
+ * For example:
+ * wrappedDifference(0.9, 0.7, 1.0) = 0.2
+ * wrappedDifference(0.7, 0.9, 1.0) = -0.2
+ * wrappedDifference(1.1, 0.9, 1.0) = 0.2
+ * wrappedDifference(0.9, 1.1, 1.0) = -0.2
+ *
+ * @param {number} a The first value
+ * @param {number} b The second value
+ * @param {number} wrap The wrapping value
+ * @returns The wrapped difference
+ */
+ static wrappedDifference(a, b, wrap) {
+ const wrappedA = (a %= wrap);
+ const wrappedB = (b %= wrap);
+ const diff = wrappedA - wrappedB;
+ const absDiff = Math.abs(diff);
+ if (absDiff < wrap - absDiff) {
+ return diff;
+ }
+ if (diff < 0) {
+ return diff + wrap;
+ }
+ return diff - wrap;
+ }
}
export default ImageryCoverage;
diff --git a/packages/engine/Source/Scene/Model/ImageryPipelineStage.js b/packages/engine/Source/Scene/Model/ImageryPipelineStage.js
index 29ee3baf16fd..e6114f9265be 100644
--- a/packages/engine/Source/Scene/Model/ImageryPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/ImageryPipelineStage.js
@@ -924,6 +924,9 @@ class ImageryPipelineStage {
}
/**
+ * XXX_DRAPING: This should essentially do the same as _localizeCartographicRectanglesToCartesianRectangle,
+ * but whether or not this should (or has to) operate on the so-called "native" rectangles has to be checked.
+ *
* Compute the translation and scale that has to be applied to
* the texture coordinates for mapping the given imagery to
* the geometry.
diff --git a/packages/engine/Source/Scene/Model/ModelImageryMapping.js b/packages/engine/Source/Scene/Model/ModelImageryMapping.js
index 6d053cf6982e..1ddd39a8f7da 100644
--- a/packages/engine/Source/Scene/Model/ModelImageryMapping.js
+++ b/packages/engine/Source/Scene/Model/ModelImageryMapping.js
@@ -1,4 +1,5 @@
import defined from "../../Core/defined.js";
+import CesiumMath from "../../Core/Math.js";
import Cartesian2 from "../../Core/Cartesian2.js";
import Cartesian3 from "../../Core/Cartesian3.js";
import Matrix4 from "../../Core/Matrix4.js";
@@ -12,6 +13,12 @@ import AttributeType from "../AttributeType.js";
import ModelReader from "./ModelReader.js";
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
+// Scratch variables for boundingRectangleFromRectangle
+const boundingRectangleFromRectangleScratchCartographicSW = new Cartographic();
+const boundingRectangleFromRectangleScratchCartographicNE = new Cartographic();
+const boundingRectangleFromRectangleScratchProjectedSW = new Cartesian3();
+const boundingRectangleFromRectangleScratchProjectedNE = new Cartesian3();
+
/**
* A class for computing the texture coordinates of imagery that is
* supposed to be mapped on a ModelComponents.Primitive.
@@ -91,16 +98,33 @@ class ModelImageryMapping {
// Convert the bounding `Rectangle`(!) of the cartographic positions
// into a `BoundingRectangle`(!) using the given projection
const boundingRectangle = new BoundingRectangle();
+ /*/
+ // XXX_DRAPING: BoundingRectangle.fromRectangle is broken, as
+ // it does not handle the antimeridian
BoundingRectangle.fromRectangle(
cartographicBoundingRectangle,
projection,
boundingRectangle,
);
+ //*/
+ ModelImageryMapping.boundingRectangleFromRectangle(
+ cartographicBoundingRectangle,
+ projection,
+ boundingRectangle,
+ );
+ console.log(
+ "ModelPrimitiveImagery._createTextureCoordinates: boundingRectangle with antimeridian wrapping",
+ boundingRectangle,
+ );
+
+ const wrapped =
+ cartographicBoundingRectangle.west > cartographicBoundingRectangle.east;
// Compute the projected positions, using the given projection
const projectedPositions = ModelImageryMapping.createProjectedPositions(
cartographicPositions,
projection,
+ wrapped,
);
// Relativize the projected positions into the bounding rectangle
@@ -120,6 +144,51 @@ class ModelImageryMapping {
return texCoordsTypedArray;
}
+ /**
+ * Computes a bounding rectangle from a rectangle.
+ *
+ * This is similar to BoundingRectangle.fromRectangle, but
+ * does not make assumptions about whether the input rectangle is
+ * crossing the antimeridian or not.
+ *
+ * @param {Rectangle} rectangle The valid rectangle used to create a bounding rectangle.
+ * @param {object} projection The projection used to project the rectangle into 2D.
+ * @param {BoundingRectangle} [result] The object onto which to store the result.
+ * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided.
+ */
+ static boundingRectangleFromRectangle(rectangle, projection, result) {
+ if (!defined(result)) {
+ result = new BoundingRectangle();
+ }
+ const sw = Rectangle.southwest(
+ rectangle,
+ boundingRectangleFromRectangleScratchCartographicSW,
+ );
+ const ne = Rectangle.northeast(
+ rectangle,
+ boundingRectangleFromRectangleScratchCartographicNE,
+ );
+ // XXX_DRAPING This is the only relevant difference to BoundingRectangle.fromRectangle,
+ // but subsequent processing steps may have to be adjusted to take this into account:
+ if (sw.longitude > ne.longitude) {
+ ne.longitude += CesiumMath.TWO_PI;
+ }
+ const projectedSW = projection.project(
+ sw,
+ boundingRectangleFromRectangleScratchProjectedSW,
+ );
+ const projectedNE = projection.project(
+ ne,
+ boundingRectangleFromRectangleScratchProjectedNE,
+ );
+
+ result.x = projectedSW.x;
+ result.y = projectedSW.y;
+ result.width = projectedNE.x - projectedSW.x;
+ result.height = projectedNE.y - projectedSW.y;
+ return result;
+ }
+
/**
* Creates the `ModelComponents.Attribute` for the texture coordinates
* for a primitive
@@ -272,6 +341,9 @@ class ModelImageryMapping {
* Computes the bounding rectangle of the given cartographic positions,
* stores it in the given result, and returns it.
*
+ * This is taken from Rectangle.fromCartographicArray, but
+ * operates on an iterable instead of an array.
+ *
* If the given result is `undefined`, a new rectangle will be created
* and returned.
*
@@ -287,17 +359,37 @@ class ModelImageryMapping {
if (!defined(result)) {
result = new Rectangle();
}
- // One could store these directly in the result, but that would
- // violate the constraint of the PI-related ranges..
let north = Number.NEGATIVE_INFINITY;
let south = Number.POSITIVE_INFINITY;
let east = Number.NEGATIVE_INFINITY;
let west = Number.POSITIVE_INFINITY;
+ let westOverIDL = Number.POSITIVE_INFINITY;
+ let eastOverIDL = Number.NEGATIVE_INFINITY;
+
for (const cartographicPosition of cartographicPositions) {
north = Math.max(north, cartographicPosition.latitude);
south = Math.min(south, cartographicPosition.latitude);
east = Math.max(east, cartographicPosition.longitude);
west = Math.min(west, cartographicPosition.longitude);
+
+ const lonAdjusted =
+ cartographicPosition.longitude >= 0
+ ? cartographicPosition.longitude
+ : cartographicPosition.longitude + CesiumMath.TWO_PI;
+ westOverIDL = Math.min(westOverIDL, lonAdjusted);
+ eastOverIDL = Math.max(eastOverIDL, lonAdjusted);
+ }
+
+ if (east - west > eastOverIDL - westOverIDL) {
+ west = westOverIDL;
+ east = eastOverIDL;
+
+ if (east > CesiumMath.PI) {
+ east = east - CesiumMath.TWO_PI;
+ }
+ if (west > CesiumMath.PI) {
+ west = west - CesiumMath.TWO_PI;
+ }
}
result.north = north;
result.south = south;
@@ -371,18 +463,27 @@ class ModelImageryMapping {
* @param {Iterable} cartographicPositions The cartographic
* positions
* @param {MapProjection} projection The projection to use
+ * @param {boolean} wrapped Whether the coordinates should be wrapped
+ * at the antimeridian. Negative longitude values will be brought
+ * into the positive range by adding 2*PI
* @returns {Iterable} The projected positions
*/
- static createProjectedPositions(cartographicPositions, projection) {
+ static createProjectedPositions(cartographicPositions, projection, wrapped) {
//>>includeStart('debug', pragmas.debug);
Check.defined("cartographicPositions", cartographicPositions);
Check.defined("projection", projection);
//>>includeEnd('debug');
+ const wrappedCartographic = new Cartographic();
const projectedPosition = new Cartesian3();
const projectedPositions = ModelImageryMapping.map(
cartographicPositions,
(c) => {
+ wrappedCartographic.latitude = c.latitude;
+ wrappedCartographic.longitude = c.longitude;
+ if (wrapped && wrappedCartographic.longitude < 0) {
+ wrappedCartographic.longitude += CesiumMath.TWO_PI;
+ }
projection.project(c, projectedPosition);
return projectedPosition;
},
diff --git a/packages/engine/Source/Scene/Model/ModelPrimitiveImagery.js b/packages/engine/Source/Scene/Model/ModelPrimitiveImagery.js
index 6f088609a9d9..4d447b85485d 100644
--- a/packages/engine/Source/Scene/Model/ModelPrimitiveImagery.js
+++ b/packages/engine/Source/Scene/Model/ModelPrimitiveImagery.js
@@ -707,10 +707,16 @@ class ModelPrimitiveImagery {
);
// Clamp the level to a valid range, and an integer value
- const imageryLevel = ImageryCoverage._clampImageryLevel(
+ let imageryLevel = ImageryCoverage._clampImageryLevel(
imageryProvider,
desiredLevel,
);
+ // XXX_DRAPING Using fixed imagery level for debugging
+ imageryLevel = -1; // Comment this out to use the fixed level
+ if (imageryLevel < 0) {
+ imageryLevel = 12;
+ console.log(`XXX_DRAPING: Using fixed imagery level ${imageryLevel}`);
+ }
return imageryLevel;
}
diff --git a/packages/engine/Specs/Scene/Model/ImageryCoverageSpec.js b/packages/engine/Specs/Scene/Model/ImageryCoverageSpec.js
new file mode 100644
index 000000000000..5dd6139b1332
--- /dev/null
+++ b/packages/engine/Specs/Scene/Model/ImageryCoverageSpec.js
@@ -0,0 +1,128 @@
+import {
+ Rectangle,
+ ImageryCoverage,
+ CartesianRectangle,
+ Math as CesiumMath,
+} from "../../../index.js";
+
+describe("Scene/Model/ImageryCoverage", function () {
+ it("_localizeCartographicRectanglesToCartesianRectangle returns unit rectangle for equal inputs", async function () {
+ const ra = Rectangle.fromDegrees(10, 10, 20, 20);
+ const rb = Rectangle.fromDegrees(10, 10, 20, 20);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(0, 0, 1, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle computes offset for overlapping inputs", async function () {
+ const ra = Rectangle.fromDegrees(15, 15, 25, 25);
+ const rb = Rectangle.fromDegrees(10, 10, 20, 20);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(0.5, 0.5, 1.5, 1.5);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle computes offset for non-overlapping inputs", async function () {
+ const ra = Rectangle.fromDegrees(30, 30, 50, 50);
+ const rb = Rectangle.fromDegrees(10, 10, 20, 20);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(2, 2, 4, 4);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner rectangle is left of antimeridian", async function () {
+ const ra = Rectangle.fromDegrees(160, 10, 170, 50);
+ const rb = Rectangle.fromDegrees(155, 10, -155, 50);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(0.1, 0, 0.3, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner rectangle is right of antimeridian", async function () {
+ const ra = Rectangle.fromDegrees(-175, 10, -165, 50);
+ const rb = Rectangle.fromDegrees(155, 10, -155, 50);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(0.6, 0, 0.8, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner rectangle crosses antimeridian", async function () {
+ const ra = Rectangle.fromDegrees(175, 10, -175, 50);
+ const rb = Rectangle.fromDegrees(155, 10, -155, 50);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(0.4, 0, 0.6, 1);
+
+ console.log(actual);
+
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner is left and outer is right of antimeridian", async function () {
+ const ra = Rectangle.fromDegrees(-175, 10, -165, 50);
+ const rb = Rectangle.fromDegrees(165, 10, 175, 50);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(2, 0, 3, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner is right and outer is left or antimeridian", async function () {
+ const ra = Rectangle.fromDegrees(165, 10, 175, 50);
+ const rb = Rectangle.fromDegrees(-175, 10, -165, 50);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(-2, 0, -1, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+
+ it("_localizeCartographicRectanglesToCartesianRectangle works when inner is left or meridian and outer is right of meridian", async function () {
+ const ra = Rectangle.fromDegrees(-20, 10, -10, 20);
+ const rb = Rectangle.fromDegrees(10, 10, 20, 20);
+ const actual = new CartesianRectangle();
+ const expected = new CartesianRectangle(-3, 0, -2, 1);
+ ImageryCoverage._localizeCartographicRectanglesToCartesianRectangle(
+ ra,
+ rb,
+ actual,
+ );
+ expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON8);
+ });
+});