expo-arcgis 0.1.2 → 0.1.3

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 (60) hide show
  1. package/android/src/main/java/expo/modules/arcgis/DynamicEntityLayerRef.kt +30 -0
  2. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisExtrasModule.kt +15 -0
  3. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisGeometryModule.kt +2 -0
  4. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisModule.kt +7 -2
  5. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisSceneView.kt +20 -0
  6. package/android/src/main/java/expo/modules/arcgis/GeometryEngineFunctions.kt +68 -0
  7. package/android/src/main/java/expo/modules/arcgis/GeoprocessingFunctions.kt +25 -0
  8. package/android/src/main/java/expo/modules/arcgis/GraphicsOverlayRef.kt +7 -0
  9. package/android/src/main/java/expo/modules/arcgis/OfflineFunctions.kt +19 -0
  10. package/build/DynamicEntityLayer.d.ts +4 -1
  11. package/build/DynamicEntityLayer.d.ts.map +1 -1
  12. package/build/DynamicEntityLayer.js +8 -1
  13. package/build/DynamicEntityLayer.js.map +1 -1
  14. package/build/ExpoArcgis.types.d.ts +133 -1
  15. package/build/ExpoArcgis.types.d.ts.map +1 -1
  16. package/build/ExpoArcgis.types.js.map +1 -1
  17. package/build/ExpoArcgisExtrasModule.d.ts +7 -1
  18. package/build/ExpoArcgisExtrasModule.d.ts.map +1 -1
  19. package/build/ExpoArcgisExtrasModule.js.map +1 -1
  20. package/build/ExpoArcgisGeometryModule.d.ts +3 -1
  21. package/build/ExpoArcgisGeometryModule.d.ts.map +1 -1
  22. package/build/ExpoArcgisGeometryModule.js.map +1 -1
  23. package/build/ExpoArcgisModule.d.ts +2 -1
  24. package/build/ExpoArcgisModule.d.ts.map +1 -1
  25. package/build/ExpoArcgisModule.js.map +1 -1
  26. package/build/auth.d.ts +14 -0
  27. package/build/auth.d.ts.map +1 -1
  28. package/build/auth.js +17 -0
  29. package/build/auth.js.map +1 -1
  30. package/build/geometryEngine.d.ts +13 -1
  31. package/build/geometryEngine.d.ts.map +1 -1
  32. package/build/geometryEngine.js +12 -0
  33. package/build/geometryEngine.js.map +1 -1
  34. package/build/index.d.ts +1 -1
  35. package/build/index.d.ts.map +1 -1
  36. package/build/index.js +1 -1
  37. package/build/index.js.map +1 -1
  38. package/build/offline.d.ts +7 -1
  39. package/build/offline.d.ts.map +1 -1
  40. package/build/offline.js +7 -0
  41. package/build/offline.js.map +1 -1
  42. package/ios/DynamicEntityLayerRef.swift +46 -0
  43. package/ios/ExpoArcgisExtrasModule.swift +19 -0
  44. package/ios/ExpoArcgisGeometryModule.swift +2 -0
  45. package/ios/ExpoArcgisModule.swift +4 -0
  46. package/ios/ExpoArcgisSceneView.swift +24 -0
  47. package/ios/GeometryEngineFunctions.swift +83 -0
  48. package/ios/GeoprocessingFunctions.swift +20 -0
  49. package/ios/GraphicsOverlayRef.swift +4 -0
  50. package/ios/OfflineFunctions.swift +14 -0
  51. package/package.json +1 -1
  52. package/src/DynamicEntityLayer.tsx +12 -1
  53. package/src/ExpoArcgis.types.ts +136 -2
  54. package/src/ExpoArcgisExtrasModule.ts +17 -0
  55. package/src/ExpoArcgisGeometryModule.ts +4 -0
  56. package/src/ExpoArcgisModule.ts +2 -0
  57. package/src/auth.ts +23 -0
  58. package/src/geometryEngine.ts +18 -0
  59. package/src/index.ts +1 -0
  60. package/src/offline.ts +14 -0
@@ -39,6 +39,18 @@ class DynamicEntityLayerRef(appContext: AppContext, props: Map<String, Any?>) :
39
39
  emit("onConnectionStatusChange", mapOf("status" to connectionStatusString(status)))
40
40
  }
41
41
  }
42
+ // Emit entity-received events (new/updated observation arrived for an entity).
43
+ scope.launch {
44
+ dataSource.dynamicEntityReceivedEvent.collect { info ->
45
+ emit("onDynamicEntityChange", dynamicEntityPayload("received", info.dynamicEntity))
46
+ }
47
+ }
48
+ // Emit entity-purged events (entity evicted by purge rules).
49
+ scope.launch {
50
+ dataSource.dynamicEntityPurgedEvent.collect { info ->
51
+ emit("onDynamicEntityChange", dynamicEntityPayload("purged", info.dynamicEntity))
52
+ }
53
+ }
42
54
  }
43
55
 
44
56
  /** Pushes an observation into a custom data source (no-op for a stream service). */
@@ -97,6 +109,24 @@ fun connectionStatusString(status: ConnectionStatus): String = when (status) {
97
109
  is ConnectionStatus.Failed -> "failed"
98
110
  }
99
111
 
