react-native-cesium 1.2.19 → 1.2.21

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.
Files changed (34) hide show
  1. package/README.md +11 -8
  2. package/android/src/main/cpp/CesiumBridgeAndroid.cpp +40 -4
  3. package/android/src/main/cpp/CesiumBridgeAndroid.h +7 -0
  4. package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +3 -0
  5. package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +9 -0
  6. package/cpp/engine/CesiumEngine.cpp +35 -8
  7. package/cpp/engine/CesiumEngine.hpp +3 -0
  8. package/cpp/engine/GeoidConverter.cpp +32 -11
  9. package/cpp/engine/GeoidConverter.hpp +41 -0
  10. package/cpp/engine/test/GeoidConverterTest.cpp +77 -0
  11. package/cpp/engine/test/build_and_run.sh +20 -6
  12. package/ios/CesiumBridge.h +5 -0
  13. package/ios/CesiumBridge.mm +55 -6
  14. package/ios/HybridCesiumView.swift +11 -0
  15. package/lib/typescript/src/specs/cesium-view.nitro.d.ts +27 -3
  16. package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
  17. package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +1 -1
  18. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +9 -0
  19. package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +2 -0
  20. package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +5 -0
  21. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +6 -0
  22. package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +15 -0
  23. package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +7 -0
  24. package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +6 -0
  25. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +1 -0
  26. package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +24 -0
  27. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +2 -0
  28. package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +2 -0
  29. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +2 -0
  30. package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +3 -0
  31. package/nitrogen/generated/shared/json/CesiumViewConfig.json +1 -0
  32. package/package.json +1 -1
  33. package/scripts/cesium/config.mjs +1 -1
  34. package/src/specs/cesium-view.nitro.ts +27 -3
package/README.md CHANGED
@@ -184,7 +184,7 @@ npm run build --prefix node_modules/react-native-cesium
184
184
 
185
185
  **Yarn/npm workspaces:** if `react-native-cesium` is a workspace package in your monorepo, use your tool’s workspace runner, e.g. `yarn workspace react-native-cesium run update` (exact syntax depends on your workspace layout).
186
186
 
187
- - `update` checks out **Cesium Native `v0.65.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
187
+ - `update` checks out **Cesium Native `v0.66.0`** into `vendor/cesium-native` under the package (next to its other files; typically ignored in app repos).
188
188
  - `build` runs `scripts/cesium/build.mjs` (**CMake**, **vcpkg** under `vendor/vcpkg` unless **`VCPKG_ROOT`** is set, **Ninja** on macOS) and writes **`vendor/ios/CesiumNative.xcframework`** and/or **`vendor/android`** depending on **`CESIUM_BUILD_ONLY`**.
189
189
 
190
190
  Then install pods (iOS):
@@ -294,6 +294,7 @@ When you pass a callback ref to `CesiumView`, wrap it with `callback(...)` from
294
294
 
295
295
  | Prop | Type | Default | Description |
296
296
  | --- | --- | --- | --- |
297
+ | `altitudeDatum` | `"msl"` \| `"ellipsoid"` | `"msl"` | Vertical datum of every public altitude: `initialCamera`, `setAltitude`, `teleport`, and the altitude returned by `getActualCamera`, `getDemandCamera`, and `onActualCamera`. `"msl"` is metres above mean sea level. `"ellipsoid"` is WGS84 ellipsoidal height (Android `Location.getAltitude()`). Ellipsoidal heights are converted to mean sea level with EGM96 before the camera integrator, and converted back on read. Switching the prop does not move the camera: later reads use the new unit, and the next altitude or position write is interpreted in that unit. Leave this at `"msl"` for gesture and scripted altitudes. Use `"ellipsoid"` for a GPS ellipsoidal height, and call `setPosition` and `setAltitude` one after the other on the same thread. |
297
298
  | `onMetrics` | `(metrics: CesiumMetrics) => void` | `undefined` | Receives throttled runtime stats and credits text. |
298
299
 
299
300
  **Optional performance / quality knobs**
@@ -348,7 +349,7 @@ type CameraState = {
348
349
  | --- | --- |------------------------------|-------------------------------------------------------------------------------------------------------|
349
350
  | `latitude` | `number` | `-90..90` | Camera latitude in degrees. |
350
351
  | `longitude` | `number` | `-180..180` | Camera longitude in degrees. |
351
- | `altitude` | `number` | Meters above sea level (MSL) | Camera height in meters. |
352
+ | `altitude` | `number` | Metres | Camera height. Mean sea level unless `altitudeDatum` is `"ellipsoid"`, in which case this is WGS84 ellipsoidal height. Writes and reads use the same unit. |
352
353
  | `heading` | `number` | Degrees | Compass direction the camera faces. Example: `0` faces north, `90` faces east. |
353
354
  | `pitch` | `number` | Degrees | Tilt angle. Negative values look downward toward terrain; positive values tilt up toward horizon/sky. |
354
355
  | `roll` | `number` | Degrees | Bank/rotation around forward axis. Positive right bank, negative left bank. |
@@ -385,15 +386,15 @@ Each axis (position, altitude, heading, attitude, viewCorrection, verticalFov) h
385
386
 
386
387
  | Method | Signature | Description |
387
388
  | --- | --- | --- |
388
- | `setPosition` | `(latitude: number, longitude: number) => void` | New geographic position **demand** (degrees). Each call stamps the measurement with the native steady-clock time of arrival and runs one α-β update against the predicted state at that instant. Latitude is clamped to ±90 on extrapolation; longitude is continuous (no wrap normalisation) so shortest-arc residual maths stays correct across the antimeridian. |
389
- | `setAltitude` | `(altitudeMeters: number) => void` | New altitude demand (metres MSL). |
389
+ | `setPosition` | `(latitude: number, longitude: number) => void` | New geographic position **demand** (degrees). Each call stamps the measurement with the native steady-clock time of arrival and runs one α-β update against the predicted state at that instant. Latitude is clamped to ±90 on extrapolation; longitude is continuous (no wrap normalisation) so shortest-arc residual maths stays correct across the antimeridian. When `altitudeDatum` is `"ellipsoid"`, a horizontal move keeps the demanded ellipsoidal height and recomputes mean sea level at the new point. `"msl"` leaves altitude unchanged. |
390
+ | `setAltitude` | `(altitudeMeters: number) => void` | New altitude demand, in metres. Mean sea level unless `altitudeDatum` is `"ellipsoid"`, in which case it is WGS84 height and is converted to mean sea level before the integrator. A later read reports the same sample back in this unit. |
390
391
  | `setHeading` | `(headingDeg: number) => void` | New heading demand (degrees; `0` = north, increasing clockwise). The integrator computes the shortest-arc residual, so feeding it `359 → 1` rotates 2° clockwise, not 358° counter-clockwise. |
391
392
  | `setAttitude` | `(pitchDeg: number, rollDeg: number) => void` | New pitch and roll demand (degrees). Bundled because almost every IMU emits them together — but you can pass the previous value for either axis if only one of them has changed. |
392
393
  | `setViewCorrection` | `(q: Quaternion) => void` | New camera-space rotation applied **after** HPR. SLERPed toward the latest demand each frame. Use for boresight calibration, screen-fixed HUD alignment, etc. Non-unit values are normalised on the native side. |
393
394
  | `setVerticalFov` | `(deg: number) => void` | New vertical FOV demand (degrees). Clamped to `20..100` on the native side. |
394
- | `teleport` | `(camera: CameraState) => void` | Hard scene jump — atomically resets every DoF (value, velocity, demand) to `camera`. Bypasses the integrator entirely. Use for `Fly to coordinate` actions or seeding from saved state. |
395
- | `getActualCamera` | `() => Promise<CameraState>` | Most recently rendered camera (post-integration). This is what the user is currently looking at and what a HUD overlay should mirror. Differs from `getDemandCamera()` while a glide is in progress or when the terrain-floor clamp has raised the altitude. |
396
- | `getDemandCamera` | `() => Promise<CameraState>` | What the consumer last asked for (per-DoF demand). Useful for diagnostics, or for showing the requested camera alongside the rendered one. |
395
+ | `teleport` | `(camera: CameraState) => void` | Hard scene jump — atomically resets every DoF (value, velocity, demand) to `camera`. Bypasses the integrator entirely. Use for `Fly to coordinate` actions or seeding from saved state. `camera.altitude` uses the unit selected by `altitudeDatum`. |
396
+ | `getActualCamera` | `() => Promise<CameraState>` | Most recently rendered camera (post-integration). This is what the user is currently looking at and what a HUD overlay should mirror. Differs from `getDemandCamera()` while a glide is in progress or when the terrain-floor clamp has raised the altitude. Altitude is in the unit selected by `altitudeDatum`. |
397
+ | `getDemandCamera` | `() => Promise<CameraState>` | What the consumer last asked for (per-DoF demand). Useful for diagnostics, or for showing the requested camera alongside the rendered one. Altitude is in the unit selected by `altitudeDatum`. |
397
398
  | `getViewCorrection` | `() => Promise<Quaternion>` | Current view-correction quaternion (smoothed toward the latest demand). Identity (`w=1, x=y=z=0`) if you have never called `setViewCorrection`. |
398
399
 
399
400
  #### Threading: setters vs getters
@@ -415,7 +416,7 @@ Avoid assuming **main-thread-only** vs **JS-thread-only** labels beyond the abov
415
416
 
416
417
  The integrator tracks two camera states simultaneously:
417
418
 
418
- - **Demand** — what the consumer asked for, exactly as it was passed to the setters. `getDemandCamera()` returns this.
419
+ - **Demand** — what the consumer asked for. `getDemandCamera()` returns this, with altitude in the unit selected by `altitudeDatum`.
419
420
  - **Actual** — what the integrator extrapolated to and what was passed to the GPU on the last frame. `getActualCamera()` returns this.
420
421
 
421
422
  For a steady GPS feed with no terrain clamp the two values converge within a few frames. They diverge when:
@@ -461,6 +462,8 @@ imuSubscription.on('attitude', (att) => {
461
462
  })
462
463
  ```
