"use strict";
/*global require,Int16Array,Uint8Array,Int16Array*/
var CesiumMath = require("terriajs-cesium/Source/Core/Math").default;
var defined = require("terriajs-cesium/Source/Core/defined").default;
var loadArrayBuffer = require("../Core/loadArrayBuffer");
var when = require("terriajs-cesium/Source/ThirdParty/when").default;
/**
* The Earth Gravity Model 1996 (EGM96) geoid.
* @param {String} gridFileUrl The URL of the WW15MGH.DAC file.
*/
var EarthGravityModel1996 = function(gridFileUrl) {
this.gridFileUrl = gridFileUrl;
this.data = undefined;
// These values were determined by inspecting the WW15MGH.DAC file. We hard-code them here because
// we need them available before that file finishes loading.
this.minimumHeight = -106.99;
this.maximumHeight = 85.39;
};
/**
* Determines if this class will work in the current environment. It will return false on older browsers without support
* for typed arrays.
* @return {Boolean} True if this class may be used in this environment; otherwise, false.
*/
EarthGravityModel1996.isSupported = function() {
return typeof Int16Array !== "undefined" && typeof Uint8Array !== "undefined";
};
/**
* Gets the height of EGM96 above the surface of the ellipsoid.
* @param {String} baseUrl The base URL for TerriaJS resources.
* @param {Number} longitude The longitude.
* @param {Number} latitude The latitude
* @return {Promise|Number} A promise, that, when it results The height of mean sea level above the ellipsoid at the specified location. Negative numbers indicate that mean sea level
* is below the ellipsoid.
*/
EarthGravityModel1996.prototype.getHeight = function(longitude, latitude) {
return getHeightData(this).then(function(data) {
return getHeightFromData(data, longitude, latitude);
});
};
EarthGravityModel1996.prototype.getHeights = function(cartographicArray) {
return getHeightData(this).then(function(data) {
for (var i = 0; i < cartographicArray.length; ++i) {
var cartographic = cartographicArray[i];
cartographic.height = getHeightFromData(
data,
cartographic.longitude,
cartographic.latitude
);
}
return cartographicArray;
});
};
function getHeightData(model) {
if (!defined(model.data)) {
model.data = loadArrayBuffer(model.gridFileUrl);
}
return when(model.data, function(data) {
if (!(model.data instanceof Int16Array)) {
// Data file is big-endian, all relevant platforms are little endian, so swap the byte order.
var byteView = new Uint8Array(data);
for (var k = 0; k < byteView.length; k += 2) {
var tmp = byteView[k];
byteView[k] = byteView[k + 1];
byteView[k + 1] = tmp;
}
model.data = new Int16Array(data);
}
return model.data;
});
}
function getHeightFromData(data, longitude, latitude) {
var recordIndex = (720 * (CesiumMath.PI_OVER_TWO - latitude)) / Math.PI;
if (recordIndex < 0) {
recordIndex = 0;
} else if (recordIndex > 720) {
recordIndex = 720;
}
longitude = CesiumMath.zeroToTwoPi(longitude);
var heightIndex = (1440 * longitude) / CesiumMath.TWO_PI;
if (heightIndex < 0) {
heightIndex = 0;
} else if (heightIndex > 1440) {
heightIndex = 1440;
}
var i = heightIndex | 0;
var j = recordIndex | 0;
var xMinusX1 = heightIndex - i;
var yMinusY1 = recordIndex - j;
var x2MinusX = 1.0 - xMinusX1;
var y2MinusY = 1.0 - yMinusY1;
var f11 = getHeightValue(data, j, i);
var f21 = getHeightValue(data, j, i + 1);
var f12 = getHeightValue(data, j + 1, i);
var f22 = getHeightValue(data, j + 1, i + 1);
return (
(f11 * x2MinusX * y2MinusY +
f21 * xMinusX1 * y2MinusY +
f12 * x2MinusX * yMinusY1 +
f22 * xMinusX1 * yMinusY1) /
100.0
);
}
// Heights returned by this function are in centimeters.
function getHeightValue(data, recordIndex, heightIndex) {
if (recordIndex > 720) {
recordIndex = 720;
} else if (recordIndex < 0) {
recordIndex = 0;
}
if (heightIndex > 1439) {
heightIndex -= 1440;
} else if (heightIndex < 0) {
heightIndex += 1440;
}
return data[recordIndex * 1440 + heightIndex];
}
module.exports = EarthGravityModel1996;