diff --git a/Apps/Sandcastle/gallery/Extruded Polygon on Terrain.html b/Apps/Sandcastle/gallery/Extruded Polygon on Terrain.html new file mode 100644 index 000000000000..0daead9e19d1 --- /dev/null +++ b/Apps/Sandcastle/gallery/Extruded Polygon on Terrain.html @@ -0,0 +1,68 @@ + + +
+ + + + + +true
if they are equal, false
otherwise.
+ *
+ * @param {Property} [other] The other property.
+ * @returns {Boolean} true
if left and right are equal, false
otherwise.
+ */
+ CentroidPositionProperty.prototype.equals = function(other) {
+ return this === other ||
+ (other instanceof CentroidPositionProperty &&
+ Cartesian3.equals(this._positions, other._positions));
+ };
+
+ return CentroidPositionProperty;
+});
diff --git a/Source/DataSources/MinimumTerrainHeightProperty.js b/Source/DataSources/MinimumTerrainHeightProperty.js
new file mode 100644
index 000000000000..bc17d8b61562
--- /dev/null
+++ b/Source/DataSources/MinimumTerrainHeightProperty.js
@@ -0,0 +1,120 @@
+define([
+ '../Core/ApproximateTerrainHeights',
+ '../Core/defined',
+ '../Core/defineProperties',
+ '../Core/isArray',
+ '../Core/Check',
+ '../Core/Event',
+ '../Core/Rectangle',
+ './createPropertyDescriptor',
+ './Property'
+], function(
+ ApproximateTerrainHeights,
+ defined,
+ defineProperties,
+ isArray,
+ Check,
+ Event,
+ Rectangle,
+ createPropertyDescriptor,
+ Property) {
+ 'use strict';
+
+ var scratchRectangle = new Rectangle();
+
+ /**
+ * A {@link Property} which evaluates to a Number based on the minimum height of terrain
+ * within the bounds of the provided positions.
+ *
+ * @alias MinimumTerrainHeightProperty
+ * @constructor
+ *
+ * @param {Property} [positions] A Property specifying an array of {@link Cartesian3} positions.
+ *
+ * @example
+ * var polygonPositions = new Cesium.ConstantProperty(polygonPositions);
+ * var redPolygon = viewer.entities.add({
+ * polygon : {
+ * hierarchy : polygonPositions,
+ * material : Cesium.Color.RED,
+ * height : 1800.0,
+ * extrudedHeight : new Cesium.MinimumTerrainHeightProperty(polygonPositions)
+ * }
+ * });
+ */
+ function MinimumTerrainHeightProperty(positions) {
+ this._positions = undefined;
+ this._positionsSubscription = undefined;
+ this._definitionChanged = new Event();
+
+ this.positions = positions;
+ }
+
+ defineProperties(MinimumTerrainHeightProperty.prototype, {
+ /**
+ * Gets a value indicating if this property is constant.
+ * @memberof MinimumTerrainHeightProperty.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ */
+ isConstant : {
+ get : function() {
+ return Property.isConstant(this._positions);
+ }
+ },
+ /**
+ * Gets the event that is raised whenever the definition of this property changes.
+ * @memberof MinimumTerrainHeightProperty.prototype
+ *
+ * @type {Event}
+ * @readonly
+ */
+ definitionChanged : {
+ get : function() {
+ return this._definitionChanged;
+ }
+ },
+ /**
+ * Gets or sets the positions property used to compute the value.
+ * @memberof MinimumTerrainHeightProperty.prototype
+ *
+ * @type {Property}
+ */
+ positions : createPropertyDescriptor('positions')
+ });
+
+ /**
+ * Gets the minimum terrain height based on the positions at the provided time.
+ *
+ * @param {JulianDate} time The time for which to retrieve the value.
+ * @returns {Number} The minimum terrain height
+ */
+ MinimumTerrainHeightProperty.prototype.getValue = function(time) {
+ //>>includeStart('debug', pragmas.debug);
+ Check.defined('time', time);
+ //>>includeEnd('debug');
+
+ var positions = Property.getValueOrUndefined(this._positions, time);
+ if (!defined(positions)) {
+ return;
+ }
+ var rectangle = Rectangle.fromCartesianArray(positions, undefined, scratchRectangle);
+ return ApproximateTerrainHeights.getApproximateTerrainHeights(rectangle).minimumTerrainHeight;
+ };
+
+ /**
+ * Compares this property to the provided property and returns
+ * true
if they are equal, false
otherwise.
+ *
+ * @param {Property} [other] The other property.
+ * @returns {Boolean} true
if left and right are equal, false
otherwise.
+ */
+ MinimumTerrainHeightProperty.prototype.equals = function(other) {
+ return this === other ||//
+ (other instanceof MinimumTerrainHeightProperty &&
+ Property.equals(this._positions, other._positions));
+ };
+
+ return MinimumTerrainHeightProperty;
+});
diff --git a/Source/DataSources/PositionProperty.js b/Source/DataSources/PositionProperty.js
index af5b00665e19..bffeacf488c0 100644
--- a/Source/DataSources/PositionProperty.js
+++ b/Source/DataSources/PositionProperty.js
@@ -105,7 +105,7 @@ define([
*/
PositionProperty.convertToReferenceFrame = function(time, value, inputFrame, outputFrame, result) {
if (!defined(value)) {
- return value;
+ return;
}
if (!defined(result)){
result = new Cartesian3();
diff --git a/Source/DataSources/RelativeToTerrainHeightProperty.js b/Source/DataSources/RelativeToTerrainHeightProperty.js
new file mode 100644
index 000000000000..e1b538079602
--- /dev/null
+++ b/Source/DataSources/RelativeToTerrainHeightProperty.js
@@ -0,0 +1,192 @@
+define([
+ '../Core/ApproximateTerrainHeights',
+ '../Core/defaultValue',
+ '../Core/defined',
+ '../Core/defineProperties',
+ '../Core/isArray',
+ '../Core/Cartesian3',
+ '../Core/Cartographic',
+ '../Core/Check',
+ '../Core/Event',
+ '../Core/Iso8601',
+ '../Core/Rectangle',
+ '../Core/RuntimeError',
+ '../Core/sampleTerrainMostDetailed',
+ './createPropertyDescriptor',
+ './Property'
+], function(
+ ApproximateTerrainHeights,
+ defaultValue,
+ defined,
+ defineProperties,
+ isArray,
+ Cartesian3,
+ Cartographic,
+ Check,
+ Event,
+ Iso8601,
+ Rectangle,
+ RuntimeError,
+ sampleTerrainMostDetailed,
+ createPropertyDescriptor,
+ Property) {
+ 'use strict';
+
+ /**
+ * A {@link Property} which evaluates to a Number based on the height of terrain
+ * within the bounds of the provided positions.
+ *
+ * @alias RelativeToTerrainHeightProperty
+ * @constructor
+ *
+ * @param {TerrainProvider} terrainProvider The terrain provider used on the globe
+ * @param {PositionProperty} position A Property specifying the position the height should be relative to.
+ * @param {Property} [heightRelativeToTerrain] A Property specifying the numeric height value relative to terrain
+ *
+ * @example
+ * var hierarchy = new Cesium.ConstantProperty(polygonPositions);
+ * var redPolygon = viewer.entities.add({
+ * ellipse : {
+ * hierarchy : hierarchy,
+ * material : Cesium.Color.RED,
+ * height : new Cesium.RelativeToTerrainHeightProperty(viewer.terrainProvider, positions, 11.0)
+ * }
+ * });
+ */
+ function RelativeToTerrainHeightProperty(terrainProvider, position, heightRelativeToTerrain) {
+ //>>includeStart('debug', pragmas.debug);
+ Check.defined('terrainProvider', terrainProvider);
+ //>>includeEnd('debug');
+
+ this._position = undefined;
+ this._subscription = undefined;
+ this._heightRelativeToTerrain = undefined;
+ this._heightRelativeToTerrainSubscription = undefined;
+ this._definitionChanged = new Event();
+
+ this._terrainProvider = terrainProvider;
+ this._cartographicPosition = new Cartographic();
+ this._terrainHeight = 0;
+ this._pending = false;
+
+ this.position = position;
+ this.heightRelativeToTerrain = heightRelativeToTerrain;
+ }
+
+ defineProperties(RelativeToTerrainHeightProperty.prototype, {
+ /**
+ * Gets a value indicating if this property is constant.
+ * @memberof RelativeToTerrainHeightProperty.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ */
+ isConstant : {
+ get : function() {
+ return Property.isConstant(this._positions);
+ }
+ },
+ /**
+ * Gets the event that is raised whenever the definition of this property changes.
+ * @memberof RelativeToTerrainHeightProperty.prototype
+ *
+ * @type {Event}
+ * @readonly
+ */
+ definitionChanged : {
+ get : function() {
+ return this._definitionChanged;
+ }
+ },
+ /**
+ * Gets or sets the position property used to compute the value.
+ * @memberof RelativeToTerrainHeightProperty.prototype
+ *
+ * @type {PositionProperty}
+ */
+ position : {
+ get : function() {
+ return this._position;
+ },
+ set : function(value) {
+ var oldValue = this._positions;
+ if (oldValue !== value) {
+ if (defined(oldValue)) {
+ this._subscription();
+ }
+
+ this._position = value;
+
+ if (defined(value)) {
+ this._subscription = value._definitionChanged.addEventListener(function() {
+ this._fetchTerrainHeight();
+ }, this);
+ }
+
+ this._fetchTerrainHeight();
+ }
+ }
+ },
+ heightRelativeToTerrain : createPropertyDescriptor('heightRelativeToTerrain')
+ });
+
+ /**
+ * @private
+ */
+ RelativeToTerrainHeightProperty.prototype._fetchTerrainHeight = function() {
+ if (this._pending) {
+ return;
+ }
+ this._terrainHeight = 0;
+ var property = this._position;
+ var position = Property.getValueOrUndefined(property, Iso8601.MINIMUM_VALUE);
+ if (!defined(position)) {
+ return;
+ }
+
+ var carto = Cartographic.fromCartesian(position, undefined, this._cartographicPosition);
+ carto.height = 0.0;
+ var that = this;
+ this._pending = true;
+ RelativeToTerrainHeightProperty._sampleTerrainMostDetailed(this._terrainProvider, [carto])
+ .then(function(results) {
+ if (that._position !== property || !Cartesian3.equals(position, Property.getValueOrUndefined(that._position, Iso8601.MINIMUM_VALUE))) {
+ return;
+ }
+ that._terrainHeight = defaultValue(results[0].height, 0);
+ that._definitionChanged.raiseEvent(that);
+ })
+ .always(function() {
+ that._pending = false;
+ });
+ };
+
+ /**
+ * Gets the height relative to the terrain based on the positions.
+ *
+ * @returns {Number} The height relative to terrain
+ */
+ RelativeToTerrainHeightProperty.prototype.getValue = function(time) {
+ return this._terrainHeight + Property.getValueOrDefault(this.heightRelativeToTerrain, time, 0);
+ };
+
+ /**
+ * Compares this property to the provided property and returns
+ * true
if they are equal, false
otherwise.
+ *
+ * @param {Property} [other] The other property.
+ * @returns {Boolean} true
if left and right are equal, false
otherwise.
+ */
+ RelativeToTerrainHeightProperty.prototype.equals = function(other) {
+ return this === other ||//
+ (other instanceof RelativeToTerrainHeightProperty &&
+ this._terrainProvider === other._terrainProvider &&
+ Property.equals(this._position, other._position) &&
+ Property.equals(this.heightRelativeToTerrain, other.heightRelativeToTerrain));
+ };
+
+ //for specs
+ RelativeToTerrainHeightProperty._sampleTerrainMostDetailed = sampleTerrainMostDetailed;
+
+ return RelativeToTerrainHeightProperty;
+});
diff --git a/Source/DataSources/VelocityOrientationProperty.js b/Source/DataSources/VelocityOrientationProperty.js
index 46c11685fc6b..842777d63ff2 100644
--- a/Source/DataSources/VelocityOrientationProperty.js
+++ b/Source/DataSources/VelocityOrientationProperty.js
@@ -123,7 +123,7 @@ define([
/**
* Gets the value of the property at the provided time.
*
- * @param {JulianDate} [time] The time for which to retrieve the value.
+ * @param {JulianDate} time The time for which to retrieve the value.
* @param {Quaternion} [result] The object to store the value into, if omitted, a new instance is created and returned.
* @returns {Quaternion} The modified result parameter or a new instance if the result parameter was not supplied.
*/
diff --git a/Source/DataSources/VelocityVectorProperty.js b/Source/DataSources/VelocityVectorProperty.js
index 96a6e14f9535..c1ef5fb81eaa 100644
--- a/Source/DataSources/VelocityVectorProperty.js
+++ b/Source/DataSources/VelocityVectorProperty.js
@@ -133,7 +133,7 @@ define([
/**
* Gets the value of the property at the provided time.
*
- * @param {JulianDate} [time] The time for which to retrieve the value.
+ * @param {JulianDate} time The time for which to retrieve the value.
* @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned.
* @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied.
*/
diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js
index c8a68f040822..40f94b847938 100644
--- a/Source/Scene/GroundPrimitive.js
+++ b/Source/Scene/GroundPrimitive.js
@@ -1,4 +1,5 @@
define([
+ '../Core/ApproximateTerrainHeights',
'../Core/BoundingSphere',
'../Core/buildModuleUrl',
'../Core/Cartesian2',
@@ -22,6 +23,7 @@ define([
'./ClassificationType',
'./SceneMode'
], function(
+ ApproximateTerrainHeights,
BoundingSphere,
buildModuleUrl,
Cartesian2,
@@ -207,8 +209,8 @@ define([
this._maxHeight = undefined;
this._minHeight = undefined;
- this._maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
- this._minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
+ this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
+ this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
this._boundingSpheresKeys = [];
this._boundingSpheres = [];
@@ -364,12 +366,6 @@ define([
*/
GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;
- GroundPrimitive._defaultMaxTerrainHeight = 9000.0;
- GroundPrimitive._defaultMinTerrainHeight = -100000.0;
-
- GroundPrimitive._terrainHeights = undefined;
- GroundPrimitive._terrainHeightsMaxLevel = 6;
-
function getComputeMaximumHeightFunction(primitive) {
return function(granularity, ellipsoid) {
var r = ellipsoid.maximumRadius;
@@ -389,9 +385,6 @@ define([
var scratchBVCartesian = new Cartesian3();
var scratchBVCartographic = new Cartographic();
var scratchBVRectangle = new Rectangle();
- var tilingScheme = new GeographicTilingScheme();
- var scratchCorners = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
- var scratchTileXY = new Cartesian2();
function getRectangle(frameState, geometry) {
var ellipsoid = frameState.mapProjection.ellipsoid;
@@ -438,110 +431,11 @@ define([
return rectangle;
}
- var scratchDiagonalCartesianNE = new Cartesian3();
- var scratchDiagonalCartesianSW = new Cartesian3();
- var scratchDiagonalCartographic = new Cartographic();
- var scratchCenterCartesian = new Cartesian3();
- var scratchSurfaceCartesian = new Cartesian3();
-
- function getTileXYLevel(rectangle) {
- Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]);
- Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]);
- Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]);
- Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]);
-
- // Determine which tile the bounding rectangle is in
- var lastLevelX = 0, lastLevelY = 0;
- var currentX = 0, currentY = 0;
- var maxLevel = GroundPrimitive._terrainHeightsMaxLevel;
- var i;
- for(i = 0; i <= maxLevel; ++i) {
- var failed = false;
- for(var j = 0; j < 4; ++j) {
- var corner = scratchCorners[j];
- tilingScheme.positionToTileXY(corner, i, scratchTileXY);
- if (j === 0) {
- currentX = scratchTileXY.x;
- currentY = scratchTileXY.y;
- } else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
- failed = true;
- break;
- }
- }
-
- if (failed) {
- break;
- }
-
- lastLevelX = currentX;
- lastLevelY = currentY;
- }
-
- if (i === 0) {
- return undefined;
- }
-
- return {
- x : lastLevelX,
- y : lastLevelY,
- level : (i > maxLevel) ? maxLevel : (i - 1)
- };
- }
-
function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) {
- var xyLevel = getTileXYLevel(rectangle);
-
- // Get the terrain min/max for that tile
- var minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
- var maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
- if (defined(xyLevel)) {
- var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
- var heights = GroundPrimitive._terrainHeights[key];
- if (defined(heights)) {
- minTerrainHeight = heights[0];
- maxTerrainHeight = heights[1];
- }
+ var result = ApproximateTerrainHeights.getApproximateTerrainHeights(rectangle, ellipsoid);
- // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
- ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle, scratchDiagonalCartographic),
- scratchDiagonalCartesianNE);
- ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle, scratchDiagonalCartographic),
- scratchDiagonalCartesianSW);
-
- Cartesian3.subtract(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian);
- Cartesian3.add(scratchDiagonalCartesianNE,
- Cartesian3.multiplyByScalar(scratchCenterCartesian, 0.5, scratchCenterCartesian), scratchCenterCartesian);
- var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian);
- if (defined(surfacePosition)) {
- var distance = Cartesian3.distance(scratchCenterCartesian, surfacePosition);
- minTerrainHeight = Math.min(minTerrainHeight, -distance);
- } else {
- minTerrainHeight = GroundPrimitive._defaultMinTerrainHeight;
- }
- }
-
- primitive._minTerrainHeight = Math.max(GroundPrimitive._defaultMinTerrainHeight, minTerrainHeight);
- primitive._maxTerrainHeight = maxTerrainHeight;
- }
-
- var scratchBoundingSphere = new BoundingSphere();
- function getInstanceBoundingSphere(rectangle, ellipsoid) {
- var xyLevel = getTileXYLevel(rectangle);
-
- // Get the terrain max for that tile
- var maxTerrainHeight = GroundPrimitive._defaultMaxTerrainHeight;
- if (defined(xyLevel)) {
- var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
- var heights = GroundPrimitive._terrainHeights[key];
- if (defined(heights)) {
- maxTerrainHeight = heights[1];
- }
- }
-
- var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
- BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere);
-
- return BoundingSphere.union(result, scratchBoundingSphere, result);
+ primitive._minTerrainHeight = result.minimumTerrainHeight;
+ primitive._maxTerrainHeight = result.maximumTerrainHeight;
}
function createBoundingVolume(groundPrimitive, frameState, geometry) {
@@ -663,10 +557,10 @@ define([
return initPromise;
}
- GroundPrimitive._initPromise = Resource.fetchJson(buildModuleUrl('Assets/approximateTerrainHeights.json')).then(function(json) {
- GroundPrimitive._initialized = true;
- GroundPrimitive._terrainHeights = json;
- });
+ GroundPrimitive._initPromise = ApproximateTerrainHeights.initialize()
+ .then(function() {
+ GroundPrimitive._initialized = true;
+ });
return GroundPrimitive._initPromise;
};
@@ -727,7 +621,7 @@ define([
var id = instance.id;
if (defined(id) && defined(instanceRectangle)) {
- var boundingSphere = getInstanceBoundingSphere(instanceRectangle, ellipsoid);
+ var boundingSphere = ApproximateTerrainHeights.getInstanceBoundingSphere(instanceRectangle, ellipsoid);
this._boundingSpheresKeys.push(id);
this._boundingSpheres.push(boundingSphere);
}
diff --git a/Specs/Core/ApproximateTerrainHeightsSpec.js b/Specs/Core/ApproximateTerrainHeightsSpec.js
new file mode 100644
index 000000000000..df85d432a348
--- /dev/null
+++ b/Specs/Core/ApproximateTerrainHeightsSpec.js
@@ -0,0 +1,70 @@
+defineSuite([
+ 'Core/ApproximateTerrainHeights',
+ 'Core/Cartesian3',
+ 'Core/Math',
+ 'Core/Rectangle'
+], function(
+ ApproximateTerrainHeights,
+ Cartesian3,
+ CesiumMath,
+ Rectangle) {
+ 'use strict';
+
+ beforeAll(function() {
+ return ApproximateTerrainHeights.initialize();
+ });
+
+ afterAll(function() {
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
+ });
+
+ it('initializes', function() {
+ return ApproximateTerrainHeights.initialize()
+ .then(function() {
+ expect(ApproximateTerrainHeights._terrainHeights).toBeDefined();
+ });
+ });
+
+ it('getApproximateTerrainHeights computes minimum and maximum terrain heights', function() {
+ var result = ApproximateTerrainHeights.getApproximateTerrainHeights(Rectangle.fromDegrees(-121.0, 10.0, -120.0, 11.0));
+ expect(result.minimumTerrainHeight).toEqualEpsilon(-476.125711887558, CesiumMath.EPSILON10);
+ expect(result.maximumTerrainHeight).toEqualEpsilon(-28.53441619873047, CesiumMath.EPSILON10);
+ });
+
+ it('getApproximateTerrainHeights throws with no rectangle', function() {
+ expect(function() {
+ return ApproximateTerrainHeights.getApproximateTerrainHeights();
+ }).toThrowDeveloperError();
+ });
+
+ it('getApproximateTerrainHeights throws if ApproximateTerrainHeights was not initialized first', function() {
+ var heights = ApproximateTerrainHeights._terrainHeights;
+ ApproximateTerrainHeights._terrainHeights = undefined;
+ expect(function() {
+ return ApproximateTerrainHeights.getApproximateTerrainHeights(Rectangle.fromDegrees(-121.0, 10.0, -120.0, 11.0));
+ });
+ ApproximateTerrainHeights._terrainHeights = heights;
+ });
+
+ it('getInstanceBoundingSphere computes a bounding sphere', function() {
+ var result = ApproximateTerrainHeights.getInstanceBoundingSphere(Rectangle.fromDegrees(-121.0, 10.0, -120.0, 11.0));
+ expect(result.center).toEqualEpsilon(new Cartesian3(-3183013.8480289434, -5403772.557261968, 1154581.5817616477), CesiumMath.EPSILON10);
+ expect(result.radius).toEqualEpsilon(77884.16539096291, CesiumMath.EPSILON10);
+ });
+
+ it('getInstanceBoundingSphere throws with no rectangle', function() {
+ expect(function() {
+ return ApproximateTerrainHeights.getInstanceBoundingSphere();
+ }).toThrowDeveloperError();
+ });
+
+ it('getInstanceBoundingSphere throws if ApproximateTerrainHeights was not initialized first', function() {
+ var heights = ApproximateTerrainHeights._terrainHeights;
+ ApproximateTerrainHeights._terrainHeights = undefined;
+ expect(function() {
+ return ApproximateTerrainHeights.getInstanceBoundingSphere(Rectangle.fromDegrees(-121.0, 10.0, -120.0, 11.0));
+ });
+ ApproximateTerrainHeights._terrainHeights = heights;
+ });
+});
diff --git a/Specs/DataSources/CentroidPositionPropertySpec.js b/Specs/DataSources/CentroidPositionPropertySpec.js
new file mode 100644
index 000000000000..f8623d9bb010
--- /dev/null
+++ b/Specs/DataSources/CentroidPositionPropertySpec.js
@@ -0,0 +1,99 @@
+defineSuite([
+ 'DataSources/CentroidPositionProperty',
+ 'Core/Cartesian3',
+ 'Core/JulianDate',
+ 'Core/ReferenceFrame',
+ 'DataSources/ConstantProperty',
+ 'DataSources/PositionProperty'
+], function(
+ CentroidPositionProperty,
+ Cartesian3,
+ JulianDate,
+ ReferenceFrame,
+ ConstantProperty,
+ PositionProperty) {
+ 'use strict';
+
+ var time = JulianDate.now();
+
+ it('Constructor sets expected defaults', function() {
+ var positions = new ConstantProperty(Cartesian3.fromDegreesArray([0.0, 0.0,
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0]));
+ var property = new CentroidPositionProperty(positions);
+ expect(property.referenceFrame).toBe(ReferenceFrame.FIXED);
+ expect(property.positions).toBe(positions);
+ });
+
+ it('getValue works without a result parameter', function() {
+ var positions = new ConstantProperty(new Cartesian3(0.0, 0.0, 0.0),
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(1.0, 1.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0));
+ var property = new CentroidPositionProperty(positions);
+
+ var result = property.getValue(time);
+ expect(result).toEqual(new Cartesian3(0.5, 0.5, 0.0));
+ });
+
+ it('getValue works with a result parameter', function() {
+ var positions = new ConstantProperty([new Cartesian3(0.0, 0.0, 0.0),
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(1.0, 1.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0)]);
+ var property = new CentroidPositionProperty(positions);
+
+ var expected = new Cartesian3();
+ var result = property.getValue(time, expected);
+ expect(result).toBe(expected);
+ expect(expected).toEqual(new Cartesian3(0.5, 0.5, 0.0));
+ });
+
+ it('getValueInReferenceFrame works without a result parameter', function() {
+ var positions = new ConstantProperty([new Cartesian3(0.0, 0.0, 0.0),
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(1.0, 1.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0)]);
+ var property = new CentroidPositionProperty(positions);
+
+ var result = property.getValueInReferenceFrame(time, ReferenceFrame.INERTIAL);
+ expect(result).toEqual(PositionProperty.convertToReferenceFrame(time, new Cartesian3(0.5, 0.5, 0.0), ReferenceFrame.FIXED, ReferenceFrame.INERTIAL));
+ });
+
+ it('getValueInReferenceFrame works with a result parameter', function() {
+ var positions = new ConstantProperty([new Cartesian3(0.0, 0.0, 0.0),
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(1.0, 1.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0)]);
+ var property = new CentroidPositionProperty(positions);
+
+ var expected = new Cartesian3();
+ var result = property.getValueInReferenceFrame(time, ReferenceFrame.FIXED, expected);
+ expect(result).toBe(expected);
+ expect(expected).toEqual(PositionProperty.convertToReferenceFrame(time, new Cartesian3(0.5, 0.5, 0.0), ReferenceFrame.INERTIAL, ReferenceFrame.FIXED));
+ });
+
+ it('equals works', function() {
+ var left = new CentroidPositionProperty([new Cartesian3(1, 2, 3)]);
+ var right = new CentroidPositionProperty([new Cartesian3(1, 2, 3)]);
+ expect(left.equals(right)).toEqual(true);
+
+ right = new CentroidPositionProperty([new Cartesian3(1, 2, 4)]);
+ expect(left.equals(right)).toEqual(false);
+ });
+
+ it('getValue throws without time parameter', function() {
+ var property = new CentroidPositionProperty([new Cartesian3(1, 2, 3)]);
+ expect(function() {
+ property.getValue(undefined);
+ }).toThrowDeveloperError();
+ });
+
+ it('getValueInReferenceFrame throws with no referenceFrame parameter', function() {
+ var property = new CentroidPositionProperty([new Cartesian3(1, 2, 3)]);
+ expect(function() {
+ property.getValueInReferenceFrame(time, undefined);
+ }).toThrowDeveloperError();
+ });
+});
diff --git a/Specs/DataSources/DataSourceDisplaySpec.js b/Specs/DataSources/DataSourceDisplaySpec.js
index 6c243b072063..fe27ce8bcb7f 100644
--- a/Specs/DataSources/DataSourceDisplaySpec.js
+++ b/Specs/DataSources/DataSourceDisplaySpec.js
@@ -1,5 +1,6 @@
defineSuite([
'DataSources/DataSourceDisplay',
+ 'Core/ApproximateTerrainHeights',
'Core/BoundingSphere',
'Core/Cartesian3',
'Core/Iso8601',
@@ -11,6 +12,7 @@ defineSuite([
'Specs/MockDataSource'
], function(
DataSourceDisplay,
+ ApproximateTerrainHeights,
BoundingSphere,
Cartesian3,
Iso8601,
@@ -38,7 +40,8 @@ defineSuite([
// Leave ground primitive uninitialized
GroundPrimitive._initialized = false;
GroundPrimitive._initPromise = undefined;
- GroundPrimitive._terrainHeights = undefined;
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
});
afterEach(function() {
@@ -357,7 +360,8 @@ defineSuite([
it('verify update returns false till terrain heights are initialized', function() {
GroundPrimitive._initialized = false;
GroundPrimitive._initPromise = undefined;
- GroundPrimitive._terrainHeights = undefined;
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
var source1 = new MockDataSource();
var source2 = new MockDataSource();
diff --git a/Specs/DataSources/GeometryVisualizerSpec.js b/Specs/DataSources/GeometryVisualizerSpec.js
index 6ed873b66239..00fab66b830c 100644
--- a/Specs/DataSources/GeometryVisualizerSpec.js
+++ b/Specs/DataSources/GeometryVisualizerSpec.js
@@ -1,5 +1,6 @@
defineSuite([
'DataSources/GeometryVisualizer',
+ 'Core/ApproximateTerrainHeights',
'Core/BoundingSphere',
'Core/Cartesian3',
'Core/Color',
@@ -31,6 +32,7 @@ defineSuite([
'Specs/pollToPromise'
], function(
GeometryVisualizer,
+ ApproximateTerrainHeights,
BoundingSphere,
Cartesian3,
Color,
@@ -77,7 +79,8 @@ defineSuite([
// Leave ground primitive uninitialized
GroundPrimitive._initialized = false;
GroundPrimitive._initPromise = undefined;
- GroundPrimitive._terrainHeights = undefined;
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
});
it('Can create and destroy', function() {
diff --git a/Specs/DataSources/MinimumTerrainHeightPropertySpec.js b/Specs/DataSources/MinimumTerrainHeightPropertySpec.js
new file mode 100644
index 000000000000..2deb902ef04d
--- /dev/null
+++ b/Specs/DataSources/MinimumTerrainHeightPropertySpec.js
@@ -0,0 +1,122 @@
+defineSuite([
+ 'DataSources/MinimumTerrainHeightProperty',
+ 'Core/ApproximateTerrainHeights',
+ 'Core/Cartesian3',
+ 'Core/Event',
+ 'Core/ExtrapolationType',
+ 'Core/JulianDate',
+ 'Core/Math',
+ 'DataSources/CallbackProperty',
+ 'DataSources/ConstantProperty'
+], function(
+ MinimumTerrainHeightProperty,
+ ApproximateTerrainHeights,
+ Cartesian3,
+ Event,
+ ExtrapolationType,
+ JulianDate,
+ CesiumMath,
+ CallbackProperty,
+ ConstantProperty) {
+ 'use strict';
+
+ var time = JulianDate.now();
+
+ beforeAll(function() {
+ return ApproximateTerrainHeights.initialize();
+ });
+
+ afterAll(function() {
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
+ });
+
+ it('can default construct', function() {
+ var property = new MinimumTerrainHeightProperty();
+ expect(property.isConstant).toBe(true);
+ expect(property.definitionChanged).toBeInstanceOf(Event);
+ expect(property.positions).toBeUndefined();
+ expect(property.getValue(time)).toBeUndefined();
+ });
+
+ it('can construct with arguments', function() {
+ var positions = new ConstantProperty();
+ var property = new MinimumTerrainHeightProperty(positions);
+ expect(property.isConstant).toBe(true);
+ expect(property.definitionChanged).toBeInstanceOf(Event);
+ expect(property.positions).toBe(positions);
+ });
+
+ it('raises definitionChanged event when positions is set', function() {
+ var property = new MinimumTerrainHeightProperty();
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ var positions = new ConstantProperty();
+ property.positions = positions;
+ expect(listener).toHaveBeenCalledWith(property, 'positions', positions, undefined);
+ });
+
+ it('subscribes and unsubscribes to position definitionChanged and propagates up', function() {
+ var positions = new ConstantProperty();
+ var property = new MinimumTerrainHeightProperty(positions);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ //Position changing should raise out property change event
+ positions.definitionChanged.raiseEvent(positions);
+ expect(listener).toHaveBeenCalledWith(property, 'positions', positions, positions);
+
+ //Make sure it unsubscribes when value is changed
+ property.positions = undefined;
+
+ listener.calls.reset();
+ positions.definitionChanged.raiseEvent(positions);
+ expect(listener.calls.count()).toBe(0);
+ });
+
+ it('does not raise definitionChanged event when position is set to the same instance', function() {
+ var positions = new ConstantProperty();
+ var property = new MinimumTerrainHeightProperty(positions);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ property.positions = positions;
+ expect(listener.calls.count()).toBe(0);
+ });
+
+ it('produces correct value', function() {
+ var positions = new ConstantProperty(Cartesian3.fromDegreesArray([-120.0, 40.0,
+ -119.0, 40.0,
+ -119.0, 41.0,
+ -120.0, 41.0]));
+ var property = new MinimumTerrainHeightProperty(positions);
+
+ expect(property.getValue(time)).toEqualEpsilon(-382.8696126443784, CesiumMath.EPSILON10);
+ });
+
+ it('equals works', function() {
+ var positions = new ConstantProperty();
+
+ var left = new MinimumTerrainHeightProperty();
+ var right = new MinimumTerrainHeightProperty();
+
+ expect(left.equals(right)).toBe(true);
+
+ left.positions = positions;
+ expect(left.equals(right)).toBe(false);
+
+ right.positions = positions;
+ expect(left.equals(right)).toBe(true);
+ });
+
+ it('getValue throws without time', function() {
+ var property = new MinimumTerrainHeightProperty();
+ expect(function() {
+ property.getValue();
+ }).toThrowDeveloperError();
+ });
+});
diff --git a/Specs/DataSources/RelativeToTerrainHeightPropertySpec.js b/Specs/DataSources/RelativeToTerrainHeightPropertySpec.js
new file mode 100644
index 000000000000..db60a594dd0d
--- /dev/null
+++ b/Specs/DataSources/RelativeToTerrainHeightPropertySpec.js
@@ -0,0 +1,166 @@
+defineSuite([
+ 'DataSources/RelativeToTerrainHeightProperty',
+ 'Core/ApproximateTerrainHeights',
+ 'Core/Cartesian3',
+ 'Core/EllipsoidTerrainProvider',
+ 'Core/Event',
+ 'Core/ExtrapolationType',
+ 'Core/JulianDate',
+ 'Core/Math',
+ 'DataSources/CallbackProperty',
+ 'DataSources/ConstantProperty',
+ 'ThirdParty/when'
+], function(
+ RelativeToTerrainHeightProperty,
+ ApproximateTerrainHeights,
+ Cartesian3,
+ EllipsoidTerrainProvider,
+ Event,
+ ExtrapolationType,
+ JulianDate,
+ CesiumMath,
+ CallbackProperty,
+ ConstantProperty,
+ when) {
+ 'use strict';
+
+ var time = JulianDate.now();
+ var terrainProvider = new EllipsoidTerrainProvider();
+
+ it('can default construct', function() {
+ var property = new RelativeToTerrainHeightProperty(terrainProvider);
+ expect(property.isConstant).toBe(true);
+ expect(property.definitionChanged).toBeInstanceOf(Event);
+ expect(property.position).toBeUndefined();
+ expect(property.heightRelativeToTerrain).toBeUndefined();
+ expect(property.getValue(time)).toBe(0);
+ });
+
+ it('can construct with arguments', function() {
+ var position = new ConstantProperty();
+ var heightRelativeToTerrain = new ConstantProperty();
+ var property = new RelativeToTerrainHeightProperty(terrainProvider, position, heightRelativeToTerrain);
+ expect(property.isConstant).toBe(true);
+ expect(property.definitionChanged).toBeInstanceOf(Event);
+ expect(property.position).toBe(position);
+ expect(property.heightRelativeToTerrain).toBe(heightRelativeToTerrain);
+ });
+
+ it('raises definitionChanged event when position is set', function() {
+ var property = new RelativeToTerrainHeightProperty(terrainProvider);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ var position = new ConstantProperty();
+ property.position = position;
+ expect(listener).toHaveBeenCalledWith(property, 'position', position, undefined);
+ });
+
+ it('raises definitionChanged event when heightRelativeToTerrain is set', function() {
+ var property = new RelativeToTerrainHeightProperty(terrainProvider);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ var heightRelativeToTerrain = new ConstantProperty();
+ property.heightRelativeToTerrain = heightRelativeToTerrain;
+ expect(listener).toHaveBeenCalledWith(property, 'heightRelativeToTerrain', heightRelativeToTerrain, undefined);
+ });
+
+ it('subscribes and unsubscribes to position definitionChanged and propagates up', function() {
+ var position = new ConstantProperty();
+ var property = new RelativeToTerrainHeightProperty(terrainProvider, position);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ //Position changing should raise out property change event
+ position.definitionChanged.raiseEvent(position);
+ expect(listener).toHaveBeenCalledWith(property, 'position', position, position);
+
+ //Make sure it unsubscribes when value is changed
+ property.position = undefined;
+
+ listener.calls.reset();
+ position.definitionChanged.raiseEvent(position);
+ expect(listener.calls.count()).toBe(0);
+ });
+
+ it('subscribes and unsubscribes to heightRelativeToTerrain definitionChanged and propagates up', function() {
+ var heightRelativeToTerrain = new ConstantProperty();
+ var property = new RelativeToTerrainHeightProperty(terrainProvider, undefined, heightRelativeToTerrain);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ //Position changing should raise out property change event
+ heightRelativeToTerrain.definitionChanged.raiseEvent(heightRelativeToTerrain);
+ expect(listener).toHaveBeenCalledWith(property, 'heightRelativeToTerrain', heightRelativeToTerrain, heightRelativeToTerrain);
+
+ //Make sure it unsubscribes when value is changed
+ property.heightRelativeToTerrain = undefined;
+
+ listener.calls.reset();
+ heightRelativeToTerrain.definitionChanged.raiseEvent(heightRelativeToTerrain);
+ expect(listener.calls.count()).toBe(0);
+ });
+
+ it('does not raise definitionChanged event when position is set to the same instance', function() {
+ var position = new ConstantProperty();
+ var heightRelativeToTerrain = new ConstantProperty();
+ var property = new RelativeToTerrainHeightProperty(terrainProvider, position, heightRelativeToTerrain);
+
+ var listener = jasmine.createSpy('listener');
+ property.definitionChanged.addEventListener(listener);
+
+ property.position = position;
+ property.heightRelativeToTerrain = heightRelativeToTerrain;
+ expect(listener.calls.count()).toBe(0);
+ });
+
+ it('produces correct value', function() {
+ var terrainHeight = 30.0;
+ spyOn(RelativeToTerrainHeightProperty, '_sampleTerrainMostDetailed').and.returnValue(when.resolve(terrainHeight));
+ var position = new ConstantProperty(Cartesian3.fromDegrees(-120.0, 40.0));
+ var heightRelativeToTerrain = new ConstantProperty(40.0);
+ var property = new RelativeToTerrainHeightProperty(terrainProvider, position, heightRelativeToTerrain);
+
+ expect(property.getValue(time)).toEqual(70.0);
+ });
+
+ it('equals works', function() {
+ var position = new ConstantProperty();
+ var heightRelativeToTerrain = new ConstantProperty();
+
+ var left = new RelativeToTerrainHeightProperty(terrainProvider);
+ var right = new RelativeToTerrainHeightProperty(terrainProvider);
+
+ expect(left.equals(right)).toBe(true);
+
+ left.position = position;
+ expect(left.equals(right)).toBe(false);
+
+ right.position = position;
+ expect(left.equals(right)).toBe(true);
+
+ left.heightRelativeToTerrain = heightRelativeToTerrain;
+ expect(left.equals(right)).toBe(false);
+
+ right.heightRelativeToTerrain = heightRelativeToTerrain;
+ expect(left.equals(right)).toBe(false);
+ });
+
+ it('constructor throws without terrainProvider', function() {
+ expect(function() {
+ return new RelativeToTerrainHeightProperty();
+ }).toThrowDeveloperError();
+ });
+
+ it('getValue throws without time', function() {
+ var property = new RelativeToTerrainHeightProperty(terrainProvider);
+ expect(function() {
+ property.getValue();
+ }).toThrowDeveloperError();
+ });
+});
diff --git a/Specs/Scene/GroundPrimitiveSpec.js b/Specs/Scene/GroundPrimitiveSpec.js
index a17aab5a121c..1d8c8b4d701e 100644
--- a/Specs/Scene/GroundPrimitiveSpec.js
+++ b/Specs/Scene/GroundPrimitiveSpec.js
@@ -1,5 +1,6 @@
defineSuite([
'Scene/GroundPrimitive',
+ 'Core/ApproximateTerrainHeights',
'Core/Color',
'Core/ColorGeometryInstanceAttribute',
'Core/destroyObject',
@@ -20,6 +21,7 @@ defineSuite([
'Specs/pollToPromise'
], function(
GroundPrimitive,
+ ApproximateTerrainHeights,
Color,
ColorGeometryInstanceAttribute,
destroyObject,
@@ -69,7 +71,8 @@ defineSuite([
// Leave ground primitive uninitialized
GroundPrimitive._initialized = false;
GroundPrimitive._initPromise = undefined;
- GroundPrimitive._terrainHeights = undefined;
+ ApproximateTerrainHeights._initPromise = undefined;
+ ApproximateTerrainHeights._terrainHeights = undefined;
});
function MockGlobePrimitive(primitive) {