expo-arcgis 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/android/build.gradle +39 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/java/expo/modules/arcgis/AnalysisOverlayRef.kt +165 -0
  6. package/android/src/main/java/expo/modules/arcgis/AuthChallengeHandler.kt +78 -0
  7. package/android/src/main/java/expo/modules/arcgis/CoordinateFormatterFunctions.kt +44 -0
  8. package/android/src/main/java/expo/modules/arcgis/DynamicEntityLayerRef.kt +133 -0
  9. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisGeometryModule.kt +119 -0
  10. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisMapView.kt +237 -0
  11. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisModule.kt +458 -0
  12. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisSceneView.kt +174 -0
  13. package/android/src/main/java/expo/modules/arcgis/GeocoderFunctions.kt +71 -0
  14. package/android/src/main/java/expo/modules/arcgis/GeometryCodec.kt +194 -0
  15. package/android/src/main/java/expo/modules/arcgis/GeometryEditorRef.kt +76 -0
  16. package/android/src/main/java/expo/modules/arcgis/GeometryEngineFunctions.kt +223 -0
  17. package/android/src/main/java/expo/modules/arcgis/GeoprocessingFunctions.kt +84 -0
  18. package/android/src/main/java/expo/modules/arcgis/GraphicsOverlayRef.kt +280 -0
  19. package/android/src/main/java/expo/modules/arcgis/JobRef.kt +41 -0
  20. package/android/src/main/java/expo/modules/arcgis/LayerRef.kt +261 -0
  21. package/android/src/main/java/expo/modules/arcgis/MapRef.kt +93 -0
  22. package/android/src/main/java/expo/modules/arcgis/OfflineFunctions.kt +171 -0
  23. package/android/src/main/java/expo/modules/arcgis/QueryCodec.kt +111 -0
  24. package/android/src/main/java/expo/modules/arcgis/RouterFunctions.kt +100 -0
  25. package/android/src/main/java/expo/modules/arcgis/SceneRef.kt +109 -0
  26. package/android/src/main/java/expo/modules/arcgis/UtilityNetworkRef.kt +198 -0
  27. package/app.plugin.js +3 -0
  28. package/build/AnalysisOverlay.d.ts +9 -0
  29. package/build/AnalysisOverlay.d.ts.map +1 -0
  30. package/build/AnalysisOverlay.js +30 -0
  31. package/build/AnalysisOverlay.js.map +1 -0
  32. package/build/DistanceMeasurement.d.ts +8 -0
  33. package/build/DistanceMeasurement.d.ts.map +1 -0
  34. package/build/DistanceMeasurement.js +47 -0
  35. package/build/DistanceMeasurement.js.map +1 -0
  36. package/build/DynamicEntityLayer.d.ts +18 -0
  37. package/build/DynamicEntityLayer.d.ts.map +1 -0
  38. package/build/DynamicEntityLayer.js +50 -0
  39. package/build/DynamicEntityLayer.js.map +1 -0
  40. package/build/ExpoArcgis.types.d.ts +1090 -0
  41. package/build/ExpoArcgis.types.d.ts.map +1 -0
  42. package/build/ExpoArcgis.types.js +2 -0
  43. package/build/ExpoArcgis.types.js.map +1 -0
  44. package/build/ExpoArcgisGeometryModule.d.ts +75 -0
  45. package/build/ExpoArcgisGeometryModule.d.ts.map +1 -0
  46. package/build/ExpoArcgisGeometryModule.js +3 -0
  47. package/build/ExpoArcgisGeometryModule.js.map +1 -0
  48. package/build/ExpoArcgisModule.d.ts +202 -0
  49. package/build/ExpoArcgisModule.d.ts.map +1 -0
  50. package/build/ExpoArcgisModule.js +3 -0
  51. package/build/ExpoArcgisModule.js.map +1 -0
  52. package/build/ExpoArcgisModule.web.d.ts +39 -0
  53. package/build/ExpoArcgisModule.web.d.ts.map +1 -0
  54. package/build/ExpoArcgisModule.web.js +38 -0
  55. package/build/ExpoArcgisModule.web.js.map +1 -0
  56. package/build/FeatureLayer.d.ts +14 -0
  57. package/build/FeatureLayer.d.ts.map +1 -0
  58. package/build/FeatureLayer.js +42 -0
  59. package/build/FeatureLayer.js.map +1 -0
  60. package/build/GeometryEditor.d.ts +11 -0
  61. package/build/GeometryEditor.d.ts.map +1 -0
  62. package/build/GeometryEditor.js +58 -0
  63. package/build/GeometryEditor.js.map +1 -0
  64. package/build/Graphic.d.ts +4 -0
  65. package/build/Graphic.d.ts.map +1 -0
  66. package/build/Graphic.js +36 -0
  67. package/build/Graphic.js.map +1 -0
  68. package/build/GraphicsOverlay.d.ts +12 -0
  69. package/build/GraphicsOverlay.d.ts.map +1 -0
  70. package/build/GraphicsOverlay.js +29 -0
  71. package/build/GraphicsOverlay.js.map +1 -0
  72. package/build/LineOfSight.d.ts +8 -0
  73. package/build/LineOfSight.d.ts.map +1 -0
  74. package/build/LineOfSight.js +50 -0
  75. package/build/LineOfSight.js.map +1 -0
  76. package/build/Map.d.ts +8 -0
  77. package/build/Map.d.ts.map +1 -0
  78. package/build/Map.js +34 -0
  79. package/build/Map.js.map +1 -0
  80. package/build/MapImageLayer.d.ts +4 -0
  81. package/build/MapImageLayer.d.ts.map +1 -0
  82. package/build/MapImageLayer.js +36 -0
  83. package/build/MapImageLayer.js.map +1 -0
  84. package/build/MapSettings.d.ts +17 -0
  85. package/build/MapSettings.d.ts.map +1 -0
  86. package/build/MapSettings.js +20 -0
  87. package/build/MapSettings.js.map +1 -0
  88. package/build/MapView.d.ts +10 -0
  89. package/build/MapView.d.ts.map +1 -0
  90. package/build/MapView.js +30 -0
  91. package/build/MapView.js.map +1 -0
  92. package/build/MapView.web.d.ts +4 -0
  93. package/build/MapView.web.d.ts.map +1 -0
  94. package/build/MapView.web.js +5 -0
  95. package/build/MapView.web.js.map +1 -0
  96. package/build/Scene.d.ts +8 -0
  97. package/build/Scene.d.ts.map +1 -0
  98. package/build/Scene.js +33 -0
  99. package/build/Scene.js.map +1 -0
  100. package/build/SceneLayer.d.ts +4 -0
  101. package/build/SceneLayer.d.ts.map +1 -0
  102. package/build/SceneLayer.js +36 -0
  103. package/build/SceneLayer.js.map +1 -0
  104. package/build/SceneView.d.ts +10 -0
  105. package/build/SceneView.d.ts.map +1 -0
  106. package/build/SceneView.js +29 -0
  107. package/build/SceneView.js.map +1 -0
  108. package/build/SceneView.web.d.ts +4 -0
  109. package/build/SceneView.web.d.ts.map +1 -0
  110. package/build/SceneView.web.js +5 -0
  111. package/build/SceneView.web.js.map +1 -0
  112. package/build/TileLayer.d.ts +4 -0
  113. package/build/TileLayer.d.ts.map +1 -0
  114. package/build/TileLayer.js +36 -0
  115. package/build/TileLayer.js.map +1 -0
  116. package/build/UtilityNetwork.d.ts +8 -0
  117. package/build/UtilityNetwork.d.ts.map +1 -0
  118. package/build/UtilityNetwork.js +38 -0
  119. package/build/UtilityNetwork.js.map +1 -0
  120. package/build/Viewshed.d.ts +7 -0
  121. package/build/Viewshed.d.ts.map +1 -0
  122. package/build/Viewshed.js +39 -0
  123. package/build/Viewshed.js.map +1 -0
  124. package/build/auth.d.ts +36 -0
  125. package/build/auth.d.ts.map +1 -0
  126. package/build/auth.js +47 -0
  127. package/build/auth.js.map +1 -0
  128. package/build/contexts.d.ts +34 -0
  129. package/build/contexts.d.ts.map +1 -0
  130. package/build/contexts.js +42 -0
  131. package/build/contexts.js.map +1 -0
  132. package/build/coordinateFormatter.d.ts +26 -0
  133. package/build/coordinateFormatter.d.ts.map +1 -0
  134. package/build/coordinateFormatter.js +26 -0
  135. package/build/coordinateFormatter.js.map +1 -0
  136. package/build/createLayerComponent.d.ts +8 -0
  137. package/build/createLayerComponent.d.ts.map +1 -0
  138. package/build/createLayerComponent.js +41 -0
  139. package/build/createLayerComponent.js.map +1 -0
  140. package/build/geocoder.d.ts +16 -0
  141. package/build/geocoder.d.ts.map +1 -0
  142. package/build/geocoder.js +16 -0
  143. package/build/geocoder.js.map +1 -0
  144. package/build/geometryEngine.d.ts +87 -0
  145. package/build/geometryEngine.d.ts.map +1 -0
  146. package/build/geometryEngine.js +87 -0
  147. package/build/geometryEngine.js.map +1 -0
  148. package/build/geoprocessor.d.ts +13 -0
  149. package/build/geoprocessor.d.ts.map +1 -0
  150. package/build/geoprocessor.js +12 -0
  151. package/build/geoprocessor.js.map +1 -0
  152. package/build/hooks/usePrevious.d.ts +4 -0
  153. package/build/hooks/usePrevious.d.ts.map +1 -0
  154. package/build/hooks/usePrevious.js +11 -0
  155. package/build/hooks/usePrevious.js.map +1 -0
  156. package/build/hooks/useUpdateEffect.d.ts +6 -0
  157. package/build/hooks/useUpdateEffect.d.ts.map +1 -0
  158. package/build/hooks/useUpdateEffect.js +16 -0
  159. package/build/hooks/useUpdateEffect.js.map +1 -0
  160. package/build/index.d.ts +31 -0
  161. package/build/index.d.ts.map +1 -0
  162. package/build/index.js +31 -0
  163. package/build/index.js.map +1 -0
  164. package/build/layers.d.ts +26 -0
  165. package/build/layers.d.ts.map +1 -0
  166. package/build/layers.js +27 -0
  167. package/build/layers.js.map +1 -0
  168. package/build/offline.d.ts +44 -0
  169. package/build/offline.d.ts.map +1 -0
  170. package/build/offline.js +39 -0
  171. package/build/offline.js.map +1 -0
  172. package/build/router.d.ts +12 -0
  173. package/build/router.d.ts.map +1 -0
  174. package/build/router.js +12 -0
  175. package/build/router.js.map +1 -0
  176. package/build/utils/getPropsDiffs.d.ts +3 -0
  177. package/build/utils/getPropsDiffs.d.ts.map +1 -0
  178. package/build/utils/getPropsDiffs.js +23 -0
  179. package/build/utils/getPropsDiffs.js.map +1 -0
  180. package/expo-module.config.json +9 -0
  181. package/ios/AnalysisOverlayRef.swift +184 -0
  182. package/ios/AuthChallengeHandler.swift +30 -0
  183. package/ios/CoordinateFormatterFunctions.swift +52 -0
  184. package/ios/DynamicEntityLayerRef.swift +136 -0
  185. package/ios/ExpoArcgis.podspec +35 -0
  186. package/ios/ExpoArcgisGeometryModule.swift +113 -0
  187. package/ios/ExpoArcgisMapView.swift +258 -0
  188. package/ios/ExpoArcgisModule.swift +488 -0
  189. package/ios/ExpoArcgisSceneView.swift +200 -0
  190. package/ios/GeocoderFunctions.swift +90 -0
  191. package/ios/GeometryCodec.swift +199 -0
  192. package/ios/GeometryEditorRef.swift +66 -0
  193. package/ios/GeometryEngineFunctions.swift +201 -0
  194. package/ios/GeoprocessingFunctions.swift +88 -0
  195. package/ios/GraphicsOverlayRef.swift +292 -0
  196. package/ios/JobRef.swift +37 -0
  197. package/ios/LayerRef.swift +258 -0
  198. package/ios/MapRef.swift +88 -0
  199. package/ios/OfflineFunctions.swift +132 -0
  200. package/ios/QueryCodec.swift +126 -0
  201. package/ios/RouterFunctions.swift +109 -0
  202. package/ios/SceneRef.swift +111 -0
  203. package/ios/UtilityNetworkRef.swift +182 -0
  204. package/package.json +75 -0
  205. package/src/AnalysisOverlay.tsx +37 -0
  206. package/src/DistanceMeasurement.tsx +58 -0
  207. package/src/DynamicEntityLayer.tsx +65 -0
  208. package/src/ExpoArcgis.types.ts +1248 -0
  209. package/src/ExpoArcgisGeometryModule.ts +140 -0
  210. package/src/ExpoArcgisModule.ts +258 -0
  211. package/src/ExpoArcgisModule.web.ts +46 -0
  212. package/src/FeatureLayer.tsx +50 -0
  213. package/src/GeometryEditor.tsx +68 -0
  214. package/src/Graphic.tsx +41 -0
  215. package/src/GraphicsOverlay.tsx +41 -0
  216. package/src/LineOfSight.tsx +55 -0
  217. package/src/Map.tsx +39 -0
  218. package/src/MapImageLayer.tsx +41 -0
  219. package/src/MapSettings.tsx +33 -0
  220. package/src/MapView.tsx +71 -0
  221. package/src/MapView.web.tsx +8 -0
  222. package/src/Scene.tsx +38 -0
  223. package/src/SceneLayer.tsx +41 -0
  224. package/src/SceneView.tsx +72 -0
  225. package/src/SceneView.web.tsx +8 -0
  226. package/src/TileLayer.tsx +41 -0
  227. package/src/UtilityNetwork.tsx +43 -0
  228. package/src/Viewshed.tsx +44 -0
  229. package/src/auth.ts +74 -0
  230. package/src/contexts.ts +76 -0
  231. package/src/coordinateFormatter.ts +62 -0
  232. package/src/createLayerComponent.tsx +46 -0
  233. package/src/geocoder.ts +29 -0
  234. package/src/geometryEngine.ts +162 -0
  235. package/src/geoprocessor.ts +17 -0
  236. package/src/hooks/usePrevious.ts +12 -0
  237. package/src/hooks/useUpdateEffect.ts +17 -0
  238. package/src/index.ts +60 -0
  239. package/src/layers.tsx +76 -0
  240. package/src/offline.ts +85 -0
  241. package/src/router.ts +14 -0
  242. package/src/utils/getPropsDiffs.ts +23 -0
