react-native-cesium 1.0.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -11
- package/ReactNativeCesium.podspec +1 -1
- package/android/CMakeLists.txt +5 -3
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +197 -227
- package/android/src/main/cpp/CesiumBridgeAndroid.h +48 -49
- package/android/src/main/cpp/cpp-adapter.cpp +2 -2
- package/android/src/main/cpp/vulkan/VulkanBackend.cpp +1010 -569
- package/android/src/main/cpp/vulkan/VulkanBackend.h +121 -12
- package/android/src/main/cpp/vulkan/shaders/sky.frag +20 -10
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +28 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +295 -58
- package/cpp/engine/CameraSmoother.cpp +99 -0
- package/cpp/engine/CameraSmoother.hpp +29 -0
- package/cpp/engine/CameraTargetState.cpp +49 -0
- package/cpp/engine/CameraTargetState.hpp +121 -0
- package/cpp/engine/CesiumEngine.cpp +206 -60
- package/cpp/engine/CesiumEngine.hpp +77 -10
- package/cpp/engine/EngineTunables.hpp +79 -0
- package/cpp/engine/GeoidConverter.cpp +36 -0
- package/cpp/engine/GeoidConverter.hpp +10 -0
- package/cpp/engine/GlobeCamera.cpp +7 -2
- package/cpp/engine/MetricsAggregator.cpp +71 -0
- package/cpp/engine/MetricsAggregator.hpp +54 -0
- package/cpp/engine/TileLifecycleManager.cpp +0 -12
- package/cpp/engine/TileLifecycleManager.hpp +0 -3
- package/cpp/third_party/egm96/EGM96.c +289 -0
- package/cpp/third_party/egm96/EGM96.h +45 -0
- package/cpp/third_party/egm96/EGM96_data.h +65372 -0
- package/ios/CesiumBridge.h +24 -5
- package/ios/CesiumBridge.mm +149 -271
- package/ios/HybridCesiumView.swift +197 -40
- package/ios/metal/MetalBackend.mm +72 -23
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +26 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JCesiumMetrics.hpp +5 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +109 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +24 -0
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +48 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CameraState.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/CesiumMetrics.kt +32 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +72 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Quaternion.kt +19 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +30 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +85 -1
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +60 -0
- package/nitrogen/generated/ios/swift/CesiumMetrics.swift +7 -2
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +12 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +288 -0
- package/nitrogen/generated/shared/c++/CesiumMetrics.hpp +5 -1
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +25 -1
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +132 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +13 -1
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +12 -0
- package/package.json +4 -4
- package/scripts/cesium/clone.mjs +1 -1
- package/src/specs/cesium-view.nitro.ts +29 -0
- package/cpp/engine/DeletionQueue.cpp +0 -28
- package/cpp/engine/DeletionQueue.hpp +0 -28
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
namespace reactnativecesium {
|
|
7
|
+
|
|
8
|
+
// Centralised default values that previously lived as anonymous magic numbers
|
|
9
|
+
// scattered across the engine and platform backends. Keep this header tiny and
|
|
10
|
+
// dependency-free so it is safe to include from any translation unit.
|
|
11
|
+
namespace tunables {
|
|
12
|
+
|
|
13
|
+
// CPU pipeline stages allowed to be in flight at once on the GPU. Both Metal
|
|
14
|
+
// (triple-buffered MTLBuffer slots) and Vulkan (in-flight fences / command
|
|
15
|
+
// buffers) use this constant as their core depth, so changing it here
|
|
16
|
+
// propagates to every backend.
|
|
17
|
+
inline constexpr int kMaxFramesInFlight = 3;
|
|
18
|
+
|
|
19
|
+
// Upper bound on simultaneously live raster overlay textures held by the
|
|
20
|
+
// Vulkan descriptor pool. Cesium can cache many hundreds of tiles at high
|
|
21
|
+
// zoom; 2048 gives substantial headroom without exhausting GPU descriptor
|
|
22
|
+
// state.
|
|
23
|
+
inline constexpr int kMaxRasterTextures = 2048;
|
|
24
|
+
|
|
25
|
+
// Cesium Native tileset cache (decoded geometry / textures kept in RAM).
|
|
26
|
+
inline constexpr int64_t kDefaultMaxCachedBytes = 256LL * 1024LL * 1024LL;
|
|
27
|
+
|
|
28
|
+
// SqliteCache row budget — number of cached HTTP responses Cesium keeps on
|
|
29
|
+
// disk between sessions. 4k rows is a reasonable phone default; tablets can
|
|
30
|
+
// happily go to 16k.
|
|
31
|
+
inline constexpr int32_t kDefaultSqliteCacheMaxRows = 4096;
|
|
32
|
+
|
|
33
|
+
// FrameResult initial reservations (CPU-side merged geometry buffer) used to
|
|
34
|
+
// avoid reallocations during the first few frames of a session. The buffers
|
|
35
|
+
// are then re-used (only re-grown if visible geometry exceeds capacity).
|
|
36
|
+
inline constexpr std::size_t kFrameResultPositionFloatReserve = 512u * 1024u;
|
|
37
|
+
inline constexpr std::size_t kFrameResultIndexUint32Reserve = 3u * 1024u * 1024u;
|
|
38
|
+
|
|
39
|
+
// Tileset selection knobs (mirrored to TilesetOptions defaults).
|
|
40
|
+
inline constexpr double kDefaultMaximumScreenSpaceError = 32.0;
|
|
41
|
+
inline constexpr int32_t kDefaultMaximumSimultaneousTileLoads = 12;
|
|
42
|
+
inline constexpr int32_t kDefaultLoadingDescendantLimit = 20;
|
|
43
|
+
|
|
44
|
+
// Camera smoothing constants — shared between platform bridges so jank /
|
|
45
|
+
// motion feel is identical on iOS and Android.
|
|
46
|
+
inline constexpr double kSmoothAltitude = 25.0;
|
|
47
|
+
inline constexpr double kSmoothHeading = 30.0;
|
|
48
|
+
inline constexpr double kSmoothPitch = 50.0;
|
|
49
|
+
inline constexpr double kSmoothRoll = 50.0;
|
|
50
|
+
inline constexpr double kSmoothViewCorr = 50.0;
|
|
51
|
+
inline constexpr double kEpsLatLon = 1e-7;
|
|
52
|
+
inline constexpr double kEpsAltitudeMeters = 0.1;
|
|
53
|
+
inline constexpr double kEpsAngleDeg = 0.05;
|
|
54
|
+
inline constexpr double kEpsQuatDot = 1e-8;
|
|
55
|
+
|
|
56
|
+
// Lat/lon are smoothed adaptively: τ = clamp(α · ewmaInterval, τmin, τmax),
|
|
57
|
+
// then per frame we apply 1 - exp(-dt/τ). α<1 means the camera converges
|
|
58
|
+
// before the next sample lands. With ~60 Hz gestures the EWMA interval is
|
|
59
|
+
// ~16 ms ⇒ τ at the floor (5 ms) ⇒ ≤1-frame trail (indistinguishable from
|
|
60
|
+
// the previous snap). With a 1 Hz GPS feed τ ≈ 450 ms ⇒ ~95 % converged
|
|
61
|
+
// before the next sample arrives, removing the per-second teleport.
|
|
62
|
+
inline constexpr double kSmoothPositionAlpha = 0.45;
|
|
63
|
+
inline constexpr double kSmoothPositionTauMin = 0.005;
|
|
64
|
+
inline constexpr double kSmoothPositionTauMax = 0.6;
|
|
65
|
+
// Genuine teleports (e.g. setCamera(NewYork) → setCamera(Tokyo)) skip the
|
|
66
|
+
// smoother — easing across the planet over 600 ms looks worse than a snap.
|
|
67
|
+
inline constexpr double kPosSnapThroughDeg = 0.5;
|
|
68
|
+
// EWMA mixing factor for the inter-arrival interval of setCamera calls.
|
|
69
|
+
inline constexpr double kEwmaIntervalAlpha = 0.3;
|
|
70
|
+
|
|
71
|
+
// Metrics throttle: emit telemetry every N rendered frames.
|
|
72
|
+
inline constexpr int kMetricsEmitEveryFrames = 20;
|
|
73
|
+
|
|
74
|
+
// Idle-probe interval (seconds): when the engine is idle, periodically tick
|
|
75
|
+
// to catch late tile completions without redundant full draws.
|
|
76
|
+
inline constexpr double kIdleProbeSeconds = 0.25;
|
|
77
|
+
|
|
78
|
+
} // namespace tunables
|
|
79
|
+
} // namespace reactnativecesium
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#include "GeoidConverter.hpp"
|
|
2
|
+
|
|
3
|
+
#include "../third_party/egm96/EGM96.h"
|
|
4
|
+
|
|
5
|
+
#include <cmath>
|
|
6
|
+
|
|
7
|
+
namespace reactnativecesium {
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
|
|
11
|
+
double normalizeLongitudeDegrees(double longitudeDeg) {
|
|
12
|
+
if (!std::isfinite(longitudeDeg)) return 0.0;
|
|
13
|
+
double wrapped = std::fmod(longitudeDeg + 180.0, 360.0);
|
|
14
|
+
if (wrapped < 0.0) wrapped += 360.0;
|
|
15
|
+
return wrapped - 180.0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
} // namespace
|
|
19
|
+
|
|
20
|
+
double mslToEllipsoidMeters(double latitudeDeg,
|
|
21
|
+
double longitudeDeg,
|
|
22
|
+
double mslMeters) {
|
|
23
|
+
if (!std::isfinite(latitudeDeg) || !std::isfinite(longitudeDeg) ||
|
|
24
|
+
!std::isfinite(mslMeters)) {
|
|
25
|
+
return mslMeters;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const double normalizedLon = normalizeLongitudeDegrees(longitudeDeg);
|
|
29
|
+
const double geoidOffset =
|
|
30
|
+
egm96_compute_altitude_offset(latitudeDeg, normalizedLon);
|
|
31
|
+
|
|
32
|
+
if (!std::isfinite(geoidOffset)) return mslMeters;
|
|
33
|
+
return mslMeters + geoidOffset;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
} // namespace reactnativecesium
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "GlobeCamera.hpp"
|
|
2
|
+
#include "GeoidConverter.hpp"
|
|
2
3
|
|
|
3
4
|
#include <CesiumGeospatial/GlobeTransforms.h>
|
|
4
5
|
|
|
@@ -43,9 +44,11 @@ void GlobeCamera::recompute() const {
|
|
|
43
44
|
dirty_ = false;
|
|
44
45
|
|
|
45
46
|
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
47
|
+
const double ellipsoidAltitude =
|
|
48
|
+
mslToEllipsoidMeters(params_.latitude, params_.longitude, params_.altitude);
|
|
46
49
|
CesiumGeospatial::Cartographic carto(glm::radians(params_.longitude),
|
|
47
50
|
glm::radians(params_.latitude),
|
|
48
|
-
|
|
51
|
+
ellipsoidAltitude);
|
|
49
52
|
ecefPosition_ = ellipsoid.cartographicToCartesian(carto);
|
|
50
53
|
|
|
51
54
|
glm::dmat4 enuToECEF =
|
|
@@ -117,10 +120,12 @@ void GlobeCamera::computeHeadingPitchToward(double targetLatDeg,
|
|
|
117
120
|
recompute();
|
|
118
121
|
|
|
119
122
|
const auto& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
123
|
+
const double targetEllipsoidAlt =
|
|
124
|
+
mslToEllipsoidMeters(targetLatDeg, targetLonDeg, targetAltMeters);
|
|
120
125
|
CesiumGeospatial::Cartographic targetCarto(
|
|
121
126
|
glm::radians(targetLonDeg),
|
|
122
127
|
glm::radians(targetLatDeg),
|
|
123
|
-
|
|
128
|
+
targetEllipsoidAlt);
|
|
124
129
|
glm::dvec3 targetEcef = ellipsoid.cartographicToCartesian(targetCarto);
|
|
125
130
|
glm::dvec3 dirEcef = glm::normalize(targetEcef - ecefPosition_);
|
|
126
131
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#include "MetricsAggregator.hpp"
|
|
2
|
+
|
|
3
|
+
#include "EngineTunables.hpp"
|
|
4
|
+
|
|
5
|
+
#include <regex>
|
|
6
|
+
|
|
7
|
+
namespace reactnativecesium {
|
|
8
|
+
|
|
9
|
+
std::string MetricsAggregator::stripHtmlToPlain(const std::string& html) {
|
|
10
|
+
if (html.empty()) return "";
|
|
11
|
+
// The regexes are static so we pay the (very real) cost of compilation only
|
|
12
|
+
// once per process. std::regex matches behaviour previously implemented twice
|
|
13
|
+
// (NSRegularExpression on iOS, std::regex on Android) — both bridges now hit
|
|
14
|
+
// this path so we cannot drift.
|
|
15
|
+
static const std::regex tagRx("<[^>]+>");
|
|
16
|
+
static const std::regex wsRx("\\s+");
|
|
17
|
+
std::string plain = std::regex_replace(html, tagRx, " ");
|
|
18
|
+
plain = std::regex_replace(plain, wsRx, " ");
|
|
19
|
+
size_t start = plain.find_first_not_of(" \t\n\r");
|
|
20
|
+
size_t end = plain.find_last_not_of(" \t\n\r");
|
|
21
|
+
if (start == std::string::npos) return "";
|
|
22
|
+
return plain.substr(start, end - start + 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
std::optional<MetricsSnapshot>
|
|
26
|
+
MetricsAggregator::tick(double dt, const FrameResult& frame, bool tlsConfigured) {
|
|
27
|
+
const double instFps = (dt > 1e-6) ? (1.0 / dt) : 0.0;
|
|
28
|
+
fpsEma_ = (fpsEma_ <= 1e-6) ? instFps : (fpsEma_ * 0.85 + instFps * 0.15);
|
|
29
|
+
|
|
30
|
+
if (++metricsTick_ < tunables::kMetricsEmitEveryFrames) {
|
|
31
|
+
return std::nullopt;
|
|
32
|
+
}
|
|
33
|
+
metricsTick_ = 0;
|
|
34
|
+
|
|
35
|
+
latest_.fps = fpsEma_;
|
|
36
|
+
latest_.tilesRendered = frame.tilesRendered;
|
|
37
|
+
latest_.tilesLoading = frame.tilesLoading;
|
|
38
|
+
latest_.tilesVisited = frame.tilesVisited;
|
|
39
|
+
latest_.ionTokenConfigured = frame.ionTokenConfigured;
|
|
40
|
+
latest_.tilesetReady = frame.tilesetActive;
|
|
41
|
+
latest_.tlsConfigured = tlsConfigured;
|
|
42
|
+
|
|
43
|
+
if (!frame.creditHtmlLines.empty()) {
|
|
44
|
+
std::string joined;
|
|
45
|
+
joined.reserve(256);
|
|
46
|
+
for (const auto& html : frame.creditHtmlLines) {
|
|
47
|
+
if (!joined.empty()) joined += "|";
|
|
48
|
+
joined += html;
|
|
49
|
+
}
|
|
50
|
+
if (joined != lastCreditHtmlJoined_) {
|
|
51
|
+
lastCreditHtmlJoined_ = std::move(joined);
|
|
52
|
+
std::string credits;
|
|
53
|
+
credits.reserve(256);
|
|
54
|
+
for (const auto& html : frame.creditHtmlLines) {
|
|
55
|
+
if (html.find("Error: Invalid Credit") != std::string::npos) continue;
|
|
56
|
+
std::string chunk = stripHtmlToPlain(html);
|
|
57
|
+
if (chunk.empty()) continue;
|
|
58
|
+
if (!credits.empty()) credits += " \xC2\xB7 "; // " · "
|
|
59
|
+
credits += chunk;
|
|
60
|
+
}
|
|
61
|
+
latest_.creditsPlainText = std::move(credits);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
lastCreditHtmlJoined_.clear();
|
|
65
|
+
latest_.creditsPlainText.clear();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return latest_;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
} // namespace reactnativecesium
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "../renderer/RenderTypes.hpp"
|
|
4
|
+
|
|
5
|
+
#include <optional>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
namespace reactnativecesium {
|
|
10
|
+
|
|
11
|
+
// Per-frame snapshot the platform shells expose to JS via Nitro / JNI.
|
|
12
|
+
struct MetricsSnapshot {
|
|
13
|
+
double fps;
|
|
14
|
+
int tilesRendered;
|
|
15
|
+
int tilesLoading;
|
|
16
|
+
int tilesVisited;
|
|
17
|
+
bool ionTokenConfigured;
|
|
18
|
+
bool tilesetReady;
|
|
19
|
+
bool tlsConfigured;
|
|
20
|
+
std::string creditsPlainText;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Aggregates per-frame metrics on the render thread, throttling emission
|
|
24
|
+
// (every kMetricsEmitEveryFrames frames) so we do not spam the JS bridge.
|
|
25
|
+
//
|
|
26
|
+
// Usage: call `tick(dt, frameResult, tlsConfigured)` once per rendered frame;
|
|
27
|
+
// take the optional return value as the "publish to JS" signal. The internal
|
|
28
|
+
// FPS EMA persists between ticks.
|
|
29
|
+
class MetricsAggregator {
|
|
30
|
+
public:
|
|
31
|
+
MetricsAggregator() = default;
|
|
32
|
+
|
|
33
|
+
// Returns std::nullopt on most frames; returns the latest values once the
|
|
34
|
+
// throttle counter wraps. Idempotent w.r.t. credit text — only re-strips
|
|
35
|
+
// HTML when the joined HTML lines actually change frame-to-frame.
|
|
36
|
+
std::optional<MetricsSnapshot>
|
|
37
|
+
tick(double dt, const FrameResult& frame, bool tlsConfigured);
|
|
38
|
+
|
|
39
|
+
// Returns the most recently *computed* snapshot (fps EMA + counters); used
|
|
40
|
+
// by the platform read-back accessors. Cheap.
|
|
41
|
+
const MetricsSnapshot& latest() const { return latest_; }
|
|
42
|
+
|
|
43
|
+
// Strips HTML tags + collapses whitespace + trims. Pure utility, exposed so
|
|
44
|
+
// platform shells that prefer to render their own credit lines can reuse it.
|
|
45
|
+
static std::string stripHtmlToPlain(const std::string& html);
|
|
46
|
+
|
|
47
|
+
private:
|
|
48
|
+
double fpsEma_ = 0.0;
|
|
49
|
+
int metricsTick_ = 0;
|
|
50
|
+
std::string lastCreditHtmlJoined_;
|
|
51
|
+
MetricsSnapshot latest_{};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
} // namespace reactnativecesium
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#include "TileLifecycleManager.hpp"
|
|
2
2
|
|
|
3
|
-
#include <Cesium3DTilesSelection/TileContent.h>
|
|
4
|
-
#include <Cesium3DTilesSelection/TileSelectionState.h>
|
|
5
|
-
|
|
6
3
|
namespace reactnativecesium {
|
|
7
4
|
|
|
8
5
|
void TileLifecycleManager::stampTileUsed(TileGPUResources* resources) {
|
|
@@ -18,13 +15,4 @@ bool TileLifecycleManager::shouldDeferFree(
|
|
|
18
15
|
return resources->lastUsedFrame >= frameNumber_;
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
bool TileLifecycleManager::isRefinementLocked(
|
|
22
|
-
const Cesium3DTilesSelection::Tile& /*tile*/) const {
|
|
23
|
-
// Cesium Native guarantees that free() is not called on tiles that are
|
|
24
|
-
// currently needed for rendering (including refined parent tiles whose
|
|
25
|
-
// children are loading). Therefore, explicit refinement locking is not
|
|
26
|
-
// required here.
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
18
|
} // namespace reactnativecesium
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
#include "../renderer/RenderTypes.hpp"
|
|
4
4
|
|
|
5
|
-
#include <Cesium3DTilesSelection/Tile.h>
|
|
6
|
-
|
|
7
5
|
#include <cstdint>
|
|
8
6
|
|
|
9
7
|
namespace reactnativecesium {
|
|
@@ -16,7 +14,6 @@ public:
|
|
|
16
14
|
void stampTileUsed(TileGPUResources* resources);
|
|
17
15
|
|
|
18
16
|
bool shouldDeferFree(const TileGPUResources* resources) const;
|
|
19
|
-
bool isRefinementLocked(const Cesium3DTilesSelection::Tile& tile) const;
|
|
20
17
|
|
|
21
18
|
private:
|
|
22
19
|
uint64_t frameNumber_ = 0;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2006 D.Ineiev <ineiev@yahoo.co.uk>
|
|
3
|
+
* Copyright (c) 2020 Emeric Grange <emeric.grange@gmail.com>
|
|
4
|
+
*
|
|
5
|
+
* This software is provided 'as-is', without any express or implied warranty.
|
|
6
|
+
* In no event will the authors be held liable for any damages arising from
|
|
7
|
+
* the use of this software.
|
|
8
|
+
*
|
|
9
|
+
* Permission is granted to anyone to use this software for any purpose,
|
|
10
|
+
* including commercial applications, and to alter it and redistribute it
|
|
11
|
+
* freely, subject to the following restrictions:
|
|
12
|
+
*
|
|
13
|
+
* 1. The origin of this software must not be misrepresented; you must not
|
|
14
|
+
* claim that you wrote the original software. If you use this software
|
|
15
|
+
* in a product, an acknowledgment in the product documentation would be
|
|
16
|
+
* appreciated but is not required.
|
|
17
|
+
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
18
|
+
* misrepresented as being the original software.
|
|
19
|
+
* 3. This notice may not be removed or altered from any source distribution.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* This program is designed for the calculation of a geoid undulation at a point
|
|
24
|
+
* whose latitude and longitude is specified.
|
|
25
|
+
*
|
|
26
|
+
* This program is designed to be used with the constants of EGM96 and those of
|
|
27
|
+
* the WGS84(g873) system. The undulation will refer to the WGS84 ellipsoid.
|
|
28
|
+
*
|
|
29
|
+
* It's designed to use the potential coefficient model EGM96 and a set of
|
|
30
|
+
* spherical harmonic coefficients of a correction term.
|
|
31
|
+
* The correction term is composed of several different components, the primary
|
|
32
|
+
* one being the conversion of a height anomaly to a geoid undulation.
|
|
33
|
+
* The principles of this procedure were initially described in the paper:
|
|
34
|
+
* - use of potential coefficient models for geoid undulation determination using
|
|
35
|
+
* a spherical harmonic representation of the height anomaly/geoid undulation
|
|
36
|
+
* difference by R.H. Rapp, Journal of Geodesy, 1996.
|
|
37
|
+
*
|
|
38
|
+
* This program is a modification of the program described in the following report:
|
|
39
|
+
* - a fortran program for the computation of gravimetric quantities from high
|
|
40
|
+
* degree spherical harmonic expansions, Richard H. Rapp, report 334, Department
|
|
41
|
+
* of Geodetic Science and Surveying, the Ohio State University, Columbus, 1982
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
#include "EGM96.h"
|
|
45
|
+
#include "EGM96_data.h"
|
|
46
|
+
|
|
47
|
+
#include <math.h>
|
|
48
|
+
#include <stdlib.h>
|
|
49
|
+
#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
|
|
50
|
+
#include <threads.h>
|
|
51
|
+
#else
|
|
52
|
+
#ifndef thread_local
|
|
53
|
+
#define thread_local _Thread_local
|
|
54
|
+
#endif
|
|
55
|
+
#endif
|
|
56
|
+
|
|
57
|
+
/* ************************************************************************** */
|
|
58
|
+
|
|
59
|
+
#define _coeffs (65341) //!< Size of correction and harmonic coefficients arrays (361*181)
|
|
60
|
+
#define _nmax (360) //!< Maximum degree and orders of harmonic coefficients.
|
|
61
|
+
#define _361 (361)
|
|
62
|
+
|
|
63
|
+
/* ************************************************************************** */
|
|
64
|
+
|
|
65
|
+
double hundu(const double p[_coeffs+1],
|
|
66
|
+
const double sinml[_361+1], const double cosml[_361+1],
|
|
67
|
+
const double gr, const double re)
|
|
68
|
+
{
|
|
69
|
+
// WGS84 gravitational constant in m³/s² (mass of Earth’s atmosphere included)
|
|
70
|
+
const double GM = 0.3986004418e15;
|
|
71
|
+
// WGS84 datum surface equatorial radius
|
|
72
|
+
const double ae = 6378137.0;
|
|
73
|
+
|
|
74
|
+
const double ar = ae / re;
|
|
75
|
+
double arn = ar;
|
|
76
|
+
double ac = 0;
|
|
77
|
+
double a = 0;
|
|
78
|
+
|
|
79
|
+
unsigned k = 3;
|
|
80
|
+
for (unsigned n = 2; n <= _nmax; n++)
|
|
81
|
+
{
|
|
82
|
+
arn *= ar;
|
|
83
|
+
k++;
|
|
84
|
+
double sumc = p[k] * egm96_data[k][0];
|
|
85
|
+
double sum = p[k] * egm96_data[k][2];
|
|
86
|
+
|
|
87
|
+
for (unsigned m = 1; m <= n; m++)
|
|
88
|
+
{
|
|
89
|
+
k++;
|
|
90
|
+
double tempc = (egm96_data[k][0] * cosml[m]) + (egm96_data[k][1] * sinml[m]);
|
|
91
|
+
double temp = (egm96_data[k][2] * cosml[m]) + (egm96_data[k][3] * sinml[m]);
|
|
92
|
+
sumc += p[k] * tempc;
|
|
93
|
+
sum += p[k] * temp;
|
|
94
|
+
}
|
|
95
|
+
ac += sumc;
|
|
96
|
+
a += sum * arn;
|
|
97
|
+
}
|
|
98
|
+
ac += egm96_data[1][0] + (p[2] * egm96_data[2][0]) + (p[3] * ((egm96_data[3][0] * cosml[1]) + (egm96_data[3][1] * sinml[1])));
|
|
99
|
+
|
|
100
|
+
// Add haco = ac/100 to convert height anomaly on the ellipsoid to the undulation
|
|
101
|
+
// Add -0.53m to make undulation refer to the WGS84 ellipsoid
|
|
102
|
+
|
|
103
|
+
return ((a * GM) / (gr * re)) + (ac / 100.0) - 0.53;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void dscml(const double rlon, double sinml[_361+1], double cosml[_361+1])
|
|
107
|
+
{
|
|
108
|
+
const double a = sin(rlon);
|
|
109
|
+
const double b = cos(rlon);
|
|
110
|
+
|
|
111
|
+
sinml[1] = a;
|
|
112
|
+
cosml[1] = b;
|
|
113
|
+
sinml[2] = (2 * b * a);
|
|
114
|
+
cosml[2] = (2 * b * b) - 1;
|
|
115
|
+
|
|
116
|
+
for (unsigned m = 3; m <= _nmax; m++)
|
|
117
|
+
{
|
|
118
|
+
sinml[m] = (2 * b * sinml[m-1]) - sinml[m-2];
|
|
119
|
+
cosml[m] = (2 * b * cosml[m-1]) - cosml[m-2];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/*!
|
|
124
|
+
* \param m: order.
|
|
125
|
+
* \param theta: Colatitude (in radians).
|
|
126
|
+
* \param rleg: Normalized legendre function.
|
|
127
|
+
*
|
|
128
|
+
* This subroutine computes all normalized legendre function in 'rleg'.
|
|
129
|
+
* The dimensions of array 'rleg' must be at least equal to nmax+1.
|
|
130
|
+
* All calculations are in double precision.
|
|
131
|
+
*
|
|
132
|
+
* Original programmer: Oscar L. Colombo, Dept. of Geodetic Science the Ohio State University, August 1980.
|
|
133
|
+
* ineiev: I removed the derivatives, for they are never computed here.
|
|
134
|
+
*/
|
|
135
|
+
void legfdn(const unsigned m, const double theta, double rleg[_361+1])
|
|
136
|
+
{
|
|
137
|
+
static thread_local double drts[1301], dirt[1301];
|
|
138
|
+
static thread_local int ir = 0;
|
|
139
|
+
|
|
140
|
+
const unsigned nmax1 = _nmax + 1;
|
|
141
|
+
const unsigned nmax2p = (2 * _nmax) + 1;
|
|
142
|
+
const unsigned m1 = m + 1;
|
|
143
|
+
const unsigned m2 = m + 2;
|
|
144
|
+
const unsigned m3 = m + 3;
|
|
145
|
+
|
|
146
|
+
unsigned n, n1, n2;
|
|
147
|
+
|
|
148
|
+
if (ir == 0)
|
|
149
|
+
{
|
|
150
|
+
ir = 1;
|
|
151
|
+
for (n = 1; n <= nmax2p; n++)
|
|
152
|
+
{
|
|
153
|
+
drts[n] = sqrt(n);
|
|
154
|
+
dirt[n] = 1 / drts[n];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const double cothet = cos(theta);
|
|
159
|
+
const double sithet = sin(theta);
|
|
160
|
+
|
|
161
|
+
// compute the legendre functions
|
|
162
|
+
double rlnn[_361+1];
|
|
163
|
+
rlnn[1] = 1;
|
|
164
|
+
rlnn[2] = sithet * drts[3];
|
|
165
|
+
for (n1 = 3; n1 <= m1; n1++)
|
|
166
|
+
{
|
|
167
|
+
n = n1 - 1;
|
|
168
|
+
n2 = 2 * n;
|
|
169
|
+
rlnn[n1] = drts[n2 + 1] * dirt[n2] * sithet * rlnn[n];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
switch (m)
|
|
173
|
+
{
|
|
174
|
+
case 1:
|
|
175
|
+
rleg[2] = rlnn[2];
|
|
176
|
+
rleg[3] = drts[5] * cothet * rleg[2];
|
|
177
|
+
break;
|
|
178
|
+
case 0:
|
|
179
|
+
rleg[1] = 1;
|
|
180
|
+
rleg[2] = cothet * drts[3];
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
rleg[m1] = rlnn[m1];
|
|
184
|
+
|
|
185
|
+
if (m2 <= nmax1)
|
|
186
|
+
{
|
|
187
|
+
rleg[m2] = drts[(2 * m1) + 1] * cothet * rleg[m1];
|
|
188
|
+
if (m3 <= nmax1)
|
|
189
|
+
{
|
|
190
|
+
for (n1 = m3; n1 <= nmax1; n1++)
|
|
191
|
+
{
|
|
192
|
+
n = n1 - 1;
|
|
193
|
+
if ((!m && n < 2) || (m == 1 && n < 3)) continue;
|
|
194
|
+
n2 = 2 * n;
|
|
195
|
+
rleg[n1] = drts[n2+1] * dirt[n+m] * dirt[n-m] * ((drts[n2-1] * cothet * rleg[n1-1]) - (drts[n+m-1] * drts[n-m-1] * dirt[n2-3] * rleg[n1-2]));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/*!
|
|
202
|
+
* \param lat: Latitude (in radians).
|
|
203
|
+
* \param lon: Longitude (in radians).
|
|
204
|
+
* \param re: Geocentric radius.
|
|
205
|
+
* \param rlat: Geocentric latitude.
|
|
206
|
+
* \param gr: Normal gravity (m/sec²).
|
|
207
|
+
*
|
|
208
|
+
* This subroutine computes geocentric distance to the point, the geocentric
|
|
209
|
+
* latitude, and an approximate value of normal gravity at the point based the
|
|
210
|
+
* constants of the WGS84(g873) system are used.
|
|
211
|
+
*/
|
|
212
|
+
void radgra(const double lat, const double lon, double *rlat, double *gr, double *re)
|
|
213
|
+
{
|
|
214
|
+
const double a = 6378137.0;
|
|
215
|
+
const double e2 = 0.00669437999013;
|
|
216
|
+
const double geqt = 9.7803253359;
|
|
217
|
+
const double k = 0.00193185265246;
|
|
218
|
+
const double t1 = sin(lat) * sin(lat);
|
|
219
|
+
const double n = a / sqrt(1.0 - (e2 * t1));
|
|
220
|
+
const double t2 = n * cos(lat);
|
|
221
|
+
const double x = t2 * cos(lon);
|
|
222
|
+
const double y = t2 * sin(lon);
|
|
223
|
+
const double z = (n * (1 - e2)) * sin(lat);
|
|
224
|
+
|
|
225
|
+
*re = sqrt((x * x) + (y * y) + (z * z)); // compute the geocentric radius
|
|
226
|
+
*rlat = atan(z / sqrt((x * x) + (y * y))); // compute the geocentric latitude
|
|
227
|
+
*gr = geqt * (1 + (k * t1)) / sqrt(1 - (e2 * t1)); // compute normal gravity (m/sec²)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/*!
|
|
231
|
+
* \brief Compute the geoid undulation from the EGM96 potential coefficient model, for a given latitude and longitude.
|
|
232
|
+
* \param lat: Latitude (in radians).
|
|
233
|
+
* \param lon: Longitude (in radians).
|
|
234
|
+
* \return The geoid undulation / altitude offset (in meters).
|
|
235
|
+
*/
|
|
236
|
+
double undulation(const double lat, const double lon)
|
|
237
|
+
{
|
|
238
|
+
// Allocate large arrays on heap instead of stack to avoid stack overflow
|
|
239
|
+
// on threads with limited stack size (e.g., iOS DispatchQueue threads)
|
|
240
|
+
double* p = (double*)malloc((_coeffs+1) * sizeof(double));
|
|
241
|
+
double* sinml = (double*)malloc((_361+1) * sizeof(double));
|
|
242
|
+
double* cosml = (double*)malloc((_361+1) * sizeof(double));
|
|
243
|
+
double* rleg = (double*)malloc((_361+1) * sizeof(double));
|
|
244
|
+
|
|
245
|
+
if (!p || !sinml || !cosml || !rleg) {
|
|
246
|
+
free(p);
|
|
247
|
+
free(sinml);
|
|
248
|
+
free(cosml);
|
|
249
|
+
free(rleg);
|
|
250
|
+
return 0.0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
double rlat, gr, re;
|
|
254
|
+
|
|
255
|
+
// compute the geocentric latitude, geocentric radius, normal gravity
|
|
256
|
+
radgra(lat, lon, &rlat, &gr, &re);
|
|
257
|
+
rlat = (M_PI / 2) - rlat;
|
|
258
|
+
|
|
259
|
+
const unsigned nmax1 = _nmax + 1;
|
|
260
|
+
for (unsigned j = 1; j <= nmax1; j++)
|
|
261
|
+
{
|
|
262
|
+
unsigned m = j - 1;
|
|
263
|
+
legfdn(m, rlat, rleg);
|
|
264
|
+
for (unsigned i = j ; i <= nmax1; i++)
|
|
265
|
+
{
|
|
266
|
+
p[(((i - 1) * i) / 2) + m + 1] = rleg[i];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
dscml(lon, sinml, cosml);
|
|
270
|
+
|
|
271
|
+
double result = hundu(p, sinml, cosml, gr, re);
|
|
272
|
+
|
|
273
|
+
free(p);
|
|
274
|
+
free(sinml);
|
|
275
|
+
free(cosml);
|
|
276
|
+
free(rleg);
|
|
277
|
+
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* ************************************************************************** */
|
|
282
|
+
|
|
283
|
+
double egm96_compute_altitude_offset(const double latitude, const double longitude)
|
|
284
|
+
{
|
|
285
|
+
const double rad = (180.0 / M_PI);
|
|
286
|
+
return undulation(latitude / rad, longitude / rad);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* ************************************************************************** */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2006 D.Ineiev <ineiev@yahoo.co.uk>
|
|
3
|
+
* Copyright (c) 2020 Emeric Grange <emeric.grange@gmail.com>
|
|
4
|
+
*
|
|
5
|
+
* This software is provided 'as-is', without any express or implied warranty.
|
|
6
|
+
* In no event will the authors be held liable for any damages arising from
|
|
7
|
+
* the use of this software.
|
|
8
|
+
*
|
|
9
|
+
* Permission is granted to anyone to use this software for any purpose,
|
|
10
|
+
* including commercial applications, and to alter it and redistribute it
|
|
11
|
+
* freely, subject to the following restrictions:
|
|
12
|
+
*
|
|
13
|
+
* 1. The origin of this software must not be misrepresented; you must not
|
|
14
|
+
* claim that you wrote the original software. If you use this software
|
|
15
|
+
* in a product, an acknowledgment in the product documentation would be
|
|
16
|
+
* appreciated but is not required.
|
|
17
|
+
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
18
|
+
* misrepresented as being the original software.
|
|
19
|
+
* 3. This notice may not be removed or altered from any source distribution.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#ifndef EGM96_H
|
|
23
|
+
#define EGM96_H
|
|
24
|
+
|
|
25
|
+
#ifdef __cplusplus
|
|
26
|
+
extern "C" {
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
/* ************************************************************************** */
|
|
30
|
+
|
|
31
|
+
/*!
|
|
32
|
+
* \brief Compute the geoid undulation from the EGM96 potential coefficient model, for a given latitude and longitude.
|
|
33
|
+
* \param latitude: Latitude (in degrees).
|
|
34
|
+
* \param longitude: Longitude (in degrees).
|
|
35
|
+
* \return The geoid undulation / altitude offset (in meters).
|
|
36
|
+
*/
|
|
37
|
+
double egm96_compute_altitude_offset(double latitude, double longitude);
|
|
38
|
+
|
|
39
|
+
/* ************************************************************************** */
|
|
40
|
+
|
|
41
|
+
#ifdef __cplusplus
|
|
42
|
+
}
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
#endif // EGM96_H
|