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.
564 lines
18 KiB
JavaScript
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();
|
|
});
|
|
});
|