112
+ /**
113
+ * Builds a compact payload for the `onDynamicEntityChange` event.
114
+ * Only `received` and `purged` change types are emitted (observation-only updates are
115
+ * captured within `dynamicEntityReceivedEvent` which fires per-entity arrival, not per
116
+ * observation, so the event rate is bounded to entity lifecycle changes).
117
+ * Geometry is serialized only when present; attributes are passed as-is (the map returned
118
+ * by the SDK is a shallow snapshot of the current entity attributes).
119
+ */
120
+ private fun dynamicEntityPayload(changeType: String, entity: com.arcgismaps.realtime.DynamicEntity): Map<String, Any?> {
121
+ val payload = mutableMapOf<String, Any?>(
122
+ "changeType" to changeType,
123
+ "entityId" to entity.id,
124
+ "attributes" to entity.attributes.toMap(),
125
+ )
126
+ entity.geometry?.let { payload["geometry"] = dictFromGeometry(it) }
127
+ return payload
128
+ }
129
+
100
130
  /** Builds the real-time data source: a custom feed (push from JS) or a stream service. */
101
131
  private fun buildDataSource(
102
132
  props: Map<String, Any?>,
@@ -1,5 +1,7 @@
1
1
  package expo.modules.arcgis
2
2
 
3
+ import com.arcgismaps.ArcGISEnvironment
4
+ import com.arcgismaps.httpcore.authentication.TokenCredential
3
5
  import expo.modules.kotlin.functions.Coroutine
4
6
  import expo.modules.kotlin.modules.Module
5
7
  import expo.modules.kotlin.modules.ModuleDefinition
@@ -71,6 +73,19 @@ class ExpoArcgisExtrasModule : Module() {
71
73
  }
72
74
  }
73
75
 
76
+ // Per-service token credential — mint a token for a specific URL and add it to the store.
77
+ AsyncFunction("setServiceCredential") Coroutine { serviceUrl: String, username: String, password: String, tokenExpirationMinutes: Int? ->
78
+ val credential = TokenCredential.create(serviceUrl, username, password, tokenExpirationMinutes)
79
+ .getOrThrow()
80
+ ArcGISEnvironment.authenticationManager.arcGISCredentialStore.add(credential, serviceUrl)
81
+ .getOrThrow()
82
+ }
83
+
84
+ // Tile-cache size estimation — quick estimate before committing to a download.
85
+ AsyncFunction("estimateTileCacheSize") Coroutine { tileServiceUrl: String, areaOfInterest: Map<String, Any?>, options: Map<String, Any?>? ->
86
+ estimateTileCacheSize(tileServiceUrl, areaOfInterest, options)
87
+ }
88
+
74
89
  // Turn-by-turn navigation — solve a route and track device locations against it.