@@ -0,0 +1,119 @@
1
+ package expo.modules.arcgis
2
+
3
+ import expo.modules.kotlin.functions.Coroutine
4
+ import expo.modules.kotlin.modules.Module
5
+ import expo.modules.kotlin.modules.ModuleDefinition
6
+
7
+ /**
8
+ * Second native module hosting the GeometryEngine + CoordinateFormatter operations, exposed to
9
+ * JS as `ExpoArcgisGeometry`. Split out of [ExpoArcgisModule] so that neither module's
10
+ * `definition()` exceeds the JVM 64 KB method-size limit.
11
+ */
12
+ class ExpoArcgisGeometryModule : Module() {
13
+ override fun definition() = ModuleDefinition {
14
+ Name("ExpoArcgisGeometry")
15
+
16
+ // GeometryEngine — spatial operations exposed as the JS `geometryEngine` namespace.
17
+ Function("geProject", ::geProject)
18
+ Function("geBuffer", ::geBuffer)
19
+ Function("geGeodeticBuffer", ::geGeodeticBuffer)
20
+ Function("geArea", ::geArea)
21
+ Function("geGeodeticArea", ::geGeodeticArea)
22
+ Function("geLength", ::geLength)
23
+ Function("geGeodeticLength", ::geGeodeticLength)
24
+ Function("geDistance", ::geDistance)
25
+ Function("geGeodeticDistance", ::geGeodeticDistance)
26
+ Function("geUnion", ::geUnion)
27
+ Function("geIntersection", ::geIntersection)
28
+ Function("geDifference", ::geDifference)
29
+ Function("geSymmetricDifference", ::geSymmetricDifference)
30
+ Function("geClip", ::geClip)
31
+ Function("geCut", ::geCut)
32
+ Function("geConvexHull", ::geConvexHull)
33
+ Function("geBoundary", ::geBoundary)
34
+ Function("geSimplify", ::geSimplify)
35
+ Function("geDensify", ::geDensify)
36
+ Function("geGeneralize", ::geGeneralize)
37
+ Function("geOffset", ::geOffset)
38
+ Function("geCombineExtents", ::geCombineExtents)
39
+ Function("geContains", ::geContains)
40
+ Function("geCrosses", ::geCrosses)
41
+ Function("geDisjoint", ::geDisjoint)
42
+ Function("geEquals", ::geEquals)
43
+ Function("geIntersects", ::geIntersects)
44
+ Function("geOverlaps", ::geOverlaps)
45
+ Function("geTouches", ::geTouches)
46
+ Function("geWithin", ::geWithin)
47
+ Function("geRelate", ::geRelate)
48
+ Function("geIsSimple", ::geIsSimple)
49
+ Function("geNearestCoordinate", ::geNearestCoordinate)
50
+ Function("geNearestVertex", ::geNearestVertex)
51
+ Function("geMove", ::geMove)
52
+ Function("geRotate", ::geRotate)
53
+ Function("geScale", ::geScale)
54
+
55
+ // CoordinateFormatter — point <-> notation strings, exposed as the JS `coordinateFormatter` namespace.
56
+ Function("cfToLatLong", ::cfToLatLong)
57
+ Function("cfFromLatLong", ::cfFromLatLong)
58
+ Function("cfToMgrs", ::cfToMgrs)
59
+ Function("cfFromMgrs", ::cfFromMgrs)
60
+ Function("cfToUsng", ::cfToUsng)
61
+ Function("cfFromUsng", ::cfFromUsng)
62
+ Function("cfToUtm", ::cfToUtm)
63
+ Function("cfFromUtm", ::cfFromUtm)
64
+
65
+ // Geocoding — address <-> coordinates search, exposed as the JS `geocoder` namespace.
66
+ AsyncFunction("geocode") Coroutine { searchText: String, params: Map<String, Any?> ->
67
+ geocode(searchText, params)
68
+ }
69
+ AsyncFunction("reverseGeocode") Coroutine { point: Map<String, Any?>, params: Map<String, Any?> ->
70
+ reverseGeocode(point, params)
71
+ }
72
+ AsyncFunction("suggest") Coroutine { searchText: String, params: Map<String, Any?> ->
73
+ suggest(searchText, params)
74
+ }
75
+
76
+ // Routing — solve a route between stops, exposed as the JS `router` namespace.
77
+ AsyncFunction("solveRoute") Coroutine { stops: List<Map<String, Any?>>, params: Map<String, Any?> ->
78
+ solveRoute(stops, params)
79
+ }
80
+
81
+ // Geoprocessing — run a geoprocessing service, exposed as the JS `geoprocessor` namespace.
82
+ AsyncFunction("executeGeoprocessing") Coroutine { serviceUrl: String, inputs: Map<String, Any?> ->
83
+ executeGeoprocessing(appContext, serviceUrl, inputs)
84
+ }
85
+
86
+ // Job handle for long-running downloads — emits onProgress, awaits via result(), supports cancel().
87
+ Class(JobRef::class) {
88
+ Events("onProgress")
89
+ AsyncFunction("result") Coroutine { ref: JobRef -> ref.result() }
90
+ AsyncFunction("cancel") Coroutine { ref: JobRef -> ref.cancel() }
91
+ }
92
+
93
+ // Offline — take maps/data offline, exposed as the JS `offline` namespace.
94
+ AsyncFunction("generateOfflineMap") Coroutine { portalItemId: String, areaOfInterest: Map<String, Any?>, downloadName: String ->
95
+ generateOfflineMap(appContext, appContext.reactContext?.filesDir, portalItemId, areaOfInterest, downloadName)
96
+ }
97
+ AsyncFunction("syncOfflineMap") Coroutine { mobileMapPackagePath: String ->
98
+ syncOfflineMap(appContext, mobileMapPackagePath)
99
+ }
100
+ AsyncFunction("preplannedMapAreas") Coroutine { portalItemId: String ->
101
+ preplannedMapAreas(portalItemId)
102
+ }
103
+ AsyncFunction("downloadPreplannedOfflineMap") Coroutine { portalItemId: String, areaIndex: Int, downloadName: String ->
104
+ downloadPreplannedOfflineMap(appContext, appContext.reactContext?.filesDir, portalItemId, areaIndex, downloadName)
105
+ }
106
+ AsyncFunction("generateGeodatabase") Coroutine { featureServiceUrl: String, extent: Map<String, Any?>, downloadName: String ->
107
+ generateGeodatabase(appContext, appContext.reactContext?.filesDir, featureServiceUrl, extent, downloadName)
108
+ }
109
+ AsyncFunction("syncGeodatabase") Coroutine { geodatabasePath: String, featureServiceUrl: String ->
110
+ syncGeodatabase(appContext, geodatabasePath, featureServiceUrl)
111
+ }
112
+ AsyncFunction("exportTileCache") Coroutine { tileServiceUrl: String, areaOfInterest: Map<String, Any?>, downloadName: String ->
113
+ exportTileCache(appContext, appContext.reactContext?.filesDir, tileServiceUrl, areaOfInterest, downloadName)
114
+ }
115
+ AsyncFunction("exportVectorTiles") Coroutine { vectorTileServiceUrl: String, areaOfInterest: Map<String, Any?>, downloadName: String ->
116
+ exportVectorTiles(appContext, appContext.reactContext?.filesDir, vectorTileServiceUrl, areaOfInterest, downloadName)
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,237 @@
1
+ package expo.modules.arcgis
2
+
3
+ import android.content.Context
4
+ import android.view.ViewGroup
5
+ import androidx.lifecycle.Lifecycle
6
+ import androidx.lifecycle.findViewTreeLifecycleOwner
7
+ import com.arcgismaps.geometry.GeometryEngine
8
+ import com.arcgismaps.geometry.Point
9
+ import com.arcgismaps.geometry.SpatialReference
10
+ import com.arcgismaps.geometry.Polyline
11
+ import com.arcgismaps.location.Location
12
+ import com.arcgismaps.location.LocationDataSource
13
+ import com.arcgismaps.location.LocationDisplayAutoPanMode
14
+ import com.arcgismaps.location.SimulatedLocationDataSource
15
+ import com.arcgismaps.location.SimulationParameters
16
+ import com.arcgismaps.location.SystemLocationDataSource
17
+ import java.time.Instant
18
+ import com.arcgismaps.mapping.Viewpoint
19
+ import com.arcgismaps.mapping.view.MapView
20
+ import com.arcgismaps.mapping.view.ScreenCoordinate
21
+ import expo.modules.kotlin.AppContext
22
+ import expo.modules.kotlin.Promise
23
+ import expo.modules.kotlin.records.Field
24
+ import expo.modules.kotlin.records.Record
25
+ import expo.modules.kotlin.viewevent.EventDispatcher
26
+ import expo.modules.kotlin.views.ExpoView
27
+ import kotlinx.coroutines.CoroutineScope
28
+ import kotlinx.coroutines.Dispatchers
29
+ import kotlinx.coroutines.Job
30
+ import kotlinx.coroutines.SupervisorJob
31
+ import kotlinx.coroutines.launch
32
+
33
+ class MapLoadedEventPayload(@Field val spatialReferenceWkid: Int? = null) : Record
34
+ class MapLoadErrorEventPayload(@Field val message: String = "") : Record
35
+
36
+ class PointRecord(
37
+ @Field val latitude: Double = 0.0,
38
+ @Field val longitude: Double = 0.0,
39
+ ) : Record
40
+
41
+ class ScreenPointRecord(
42
+ @Field val x: Double = 0.0,
43
+ @Field val y: Double = 0.0,
44
+ ) : Record
45
+
46
+ class TapEventPayload(
47
+ @Field val mapPoint: PointRecord = PointRecord(),
48
+ @Field val screenPoint: ScreenPointRecord = ScreenPointRecord(),
49
+ ) : Record
50
+
51
+ class LocationPositionRecord(
52
+ @Field val latitude: Double = 0.0,
53
+ @Field val longitude: Double = 0.0,
54
+ @Field val z: Double? = null,
55
+ ) : Record
56
+
57
+ class LocationEventPayload(
58
+ @Field val position: LocationPositionRecord = LocationPositionRecord(),
59
+ @Field val horizontalAccuracy: Double = 0.0,
60
+ @Field val verticalAccuracy: Double = 0.0,
61
+ @Field val course: Double = 0.0,
62
+ @Field val speed: Double = 0.0,
63
+ @Field val timestamp: Double = 0.0,
64
+ ) : Record
65
+
66
+ /** Declarative 2D map host. Renders the [MapRef] passed as the `map` view prop. */
67
+ class ExpoArcgisMapView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
68
+ private val onMapLoaded by EventDispatcher<MapLoadedEventPayload>()
69
+ private val onMapLoadError by EventDispatcher<MapLoadErrorEventPayload>()
70
+ private val onTap by EventDispatcher<TapEventPayload>()
71
+ private val onLocationChange by EventDispatcher<LocationEventPayload>()
72
+
73
+ private val mapView = MapView(context).also {
74
+ it.layoutParams = ViewGroup.LayoutParams(
75
+ ViewGroup.LayoutParams.MATCH_PARENT,
76
+ ViewGroup.LayoutParams.MATCH_PARENT
77
+ )
78
+ addView(it)
79
+ }
80
+
81
+ private val scope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
82
+ private var loadJob: Job? = null
83
+ private var observedLifecycle: Lifecycle? = null
84
+
85
+ init {
86
+ // Emit tap events with the map location (projected to WGS84).
87
+ scope.launch {
88
+ mapView.onSingleTapConfirmed.collect { event ->
89
+ val wgs84 = event.mapPoint?.let {
90
+ GeometryEngine.projectOrNull(it, SpatialReference.wgs84()) as? Point
91
+ }
92
+ onTap(
93
+ TapEventPayload(
94
+ mapPoint = PointRecord(wgs84?.y ?: 0.0, wgs84?.x ?: 0.0),
95
+ screenPoint = ScreenPointRecord(event.screenCoordinate.x, event.screenCoordinate.y)
96
+ )
97
+ )
98
+ }
99
+ }
100
+ // Emit a location event on each device-location update.
101
+ scope.launch {
102
+ mapView.locationDisplay.location.collect { location ->
103
+ location?.let { onLocationChange(locationPayload(it)) }
104
+ }
105
+ }
106
+ }
107
+
108
+ /** Receives the native map (by reference) from the `<Map>` SharedObject. */
109
+ fun setMap(ref: MapRef?) {
110
+ ref ?: return
111
+ applyMap(ref.map)
112
+ // The map may be replaced asynchronously (e.g. once a mobile map package loads) — re-apply then.
113
+ ref.onMapChanged = { newMap -> applyMap(newMap) }
114
+ }
115
+
116
+ private fun applyMap(map: com.arcgismaps.mapping.ArcGISMap) {
117
+ mapView.map = map
118
+ loadJob?.cancel()
119
+ loadJob = scope.launch {
120
+ map.load()
121
+ .onSuccess { onMapLoaded(MapLoadedEventPayload()) }
122
+ .onFailure { error ->
123
+ onMapLoadError(MapLoadErrorEventPayload(error.message ?: "Failed to load map"))
124
+ }
125
+ }
126
+ }
127
+
128
+ /** Receives the graphics overlays declared as `<GraphicsOverlay>` children of the `<MapView>`. */
129
+ fun setGraphicsOverlays(refs: List<GraphicsOverlayRef>) {
130
+ mapView.graphicsOverlays.clear()
131
+ mapView.graphicsOverlays.addAll(refs.map { it.overlay })
132
+ }
133
+
134
+ /** Animates the view to a runtime viewpoint sent from JS. */
135
+ fun setViewpoint(vp: Map<String, Any?>?) {
136
+ vp ?: return
137
+ val lat = (vp["latitude"] as? Number)?.toDouble() ?: return
138
+ val lon = (vp["longitude"] as? Number)?.toDouble() ?: return
139
+ val scale = (vp["scale"] as? Number)?.toDouble() ?: return
140
+ scope.launch { mapView.setViewpointAnimated(Viewpoint(lat, lon, scale), 0.5f) }
141
+ }
142
+
143
+ /** Enables/configures the device location display from JS (null disables it). */
144
+ fun setLocationDisplay(config: Map<String, Any?>?) {
145
+ val locationDisplay = mapView.locationDisplay
146
+ if (config == null) {
147
+ scope.launch { locationDisplay.dataSource.stop() }
148
+ return
149
+ }
150
+ locationDisplay.setAutoPanMode(autoPanMode(config["autoPanMode"] as? String))
151
+ (config["showLocation"] as? Boolean)?.let { locationDisplay.showLocation = it }
152
+ (config["wanderExtentFactor"] as? Number)?.toFloat()?.let { locationDisplay.wanderExtentFactor = it }
153
+ val newSource = locationDataSource(config["source"])
154
+ scope.launch {
155
+ locationDisplay.dataSource.stop()
156
+ if (newSource != null) locationDisplay.dataSource = newSource
157
+ locationDisplay.dataSource.start()
158
+ }
159
+ }
160
+
161
+ /** Resolves the JS `source` to a data source. Returns null to keep the current source unchanged. */
162
+ private fun locationDataSource(source: Any?): LocationDataSource? {
163
+ if (source is Map<*, *> && source["type"] == "simulated") {
164
+ val route = (source["route"] as? Map<*, *>)?.let { geometryFromDict(it) } as? Polyline ?: return null
165
+ val speed = (source["speed"] as? Number)?.toDouble() ?: 10.0
166
+ return SimulatedLocationDataSource(route, SimulationParameters(Instant.now(), speed, 0.0, 0.0))
167
+ }
168
+ // 'system' / unspecified: swap back only if currently simulated, otherwise keep the source.
169
+ return if (mapView.locationDisplay.dataSource is SimulatedLocationDataSource) SystemLocationDataSource() else null
170
+ }
171
+
172
+ private fun locationPayload(location: Location): LocationEventPayload = LocationEventPayload(
173
+ position = LocationPositionRecord(location.position.y, location.position.x, location.position.z),
174
+ horizontalAccuracy = location.horizontalAccuracy,
175
+ verticalAccuracy = location.verticalAccuracy,
176
+ course = location.course,
177
+ speed = location.speed,
178
+ timestamp = location.timestamp.toEpochMilli().toDouble(),
179
+ )
180
+
181
+ /** Binds an interactive GeometryEditor for sketching (null clears it). */
182
+ fun setGeometryEditor(ref: GeometryEditorRef?) {
183
+ mapView.geometryEditor = ref?.editor
184
+ }
185
+
186
+ /** Identifies the features under a screen point (one result per layer with hits). */
187
+ fun identify(screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise) {
188
+ val x = (screenPoint["x"] as? Number)?.toDouble() ?: 0.0
189
+ val y = (screenPoint["y"] as? Number)?.toDouble() ?: 0.0
190
+ val tolerance = (options?.get("tolerance") as? Number)?.toDouble() ?: 12.0
191
+ val maxResults = (options?.get("maxResults") as? Number)?.toInt() ?: 1
192
+ scope.launch {
193
+ mapView.identifyLayers(ScreenCoordinate(x, y), tolerance, false, maxResults)
194
+ .onSuccess { results -> promise.resolve(results.map { serializeIdentifyResult(it) }) }
195
+ .onFailure { promise.reject("IDENTIFY_ERROR", it.message ?: "Identify failed", it) }
196
+ }
197
+ }
198
+
199
+ /** Retries loading the map (Loadable pattern) — useful after a network outage. Re-emits the result. */
200
+ fun retryLoad(promise: Promise) {
201
+ val map = mapView.map ?: run { promise.resolve(null); return }
202
+ scope.launch {
203
+ map.retryLoad()
204
+ .onSuccess { onMapLoaded(MapLoadedEventPayload()); promise.resolve(null) }
205
+ .onFailure { error ->
206
+ onMapLoadError(MapLoadErrorEventPayload(error.message ?: "Failed to load map"))
207
+ promise.resolve(null)
208
+ }
209
+ }
210
+ }
211
+
212
+ override fun onAttachedToWindow() {
213
+ super.onAttachedToWindow()
214
+ // The view-based MapView renders only while observing a lifecycle.
215
+ val lifecycle = findViewTreeLifecycleOwner()?.lifecycle
216
+ if (lifecycle != null && lifecycle !== observedLifecycle) {
217
+ observedLifecycle?.removeObserver(mapView)
218
+ lifecycle.addObserver(mapView)
219
+ observedLifecycle = lifecycle
220
+ }
221
+ }
222
+
223
+ override fun onDetachedFromWindow() {
224
+ super.onDetachedFromWindow()
225
+ loadJob?.cancel()
226
+ observedLifecycle?.removeObserver(mapView)
227
+ observedLifecycle = null
228
+ }
229
+ }
230
+
231
+ /** Maps the JS auto-pan union to the native [LocationDisplayAutoPanMode]. */
232
+ private fun autoPanMode(mode: String?): LocationDisplayAutoPanMode = when (mode) {
233
+ "recenter" -> LocationDisplayAutoPanMode.Recenter
234
+ "navigation" -> LocationDisplayAutoPanMode.Navigation
235
+ "compassNavigation" -> LocationDisplayAutoPanMode.CompassNavigation
236
+ else -> LocationDisplayAutoPanMode.Off
237
+ }