react-native-cesium 1.2.19 → 1.2.20
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 +10 -7
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +40 -4
- package/android/src/main/cpp/CesiumBridgeAndroid.h +7 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +9 -0
- package/cpp/engine/CesiumEngine.cpp +31 -8
- package/cpp/engine/CesiumEngine.hpp +3 -0
- package/cpp/engine/GeoidConverter.cpp +32 -11
- package/cpp/engine/GeoidConverter.hpp +41 -0
- package/cpp/engine/test/GeoidConverterTest.cpp +77 -0
- package/cpp/engine/test/build_and_run.sh +20 -6
- package/ios/CesiumBridge.h +5 -0
- package/ios/CesiumBridge.mm +55 -6
- package/ios/HybridCesiumView.swift +11 -0
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +27 -3
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +1 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +5 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +6 -0
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +15 -0
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +7 -0
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +6 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +24 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +3 -0
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +1 -0
- package/package.json +1 -1
- package/src/specs/cesium-view.nitro.ts +27 -3
package/README.md
CHANGED
|
@@ -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` |
|
|
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
|
|
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
|
|
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_->
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -407,19 +422,27 @@ void CesiumEngine::applyImageryOverlay(int64_t targetAssetId) {
|
|
|
407
422
|
}
|
|
408
423
|
|
|
409
424
|
void CesiumEngine::destroyTileset() {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (
|
|
414
|
-
|
|
425
|
+
// Detach the imagery overlay while this engine still owns it. Dropping
|
|
426
|
+
// currentOverlay_ before in-flight IonRasterOverlay tasks finish frees the
|
|
427
|
+
// overlay out from under their destructors (heap corruption).
|
|
428
|
+
if (tileset_ && currentOverlay_) {
|
|
429
|
+
tileset_->getOverlays().remove(currentOverlay_);
|
|
415
430
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
//
|
|
431
|
+
drainAsyncWork();
|
|
432
|
+
|
|
433
|
+
// Drop draw-cache pointers before the tileset frees TileGPUResources.
|
|
419
434
|
lastDrawSignature_ = 0;
|
|
420
435
|
stableSigFrames_ = 0;
|
|
421
436
|
cachedDraws_.clear();
|
|
422
437
|
clearResidentTerrain();
|
|
438
|
+
|
|
439
|
+
tileset_.reset();
|
|
440
|
+
currentImageryAssetId_ = 1;
|
|
441
|
+
// Tileset destruction schedules further async cleanup. Drain that while
|
|
442
|
+
// this engine still owns the overlay, then release it. Dropping the
|
|
443
|
+
// overlay first frees it under those continuations.
|
|
444
|
+
drainAsyncWork();
|
|
445
|
+
currentOverlay_ = nullptr;
|
|
423
446
|
}
|
|
424
447
|
|
|
425
448
|
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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
|
3
|
-
#
|
|
4
|
-
# not part of the
|
|
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" \
|
package/ios/CesiumBridge.h
CHANGED
|
@@ -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;
|
package/ios/CesiumBridge.mm
CHANGED
|
@@ -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
|
|
69
|
-
_suspended
|
|
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->
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,4BAA4B,CAAA;AAEnC;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,aAAa,EAAE,OAAO,CAAA;IACtB,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,WAAW,CAAA;IAC1B,cAAc,EAAE,OAAO,CAAA;IACvB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,4BAA4B,EAAE,MAAM,CAAA;IACpC,sBAAsB,EAAE,MAAM,CAAA;IAC9B,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;
|
|
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/
|
|
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() ||
|
package/package.json
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|