463
464
 
465
+ `fix.altitudeMsl` matches the default `altitudeDatum="msl"`. Android `Location.getAltitude()` is a WGS84 ellipsoidal height: set `altitudeDatum="ellipsoid"` and pass that value to `setAltitude` instead. Call `setPosition` and `setAltitude` one after the other on the same thread so the geoid is applied at that fix.
466
+
464
467
  Each setter is synchronous, takes the integrator's mutex briefly, and is safe to call at any rate from any thread.
465
468
 
466
469
  ### `CesiumMetrics`
@@ -1,6 +1,7 @@
1
1
  #include "CesiumBridgeAndroid.h"
2
2
 
3
3
  #include "engine/EngineTunables.hpp"
4
+ #include "engine/GeoidConverter.hpp"
4
5
  #include "engine/TerrainFloor.hpp"
5
6
  #include "vulkan/VulkanBackend.h"
6
7
 
@@ -149,13 +150,34 @@ void CesiumBridgeAndroid::updateImageryAssetId(int64_t assetId) {
149
150
 
150
151
  void CesiumBridgeAndroid::setPosition(double lat, double lon) {
151
152
  if (!initialized_ || !integrator_) return;
153
+ const bool ellipsoid =
154
+ altitudeIsEllipsoid_.load(std::memory_order_acquire);
155
+ double adjustedMsl = 0.0;
156
+ bool adjustAltitude = false;
157
+ if (ellipsoid) {
158
+ const auto demand = integrator_->getDemand();
159
+ adjustedMsl = reactnativecesium::mslAfterHorizontalMove(
160
+ demand.latitude, demand.longitude, demand.altitude, lat, lon);
161
+ adjustAltitude = std::abs(adjustedMsl - demand.altitude) >
162
+ reactnativecesium::kHorizontalMoveMslEpsilonMeters;
163
+ }
152
164
  integrator_->setPosition(lat, lon);
165
+ if (adjustAltitude) {
166
+ integrator_->setAltitude(adjustedMsl);
167
+ }
153
168
  forceRender_.store(true, std::memory_order_release);
154
169
  }
155
170
 
171
+ void CesiumBridgeAndroid::setAltitudeDatumEllipsoid(bool ellipsoid) {
172
+ altitudeIsEllipsoid_.store(ellipsoid, std::memory_order_release);
173
+ }
174
+
156
175
  void CesiumBridgeAndroid::setAltitude(double alt) {
157
176
  if (!initialized_ || !integrator_) return;
158
- integrator_->setAltitude(alt);
177
+ const auto demand = integrator_->getDemand();
178
+ integrator_->setAltitude(reactnativecesium::demandAltitudeToMslMeters(
179
+ demand.latitude, demand.longitude, alt,
180
+ altitudeIsEllipsoid_.load(std::memory_order_acquire)));
159
181
  forceRender_.store(true, std::memory_order_release);
160
182
  }
161
183
 
@@ -184,7 +206,8 @@ void CesiumBridgeAndroid::teleport(double lat, double lon, double alt,
184
206
  reactnativecesium::CameraParams p;
185
207
  p.latitude = lat;
186
208
  p.longitude = lon;
187
- p.altitude = alt;
209
+ p.altitude = reactnativecesium::demandAltitudeToMslMeters(
210
+ lat, lon, alt, altitudeIsEllipsoid_.load(std::memory_order_acquire));
188
211
  p.heading = heading;
189
212
  p.pitch = pitch;
190
213
  p.roll = roll;
@@ -338,7 +361,11 @@ double CesiumBridgeAndroid::readActualLongitude() {
338
361
  return integrator_ ? integrator_->getActual().longitude : 0.0;
339
362
  }
340
363
  double CesiumBridgeAndroid::readActualAltitude() {
341
- return integrator_ ? integrator_->getActual().altitude : 0.0;
364
+ if (!integrator_) return 0.0;
365
+ const auto actual = integrator_->getActual();
366
+ return reactnativecesium::mslToDemandAltitudeMeters(
367
+ actual.latitude, actual.longitude, actual.altitude,
368
+ altitudeIsEllipsoid_.load(std::memory_order_acquire));
342
369
  }
343
370
  double CesiumBridgeAndroid::readActualHeading() {
344
371
  return integrator_ ? integrator_->getActual().heading : 0.0;
@@ -362,7 +389,11 @@ double CesiumBridgeAndroid::readDemandLongitude() {
362
389
  return integrator_ ? integrator_->getDemand().longitude : 0.0;
363
390
  }
364
391
  double CesiumBridgeAndroid::readDemandAltitude() {
365
- return integrator_ ? integrator_->getDemand().altitude : 0.0;
392
+ if (!integrator_) return 0.0;
393
+ const auto demand = integrator_->getDemand();
394
+ return reactnativecesium::mslToDemandAltitudeMeters(
395
+ demand.latitude, demand.longitude, demand.altitude,
396
+ altitudeIsEllipsoid_.load(std::memory_order_acquire));
366
397
  }
367
398
  double CesiumBridgeAndroid::readDemandHeading() {
368
399
  return integrator_ ? integrator_->getDemand().heading : 0.0;
@@ -453,6 +484,11 @@ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetPosition(
453
484
  getBridge(ptr)->setPosition(lat, lon);
454
485
  }
455
486
  JNIEXPORT void JNICALL
487
+ Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetAltitudeDatumEllipsoid(
488
+ JNIEnv*, jobject, jlong ptr, jboolean ellipsoid) {
489
+ getBridge(ptr)->setAltitudeDatumEllipsoid(ellipsoid == JNI_TRUE);
490
+ }
491
+ JNIEXPORT void JNICALL
456
492
  Java_com_margelo_nitro_reactnativecesium_CesiumBridgeJNI_nativeSetAltitude(
457
493
  JNIEnv*, jobject, jlong ptr, jdouble alt) {
458
494
  getBridge(ptr)->setAltitude(alt);
@@ -48,6 +48,10 @@ struct CesiumBridgeAndroid {
48
48
 
49
49
  // ── Per-DoF camera demand setters ──────────────────────────────────────
50
50
  void setPosition(double lat, double lon);
51
+ // When true, altitudes passed to setAltitude/teleport are WGS84 ellipsoidal
52
+ // heights. They are converted to MSL before the integrator, and altitude
53
+ // readback is converted back. A horizontal move keeps that ellipsoidal height.
54
+ void setAltitudeDatumEllipsoid(bool ellipsoid);
51
55
  void setAltitude(double alt);
52
56
  void setHeading(double headingDeg);
53
57
  void setAttitude(double pitchDeg, double rollDeg);
@@ -138,6 +142,9 @@ private:
138
142
  int viewportWidth_ = 0;
139
143
  int viewportHeight_ = 0;
140
144
  bool initialized_ = false;
145
+ // Written from the render thread when the prop changes; read from the
146
+ // caller thread inside setAltitude / setPosition / teleport / readback.
147
+ std::atomic<bool> altitudeIsEllipsoid_{false};
141
148
 
142
149
  // Force-render flag — set on every demand change so the render loop knows
143
150
  // to dispatch at least one frame.
@@ -27,6 +27,8 @@ class CesiumBridgeJNI {
27
27
 
28
28
  // ── Per-DoF camera setters ───────────────────────────────────────────────
29
29
  fun setPosition(lat: Double, lon: Double) = nativeSetPosition(nativePtr, lat, lon)
30
+ fun setAltitudeDatumEllipsoid(ellipsoid: Boolean) =
31
+ nativeSetAltitudeDatumEllipsoid(nativePtr, ellipsoid)
30
32
  fun setAltitude(alt: Double) = nativeSetAltitude(nativePtr, alt)
31
33
  fun setHeading(headingDeg: Double) = nativeSetHeading(nativePtr, headingDeg)
32
34
  fun setAttitude(pitchDeg: Double, rollDeg: Double) = nativeSetAttitude(nativePtr, pitchDeg, rollDeg)
@@ -107,6 +109,7 @@ class CesiumBridgeJNI {
107
109
  private external fun nativeUpdateImageryAssetId(ptr: Long, assetId: Long)
108
110
 
109
111
  private external fun nativeSetPosition(ptr: Long, lat: Double, lon: Double)
112
+ private external fun nativeSetAltitudeDatumEllipsoid(ptr: Long, ellipsoid: Boolean)
110
113
  private external fun nativeSetAltitude(ptr: Long, alt: Double)
111
114
  private external fun nativeSetHeading(ptr: Long, headingDeg: Double)
112
115
  private external fun nativeSetAttitude(ptr: Long, pitchDeg: Double, rollDeg: Double)
@@ -105,6 +105,14 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
105
105
  override var ionImageryAssetId: Double = 1.0
106
106
  set(value) { field = value; postToRender { bridge?.updateImageryAssetId(value.toLong()) } }
107
107
 
108
+ /** `"msl"` (default) or `"ellipsoid"`. Ellipsoid heights are converted to MSL. */
109
+ override var altitudeDatum: String? = "msl"
110
+ set(value) {
111
+ field = value
112
+ val ellipsoid = value == "ellipsoid"
113
+ postToRender { bridge?.setAltitudeDatumEllipsoid(ellipsoid) }
114
+ }
115
+
108
116
  // ── Optional perf / quality knobs ────────────────────────────────────────
109
117
  override var maximumCachedMiB: Double? = null
110
118
  set(value) { field = value; if (value != null) postToRender { bridge?.setMaximumCachedMiB(value.toInt()) } }
@@ -519,6 +527,7 @@ class HybridCesiumView(private val appContext: Context) : HybridCesiumViewSpec()
519
527
  // sqliteCacheMaxRows and taskProcessorThreads are applied before init() in
520
528
  // surfaceCreated; calling them here (after buildEngine) has no effect.
521
529
  minAltitudeAboveTerrain?.let { b.setMinAltitudeAboveTerrain(it.toFloat()) }
530
+ b.setAltitudeDatumEllipsoid(altitudeDatum == "ellipsoid")
522
531
  }
523
532
 
524
533
  /**
@@ -212,6 +212,21 @@ void CesiumEngine::shutdown() {
212
212
  creditSystem_.reset();
213
213
  }
214
214
 
215
+ void CesiumEngine::drainAsyncWork() {
216
+ // Worker tasks post main-thread continuations and return. Those
217
+ // continuations (Ion raster-overlay loads in particular) can queue more
218
+ // worker tasks, so pump both sides until the pool stays idle.
219
+ for (int pass = 0; pass < 16; ++pass) {
220
+ if (taskProcessor_) {
221
+ taskProcessor_->waitUntilIdle();
222
+ }
223
+ asyncSystem_.dispatchMainThreadTasks();
224
+ }
225
+ if (taskProcessor_) {
226
+ taskProcessor_->waitUntilIdle();
227
+ }
228
+ }
229
+
215
230
  void CesiumEngine::updateConfig(const EngineConfig& config) {
216
231
  // Only token / asset id changes force a full tileset rebuild. Everything
217
232
  // else is mutated in place via tileset_->getOptions() to avoid the very
@@ -381,8 +396,12 @@ void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
381
396
  if (!tileset_) return;
382
397
  if (targetAssetId == currentImageryAssetId_) return;
383
398
 
399
+ // Same order as destroyTileset: detach, drain in-flight Ion raster work
400
+ // while this engine still owns the overlay, then release it. Releasing
401
+ // currentOverlay_ before that drain frees it under those destructors.
384
402
  if (currentOverlay_) {
385
403
  tileset_->getOverlays().remove(currentOverlay_);
404
+ drainAsyncWork();
386
405
  currentOverlay_ = nullptr;
387
406
  }
388
407
  currentImageryAssetId_ = (targetAssetId <= 0) ? 1 : targetAssetId;
@@ -407,19 +426,27 @@ void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
407
426
  }
408
427
 
409
428
  void CesiumEngine::destroyTileset() {
410
- currentOverlay_ = nullptr;
411
- currentImageryAssetId_ = 1;
412
- tileset_.reset();
413
- if (taskProcessor_) {
414
- taskProcessor_->waitUntilIdle();
429
+ // Detach the imagery overlay while this engine still owns it. Dropping
430
+ // currentOverlay_ before in-flight IonRasterOverlay tasks finish frees the
431
+ // overlay out from under their destructors (heap corruption).
432
+ if (tileset_ && currentOverlay_) {
433
+ tileset_->getOverlays().remove(currentOverlay_);
415
434
  }
416
- // every TileGPUResources* baked into cachedDraws_ has just been freed.
417
- // Clearing the cache guarantees no dangling pointer survives into the next
418
- // updateFrame and forces a clean slow-path rebuild.
435
+ drainAsyncWork();
436
+
437
+ // Drop draw-cache pointers before the tileset frees TileGPUResources.
419
438
  lastDrawSignature_ = 0;
420
439
  stableSigFrames_ = 0;
421
440
  cachedDraws_.clear();
422
441
  clearResidentTerrain();
442
+
443
+ tileset_.reset();
444
+ currentImageryAssetId_ = 1;
445
+ // Tileset destruction schedules further async cleanup. Drain that while
446
+ // this engine still owns the overlay, then release it. Dropping the
447
+ // overlay first frees it under those continuations.
448
+ drainAsyncWork();
449
+ currentOverlay_ = nullptr;
423
450
  }
424
451
 
425
452
  void CesiumEngine::setImageryAssetId(int64_t assetId) {
@@ -156,6 +156,9 @@ public:
156
156
  private:
157
157
  void createTileset();
158
158
  void destroyTileset();
159
+ // Wait until worker tasks and the main-thread continuations they post have
160
+ // finished. Safe to call with or without a live tileset.
161
+ void drainAsyncWork();
159
162
 
160
163
  // Apply runtime-mutable bits of `cfg` to the live tileset. Returns true if a
161
164
  // value actually changed (callers can use this to trigger force-render).
@@ -3,34 +3,55 @@
3
3
  #include "../third_party/egm96/EGM96.h"
4
4
 
5
5
  #include <cmath>
6
+ #include <limits>
6
7
 
7
8
  namespace reactnativecesium {
8
9
 
9
10
  namespace {
10
11
 
11
12
  double normalizeLongitudeDegrees(double longitudeDeg) {
12
- if (!std::isfinite(longitudeDeg)) return 0.0;
13
13
  double wrapped = std::fmod(longitudeDeg + 180.0, 360.0);
14
14
  if (wrapped < 0.0) wrapped += 360.0;
15
15
  return wrapped - 180.0;
16
16
  }
17
17
 
18
+ // Geoid undulation N, where HAE = MSL + N. NaN when the position or the
19
+ // model result is non-finite; callers then return the input height unchanged.
20
+ double geoidUndulationMeters(double latitudeDeg, double longitudeDeg) {
21
+ if (!std::isfinite(latitudeDeg) || !std::isfinite(longitudeDeg)) {
22
+ return std::numeric_limits<double>::quiet_NaN();
23
+ }
24
+
25
+ const double undulation = egm96_compute_altitude_offset(
26
+ latitudeDeg, normalizeLongitudeDegrees(longitudeDeg));
27
+ if (!std::isfinite(undulation)) {
28
+ return std::numeric_limits<double>::quiet_NaN();
29
+ }
30
+ return undulation;
31
+ }
32
+
33
+ double applyUndulation(double heightMeters,
34
+ double latitudeDeg,
35
+ double longitudeDeg,
36
+ double sign) {
37
+ if (!std::isfinite(heightMeters)) return heightMeters;
38
+ const double undulation = geoidUndulationMeters(latitudeDeg, longitudeDeg);
39
+ if (!std::isfinite(undulation)) return heightMeters;
40
+ return heightMeters + sign * undulation;
41
+ }
42
+
18
43
  } // namespace
19
44
 
20
45
  double mslToEllipsoidMeters(double latitudeDeg,
21
46
  double longitudeDeg,
22
47
  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);
48
+ return applyUndulation(mslMeters, latitudeDeg, longitudeDeg, 1.0);
49
+ }
31
50
 
32
- if (!std::isfinite(geoidOffset)) return mslMeters;
33
- return mslMeters + geoidOffset;
51
+ double ellipsoidToMslMeters(double latitudeDeg,
52
+ double longitudeDeg,
53
+ double ellipsoidMeters) {
54
+ return applyUndulation(ellipsoidMeters, latitudeDeg, longitudeDeg, -1.0);
34
55
  }
35
56
 
36
57
  } // namespace reactnativecesium
@@ -7,4 +7,45 @@ double mslToEllipsoidMeters(double latitudeDeg,
7
7
  double longitudeDeg,
8
8
  double mslMeters);
9
9
 
10
+ /// Converts WGS84 ellipsoidal height (HAE) to orthometric height (MSL).
11
+ double ellipsoidToMslMeters(double latitudeDeg,
12
+ double longitudeDeg,
13
+ double ellipsoidMeters);
14
+
15
+ /// `ellipsoidHeight` selects HAE→MSL. Otherwise `altitudeMeters` is already MSL.
16
+ inline double demandAltitudeToMslMeters(double latitudeDeg,
17
+ double longitudeDeg,
18
+ double altitudeMeters,
19
+ bool ellipsoidHeight) {
20
+ if (!ellipsoidHeight) return altitudeMeters;
21
+ return ellipsoidToMslMeters(latitudeDeg, longitudeDeg, altitudeMeters);
22
+ }
23
+
24
+ /// `ellipsoidHeight` selects MSL→HAE. Otherwise `mslMeters` is already the
25
+ /// demand datum. Inverse of `demandAltitudeToMslMeters`.
26
+ inline double mslToDemandAltitudeMeters(double latitudeDeg,
27
+ double longitudeDeg,
28
+ double mslMeters,
29
+ bool ellipsoidHeight) {
30
+ if (!ellipsoidHeight) return mslMeters;
31
+ return mslToEllipsoidMeters(latitudeDeg, longitudeDeg, mslMeters);
32
+ }
33
+
34
+ /// Mean-sea-level change below this is left alone when a horizontal move
35
+ /// re-expresses an ellipsoidal height. One centimetre.
36
+ inline constexpr double kHorizontalMoveMslEpsilonMeters = 0.01;
37
+
38
+ /// Keeps the same ellipsoidal height when the horizontal position changes.
39
+ /// `oldMslMeters` is mean sea level at the old point; the result is mean sea
40
+ /// level at the new point.
41
+ inline double mslAfterHorizontalMove(double oldLatitudeDeg,
42
+ double oldLongitudeDeg,
43
+ double oldMslMeters,
44
+ double newLatitudeDeg,
45
+ double newLongitudeDeg) {
46
+ const double hae =
47
+ mslToEllipsoidMeters(oldLatitudeDeg, oldLongitudeDeg, oldMslMeters);
48
+ return ellipsoidToMslMeters(newLatitudeDeg, newLongitudeDeg, hae);
49
+ }
50
+
10
51
  } // namespace reactnativecesium
@@ -0,0 +1,77 @@
1
+ // Host check for the EGM96 MSL <-> ellipsoid conversion.
2
+ //
3
+ // clang++ -std=c++20 -I cpp -o /tmp/geoid cpp/engine/test/GeoidConverterTest.cpp \
4
+ // cpp/engine/GeoidConverter.cpp cpp/third_party/egm96/EGM96.c
5
+ //
6
+ // Or: bash cpp/engine/test/build_and_run.sh
7
+
8
+ #include "../GeoidConverter.hpp"
9
+
10
+ #include <cmath>
11
+ #include <cstdio>
12
+
13
+ using namespace reactnativecesium;
14
+
15
+ static int g_failures = 0;
16
+
17
+ static void check(bool ok, const char* label) {
18
+ if (!ok) {
19
+ std::fprintf(stderr, "FAIL [%s]\n", label);
20
+ ++g_failures;
21
+ return;
22
+ }
23
+ std::fprintf(stderr, "PASS [%s]\n", label);
24
+ }
25
+
26
+ static void checkInverse(const char* label, double lat, double lon, double msl) {
27
+ const double hae = mslToEllipsoidMeters(lat, lon, msl);
28
+ const double back = ellipsoidToMslMeters(lat, lon, hae);
29
+ const double undulation = hae - msl;
30
+ if (!std::isfinite(hae) || !std::isfinite(back) || std::fabs(back - msl) > 1e-6) {
31
+ std::fprintf(stderr, "FAIL [%s]: msl=%.3f hae=%.3f back=%.3f\n", label, msl, hae, back);
32
+ ++g_failures;
33
+ return;
34
+ }
35
+ if (std::fabs(undulation) < 1.0) {
36
+ std::fprintf(stderr, "FAIL [%s]: undulation %.3f m is unexpectedly ~0\n", label, undulation);
37
+ ++g_failures;
38
+ return;
39
+ }
40
+ std::fprintf(stderr, "PASS [%s] undulation %.2f m\n", label, undulation);
41
+ }
42
+
43
+ static void checkDemandRoundTrip(const char* label, double lat, double lon, double msl) {
44
+ const double hae = mslToDemandAltitudeMeters(lat, lon, msl, true);
45
+ const double back = demandAltitudeToMslMeters(lat, lon, hae, true);
46
+ check(std::isfinite(hae) && std::isfinite(back) && std::fabs(back - msl) < 1e-6, label);
47
+
48
+ const double mslIdentity = demandAltitudeToMslMeters(lat, lon, msl, false);
49
+ const double haeIdentity = mslToDemandAltitudeMeters(lat, lon, msl, false);
50
+ check(mslIdentity == msl && haeIdentity == msl, "datum_msl_identity");
51
+ }
52
+
53
+ int main() {
54
+ // UK geoid is about +45 m. Alps about +50 m. Indian Ocean is negative.
55
+ checkInverse("uk", 51.5, -0.1, 1000.0);
56
+ checkInverse("alps", 46.0, 8.0, 2000.0);
57
+ checkInverse("indian_ocean", -10.0, 80.0, 50.0);
58
+
59
+ const double nan = std::nan("");
60
+ check(ellipsoidToMslMeters(nan, 0.0, 10.0) == 10.0, "ellipsoid_non_finite");
61
+ check(mslToEllipsoidMeters(nan, 0.0, 10.0) == 10.0, "msl_non_finite");
62
+
63
+ checkDemandRoundTrip("demand_round_trip", 51.5, -0.1, 1000.0);
64
+
65
+ constexpr double kUkLat = 51.5;
66
+ constexpr double kUkLon = -0.1;
67
+ constexpr double kOceanLat = -10.0;
68
+ constexpr double kOceanLon = 80.0;
69
+ constexpr double kMsl = 1000.0;
70
+ const double hae = mslToEllipsoidMeters(kUkLat, kUkLon, kMsl);
71
+ const double moved = mslAfterHorizontalMove(kUkLat, kUkLon, kMsl, kOceanLat, kOceanLon);
72
+ const double movedHae = mslToEllipsoidMeters(kOceanLat, kOceanLon, moved);
73
+ check(std::isfinite(moved) && std::fabs(movedHae - hae) < 1e-6, "move_preserves_hae");
74
+ check(std::fabs(moved - kMsl) > 1.0, "move_changes_msl");
75
+
76
+ return g_failures == 0 ? 0 : 1;
77
+ }
@@ -1,12 +1,29 @@
1
1
  #!/usr/bin/env bash
2
- # Compile and run CameraIntegratorTest against the iOS simulator-slice headers
3
- # from the vendored Cesium Native XCFramework. Lightweight smoke test —
4
- # not part of the production build.
2
+ # Compile and run the host engine tests. GeoidConverterTest needs only EGM96.
3
+ # CameraIntegratorTest needs the iOS simulator-slice headers from the vendored
4
+ # Cesium Native XCFramework. Lightweight smoke tests — not part of the
5
+ # production build.
5
6
 
6
7
  set -euo pipefail
7
8
 
8
9
  ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
9
10
  HEADERS="$ROOT/vendor/ios/CesiumNative.xcframework/ios-arm64-simulator/Headers"
11
+ OUT="$(mktemp -d)"
12
+ echo "Building into $OUT..."
13
+
14
+ xcrun -sdk macosx clang -std=c11 -O0 -g \
15
+ -I"$ROOT/cpp" \
16
+ -c "$ROOT/cpp/third_party/egm96/EGM96.c" \
17
+ -o "$OUT/EGM96.o"
18
+
19
+ xcrun -sdk macosx clang++ -std=c++20 -O0 -g \
20
+ -I"$ROOT/cpp" \
21
+ -o "$OUT/geoid" \
22
+ "$ROOT/cpp/engine/test/GeoidConverterTest.cpp" \
23
+ "$ROOT/cpp/engine/GeoidConverter.cpp" \
24
+ "$OUT/EGM96.o"
25
+
26
+ "$OUT/geoid"
10
27
 
11
28
  if [[ ! -d "$HEADERS" ]]; then
12
29
  echo "Cesium Native headers not found at $HEADERS"
@@ -14,9 +31,6 @@ if [[ ! -d "$HEADERS" ]]; then
14
31
  exit 1
15
32
  fi
16
33
 
17
- OUT="$(mktemp -d)"
18
- echo "Building into $OUT..."
19
-
20
34
  xcrun -sdk macosx clang++ -std=c++20 -O0 -g \
21
35
  -DGLM_FORCE_DEPTH_ZERO_TO_ONE=1 \
22
36
  -I"$ROOT/cpp" -I"$HEADERS" \
@@ -33,6 +33,11 @@ NS_ASSUME_NONNULL_BEGIN
33
33
  // instant. Pitch / roll are bundled because they typically come from a
34
34
  // single IMU sample.
35
35
  - (void)setPositionLatitude:(double)lat longitude:(double)lon;
36
+ /// When YES, altitudes passed to setAltitude: and teleport are WGS84
37
+ /// ellipsoidal heights. They are converted to MSL before the integrator, and
38
+ /// altitude readback is converted back. A horizontal move keeps that
39
+ /// ellipsoidal height.
40
+ - (void)setAltitudeDatumEllipsoid:(BOOL)ellipsoid;
36
41
  - (void)setAltitude:(double)alt;
37
42
  - (void)setHeadingDeg:(double)deg;
38
43
  - (void)setAttitudePitch:(double)pitch roll:(double)roll;
@@ -4,6 +4,7 @@
4
4
  #include "engine/CameraIntegrator.hpp"
5
5
  #include "engine/CesiumEngine.hpp"
6
6
  #include "engine/EngineTunables.hpp"
7
+ #include "engine/GeoidConverter.hpp"
7
8
  #include "engine/MetricsAggregator.hpp"
8
9
  #include "engine/TerrainFloor.hpp"
9
10
  #include "metal/MetalBackend.h"
@@ -35,6 +36,9 @@
35
36
  int _viewportHeight;
36
37
  BOOL _initialized;
37
38
  BOOL _suspended;
39
+ // Written when the prop changes; read from the caller thread inside
40
+ // setAltitude / setPosition / teleport / readback.
41
+ std::atomic<bool> _altitudeIsEllipsoid;
38
42
 
39
43
  // Last wall-clock tick used to compute the metrics-only frame dt EMA. Kept
40
44
  // per-instance (not a function-local static) so a hot reload that destroys
@@ -65,8 +69,9 @@
65
69
  if (self) {
66
70
  _viewportWidth = width;
67
71
  _viewportHeight = height;
68
- _initialized = NO;
69
- _suspended = NO;
72
+ _initialized = NO;
73
+ _suspended = NO;
74
+ _altitudeIsEllipsoid.store(false, std::memory_order_relaxed);
70
75
  _lastTickSec = 0.0;
71
76
  _forceRender.store(true, std::memory_order_release);
72
77
  _cacheDir = [cacheDir copy];
@@ -184,13 +189,45 @@
184
189
 
185
190
  - (void)setPositionLatitude:(double)lat longitude:(double)lon {
186
191
  if (!_initialized) return;
192
+ const bool ellipsoid = _altitudeIsEllipsoid.load(std::memory_order_acquire);
193
+ double adjustedMsl = 0.0;
194
+ bool adjustAltitude = false;
195
+ if (ellipsoid) {
196
+ const auto demand = _integrator->getDemand();
197
+ adjustedMsl = reactnativecesium::mslAfterHorizontalMove(
198
+ demand.latitude, demand.longitude, demand.altitude, lat, lon);
199
+ adjustAltitude = std::abs(adjustedMsl - demand.altitude) >
200
+ reactnativecesium::kHorizontalMoveMslEpsilonMeters;
201
+ }
187
202
  _integrator->setPosition(lat, lon);
203
+ if (adjustAltitude) {
204
+ _integrator->setAltitude(adjustedMsl);
205
+ }
188
206
  _forceRender.store(true, std::memory_order_release);
189
207
  }
190
208
 
209
+ - (void)setAltitudeDatumEllipsoid:(BOOL)ellipsoid {
210
+ _altitudeIsEllipsoid.store(ellipsoid == YES, std::memory_order_release);
211
+ }
212
+
213
+ - (double)mslAltitude:(double)alt latitude:(double)lat longitude:(double)lon {
214
+ return reactnativecesium::demandAltitudeToMslMeters(
215
+ lat, lon, alt, _altitudeIsEllipsoid.load(std::memory_order_acquire));
216
+ }
217
+
218
+ - (double)demandAltitudeFromMsl:(double)msl
219
+ latitude:(double)lat
220
+ longitude:(double)lon {
221
+ return reactnativecesium::mslToDemandAltitudeMeters(
222
+ lat, lon, msl, _altitudeIsEllipsoid.load(std::memory_order_acquire));
223
+ }
224
+
191
225
  - (void)setAltitude:(double)alt {
192
226
  if (!_initialized) return;
193
- _integrator->setAltitude(alt);
227
+ const auto demand = _integrator->getDemand();
228
+ _integrator->setAltitude([self mslAltitude:alt
229
+ latitude:demand.latitude
230
+ longitude:demand.longitude]);
194
231
  _forceRender.store(true, std::memory_order_release);
195
232
  }
196
233
 
@@ -223,7 +260,7 @@
223
260
  reactnativecesium::CameraParams p;
224
261
  p.latitude = lat;
225
262
  p.longitude = lon;
226
- p.altitude = alt;
263
+ p.altitude = [self mslAltitude:alt latitude:lat longitude:lon];
227
264
  p.heading = heading;
228
265
  p.pitch = pitch;
229
266
  p.roll = roll;
@@ -427,7 +464,13 @@
427
464
  // ── Actual camera readback (post-integration; what was rendered) ─────────
428
465
  - (double)readActualLatitude { return _integrator ? _integrator->getActual().latitude : 0.0; }
429
466
  - (double)readActualLongitude { return _integrator ? _integrator->getActual().longitude : 0.0; }
430
- - (double)readActualAltitude { return _integrator ? _integrator->getActual().altitude : 0.0; }
467
+ - (double)readActualAltitude {
468
+ if (!_integrator) return 0.0;
469
+ const auto actual = _integrator->getActual();
470
+ return [self demandAltitudeFromMsl:actual.altitude
471
+ latitude:actual.latitude
472
+ longitude:actual.longitude];
473
+ }
431
474
  - (double)readActualHeading { return _integrator ? _integrator->getActual().heading : 0.0; }
432
475
  - (double)readActualPitch { return _integrator ? _integrator->getActual().pitch : 0.0; }
433
476
  - (double)readActualRoll { return _integrator ? _integrator->getActual().roll : 0.0; }
@@ -436,7 +479,13 @@
436
479
  // ── Demand camera readback (what the consumer last asked for) ────────────
437
480
  - (double)readDemandLatitude { return _integrator ? _integrator->getDemand().latitude : 0.0; }
438
481
  - (double)readDemandLongitude { return _integrator ? _integrator->getDemand().longitude : 0.0; }
439
- - (double)readDemandAltitude { return _integrator ? _integrator->getDemand().altitude : 0.0; }
482
+ - (double)readDemandAltitude {
483
+ if (!_integrator) return 0.0;
484
+ const auto demand = _integrator->getDemand();
485
+ return [self demandAltitudeFromMsl:demand.altitude
486
+ latitude:demand.latitude
487
+ longitude:demand.longitude];
488
+ }
440
489
  - (double)readDemandHeading { return _integrator ? _integrator->getDemand().heading : 0.0; }
441
490
  - (double)readDemandPitch { return _integrator ? _integrator->getDemand().pitch : 0.0; }
442
491
  - (double)readDemandRoll { return _integrator ? _integrator->getDemand().roll : 0.0; }
@@ -72,6 +72,11 @@ class HybridCesiumView: HybridCesiumViewSpec {
72
72
  var ionImageryAssetId: Double = 1 {
73
73
  didSet { let v = Int64(ionImageryAssetId); postToRender { [weak self] in self?.bridge?.updateImageryAssetId(v) } }
74
74
  }
75
+ /// `"msl"` (default) or `"ellipsoid"`. Ellipsoid heights are converted to MSL
76
+ /// before the camera integrator.
77
+ var altitudeDatum: String? = "msl" {
78
+ didSet { pushAltitudeDatum() }
79
+ }
75
80
 
76
81
  // ── Optional perf / quality knobs ────────────────────────────────────────
77
82
  var maximumCachedMiB: Double? {
@@ -460,6 +465,12 @@ class HybridCesiumView: HybridCesiumViewSpec {
460
465
  // sqliteCacheMaxRows and taskProcessorThreads are applied at construction
461
466
  // time via the CesiumBridge initializer; calling them here has no effect.
462
467
  if let v = minAltitudeAboveTerrain { bridge?.setMinAltitudeAboveTerrain(Float(v)) }
468
+ bridge?.setAltitudeDatumEllipsoid(altitudeDatum == "ellipsoid")
469
+ }
470
+
471
+ private func pushAltitudeDatum() {
472
+ let ellipsoid = altitudeDatum == "ellipsoid"
473
+ postToRender { [weak self] in self?.bridge?.setAltitudeDatumEllipsoid(ellipsoid) }
463
474
  }
464
475
 
465
476
  // MARK: - Render Loop
@@ -6,6 +6,11 @@ import type { HybridView, HybridViewProps, HybridViewMethods } from 'react-nativ
6
6
  export interface CameraState {
7
7
  latitude: number;
8
8
  longitude: number;
9
+ /**
10
+ * Metres. Mean sea level unless `altitudeDatum` is `"ellipsoid"`, in which
11
+ * case this is WGS84 ellipsoidal height. `getActualCamera`,
12
+ * `getDemandCamera`, and `onActualCamera` report altitude in that same unit.
13
+ */
9
14
  altitude: number;
10
15
  heading: number;
11
16
  pitch: number;
@@ -51,6 +56,16 @@ export interface CesiumViewProps extends HybridViewProps {
51
56
  /** 1 = off; 2 or 4 = MSAA when supported. */
52
57
  msaaSampleCount: number;
53
58
  ionImageryAssetId: number;
59
+ /**
60
+ * Vertical datum of public altitude numbers, in both directions.
61
+ * `"msl"` (default) is metres above mean sea level.
62
+ * `"ellipsoid"` is WGS84 ellipsoidal height (Android `Location.getAltitude`).
63
+ * Ellipsoidal heights are converted to mean sea level with EGM96 before the
64
+ * camera integrator, and converted back on read. Switching this prop does
65
+ * not move the camera: later reads use the new unit, and the next
66
+ * `setAltitude`, `setPosition`, or `teleport` is interpreted in that unit.
67
+ */
68
+ altitudeDatum?: string;
54
69
  /** RAM budget for live tileset, in mebibytes. Default 256. */
55
70
  maximumCachedMiB?: number;
56
71
  /** Pre-load ancestor tiles around the visible set (smoother panning, more loads). Default true. */
@@ -132,9 +147,17 @@ export interface CesiumViewMethods extends HybridViewMethods {
132
147
  * for GPS or scripted position updates. Latitude is clamped to ±90 by the
133
148
  * integrator; longitude is continuous (no wrap normalisation) so the
134
149
  * shortest-arc residual maths stays correct across the antimeridian.
150
+ * When `altitudeDatum` is `"ellipsoid"`, a horizontal move keeps the
151
+ * demanded ellipsoidal height and recomputes mean sea level at the new
152
+ * point. Mean-sea-level mode leaves altitude unchanged.
135
153
  */
136
154
  setPosition(latitude: number, longitude: number): void;
137
- /** New altitude demand (metres above mean sea level). */
155
+ /**
156
+ * New altitude demand, in metres. Interpreted as mean sea level unless
157
+ * `altitudeDatum` is `"ellipsoid"`, in which case it is WGS84 height and
158
+ * converted to MSL before the integrator. A later read reports the same
159
+ * sample back in this unit.
160
+ */
138
161
  setAltitude(altitudeMeters: number): void;
139
162
  /** New heading demand (degrees, 0 = north, increasing clockwise). */
140
163
  setHeading(headingDeg: number): void;
@@ -163,13 +186,14 @@ export interface CesiumViewMethods extends HybridViewMethods {
163
186
  * Most recent camera that was rendered (post-integration). This is what
164
187
  * the user is currently looking at and is what a HUD overlay should
165
188
  * mirror. Differs from `getDemandCamera()` while a glide is in progress
166
- * or when the terrain-floor clamp has raised the altitude.
189
+ * or when the terrain-floor clamp has raised the altitude. Altitude is in
190
+ * the unit selected by `altitudeDatum`.
167
191
  */
168
192
  getActualCamera(): Promise<CameraState>;
169
193
  /**
170
194
  * What the consumer last asked for (per-DoF demand). Useful for
171
195
  * diagnostics or for showing the requested camera alongside the rendered
172
- * one.
196
+ * one. Altitude is in the unit selected by `altitudeDatum`.
173
197
  */
174
198
  getDemandCamera(): Promise<CameraState>;
175
199
  /** Current view-correction quaternion (smoothed toward the latest demand). */
@@ -1 +1 @@
1
- {"version":3,"file":"cesium-view.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/cesium-view.nitro.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cesium-view.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/cesium-view.nitro.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ int initialize(JavaVM* vm) {
30
30
  }
31
31
 
32
32
  struct JHybridCesiumViewSpecImpl: public jni::JavaClass<JHybridCesiumViewSpecImpl, JHybridCesiumViewSpec::JavaPart> {
33
- static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/reactnativecesium/HybridCesiumView;";
33
+ static constexpr auto kJavaDescriptor = "Lcom/reactnativecesium/HybridCesiumView;";
34
34
  static std::shared_ptr<JHybridCesiumViewSpec> create() {
35
35
  static const auto constructorFn = javaClassStatic()->getConstructor<JHybridCesiumViewSpecImpl::javaobject()>();
36
36
  jni::local_ref<JHybridCesiumViewSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
@@ -140,6 +140,15 @@ namespace margelo::nitro::reactnativecesium {
140
140
  static const auto method = _javaPart->javaClassStatic()->getMethod<void(double /* ionImageryAssetId */)>("setIonImageryAssetId");
141
141
  method(_javaPart, ionImageryAssetId);
142
142
  }
143
+ std::optional<std::string> JHybridCesiumViewSpec::getAltitudeDatum() {
144
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getAltitudeDatum");
145
+ auto __result = method(_javaPart);
146
+ return __result != nullptr ? std::make_optional(__result->toStdString()) : std::nullopt;
147
+ }
148
+ void JHybridCesiumViewSpec::setAltitudeDatum(const std::optional<std::string>& altitudeDatum) {
149
+ static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* altitudeDatum */)>("setAltitudeDatum");
150
+ method(_javaPart, altitudeDatum.has_value() ? jni::make_jstring(altitudeDatum.value()) : nullptr);
151
+ }
143
152
  std::optional<double> JHybridCesiumViewSpec::getMaximumCachedMiB() {
144
153
  static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getMaximumCachedMiB");
145
154
  auto __result = method(_javaPart);
@@ -68,6 +68,8 @@ namespace margelo::nitro::reactnativecesium {
68
68
  void setMsaaSampleCount(double msaaSampleCount) override;
69
69
  double getIonImageryAssetId() override;
70
70
  void setIonImageryAssetId(double ionImageryAssetId) override;
71
+ std::optional<std::string> getAltitudeDatum() override;
72
+ void setAltitudeDatum(const std::optional<std::string>& altitudeDatum) override;
71
73
  std::optional<double> getMaximumCachedMiB() override;
72
74
  void setMaximumCachedMiB(std::optional<double> maximumCachedMiB) override;
73
75
  std::optional<bool> getPreloadAncestors() override;
@@ -98,6 +98,11 @@ void JHybridCesiumViewStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
98
98
  : !newProps->ionImageryAssetId.hasSameValue(oldProps->ionImageryAssetId)) {
99
99
  hybridView->setIonImageryAssetId(newProps->ionImageryAssetId.get());
100
100
  }
101
+ if (oldProps == nullptr
102
+ ? newProps->altitudeDatum.isProvided()
103
+ : !newProps->altitudeDatum.hasSameValue(oldProps->altitudeDatum)) {
104
+ hybridView->setAltitudeDatum(newProps->altitudeDatum.get());
105
+ }
101
106
  if (oldProps == nullptr
102
107
  ? newProps->maximumCachedMiB.isProvided()
103
108
  : !newProps->maximumCachedMiB.hasSameValue(oldProps->maximumCachedMiB)) {
@@ -82,6 +82,12 @@ abstract class HybridCesiumViewSpec: HybridView() {
82
82
  @set:Keep
83
83
  abstract var ionImageryAssetId: Double
84
84
 
85
+ @get:DoNotStrip
86
+ @get:Keep
87
+ @set:DoNotStrip
88
+ @set:Keep
89
+ abstract var altitudeDatum: String?
90
+
85
91
  @get:DoNotStrip
86
92
  @get:Keep
87
93
  @set:DoNotStrip
@@ -41,6 +41,21 @@ namespace ReactNativeCesium { class HybridCesiumViewSpec_cxx; }
41
41
  */
42
42
  namespace margelo::nitro::reactnativecesium::bridge::swift {
43
43
 
44
+ // pragma MARK: std::optional<std::string>
45
+ /**
46
+ * Specialized version of `std::optional<std::string>`.
47
+ */
48
+ using std__optional_std__string_ = std::optional<std::string>;
49
+ inline std::optional<std::string> create_std__optional_std__string_(const std::string& value) noexcept {
50
+ return std::optional<std::string>(value);
51
+ }
52
+ inline bool has_value_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
53
+ return optional.has_value();
54
+ }
55
+ inline std::string get_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
56
+ return optional.value();
57
+ }
58
+
44
59
  // pragma MARK: std::optional<double>
45
60
  /**
46
61
  * Specialized version of `std::optional<double>`.
@@ -128,6 +128,13 @@ namespace margelo::nitro::reactnativecesium {
128
128
  inline void setIonImageryAssetId(double ionImageryAssetId) noexcept override {
129
129
  _swiftPart.setIonImageryAssetId(std::forward<decltype(ionImageryAssetId)>(ionImageryAssetId));
130
130
  }
131
+ inline std::optional<std::string> getAltitudeDatum() noexcept override {
132
+ auto __result = _swiftPart.getAltitudeDatum();
133
+ return __result;
134
+ }
135
+ inline void setAltitudeDatum(const std::optional<std::string>& altitudeDatum) noexcept override {
136
+ _swiftPart.setAltitudeDatum(altitudeDatum);
137
+ }
131
138
  inline std::optional<double> getMaximumCachedMiB() noexcept override {
132
139
  auto __result = _swiftPart.getMaximumCachedMiB();
133
140
  return __result;
@@ -152,6 +152,12 @@ using namespace margelo::nitro::reactnativecesium::views;
152
152
  : !newViewProps.ionImageryAssetId.hasSameValue(oldViewProps->ionImageryAssetId)) {
153
153
  swiftPart.setIonImageryAssetId(newViewProps.ionImageryAssetId.get());
154
154
  }
155
+ // altitudeDatum: optional
156
+ if (oldViewProps == nullptr
157
+ ? newViewProps.altitudeDatum.isProvided()
158
+ : !newViewProps.altitudeDatum.hasSameValue(oldViewProps->altitudeDatum)) {
159
+ swiftPart.setAltitudeDatum(newViewProps.altitudeDatum.get());
160
+ }
155
161
  // maximumCachedMiB: optional
156
162
  if (oldViewProps == nullptr
157
163
  ? newViewProps.maximumCachedMiB.isProvided()
@@ -19,6 +19,7 @@ public protocol HybridCesiumViewSpec_protocol: HybridObject, HybridView {
19
19
  var loadingDescendantLimit: Double { get set }
20
20
  var msaaSampleCount: Double { get set }
21
21
  var ionImageryAssetId: Double { get set }
22
+ var altitudeDatum: String? { get set }
22
23
  var maximumCachedMiB: Double? { get set }
23
24
  var preloadAncestors: Bool? { get set }
24
25
  var preloadSiblings: Bool? { get set }
@@ -220,6 +220,30 @@ open class HybridCesiumViewSpec_cxx {
220
220
  }
221
221
  }
222
222
 
223
+ public final var altitudeDatum: bridge.std__optional_std__string_ {
224
+ @inline(__always)
225
+ get {
226
+ return { () -> bridge.std__optional_std__string_ in
227
+ if let __unwrappedValue = self.__implementation.altitudeDatum {
228
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
229
+ } else {
230
+ return .init()
231
+ }
232
+ }()
233
+ }
234
+ @inline(__always)
235
+ set {
236
+ self.__implementation.altitudeDatum = { () -> String? in
237
+ if bridge.has_value_std__optional_std__string_(newValue) {
238
+ let __unwrapped = bridge.get_std__optional_std__string_(newValue)
239
+ return String(__unwrapped)
240
+ } else {
241
+ return nil
242
+ }
243
+ }()
244
+ }
245
+ }
246
+
223
247
  public final var maximumCachedMiB: bridge.std__optional_double_ {
224
248
  @inline(__always)
225
249
  get {
@@ -32,6 +32,8 @@ namespace margelo::nitro::reactnativecesium {
32
32
  prototype.registerHybridSetter("msaaSampleCount", &HybridCesiumViewSpec::setMsaaSampleCount);
33
33
  prototype.registerHybridGetter("ionImageryAssetId", &HybridCesiumViewSpec::getIonImageryAssetId);
34
34
  prototype.registerHybridSetter("ionImageryAssetId", &HybridCesiumViewSpec::setIonImageryAssetId);
35
+ prototype.registerHybridGetter("altitudeDatum", &HybridCesiumViewSpec::getAltitudeDatum);
36
+ prototype.registerHybridSetter("altitudeDatum", &HybridCesiumViewSpec::setAltitudeDatum);
35
37
  prototype.registerHybridGetter("maximumCachedMiB", &HybridCesiumViewSpec::getMaximumCachedMiB);
36
38
  prototype.registerHybridSetter("maximumCachedMiB", &HybridCesiumViewSpec::setMaximumCachedMiB);
37
39
  prototype.registerHybridGetter("preloadAncestors", &HybridCesiumViewSpec::getPreloadAncestors);
@@ -73,6 +73,8 @@ namespace margelo::nitro::reactnativecesium {
73
73
  virtual void setMsaaSampleCount(double msaaSampleCount) = 0;
74
74
  virtual double getIonImageryAssetId() = 0;
75
75
  virtual void setIonImageryAssetId(double ionImageryAssetId) = 0;
76
+ virtual std::optional<std::string> getAltitudeDatum() = 0;
77
+ virtual void setAltitudeDatum(const std::optional<std::string>& altitudeDatum) = 0;
76
78
  virtual std::optional<double> getMaximumCachedMiB() = 0;
77
79
  virtual void setMaximumCachedMiB(std::optional<double> maximumCachedMiB) = 0;
78
80
  virtual std::optional<bool> getPreloadAncestors() = 0;
@@ -29,6 +29,7 @@ namespace margelo::nitro::reactnativecesium::views {
29
29
  loadingDescendantLimit(nitro::ReactProp<double>::fromRawValue("CesiumView", "loadingDescendantLimit", rawProps, sourceProps.loadingDescendantLimit)),
30
30
  msaaSampleCount(nitro::ReactProp<double>::fromRawValue("CesiumView", "msaaSampleCount", rawProps, sourceProps.msaaSampleCount)),
31
31
  ionImageryAssetId(nitro::ReactProp<double>::fromRawValue("CesiumView", "ionImageryAssetId", rawProps, sourceProps.ionImageryAssetId)),
32
+ altitudeDatum(nitro::ReactProp<std::optional<std::string>>::fromRawValue("CesiumView", "altitudeDatum", rawProps, sourceProps.altitudeDatum)),
32
33
  maximumCachedMiB(nitro::ReactProp<std::optional<double>>::fromRawValue("CesiumView", "maximumCachedMiB", rawProps, sourceProps.maximumCachedMiB)),
33
34
  preloadAncestors(nitro::ReactProp<std::optional<bool>>::fromRawValue("CesiumView", "preloadAncestors", rawProps, sourceProps.preloadAncestors)),
34
35
  preloadSiblings(nitro::ReactProp<std::optional<bool>>::fromRawValue("CesiumView", "preloadSiblings", rawProps, sourceProps.preloadSiblings)),
@@ -62,6 +63,7 @@ namespace margelo::nitro::reactnativecesium::views {
62
63
  case hashString("loadingDescendantLimit"): return true;
63
64
  case hashString("msaaSampleCount"): return true;
64
65
  case hashString("ionImageryAssetId"): return true;
66
+ case hashString("altitudeDatum"): return true;
65
67
  case hashString("maximumCachedMiB"): return true;
66
68
  case hashString("preloadAncestors"): return true;
67
69
  case hashString("preloadSiblings"): return true;
@@ -54,6 +54,7 @@ namespace margelo::nitro::reactnativecesium::views {
54
54
  nitro::ReactProp<double> loadingDescendantLimit;
55
55
  nitro::ReactProp<double> msaaSampleCount;
56
56
  nitro::ReactProp<double> ionImageryAssetId;
57
+ nitro::ReactProp<std::optional<std::string>> altitudeDatum;
57
58
  nitro::ReactProp<std::optional<double>> maximumCachedMiB;
58
59
  nitro::ReactProp<std::optional<bool>> preloadAncestors;
59
60
  nitro::ReactProp<std::optional<bool>> preloadSiblings;
@@ -87,6 +88,7 @@ namespace margelo::nitro::reactnativecesium::views {
87
88
  loadingDescendantLimit.hasSameValue(other.loadingDescendantLimit) &&
88
89
  msaaSampleCount.hasSameValue(other.msaaSampleCount) &&
89
90
  ionImageryAssetId.hasSameValue(other.ionImageryAssetId) &&
91
+ altitudeDatum.hasSameValue(other.altitudeDatum) &&
90
92
  maximumCachedMiB.hasSameValue(other.maximumCachedMiB) &&
91
93
  preloadAncestors.hasSameValue(other.preloadAncestors) &&
92
94
  preloadSiblings.hasSameValue(other.preloadSiblings) &&
@@ -121,6 +123,7 @@ namespace margelo::nitro::reactnativecesium::views {
121
123
  loadingDescendantLimit.isProvided() ||
122
124
  msaaSampleCount.isProvided() ||
123
125
  ionImageryAssetId.isProvided() ||
126
+ altitudeDatum.isProvided() ||
124
127
  maximumCachedMiB.isProvided() ||
125
128
  preloadAncestors.isProvided() ||
126
129
  preloadSiblings.isProvided() ||
@@ -13,6 +13,7 @@
13
13
  "loadingDescendantLimit": true,
14
14
  "msaaSampleCount": true,
15
15
  "ionImageryAssetId": true,
16
+ "altitudeDatum": true,
16
17
  "maximumCachedMiB": true,
17
18
  "preloadAncestors": true,
18
19
  "preloadSiblings": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-cesium",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
4
4
  "description": "Experimental Cesium rendering for React Native via Nitro",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -5,4 +5,4 @@
5
5
  */
6
6
 
7
7
  /** Git tag of CesiumGS/cesium-native to clone and build. */
8
- export const CESIUM_NATIVE_REF = 'v0.65.0'
8
+ export const CESIUM_NATIVE_REF = 'v0.66.0'
@@ -11,6 +11,11 @@ import type {
11
11
  export interface CameraState {
12
12
  latitude: number
13
13
  longitude: number
14
+ /**
15
+ * Metres. Mean sea level unless `altitudeDatum` is `"ellipsoid"`, in which
16
+ * case this is WGS84 ellipsoidal height. `getActualCamera`,
17
+ * `getDemandCamera`, and `onActualCamera` report altitude in that same unit.
18
+ */
14
19
  altitude: number
15
20
  heading: number
16
21
  pitch: number
@@ -59,6 +64,16 @@ export interface CesiumViewProps extends HybridViewProps {
59
64
  /** 1 = off; 2 or 4 = MSAA when supported. */
60
65
  msaaSampleCount: number
61
66
  ionImageryAssetId: number
67
+ /**
68
+ * Vertical datum of public altitude numbers, in both directions.
69
+ * `"msl"` (default) is metres above mean sea level.
70
+ * `"ellipsoid"` is WGS84 ellipsoidal height (Android `Location.getAltitude`).
71
+ * Ellipsoidal heights are converted to mean sea level with EGM96 before the
72
+ * camera integrator, and converted back on read. Switching this prop does
73
+ * not move the camera: later reads use the new unit, and the next
74
+ * `setAltitude`, `setPosition`, or `teleport` is interpreted in that unit.
75
+ */
76
+ altitudeDatum?: string
62
77
 
63
78
  // ── Optional perf / quality knobs ────────────────────────────────────────
64
79
  // Note: all optional props below use "set once" semantics. Once a value has
@@ -157,10 +172,18 @@ export interface CesiumViewMethods extends HybridViewMethods {
157
172
  * for GPS or scripted position updates. Latitude is clamped to ±90 by the
158
173
  * integrator; longitude is continuous (no wrap normalisation) so the
159
174
  * shortest-arc residual maths stays correct across the antimeridian.
175
+ * When `altitudeDatum` is `"ellipsoid"`, a horizontal move keeps the
176
+ * demanded ellipsoidal height and recomputes mean sea level at the new
177
+ * point. Mean-sea-level mode leaves altitude unchanged.
160
178
  */
161
179
  setPosition(latitude: number, longitude: number): void
162
180
 
163
- /** New altitude demand (metres above mean sea level). */
181
+ /**
182
+ * New altitude demand, in metres. Interpreted as mean sea level unless
183
+ * `altitudeDatum` is `"ellipsoid"`, in which case it is WGS84 height and
184
+ * converted to MSL before the integrator. A later read reports the same
185
+ * sample back in this unit.
186
+ */
164
187
  setAltitude(altitudeMeters: number): void
165
188
 
166
189
  /** New heading demand (degrees, 0 = north, increasing clockwise). */
@@ -195,14 +218,15 @@ export interface CesiumViewMethods extends HybridViewMethods {
195
218
  * Most recent camera that was rendered (post-integration). This is what
196
219
  * the user is currently looking at and is what a HUD overlay should
197
220
  * mirror. Differs from `getDemandCamera()` while a glide is in progress
198
- * or when the terrain-floor clamp has raised the altitude.
221
+ * or when the terrain-floor clamp has raised the altitude. Altitude is in
222
+ * the unit selected by `altitudeDatum`.
199
223
  */
200
224
  getActualCamera(): Promise<CameraState>
201
225
 
202
226
  /**
203
227
  * What the consumer last asked for (per-DoF demand). Useful for
204
228
  * diagnostics or for showing the requested camera alongside the rendered
205
- * one.
229
+ * one. Altitude is in the unit selected by `altitudeDatum`.
206
230
  */
207
231
  getDemandCamera(): Promise<CameraState>
208
232