You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Cesium-Prequel/Specs/Core/GoogleEarthEnterpriseTerrai...

564 lines
18 KiB
JavaScript

import { Cartesian3 } from "../../Source/Cesium.js";
import { Cartographic } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { GeographicTilingScheme } from "../../Source/Cesium.js";
import { GoogleEarthEnterpriseTerrainData } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { TerrainData } from "../../Source/Cesium.js";
import { TerrainMesh } from "../../Source/Cesium.js";
import { when } from "../../Source/Cesium.js";
describe("Core/GoogleEarthEnterpriseTerrainData", function () {
var sizeOfUint8 = Uint8Array.BYTES_PER_ELEMENT;
var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT;
var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT;
var toEarthRadii = 1.0 / 6371010.0;
function getBuffer(tilingScheme, x, y, level) {
var rectangle = tilingScheme.tileXYToRectangle(x, y, level);
var center = Rectangle.center(rectangle);
var southwest = Rectangle.southwest(rectangle);
var stepX = CesiumMath.toDegrees(rectangle.width / 2) / 180.0;
var stepY = CesiumMath.toDegrees(rectangle.height / 2) / 180.0;
// 2 Uint8s: x and y values in units of step
var pointSize = 2 * sizeOfUint8 + sizeOfFloat;
// 3 shorts
var faceSize = 3 * sizeOfUint16;
// Doubles: OriginX, OriginY, SizeX, SizeY
// Int32s: numPoints, numFaces, level
// 4 corner points
// 2 face (3 shorts)
var quadSize =
4 * sizeOfDouble + 3 * sizeOfInt32 + 4 * pointSize + 2 * faceSize;
// QuadSize + size of each quad
var totalSize = 4 * (quadSize + sizeOfUint32);
var buf = new ArrayBuffer(totalSize);
var dv = new DataView(buf);
var altitudeStart = 0;
var offset = 0;
for (var i = 0; i < 4; ++i) {
altitudeStart = 0;
dv.setUint32(offset, quadSize, true);
offset += sizeOfUint32;
// Origin
var xOrigin = southwest.longitude;
var yOrigin = southwest.latitude;
if ((i & 2) !== 0) {
// Top row
if ((i & 1) === 0) {
// NE
xOrigin = center.longitude;
altitudeStart = 10;
}
yOrigin = center.latitude;
} else if ((i & 1) !== 0) {
// SE
xOrigin = center.longitude;
altitudeStart = 10;
}
dv.setFloat64(offset, CesiumMath.toDegrees(xOrigin) / 180.0, true);
offset += sizeOfDouble;
dv.setFloat64(offset, CesiumMath.toDegrees(yOrigin) / 180.0, true);
offset += sizeOfDouble;
// Step - Each step is a degree
dv.setFloat64(offset, stepX, true);
offset += sizeOfDouble;
dv.setFloat64(offset, stepY, true);
offset += sizeOfDouble;
// NumPoints
dv.setInt32(offset, 4, true);
offset += sizeOfInt32;
// NumFaces
dv.setInt32(offset, 2, true);
offset += sizeOfInt32;
// Level
dv.setInt32(offset, 0, true);
offset += sizeOfInt32;
// Points
var j;
for (j = 0; j < 4; ++j) {
var xPos = 0;
var yPos = 0;
var altitude = altitudeStart;
if (j & 1) {
++xPos;
altitude += 10;
}
if (j & 2) {
++yPos;
}
dv.setUint8(offset++, xPos);
dv.setUint8(offset++, yPos);
dv.setFloat32(offset, altitude * toEarthRadii, true);
offset += sizeOfFloat;
}
// Faces
var indices = [0, 1, 2, 1, 3, 2];
for (j = 0; j < indices.length; ++j) {
dv.setUint16(offset, indices[j], true);
offset += sizeOfUint16;
}
}
return buf;
}
it("conforms to TerrainData interface", function () {
expect(GoogleEarthEnterpriseTerrainData).toConformToInterface(TerrainData);
});
describe("upsample", function () {
it("works for all four children of a simple quad", function () {
var maxShort = 32767;
var tilingScheme = new GeographicTilingScheme();
var buffer = getBuffer(tilingScheme, 0, 0, 0);
var data = new GoogleEarthEnterpriseTerrainData({
buffer: buffer,
childTileMask: 15,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
tilingScheme = new GeographicTilingScheme();
var childRectangles = [
tilingScheme.tileXYToRectangle(0, 0, 1),
tilingScheme.tileXYToRectangle(1, 0, 1),
tilingScheme.tileXYToRectangle(0, 1, 1),
tilingScheme.tileXYToRectangle(1, 1, 1),
];
return when(
data.createMesh({
tilingScheme: tilingScheme,
x: 0,
y: 0,
level: 0,
})
)
.then(function () {
var swPromise = data.upsample(tilingScheme, 0, 0, 0, 0, 0, 1);
var sePromise = data.upsample(tilingScheme, 0, 0, 0, 1, 0, 1);
var nwPromise = data.upsample(tilingScheme, 0, 0, 0, 0, 1, 1);
var nePromise = data.upsample(tilingScheme, 0, 0, 0, 1, 1, 1);
return when.join(swPromise, sePromise, nwPromise, nePromise);
})
.then(function (upsampleResults) {
expect(upsampleResults.length).toBe(4);
for (var i = 0; i < upsampleResults.length; ++i) {
var upsampled = upsampleResults[i];
expect(upsampled).toBeDefined();
var uBuffer = upsampled._uValues;
var vBuffer = upsampled._vValues;
var ib = upsampled._indices;
var heights = upsampled._heightValues;
expect(uBuffer.length).toBe(4);
expect(vBuffer.length).toBe(4);
expect(heights.length).toBe(4);
expect(ib.length).toBe(6);
var rectangle = childRectangles[i];
var north = 0;
var south = 0;
var east = 0;
var west = 0;
var index, u, v;
for (var j = 0; j < ib.length; ++j) {
index = ib[j];
u =
(uBuffer[index] / maxShort) * rectangle.width + rectangle.west;
v =
(vBuffer[index] / maxShort) * rectangle.height +
rectangle.south;
if (
CesiumMath.equalsEpsilon(u, rectangle.west, CesiumMath.EPSILON7)
) {
++west;
} else if (
CesiumMath.equalsEpsilon(u, rectangle.east, CesiumMath.EPSILON7)
) {
++east;
}
if (
CesiumMath.equalsEpsilon(
v,
rectangle.south,
CesiumMath.EPSILON7
)
) {
++south;
} else if (
CesiumMath.equalsEpsilon(
v,
rectangle.north,
CesiumMath.EPSILON7
)
) {
++north;
}
}
// Each one is made up of 2 triangles
expect(north).toEqual(3);
expect(south).toEqual(3);
expect(east).toEqual(3);
expect(west).toEqual(3);
// Each side of quad has 2 edge points
expect(upsampled._westIndices.length).toEqual(2);
expect(upsampled._southIndices.length).toEqual(2);
expect(upsampled._eastIndices.length).toEqual(2);
expect(upsampled._northIndices.length).toEqual(2);
}
});
});
});
describe("createMesh", function () {
var data;
var tilingScheme;
var buffer;
beforeEach(function () {
tilingScheme = new GeographicTilingScheme();
buffer = getBuffer(tilingScheme, 0, 0, 0);
data = new GoogleEarthEnterpriseTerrainData({
buffer: buffer,
childTileMask: 15,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
});
it("requires tilingScheme", function () {
expect(function () {
data.createMesh({
tilingScheme: undefined,
x: 0,
y: 0,
level: 0,
});
}).toThrowDeveloperError();
});
it("requires x", function () {
expect(function () {
data.createMesh({
tilingScheme: tilingScheme,
x: undefined,
y: 0,
level: 0,
});
}).toThrowDeveloperError();
});
it("requires y", function () {
expect(function () {
data.createMesh({
tilingScheme: tilingScheme,
x: 0,
y: undefined,
level: 0,
});
}).toThrowDeveloperError();
});
it("requires level", function () {
expect(function () {
data.createMesh({
tilingScheme: tilingScheme,
x: 0,
y: 0,
level: undefined,
});
}).toThrowDeveloperError();
});
it("creates specified vertices plus skirt vertices", function () {
var rectangle = tilingScheme.tileXYToRectangle(0, 0, 0);
var wgs84 = Ellipsoid.WGS84;
return data
.createMesh({
tilingScheme: tilingScheme,
x: 0,
y: 0,
level: 0,
})
.then(function (mesh) {
expect(mesh).toBeInstanceOf(TerrainMesh);
expect(mesh.vertices.length).toBe(17 * mesh.encoding.getStride()); // 9 regular + 8 skirt vertices
expect(mesh.indices.length).toBe(4 * 6 * 3); // 2 regular + 4 skirt triangles per quad
expect(mesh.minimumHeight).toBe(0);
expect(mesh.maximumHeight).toBeCloseTo(20, 5);
var encoding = mesh.encoding;
var cartesian = new Cartesian3();
var cartographic = new Cartographic();
var count = mesh.vertices.length / mesh.encoding.getStride();
for (var i = 0; i < count; ++i) {
var height = encoding.decodeHeight(mesh.vertices, i);
if (i < 9) {
// Original vertices
expect(height).toBeBetween(0, 20);
// Only test on original positions as the skirts angle outward
encoding.decodePosition(mesh.vertices, i, cartesian);
wgs84.cartesianToCartographic(cartesian, cartographic);
cartographic.longitude = CesiumMath.convertLongitudeRange(
cartographic.longitude
);
expect(Rectangle.contains(rectangle, cartographic)).toBe(true);
} else {
// Skirts
expect(height).toBeBetween(-1000, -980);
}
}
});
});
it("exaggerates mesh", function () {
return data
.createMesh({
tilingScheme: tilingScheme,
x: 0,
y: 0,
level: 0,
exaggeration: 2,
})
.then(function (mesh) {
expect(mesh).toBeInstanceOf(TerrainMesh);
expect(mesh.vertices.length).toBe(17 * mesh.encoding.getStride()); // 9 regular + 8 skirt vertices
expect(mesh.indices.length).toBe(4 * 6 * 3); // 2 regular + 4 skirt triangles per quad
expect(mesh.minimumHeight).toBe(0);
expect(mesh.maximumHeight).toBeCloseTo(40, 5);
var encoding = mesh.encoding;
var count = mesh.vertices.length / mesh.encoding.getStride();
for (var i = 0; i < count; ++i) {
var height = encoding.decodeHeight(mesh.vertices, i);
if (i < 9) {
// Original vertices
expect(height).toBeBetween(0, 40);
} else {
// Skirts
expect(height).toBeBetween(-1000, -960);
}
}
});
});
});
describe("interpolateHeight", function () {
var tilingScheme;
var rectangle;
var mesh;
beforeEach(function () {
tilingScheme = new GeographicTilingScheme();
rectangle = tilingScheme.tileXYToRectangle(7, 6, 5);
var buffer = getBuffer(tilingScheme, 7, 6, 5);
mesh = new GoogleEarthEnterpriseTerrainData({
buffer: buffer,
childTileMask: 15,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
});
it("clamps coordinates if given a position outside the mesh", function () {
expect(mesh.interpolateHeight(rectangle, 0.0, 0.0)).toBe(
mesh.interpolateHeight(rectangle, rectangle.east, rectangle.south)
);
});
it("returns a height interpolated from the correct triangle", function () {
// position in the northwest quadrant of the tile.
var longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.25;
var latitude =
rectangle.south + (rectangle.north - rectangle.south) * 0.75;
var result = mesh.interpolateHeight(rectangle, longitude, latitude);
expect(result).toBeBetween(0.0, 10.0);
// position in the southeast quadrant of the tile.
longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.75;
latitude = rectangle.south + (rectangle.north - rectangle.south) * 0.25;
result = mesh.interpolateHeight(rectangle, longitude, latitude);
expect(result).toBeBetween(10.0, 20.0);
// position on the line between the southwest and northeast corners.
longitude = rectangle.west + (rectangle.east - rectangle.west) * 0.5;
latitude = rectangle.south + (rectangle.north - rectangle.south) * 0.5;
result = mesh.interpolateHeight(rectangle, longitude, latitude);
expect(result).toEqualEpsilon(10.0, 1e-6);
});
});
describe("isChildAvailable", function () {
var data;
beforeEach(function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 15,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
});
it("requires thisX", function () {
expect(function () {
data.isChildAvailable(undefined, 0, 0, 0);
}).toThrowDeveloperError();
});
it("requires thisY", function () {
expect(function () {
data.isChildAvailable(0, undefined, 0, 0);
}).toThrowDeveloperError();
});
it("requires childX", function () {
expect(function () {
data.isChildAvailable(0, 0, undefined, 0);
}).toThrowDeveloperError();
});
it("requires childY", function () {
expect(function () {
data.isChildAvailable(0, 0, 0, undefined);
}).toThrowDeveloperError();
});
it("returns true for all children when child mask is not explicitly specified", function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
expect(data.isChildAvailable(10, 20, 20, 40)).toBe(true);
expect(data.isChildAvailable(10, 20, 21, 40)).toBe(true);
expect(data.isChildAvailable(10, 20, 20, 41)).toBe(true);
expect(data.isChildAvailable(10, 20, 21, 41)).toBe(true);
});
it("works when only southwest child is available", function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 1,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 20, 41)).toBe(true);
expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false);
});
it("works when only southeast child is available", function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 2,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 41)).toBe(true);
});
it("works when only northeast child is available", function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 4,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
expect(data.isChildAvailable(10, 20, 20, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 40)).toBe(true);
expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false);
});
it("works when only northwest child is available", function () {
data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 8,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
expect(data.isChildAvailable(10, 20, 20, 40)).toBe(true);
expect(data.isChildAvailable(10, 20, 21, 40)).toBe(false);
expect(data.isChildAvailable(10, 20, 20, 41)).toBe(false);
expect(data.isChildAvailable(10, 20, 21, 41)).toBe(false);
});
});
it("requires buffer", function () {
expect(function () {
/*eslint-disable no-unused-vars*/
var data = new GoogleEarthEnterpriseTerrainData({
childTileMask: 8,
negativeAltitudeExponentBias: 32,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
/*eslint-enable no-unused-vars*/
}).toThrowDeveloperError();
});
it("requires negativeAltitudeExponentBias", function () {
expect(function () {
/*eslint-disable no-unused-vars*/
var data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 8,
negativeElevationThreshold: CesiumMath.EPSILON12,
});
/*eslint-enable no-unused-vars*/
}).toThrowDeveloperError();
});
it("requires negativeElevationThreshold", function () {
expect(function () {
/*eslint-disable no-unused-vars*/
var data = new GoogleEarthEnterpriseTerrainData({
buffer: new ArrayBuffer(1),
childTileMask: 8,
negativeAltitudeExponentBias: 32,
});
/*eslint-enable no-unused-vars*/
}).toThrowDeveloperError();
});
});