75
90
  AsyncFunction("createRouteTracker") Coroutine { stops: List<Map<String, Any?>>, params: Map<String, Any?> ->
76
91
  createRouteTracker(appContext, stops, params)
@@ -59,6 +59,8 @@ class ExpoArcgisGeometryModule : Module() {
59
59
  Function("geMove", ::geMove)
60
60
  Function("geRotate", ::geRotate)
61
61
  Function("geScale", ::geScale)
62
+ Function("geEllipseGeodesic", ::geEllipseGeodesic)
63
+ Function("geSectorGeodesic", ::geSectorGeodesic)
62
64
 
63
65
  // CoordinateFormatter — point <-> notation strings, exposed as the JS `coordinateFormatter` namespace.
64
66
  Function("cfToLatLong", ::cfToLatLong)
@@ -234,12 +234,13 @@ class ExpoArcgisModule : Module() {
234
234
  Function("applyProps") { ref: OgcFeatureLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
235
235
  }
236
236
 
237
- // Real-time DynamicEntityLayer (stream service) — emits onConnectionStatusChange.
237
+ // Real-time DynamicEntityLayer (stream service) — emits onConnectionStatusChange +
238
+ // onDynamicEntityChange (received/purged entity events).
238
239
  Class(DynamicEntityLayerRef::class) {
239
240
  Constructor { props: Map<String, Any?> ->
240
241
  DynamicEntityLayerRef(appContext, props).also { it.applyProps(props) }
241
242
  }
242
- Events("onConnectionStatusChange")
243
+ Events("onConnectionStatusChange", "onDynamicEntityChange")
243
244
  Function("applyProps") { ref: DynamicEntityLayerRef, changed: Map<String, Any?> ->
244
245
  ref.applyProps(changed)
245
246
  }
@@ -405,6 +406,10 @@ class ExpoArcgisModule : Module() {
405
406
  view.setCamera(camera)
406
407
  }
407
408
 
409
+ Prop("cameraController") { view: ExpoArcgisSceneView, value: Map<String, Any?>? ->
410
+ view.setCameraController(value)
411
+ }
412
+
408
413
  Prop("sunLighting") { view: ExpoArcgisSceneView, value: String? ->
409
414
  view.setSunLighting(value)
410
415
  }
@@ -9,7 +9,9 @@ import com.arcgismaps.geometry.Point
9
9
  import com.arcgismaps.geometry.SpatialReference
10
10
  import com.arcgismaps.mapping.view.AtmosphereEffect
11
11
  import com.arcgismaps.mapping.view.Camera
12
+ import com.arcgismaps.mapping.view.GlobeCameraController
12
13
  import com.arcgismaps.mapping.view.LightingMode
14
+ import com.arcgismaps.mapping.view.OrbitLocationCameraController
13
15
  import com.arcgismaps.mapping.view.SceneView
14
16
  import com.arcgismaps.mapping.view.ScreenCoordinate
15
17
  import expo.modules.kotlin.AppContext
@@ -147,6 +149,24 @@ class ExpoArcgisSceneView(context: Context, appContext: AppContext) : ExpoView(c
147
149
  scope.launch { sceneView.setViewpointCameraAnimated(camera, 0.5f) }
148
150
  }
149
151
 
152
+ /** Sets or clears the scene's camera controller (orbit/globe). `null` restores the SDK default. */
153
+ fun setCameraController(c: Map<String, Any?>?) {
154
+ sceneView.cameraController = when (c?.get("type") as? String) {
155
+ "orbitLocation" -> {
156
+ val target = c["target"] as? Map<*, *>
157
+ val x = (target?.get("x") as? Number)?.toDouble() ?: 0.0
158
+ val y = (target?.get("y") as? Number)?.toDouble() ?: 0.0
159
+ val z = (target?.get("z") as? Number)?.toDouble()
160
+ val point = if (z != null) Point(x, y, z, SpatialReference.wgs84())
161
+ else Point(x, y, SpatialReference.wgs84())
162
+ val distance = (c["distance"] as? Number)?.toDouble() ?: 1500.0
163
+ OrbitLocationCameraController(point, distance)
164
+ }
165
+ "globe" -> GlobeCameraController()
166
+ else -> GlobeCameraController() // null/absent → restore SDK default (GlobeCameraController)
167
+ }
168
+ }
169
+
150
170
  /** Sun lighting mode (shadows). */
151
171
  fun setSunLighting(s: String?) {
152
172
  sceneView.sunLighting = when (s) {
@@ -1,6 +1,8 @@
1
1
  package expo.modules.arcgis
2
2
 
3
3
  import com.arcgismaps.geometry.Envelope
4
+ import com.arcgismaps.geometry.GeodesicEllipseParameters
5
+ import com.arcgismaps.geometry.GeodesicSectorParameters
4
6
  import com.arcgismaps.geometry.Geometry
5
7
  import com.arcgismaps.geometry.GeometryEngine
6
8
  import com.arcgismaps.geometry.Multipart
@@ -256,3 +258,69 @@ internal fun geScale(
256
258
  else GeometryEngine.scale(geometry, factorX, factorY),
257
259
  )
258
260
  }
261
+
262
+ // region Geodesic construction
263
+
264
+ internal fun geEllipseGeodesic(params: Map<String, Any?>): Map<String, Any?>? {
265
+ val centerDict = params["center"] as? Map<String, Any?> ?: return null
266
+ val center = parsePoint(centerDict) ?: return null
267
+
268
+ val semiAxis1Length = (params["semiAxis1Length"] as? Number)?.toDouble() ?: 0.0
269
+ val semiAxis2Length = (params["semiAxis2Length"] as? Number)?.toDouble() ?: 0.0
270
+ val axisDirection = (params["axisDirection"] as? Number)?.toDouble() ?: 0.0
271
+ val angUnit = angularUnit(params["angularUnit"] as? String)
272
+ val linUnit = linearUnit(params["linearUnit"] as? String)
273
+ val maxSegLen = (params["maxSegmentLength"] as? Number)?.toDouble() ?: 0.0
274
+ val maxPtCount = (params["maxPointCount"] as? Number)?.toLong() ?: 10L
275
+ val geoType = params["geometryType"] as? String
276
+
277
+ val p: GeodesicEllipseParameters = when (geoType) {
278
+ "polyline" -> GeodesicEllipseParameters.Companion.createForPolyline()
279
+ "multipoint" -> GeodesicEllipseParameters.Companion.createForMultipoint()
280
+ else -> GeodesicEllipseParameters.Companion.createForPolygon()
281
+ }
282
+ p.center = center
283
+ p.semiAxis1Length = semiAxis1Length
284
+ p.semiAxis2Length = semiAxis2Length
285
+ p.axisDirection = axisDirection
286
+ p.angularUnit = angUnit
287
+ p.linearUnit = linUnit
288
+ p.maxSegmentLength = maxSegLen
289
+ p.maxPointCount = maxPtCount
290
+
291
+ return encode(GeometryEngine.ellipseGeodesicOrNull(p))
292
+ }
293
+
294
+ internal fun geSectorGeodesic(params: Map<String, Any?>): Map<String, Any?>? {
295
+ val centerDict = params["center"] as? Map<String, Any?> ?: return null
296
+ val center = parsePoint(centerDict) ?: return null
297
+
298
+ val semiAxis1Length = (params["semiAxis1Length"] as? Number)?.toDouble() ?: 0.0
299
+ val semiAxis2Length = (params["semiAxis2Length"] as? Number)?.toDouble() ?: 0.0
300
+ val axisDirection = (params["axisDirection"] as? Number)?.toDouble() ?: 0.0
301
+ val sectorAngle = (params["sectorAngle"] as? Number)?.toDouble() ?: 0.0
302
+ val startDirection = (params["startDirection"] as? Number)?.toDouble() ?: 0.0
303
+ val angUnit = angularUnit(params["angularUnit"] as? String)
304
+ val linUnit = linearUnit(params["linearUnit"] as? String)
305
+ val maxSegLen = (params["maxSegmentLength"] as? Number)?.toDouble() ?: 0.0
306
+ val maxPtCount = (params["maxPointCount"] as? Number)?.toLong() ?: 10L
307
+ val geoType = params["geometryType"] as? String
308
+
309
+ val p: GeodesicSectorParameters = when (geoType) {
310
+ "polyline" -> GeodesicSectorParameters.Companion.createForPolyline()
311
+ "multipoint" -> GeodesicSectorParameters.Companion.createForMultipoint()
312
+ else -> GeodesicSectorParameters.Companion.createForPolygon()
313
+ }
314
+ p.center = center
315
+ p.semiAxis1Length = semiAxis1Length
316
+ p.semiAxis2Length = semiAxis2Length
317
+ p.axisDirection = axisDirection
318
+ p.sectorAngle = sectorAngle
319
+ p.startDirection = startDirection
320
+ p.angularUnit = angUnit
321
+ p.linearUnit = linUnit
322
+ p.maxSegmentLength = maxSegLen
323
+ p.maxPointCount = maxPtCount
324
+
325
+ return encode(GeometryEngine.sectorGeodesicOrNull(p))
326
+ }
@@ -4,12 +4,15 @@ import com.arcgismaps.data.FeatureCollectionTable
4
4
  import com.arcgismaps.mapping.view.Graphic
5
5
  import com.arcgismaps.tasks.geoprocessing.GeoprocessingTask
6
6
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingBoolean
7
+ import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingDataFile
7
8
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingDate
8
9
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingDouble
9
10
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingFeatures
10
11
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingLinearUnit
11
12
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingLong
13
+ import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingMultiValue
12
14
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingParameter
15
+ import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingParameterType
13
16
  import com.arcgismaps.tasks.geoprocessing.geoprocessingparameters.GeoprocessingString
14
17
  import expo.modules.kotlin.AppContext
15
18
  import java.time.Instant
@@ -60,6 +63,28 @@ private fun buildGeoprocessingParameter(d: Map<*, *>): GeoprocessingParameter? =
60
63
  ?.map { Graphic().apply { geometry = it } } ?: emptyList()
61
64
  GeoprocessingFeatures(FeatureCollectionTable(graphics, emptyList()))
62
65
  }
66
+ "multiValue" -> {
67
+ // JS numbers → GeoprocessingDouble, JS strings → GeoprocessingString.
68
+ val rawValues = d["values"] as? List<*> ?: emptyList<Any>()
69
+ val elements: List<GeoprocessingParameter> = rawValues.mapNotNull { v ->
70
+ when (v) {
71
+ is Number -> GeoprocessingDouble(v.toDouble())
72
+ is String -> GeoprocessingString(v)
73
+ else -> null
74
+ }
75
+ }
76
+ // Use the element type of the first item to determine the multiValue's parameterType;
77
+ // fall back to GeoprocessingParameterType.GeoprocessingString when the list is empty.
78
+ val paramType = if (elements.firstOrNull() is GeoprocessingDouble)
79
+ GeoprocessingParameterType.GeoprocessingDouble
80
+ else
81
+ GeoprocessingParameterType.GeoprocessingString
82
+ GeoprocessingMultiValue(paramType, elements)
83
+ }
84
+ "dataFile" -> {
85
+ val url = d["url"] as? String ?: return null
86
+ GeoprocessingDataFile.Companion.createWithUrl(url)
87
+ }
63
88
  else -> null
64
89
  }
65
90
 
@@ -8,6 +8,7 @@ import com.arcgismaps.mapping.reduction.ClusteringFeatureReduction
8
8
  import com.arcgismaps.mapping.reduction.FeatureReduction
9
9
  import com.arcgismaps.mapping.symbology.ClassBreak
10
10
  import com.arcgismaps.mapping.symbology.ClassBreaksRenderer
11
+ import com.arcgismaps.mapping.symbology.CompositeSymbol
11
12
  import com.arcgismaps.mapping.symbology.HorizontalAlignment
12
13
  import com.arcgismaps.mapping.symbology.Renderer
13
14
  import com.arcgismaps.mapping.symbology.SceneSymbolAnchorPosition
@@ -213,6 +214,12 @@ private fun buildSymbol(s: Map<*, *>): Symbol? = when (s["type"]) {
213
214
  }
214
215
  composite
215
216
  }
217
+ "composite" -> {
218
+ val symbolList = (s["symbols"] as? List<*> ?: emptyList<Any>()).mapNotNull { item ->
219
+ (item as? Map<*, *>)?.let(::buildSymbol)
220
+ }
221
+ CompositeSymbol(symbolList)
222
+ }
216
223
  else -> null
217
224
  }
218
225
 
@@ -10,6 +10,7 @@ import com.arcgismaps.mapping.MobileMapPackage
10
10
  import com.arcgismaps.tasks.offlinemaptask.OfflineMapSyncTask
11
11
  import com.arcgismaps.tasks.offlinemaptask.OfflineMapTask
12
12
  import com.arcgismaps.tasks.tilecache.ExportTileCacheTask
13
+ import com.arcgismaps.tasks.tilecache.EstimateTileCacheSizeJob
13
14
  import expo.modules.kotlin.AppContext
14
15
  import java.io.File
15
16
 
@@ -151,6 +152,24 @@ internal suspend fun exportTileCache(
151
152
  }
152
153
  }
153
154
 
155
+ /**
156
+ * Estimates the on-disk file size and tile count for an [exportTileCache] download WITHOUT
157
+ * downloading. Runs the [EstimateTileCacheSizeJob] to completion and returns `{fileSize, tileCount}`.
158
+ */
159
+ internal suspend fun estimateTileCacheSize(
160
+ tileServiceUrl: String,
161
+ areaOfInterest: Map<String, Any?>,
162
+ @Suppress("UNUSED_PARAMETER") options: Map<String, Any?>?,
163
+ ): Map<String, Any?> {
164
+ val area = geometryFromDict(areaOfInterest) ?: throw IllegalArgumentException("Invalid area of interest")
165
+ val task = ExportTileCacheTask(tileServiceUrl)
166
+ val parameters = task.createDefaultExportTileCacheParameters(area, 0.0, 0.0).getOrThrow()
167
+ val job = task.createEstimateTileCacheSizeJob(parameters)
168
+ job.start()
169
+ val estimate = job.result().getOrThrow()
170
+ return mapOf("fileSize" to estimate.fileSize, "tileCount" to estimate.tileCount)
171
+ }
172
+
154
173
  internal suspend fun exportVectorTiles(
155
174
  appContext: AppContext,
156
175
  baseDir: File?,
@@ -1,4 +1,4 @@
1
- import type { ConnectionStatus, DynamicEntityLayerHandle } from './ExpoArcgis.types';
1
+ import type { ConnectionStatus, DynamicEntityChange, DynamicEntityLayerHandle } from './ExpoArcgis.types';
2
2
  /**
3
3
  * Declarative real-time `DynamicEntityLayer`. Adds itself to the nearest `<Map>` / `<Scene>`, shows
4
4
  * live moving entities from a `streamServiceUrl` (or a `customSource` you feed via the ref), reports
@@ -14,5 +14,8 @@ export declare const DynamicEntityLayer: import("react").ForwardRefExoticCompone
14
14
  geometry?: import("./ExpoArcgis.types").Geometry;
15
15
  };
16
16
  onConnectionStatusChange?: (status: ConnectionStatus) => void;
17
+ onDynamicEntityChange?: (event: {
18
+ nativeEvent: DynamicEntityChange;
19
+ }) => void;
17
20
  } & import("react").RefAttributes<DynamicEntityLayerHandle>>;
18
21
  //# sourceMappingURL=DynamicEntityLayer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DynamicEntityLayer.d.ts","sourceRoot":"","sources":["../src/DynamicEntityLayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,wBAAwB,EAEzB,MAAM,oBAAoB,CAAC;AAO5B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;mBA8CuspB,CAAC;gBAAkB,CAAC;;;4DADzvpB,CAAC"}
1
+ {"version":3,"file":"DynamicEntityLayer.d.ts","sourceRoot":"","sources":["../src/DynamicEntityLayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EAEzB,MAAM,oBAAoB,CAAC;AAO5B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;mBAwDszqB,CAAC;gBAAkB,CAAC;;;;;;4DADx2qB,CAAC"}
@@ -10,7 +10,7 @@ import { getPropsDiffs } from './utils/getPropsDiffs';
10
10
  * connection state through `onConnectionStatusChange`, and exposes `queryDynamicEntities` /
11
11
  * `pushObservation` through a `ref`.
12
12
  */
13
- export const DynamicEntityLayer = forwardRef(function DynamicEntityLayer({ onConnectionStatusChange, ...layerProps }, handle) {
13
+ export const DynamicEntityLayer = forwardRef(function DynamicEntityLayer({ onConnectionStatusChange, onDynamicEntityChange, ...layerProps }, handle) {
14
14
  const model = useGeoModel();
15
15
  const ref = useRef(undefined);
16
16
  if (!ref.current) {
@@ -33,6 +33,13 @@ export const DynamicEntityLayer = forwardRef(function DynamicEntityLayer({ onCon
33
33
  const sub = ref.current.addListener('onConnectionStatusChange', (event) => onConnectionStatusChange(event.status));
34
34
  return () => sub.remove();
35
35
  }, [onConnectionStatusChange]);
36
+ // The entity-change callback is wired as an event listener.
37
+ useEffect(() => {
38
+ if (!onDynamicEntityChange)
39
+ return;
40
+ const sub = ref.current.addListener('onDynamicEntityChange', (event) => onDynamicEntityChange({ nativeEvent: event }));
41
+ return () => sub.remove();
42
+ }, [onDynamicEntityChange]);
36
43
  useUpdateEffect(() => {
37
44
  const diffs = getPropsDiffs(prev, layerProps);
38
45
  if (diffs.length === 0)
@@ -1 +1 @@
1
- {"version":3,"file":"DynamicEntityLayer.js","sourceRoot":"","sources":["../src/DynamicEntityLayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAO3E,OAAO,gBAAgD,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAC1C,SAAS,kBAAkB,CAAC,EAAE,wBAAwB,EAAE,GAAG,UAAU,EAAE,EAAE,MAAM;IAC7E,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAoC,SAAS,CAAC,CAAC;IACjE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,GAAG,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAErC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,GAAG,CAAC,OAAQ,CAAC;QAC3B,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtB,OAAO,GAAG,EAAE;YACV,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzB,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iGAAiG;IACjG,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,wBAAwB;YAAE,OAAO;QACtC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAQ,CAAC,WAAW,CAClC,0BAA0B,EAC1B,CAAC,KAAmC,EAAE,EAAE,CAAC,wBAAwB,CAAC,KAAK,CAAC,MAAM,CAAC,CAChF,CAAC;QACF,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAE/B,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACpB,OAAO,CAAC,GAAa,CAAC,GAAI,UAAsC,CAAC,GAAa,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,yFAAyF;IACzF,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAQ,EAAE,EAAE,CAAC,CAAC;IAEpD,OAAO,IAAI,CAAC;AACd,CAAC,CACF,CAAC","sourcesContent":["import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';\n\nimport type {\n ConnectionStatus,\n DynamicEntityLayerHandle,\n DynamicEntityLayerProps,\n} from './ExpoArcgis.types';\nimport ExpoArcgisModule, { type DynamicEntityLayerRef } from './ExpoArcgisModule';\nimport { useGeoModel } from './contexts';\nimport { usePrevious } from './hooks/usePrevious';\nimport { useUpdateEffect } from './hooks/useUpdateEffect';\nimport { getPropsDiffs } from './utils/getPropsDiffs';\n\n/**\n * Declarative real-time `DynamicEntityLayer`. Adds itself to the nearest `<Map>` / `<Scene>`, shows\n * live moving entities from a `streamServiceUrl` (or a `customSource` you feed via the ref), reports\n * connection state through `onConnectionStatusChange`, and exposes `queryDynamicEntities` /\n * `pushObservation` through a `ref`.\n */\nexport const DynamicEntityLayer = forwardRef<DynamicEntityLayerHandle, DynamicEntityLayerProps>(\n function DynamicEntityLayer({ onConnectionStatusChange, ...layerProps }, handle) {\n const model = useGeoModel();\n const ref = useRef<DynamicEntityLayerRef | undefined>(undefined);\n if (!ref.current) {\n ref.current = new ExpoArcgisModule.DynamicEntityLayerRef(layerProps);\n }\n\n const prev = usePrevious(layerProps);\n\n useEffect(() => {\n const layer = ref.current!;\n model.addLayer(layer);\n return () => {\n model.removeLayer(layer);\n layer.release();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The connection-status callback is wired as an event listener (functions can't cross as props).\n useEffect(() => {\n if (!onConnectionStatusChange) return;\n const sub = ref.current!.addListener(\n 'onConnectionStatusChange',\n (event: { status: ConnectionStatus }) => onConnectionStatusChange(event.status)\n );\n return () => sub.remove();\n }, [onConnectionStatusChange]);\n\n useUpdateEffect(() => {\n const diffs = getPropsDiffs(prev, layerProps);\n if (diffs.length === 0) return;\n const changed: Record<string, unknown> = {};\n diffs.forEach((key) => {\n changed[key as string] = (layerProps as Record<string, unknown>)[key as string];\n });\n ref.current?.applyProps(changed);\n }, [layerProps]);\n\n // The native ref exposes queryDynamicEntities / pushObservation — hand it over directly.\n useImperativeHandle(handle, () => ref.current!, []);\n\n return null;\n }\n);\n"]}
1
+ {"version":3,"file":"DynamicEntityLayer.js","sourceRoot":"","sources":["../src/DynamicEntityLayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAQ3E,OAAO,gBAAgD,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAC1C,SAAS,kBAAkB,CAAC,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,GAAG,UAAU,EAAE,EAAE,MAAM;IACpG,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAoC,SAAS,CAAC,CAAC;IACjE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,GAAG,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAErC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,GAAG,CAAC,OAAQ,CAAC;QAC3B,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtB,OAAO,GAAG,EAAE;YACV,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzB,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iGAAiG;IACjG,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,wBAAwB;YAAE,OAAO;QACtC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAQ,CAAC,WAAW,CAClC,0BAA0B,EAC1B,CAAC,KAAmC,EAAE,EAAE,CAAC,wBAAwB,CAAC,KAAK,CAAC,MAAM,CAAC,CAChF,CAAC;QACF,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAE/B,4DAA4D;IAC5D,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,qBAAqB;YAAE,OAAO;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAQ,CAAC,WAAW,CAClC,uBAAuB,EACvB,CAAC,KAA0B,EAAE,EAAE,CAAC,qBAAqB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAC9E,CAAC;QACF,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAE5B,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACpB,OAAO,CAAC,GAAa,CAAC,GAAI,UAAsC,CAAC,GAAa,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,yFAAyF;IACzF,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAQ,EAAE,EAAE,CAAC,CAAC;IAEpD,OAAO,IAAI,CAAC;AACd,CAAC,CACF,CAAC","sourcesContent":["import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';\n\nimport type {\n ConnectionStatus,\n DynamicEntityChange,\n DynamicEntityLayerHandle,\n DynamicEntityLayerProps,\n} from './ExpoArcgis.types';\nimport ExpoArcgisModule, { type DynamicEntityLayerRef } from './ExpoArcgisModule';\nimport { useGeoModel } from './contexts';\nimport { usePrevious } from './hooks/usePrevious';\nimport { useUpdateEffect } from './hooks/useUpdateEffect';\nimport { getPropsDiffs } from './utils/getPropsDiffs';\n\n/**\n * Declarative real-time `DynamicEntityLayer`. Adds itself to the nearest `<Map>` / `<Scene>`, shows\n * live moving entities from a `streamServiceUrl` (or a `customSource` you feed via the ref), reports\n * connection state through `onConnectionStatusChange`, and exposes `queryDynamicEntities` /\n * `pushObservation` through a `ref`.\n */\nexport const DynamicEntityLayer = forwardRef<DynamicEntityLayerHandle, DynamicEntityLayerProps>(\n function DynamicEntityLayer({ onConnectionStatusChange, onDynamicEntityChange, ...layerProps }, handle) {\n const model = useGeoModel();\n const ref = useRef<DynamicEntityLayerRef | undefined>(undefined);\n if (!ref.current) {\n ref.current = new ExpoArcgisModule.DynamicEntityLayerRef(layerProps);\n }\n\n const prev = usePrevious(layerProps);\n\n useEffect(() => {\n const layer = ref.current!;\n model.addLayer(layer);\n return () => {\n model.removeLayer(layer);\n layer.release();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The connection-status callback is wired as an event listener (functions can't cross as props).\n useEffect(() => {\n if (!onConnectionStatusChange) return;\n const sub = ref.current!.addListener(\n 'onConnectionStatusChange',\n (event: { status: ConnectionStatus }) => onConnectionStatusChange(event.status)\n );\n return () => sub.remove();\n }, [onConnectionStatusChange]);\n\n // The entity-change callback is wired as an event listener.\n useEffect(() => {\n if (!onDynamicEntityChange) return;\n const sub = ref.current!.addListener(\n 'onDynamicEntityChange',\n (event: DynamicEntityChange) => onDynamicEntityChange({ nativeEvent: event })\n );\n return () => sub.remove();\n }, [onDynamicEntityChange]);\n\n useUpdateEffect(() => {\n const diffs = getPropsDiffs(prev, layerProps);\n if (diffs.length === 0) return;\n const changed: Record<string, unknown> = {};\n diffs.forEach((key) => {\n changed[key as string] = (layerProps as Record<string, unknown>)[key as string];\n });\n ref.current?.applyProps(changed);\n }, [layerProps]);\n\n // The native ref exposes queryDynamicEntities / pushObservation — hand it over directly.\n useImperativeHandle(handle, () => ref.current!, []);\n\n return null;\n }\n);\n"]}
@@ -522,6 +522,27 @@ export type OgcFeatureLayerProps = LayerProps & {
522
522
  };
523
523
  /** Connection state of a real-time `DynamicEntityDataSource`. Mirrors `ConnectionStatus`. */
524
524
  export type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'failed';
525
+ /**
526
+ * The kind of entity lifecycle event emitted by `onDynamicEntityChange`.
527
+ * - `received` — a new or updated entity observation arrived (fires once per entity, not per
528
+ * observation, so attribute-only updates within the same entity are collapsed).
529
+ * - `purged` — the entity was evicted by the data source's purge rules.
530
+ */
531
+ export type DynamicEntityChangeType = 'received' | 'purged';
532
+ /**
533
+ * Payload for the `<DynamicEntityLayer onDynamicEntityChange>` event.
534
+ * Contains the entity's current attribute snapshot and geometry at the time of the event.
535
+ */
536
+ export type DynamicEntityChange = {
537
+ /** Whether the entity arrived/updated (`received`) or was evicted (`purged`). */
538
+ changeType: DynamicEntityChangeType;
539
+ /** The entity's unique numeric id (from the data source's `entityIDField`). */
540
+ entityId: number;
541
+ /** The entity's current attribute values (snapshot at event time). */
542
+ attributes: Record<string, unknown>;
543
+ /** The entity's current geometry, or `undefined` when unavailable. */
544
+ geometry?: Geometry;
545
+ };
525
546
  /** Track display options for a `<DynamicEntityLayer>` (history of past observations). */
526
547
  export type TrackDisplay = {
527
548
  /** How many past observations to keep per track. */
@@ -557,6 +578,14 @@ export type DynamicEntityLayerProps = LayerProps & {
557
578
  };
558
579
  /** Fired as the data source connects / disconnects. */
559
580
  onConnectionStatusChange?: (status: ConnectionStatus) => void;
581
+ /**
582
+ * Fired when a dynamic entity is received (new/updated) or purged. High-frequency on busy
583
+ * stream services — only entity lifecycle events are emitted (one per entity arrival or purge),
584
+ * not per-observation attribute updates.
585
+ */
586
+ onDynamicEntityChange?: (event: {
587
+ nativeEvent: DynamicEntityChange;
588
+ }) => void;
560
589
  };
561
590
  /** A live dynamic entity returned by `queryDynamicEntities`. */
562
591
  export type DynamicEntityInfo = {
@@ -659,6 +688,54 @@ export type AreaUnit = 'squareMeters' | 'squareKilometers' | 'squareFeet' | 'squ
659
688
  export type GeodeticCurveType = 'geodesic' | 'loxodrome' | 'greatElliptic' | 'normalSection' | 'shapePreserving';
660
689
  /** Join style for `geometryEngine.offset`. Defaults to `mitered`. */
661
690
  export type GeometryOffsetType = 'mitered' | 'bevelled' | 'rounded' | 'squared';
691
+ /**
692
+ * Angular unit for geodesic ellipse / sector construction. Defaults to `degrees`.
693
+ * Maps to the native `AngularUnit`.
694
+ */
695
+ export type AngularUnit = 'degrees' | 'radians';
696
+ /**
697
+ * Output geometry type for `geometryEngine.ellipseGeodesic` / `sectorGeodesic`.
698
+ * Defaults to `polygon`. Maps to the native `GeometryType`.
699
+ */
700
+ export type GeodesicGeometryType = 'polygon' | 'polyline' | 'multipoint';
701
+ /**
702
+ * Parameters for `geometryEngine.ellipseGeodesic`. Mirrors the native
703
+ * `GeodesicEllipseParameters` (Swift) / `com.arcgismaps.geometry.GeodesicEllipseParameters` (Kotlin).
704
+ */
705
+ export type GeodesicEllipseParams = {
706
+ /** Center point of the ellipse. */
707
+ center: PointGeometry;
708
+ /** Length of the semi-major axis. */
709
+ semiAxis1Length: number;
710
+ /** Length of the semi-minor axis. */
711
+ semiAxis2Length: number;
712
+ /**
713
+ * Direction of the major axis, in `angularUnit` clockwise from north. Defaults to `0`.
714
+ */
715
+ axisDirection?: number;
716
+ /** Unit for `axisDirection`. Defaults to `degrees`. */
717
+ angularUnit?: AngularUnit;
718
+ /** Unit for `semiAxis1Length` / `semiAxis2Length`. Defaults to `meters`. */
719
+ linearUnit?: LinearUnit;
720
+ /**
721
+ * Maximum segment length on the output geometry. `0` / omitted lets the SDK choose.
722
+ */
723
+ maxSegmentLength?: number;
724
+ /** Maximum number of vertices on the output geometry. Defaults to `10` (SDK default). */
725
+ maxPointCount?: number;
726
+ /** Output geometry type. Defaults to `polygon`. */
727
+ geometryType?: GeodesicGeometryType;
728
+ };
729
+ /**
730
+ * Parameters for `geometryEngine.sectorGeodesic`. Extends `GeodesicEllipseParams` with
731
+ * the sector angle and start direction. Mirrors the native `GeodesicSectorParameters`.
732
+ */
733
+ export type GeodesicSectorParams = GeodesicEllipseParams & {
734
+ /** The angular size of the sector, in `angularUnit`. */
735
+ sectorAngle: number;
736
+ /** The direction from which the sector opens, in `angularUnit` clockwise from north. */
737
+ startDirection: number;
738
+ };
662
739
  /** Result of `geometryEngine.geodesicDistance` — distance plus the two azimuths (degrees). */
663
740
  export type GeodeticDistanceResult = {
664
741
  distance: number;
@@ -925,6 +1002,16 @@ export type GeoprocessingInput = {
925
1002
  } | {
926
1003
  type: 'features';
927
1004
  geometries: Geometry[];
1005
+ }
1006
+ /** An array of homogeneous strings or numbers — maps to `GeoprocessingMultiValue`. */
1007
+ | {
1008
+ type: 'multiValue';
1009
+ values: (string | number)[];
1010
+ }
1011
+ /** A remote file input (URL) — maps to `GeoprocessingDataFile`. */
1012
+ | {
1013
+ type: 'dataFile';
1014
+ url: string;
928
1015
  };
929
1016
  /** Result of `geoprocessor.execute`. Mirrors the native `GeoprocessingResult`. */
930
1017
  export type GeoprocessingResult = {
@@ -1049,6 +1136,13 @@ export type OfflineTileResult = {
1049
1136
  /** Local filesystem path of the downloaded tile package (`.tpkx` / `.vtpk`). */
1050
1137
  path: string;
1051
1138
  };
1139
+ /** Result of `offline.estimateTileCacheSize` — an estimate of the download footprint. */
1140
+ export type TileCacheSizeEstimate = {
1141
+ /** Estimated on-disk size of the tile cache in bytes. */
1142
+ fileSize: number;
1143
+ /** Estimated number of tiles in the tile cache. */
1144
+ tileCount: number;
1145
+ };
1052
1146
  /** The kind of geometry a `<GeometryEditor>` sketches. */
1053
1147
  export type GeometryEditorType = 'point' | 'multipoint' | 'polyline' | 'polygon' | 'envelope';
1054
1148
  /**
@@ -1198,8 +1292,26 @@ export type DistanceCompositeSceneSymbol = {
1198
1292
  type: 'distance-composite-scene';
1199
1293
  ranges: DistanceSymbolRange[];
1200
1294
  };
1295
+ /**
1296
+ * A composite symbol that overlays multiple symbols stacked on top of each other.
1297
+ * Useful for combining a marker with a label, or layering two markers for a ring effect.
1298
+ * Mirrors the native `CompositeSymbol`.
1299
+ *
1300
+ * @example
1301
+ * ```ts
1302
+ * { type: 'composite', symbols: [
1303
+ * { type: 'simple-marker', color: '#fff', size: 18 },
1304
+ * { type: 'simple-marker', color: '#e63946', size: 10 },
1305
+ * ] }
1306
+ * ```
1307
+ */
1308
+ export type CompositeSymbolType = {
1309
+ type: 'composite';
1310
+ /** The symbols to stack, drawn in order (first = bottom, last = top). */
1311
+ symbols: Symbol[];
1312
+ };
1201
1313
  /** Any symbol usable by a `<Graphic>`. Mirrors the native `Symbol` hierarchy. */
1202
- export type Symbol = SimpleMarkerSymbol | SimpleLineSymbol | SimpleFillSymbol | TextSymbol | SimpleMarkerSceneSymbol | PictureMarkerSymbol | PictureFillSymbol | DistanceCompositeSceneSymbol;
1314
+ export type Symbol = SimpleMarkerSymbol | SimpleLineSymbol | SimpleFillSymbol | TextSymbol | SimpleMarkerSceneSymbol | PictureMarkerSymbol | PictureFillSymbol | DistanceCompositeSceneSymbol | CompositeSymbolType;
1203
1315
  /** A renderer that draws every feature/graphic with the same `symbol`. */
1204
1316
  export type SimpleRenderer = {
1205
1317
  type: 'simple';
@@ -1307,11 +1419,31 @@ export type SceneProps = {
1307
1419
  export type SunLighting = 'off' | 'light' | 'lightAndShadows';
1308
1420
  /** Atmosphere rendering for a 3D scene view. */
1309
1421
  export type AtmosphereEffect = 'off' | 'horizonOnly' | 'realistic';
1422
+ /**
1423
+ * Camera controller for a `<SceneView>`.
1424
+ * - `orbitLocation` — orbits around a fixed target point (`OrbitLocationCameraController`).
1425
+ * - `globe` — free globe navigation (`GlobeCameraController`).
1426
+ */
1427
+ export type CameraController = {
1428
+ type: 'orbitLocation';
1429
+ /** The point the camera orbits around (WGS84 longitude/latitude, optional altitude). */
1430
+ target: Point;
1431
+ /** Initial camera distance from the target, in meters. */
1432
+ distance: number;
1433
+ } | {
1434
+ type: 'globe';
1435
+ };
1310
1436
  /** Props for the `<SceneView>` host component. */
1311
1437
  export type SceneViewProps = {
1312
1438
  style?: StyleProp<ViewStyle>;
1313
1439
  /** Animates the view to this 3D camera whenever the value changes (runtime camera control). */
1314
1440
  camera?: Camera;
1441
+ /**
1442
+ * Swaps the scene's camera-control mode. Omit (or pass `null`) to use the SDK default.
1443
+ * - `{ type: 'orbitLocation', target, distance }` — orbit around a fixed point.
1444
+ * - `{ type: 'globe' }` — free globe navigation.
1445
+ */
1446
+ cameraController?: CameraController | null;
1315
1447
  /** Sun lighting mode (shadows). Defaults to `off`. */
1316
1448
  sunLighting?: SunLighting;
1317
1449
  /** Atmosphere rendering. Defaults to `horizonOnly`. */