expo-arcgis 0.1.0 โ†’ 0.1.2

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 (79) hide show
  1. package/README.md +7 -3
  2. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisExtrasModule.kt +87 -0
  3. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisGeometryModule.kt +81 -0
  4. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisMapView.kt +16 -0
  5. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisModule.kt +14 -32
  6. package/android/src/main/java/expo/modules/arcgis/ExpoArcgisSceneView.kt +16 -0
  7. package/android/src/main/java/expo/modules/arcgis/GeocoderFunctions.kt +13 -3
  8. package/android/src/main/java/expo/modules/arcgis/GeometryEngineFunctions.kt +35 -0
  9. package/android/src/main/java/expo/modules/arcgis/GraphicsOverlayRef.kt +25 -0
  10. package/android/src/main/java/expo/modules/arcgis/LayerRef.kt +244 -7
  11. package/android/src/main/java/expo/modules/arcgis/QueryCodec.kt +21 -0
  12. package/android/src/main/java/expo/modules/arcgis/RouterFunctions.kt +61 -0
  13. package/android/src/main/java/expo/modules/arcgis/UtilityNetworkRef.kt +24 -0
  14. package/build/DynamicEntityLayer.d.ts.map +1 -1
  15. package/build/ExpoArcgis.types.d.ts +252 -8
  16. package/build/ExpoArcgis.types.d.ts.map +1 -1
  17. package/build/ExpoArcgis.types.js.map +1 -1
  18. package/build/ExpoArcgisExtrasModule.d.ts +16 -0
  19. package/build/ExpoArcgisExtrasModule.d.ts.map +1 -0
  20. package/build/ExpoArcgisExtrasModule.js +3 -0
  21. package/build/ExpoArcgisExtrasModule.js.map +1 -0
  22. package/build/ExpoArcgisGeometryModule.d.ts +18 -2
  23. package/build/ExpoArcgisGeometryModule.d.ts.map +1 -1
  24. package/build/ExpoArcgisGeometryModule.js.map +1 -1
  25. package/build/ExpoArcgisModule.d.ts +23 -4
  26. package/build/ExpoArcgisModule.d.ts.map +1 -1
  27. package/build/ExpoArcgisModule.js.map +1 -1
  28. package/build/FeatureLayer.d.ts.map +1 -1
  29. package/build/FeatureLayer.js +2 -2
  30. package/build/FeatureLayer.js.map +1 -1
  31. package/build/GroupLayer.d.ts +9 -0
  32. package/build/GroupLayer.d.ts.map +1 -0
  33. package/build/GroupLayer.js +44 -0
  34. package/build/GroupLayer.js.map +1 -0
  35. package/build/auth.d.ts +25 -0
  36. package/build/auth.d.ts.map +1 -1
  37. package/build/auth.js +30 -0
  38. package/build/auth.js.map +1 -1
  39. package/build/geometryEngine.d.ts +12 -0
  40. package/build/geometryEngine.d.ts.map +1 -1
  41. package/build/geometryEngine.js +12 -0
  42. package/build/geometryEngine.js.map +1 -1
  43. package/build/index.d.ts +5 -2
  44. package/build/index.d.ts.map +1 -1
  45. package/build/index.js +5 -2
  46. package/build/index.js.map +1 -1
  47. package/build/layers.d.ts +19 -1
  48. package/build/layers.d.ts.map +1 -1
  49. package/build/layers.js +21 -0
  50. package/build/layers.js.map +1 -1
  51. package/build/router.d.ts +6 -1
  52. package/build/router.d.ts.map +1 -1
  53. package/build/router.js +6 -0
  54. package/build/router.js.map +1 -1
  55. package/expo-module.config.json +6 -2
  56. package/ios/ExpoArcgisExtrasModule.swift +85 -0
  57. package/ios/ExpoArcgisGeometryModule.swift +94 -0
  58. package/ios/ExpoArcgisMapView.swift +15 -0
  59. package/ios/ExpoArcgisModule.swift +14 -34
  60. package/ios/ExpoArcgisSceneView.swift +15 -0
  61. package/ios/GeocoderFunctions.swift +9 -1
  62. package/ios/GeometryEngineFunctions.swift +39 -0
  63. package/ios/GraphicsOverlayRef.swift +20 -0
  64. package/ios/LayerRef.swift +244 -7
  65. package/ios/QueryCodec.swift +21 -1
  66. package/ios/RouterFunctions.swift +57 -0
  67. package/ios/UtilityNetworkRef.swift +21 -0
  68. package/package.json +2 -2
  69. package/src/ExpoArcgis.types.ts +259 -8
  70. package/src/ExpoArcgisExtrasModule.ts +22 -0
  71. package/src/ExpoArcgisGeometryModule.ts +31 -1
  72. package/src/ExpoArcgisModule.ts +29 -3
  73. package/src/FeatureLayer.tsx +3 -2
  74. package/src/GroupLayer.tsx +52 -0
  75. package/src/auth.ts +32 -0
  76. package/src/geometryEngine.ts +22 -0
  77. package/src/index.ts +10 -0
  78. package/src/layers.tsx +49 -0
  79. package/src/router.ts +16 -1
package/README.md CHANGED
@@ -6,6 +6,8 @@ SDK-faithful component API to JS/TS โ€” `<MapView>` / `<SceneView>` with layers,
6
6
  editing, query, analysis, geocoding, routing, geoprocessing, utility networks, offline, real-time and
7
7
  authentication.
8
8
 
9
+ ๐Ÿ“– **[Documentation & samples โ†’](https://alex-krassavin.github.io/expo-arcgis)**
10
+
9
11
  > **Status โ€” early / pre-1.0.** The full surface below is implemented and **compile-verified** on both
10
12
  > platforms (TypeScript ยท Android `compileDebugKotlin` ยท iOS pod build). On-device **runtime** validation
11
13
  > (rendering, network calls, device sensors) should be done in your own app. The API may still change
@@ -16,14 +18,14 @@ authentication.
16
18
  | Area | What's covered |
17
19
  |---|---|
18
20
  | **2D / 3D** | `<MapView>` + `<Map>`; `<SceneView>` + `<Scene>` (surface, camera, web scenes, light/shadows) |
19
- | **Layers** | Feature, Tile, MapImage, Vector-tile, Raster, WMS, WMTS, KML, Scene, IntegratedMesh, PointCloud, OGC 3D Tiles, WebTiled, OpenStreetMap, **WFS**, **OGC API Features**, **DynamicEntity** (stream) |
21
+ | **Layers** | Feature, Tile, MapImage, Vector-tile, Raster, WMS, WMTS, KML, Scene, IntegratedMesh, PointCloud, OGC 3D Tiles, WebTiled, OpenStreetMap, WFS, OGC API Features, DynamicEntity (stream), Annotation, Dimension, BuildingScene (3D), OrientedImagery, SubtypeFeature, **Group** (container), **FeatureCollection** (in-memory), **GeoPackage** (local `.gpkg`) |
20
22
  | **Graphics** | `<GraphicsOverlay>` + `<Graphic>`, symbols (simple marker/line/fill, text, 3D scene symbol, picture-marker), renderers (simple / unique-value / class-breaks), labels, clustering |
21
23
  | **Geometry** | `geometryEngine` (buffer, project, distance, intersect, โ€ฆ), `coordinateFormatter`, codec for all geometry types |
22
24
  | **Query** | feature query / count / extent / statistics on a `<FeatureLayer>` ref; `identify` on a view ref |
23
25
  | **Editing** | add / update / delete features, `<GeometryEditor>` (tools), feature templates |
24
26
  | **Location** | device location, simulated location data source, `onLocationChange` |
25
27
  | **Geocoding** | `geocoder.geocode` / `reverseGeocode` / `suggest`, offline `.loc` locators |
26
- | **Routing** | `router.solveRoute` / directions, travel modes, point barriers, curb approach |
28
+ | **Routing** | `router.solveRoute` / directions, travel modes, point barriers, curb approach; `router.createRouteTracker` turn-by-turn navigation |
27
29
  | **Analysis (3D)** | `<AnalysisOverlay>` + `<Viewshed>` / `<LineOfSight>` / `<DistanceMeasurement>` |
28
30
  | **Geoprocessing** | `geoprocessor.execute` โ†’ `JobRef` (progress + cancel), typed parameters |
29
31
  | **Utility network** | `<UtilityNetwork>` load + trace, named configs, associations, `describeNetwork` |
@@ -122,7 +124,9 @@ const [hit] = await geocoder.geocode('Los Angeles');
122
124
  - **Views & models** โ€” `MapSettings`, `Map`, `Scene`, `MapView`, `SceneView`
123
125
  - **Layers** โ€” `FeatureLayer`, `TileLayer`, `MapImageLayer`, `SceneLayer`, `VectorTileLayer`,
124
126
  `IntegratedMeshLayer`, `PointCloudLayer`, `Ogc3DTilesLayer`, `WebTiledLayer`, `OpenStreetMapLayer`,
125
- `WmsLayer`, `WmtsLayer`, `RasterLayer`, `KmlLayer`, `WfsLayer`, `OgcFeatureLayer`, `DynamicEntityLayer`
127
+ `WmsLayer`, `WmtsLayer`, `RasterLayer`, `KmlLayer`, `WfsLayer`, `OgcFeatureLayer`, `DynamicEntityLayer`,
128
+ `AnnotationLayer`, `DimensionLayer`, `BuildingSceneLayer`, `OrientedImageryLayer`, `SubtypeFeatureLayer`,
129
+ `GroupLayer`, `FeatureCollectionLayer`, `GeoPackageLayer`
126
130
  - **Graphics & analysis** โ€” `GraphicsOverlay`, `Graphic`, `AnalysisOverlay`, `Viewshed`, `LineOfSight`,
127
131
  `DistanceMeasurement`, `GeometryEditor`, `UtilityNetwork`
128
132
  - **Namespaces** โ€” `geometryEngine`, `coordinateFormatter`, `geocoder`, `router`, `geoprocessor`, `offline`
@@ -0,0 +1,87 @@
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
+ * Third native module, hosting the heavier operational-layer SharedObject classes (currently
9
+ * [FeatureLayerRef]), exposed to JS as `ExpoArcgisExtras`. Split out of [ExpoArcgisGeometryModule]
10
+ * so that no module's `definition()` exceeds the JVM 64 KB method-size limit. SharedObjects are
11
+ * global, so a ref constructed here attaches to a `<MapView>` from the main module fine.
12
+ */
13
+ class ExpoArcgisExtrasModule : Module() {
14
+ override fun definition() = ModuleDefinition {
15
+ Name("ExpoArcgisExtras")
16
+
17
+ Class(FeatureLayerRef::class) {
18
+ Constructor { props: Map<String, Any?> ->
19
+ FeatureLayerRef(appContext, props).also { it.applyProps(props) }
20
+ }
21
+ Function("applyProps") { ref: FeatureLayerRef, changed: Map<String, Any?> ->
22
+ ref.applyProps(changed)
23
+ }
24
+ AsyncFunction("queryFeatures") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
25
+ ref.queryFeatures(query)
26
+ }
27
+ AsyncFunction("queryFeatureCount") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
28
+ ref.queryFeatureCount(query)
29
+ }
30
+ AsyncFunction("queryExtent") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
31
+ ref.queryExtent(query)
32
+ }
33
+ AsyncFunction("queryStatistics") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?> ->
34
+ ref.queryStatistics(query)
35
+ }
36
+ AsyncFunction("queryFeatureTemplates") Coroutine { ref: FeatureLayerRef ->
37
+ ref.queryFeatureTemplates()
38
+ }
39
+ AsyncFunction("addFeature") Coroutine { ref: FeatureLayerRef, attributes: Map<String, Any?>, geometry: Map<String, Any?>?, apply: Boolean? ->
40
+ ref.addFeature(attributes, geometry, apply)
41
+ }
42
+ AsyncFunction("updateFeature") Coroutine { ref: FeatureLayerRef, objectId: Long, changes: Map<String, Any?>, apply: Boolean? ->
43
+ ref.updateFeature(objectId, changes, apply)
44
+ }
45
+ AsyncFunction("deleteFeature") Coroutine { ref: FeatureLayerRef, objectId: Long, apply: Boolean? ->
46
+ ref.deleteFeature(objectId, apply)
47
+ }
48
+ AsyncFunction("applyEdits") Coroutine { ref: FeatureLayerRef ->
49
+ ref.applyEdits()
50
+ }
51
+ AsyncFunction("undoLocalEdits") Coroutine { ref: FeatureLayerRef ->
52
+ ref.undoLocalEdits()
53
+ }
54
+ AsyncFunction("queryRelatedFeatures") Coroutine { ref: FeatureLayerRef, objectId: Long ->
55
+ ref.queryRelatedFeatures(objectId)
56
+ }
57
+ AsyncFunction("queryAttachments") Coroutine { ref: FeatureLayerRef, objectId: Long ->
58
+ ref.queryAttachments(objectId)
59
+ }
60
+ AsyncFunction("addAttachment") Coroutine { ref: FeatureLayerRef, objectId: Long, name: String, contentType: String, dataBase64: String ->
61
+ ref.addAttachment(objectId, name, contentType, dataBase64)
62
+ }
63
+ AsyncFunction("fetchAttachment") Coroutine { ref: FeatureLayerRef, objectId: Long, attachmentId: Long ->
64
+ ref.fetchAttachment(objectId, attachmentId)
65
+ }
66
+ AsyncFunction("deleteAttachment") Coroutine { ref: FeatureLayerRef, objectId: Long, attachmentId: Long ->
67
+ ref.deleteAttachment(objectId, attachmentId)
68
+ }
69
+ AsyncFunction("updateAttachment") Coroutine { ref: FeatureLayerRef, objectId: Long, attachmentId: Long, name: String, contentType: String, dataBase64: String ->
70
+ ref.updateAttachment(objectId, attachmentId, name, contentType, dataBase64)
71
+ }
72
+ }
73
+
74
+ // Turn-by-turn navigation โ€” solve a route and track device locations against it.
75
+ AsyncFunction("createRouteTracker") Coroutine { stops: List<Map<String, Any?>>, params: Map<String, Any?> ->
76
+ createRouteTracker(appContext, stops, params)
77
+ }
78
+ Class(RouteTrackerRef::class) {
79
+ AsyncFunction("trackLocation") Coroutine { ref: RouteTrackerRef, location: Map<String, Any?> ->
80
+ ref.trackLocation(location)
81
+ }
82
+ AsyncFunction("switchToNextDestination") Coroutine { ref: RouteTrackerRef ->
83
+ ref.switchToNextDestination()
84
+ }
85
+ }
86
+ }
87
+ }
@@ -1,5 +1,7 @@
1
1
  package expo.modules.arcgis
2
2
 
3
+ import com.arcgismaps.ArcGISEnvironment
4
+ import com.arcgismaps.httpcore.authentication.ArcGISCredentialStore
3
5
  import expo.modules.kotlin.functions.Coroutine
4
6
  import expo.modules.kotlin.modules.Module
5
7
  import expo.modules.kotlin.modules.ModuleDefinition
@@ -30,6 +32,12 @@ class ExpoArcgisGeometryModule : Module() {
30
32
  Function("geClip", ::geClip)
31
33
  Function("geCut", ::geCut)
32
34
  Function("geConvexHull", ::geConvexHull)
35
+ Function("geLabelPoint", ::geLabelPoint)
36
+ Function("geNormalizeCentralMeridian", ::geNormalizeCentralMeridian)
37
+ Function("geReshape", ::geReshape)
38
+ Function("geIntersections", ::geIntersections)
39
+ Function("geExtend", ::geExtend)
40
+ Function("geAutoComplete", ::geAutoComplete)
33
41
  Function("geBoundary", ::geBoundary)
34
42
  Function("geSimplify", ::geSimplify)
35
43
  Function("geDensify", ::geDensify)
@@ -90,6 +98,79 @@ class ExpoArcgisGeometryModule : Module() {
90
98
  AsyncFunction("cancel") Coroutine { ref: JobRef -> ref.cancel() }
91
99
  }
92
100
 
101
+ // Extended operational layers from a service URL โ€” registered here (not in the main module) to
102
+ // keep ExpoArcgisModule's definition under the JVM 64 KB method limit. SharedObjects are global,
103
+ // so a layer constructed via this module attaches to a `<MapView>` from the main module fine.
104
+ Class(AnnotationLayerRef::class) {
105
+ Constructor { props: Map<String, Any?> ->
106
+ AnnotationLayerRef(appContext, props["url"] as? String ?: "").also { it.applyProps(props) }
107
+ }
108
+ Function("applyProps") { ref: AnnotationLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
109
+ }
110
+ Class(DimensionLayerRef::class) {
111
+ Constructor { props: Map<String, Any?> ->
112
+ DimensionLayerRef(appContext, props["url"] as? String ?: "").also { it.applyProps(props) }
113
+ }
114
+ Function("applyProps") { ref: DimensionLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
115
+ }
116
+ Class(BuildingSceneLayerRef::class) {
117
+ Constructor { props: Map<String, Any?> ->
118
+ BuildingSceneLayerRef(appContext, props["url"] as? String ?: "").also { it.applyProps(props) }
119
+ }
120
+ Function("applyProps") { ref: BuildingSceneLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
121
+ }
122
+ Class(OrientedImageryLayerRef::class) {
123
+ Constructor { props: Map<String, Any?> ->
124
+ OrientedImageryLayerRef(appContext, props["url"] as? String ?: "").also { it.applyProps(props) }
125
+ }
126
+ Function("applyProps") { ref: OrientedImageryLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
127
+ }
128
+ Class(SubtypeFeatureLayerRef::class) {
129
+ Constructor { props: Map<String, Any?> ->
130
+ SubtypeFeatureLayerRef(appContext, props["url"] as? String ?: "").also { it.applyProps(props) }
131
+ }
132
+ Function("applyProps") { ref: SubtypeFeatureLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
133
+ }
134
+ // GroupLayer โ€” a container layer that hosts its own child layers (addLayer/removeLayer).
135
+ Class(GroupLayerRef::class) {
136
+ Constructor { props: Map<String, Any?> ->
137
+ GroupLayerRef(appContext).also { it.applyProps(props) }
138
+ }
139
+ Function("applyProps") { ref: GroupLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
140
+ Function("addLayer") { ref: GroupLayerRef, layer: LayerRef -> ref.addLayer(layer) }
141
+ Function("removeLayer") { ref: GroupLayerRef, layer: LayerRef -> ref.removeLayer(layer) }
142
+ }
143
+ // FeatureLayerRef moved to the third module (ExpoArcgisExtras) to keep this module's
144
+ // definition() under the 64 KB limit. SharedObjects are global, so it stays cross-module.
145
+ // In-memory FeatureCollectionLayer โ€” built from a client-side schema + features (no service).
146
+ Class(FeatureCollectionLayerRef::class) {
147
+ Constructor { props: Map<String, Any?> ->
148
+ FeatureCollectionLayerRef(appContext, props).also { it.applyProps(props) }
149
+ }
150
+ Function("applyProps") { ref: FeatureCollectionLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
151
+ }
152
+ // GeoPackage layer โ€” async-loads a local .gpkg, picks the feature table, wraps it in a FeatureLayer.
153
+ Class(GeoPackageLayerRef::class) {
154
+ Constructor { props: Map<String, Any?> ->
155
+ GeoPackageLayerRef(
156
+ appContext,
157
+ props["path"] as? String ?: "",
158
+ props["tableName"] as? String,
159
+ ).also { it.applyProps(props) }
160
+ }
161
+ Function("applyProps") { ref: GeoPackageLayerRef, changed: Map<String, Any?> -> ref.applyProps(changed) }
162
+ }
163
+
164
+ // Auth โ€” persistent credential store (survives app restarts via Android encrypted storage).
165
+ // Registered here (not in the main module) because the main module is at the JVM 64 KB limit.
166
+ AsyncFunction("enablePersistentCredentialStore") Coroutine { ->
167
+ val store = ArcGISCredentialStore.createWithPersistence().getOrThrow()
168
+ ArcGISEnvironment.authenticationManager.arcGISCredentialStore = store
169
+ }
170
+ AsyncFunction("clearCredentialStore") Coroutine { ->
171
+ ArcGISEnvironment.authenticationManager.arcGISCredentialStore.removeAll()
172
+ }
173
+
93
174
  // Offline โ€” take maps/data offline, exposed as the JS `offline` namespace.
94
175
  AsyncFunction("generateOfflineMap") Coroutine { portalItemId: String, areaOfInterest: Map<String, Any?>, downloadName: String ->
95
176
  generateOfflineMap(appContext, appContext.reactContext?.filesDir, portalItemId, areaOfInterest, downloadName)
@@ -196,6 +196,22 @@ class ExpoArcgisMapView(context: Context, appContext: AppContext) : ExpoView(con
196
196
  }
197
197
  }
198
198
 
199
+ /** Identifies popups under a screen point โ€” evaluates each and returns `{ title, fields }`. */
200
+ fun identifyPopups(screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise) {
201
+ val x = (screenPoint["x"] as? Number)?.toDouble() ?: 0.0
202
+ val y = (screenPoint["y"] as? Number)?.toDouble() ?: 0.0
203
+ val tolerance = (options?.get("tolerance") as? Number)?.toDouble() ?: 12.0
204
+ val maxResults = (options?.get("maxResults") as? Number)?.toInt() ?: 1
205
+ scope.launch {
206
+ try {
207
+ val results = mapView.identifyLayers(ScreenCoordinate(x, y), tolerance, false, maxResults).getOrThrow()
208
+ promise.resolve(serializePopups(results))
209
+ } catch (e: Exception) {
210
+ promise.reject("IDENTIFY_ERROR", e.message ?: "Identify failed", e)
211
+ }
212
+ }
213
+ }
214
+
199
215
  /** Retries loading the map (Loadable pattern) โ€” useful after a network outage. Re-emits the result. */
200
216
  fun retryLoad(promise: Promise) {
201
217
  val map = mapView.map ?: run { promise.resolve(null); return }
@@ -96,38 +96,8 @@ class ExpoArcgisModule : Module() {
96
96
  }
97
97
 
98
98
  // Operational layers โ€” SharedObjects the JS <FeatureLayer>/<TileLayer> construct.
99
- Class(FeatureLayerRef::class) {
100
- Constructor { props: Map<String, Any?> ->
101
- FeatureLayerRef(appContext, props).also { it.applyProps(props) }
102
- }
103
- Function("applyProps") { ref: FeatureLayerRef, changed: Map<String, Any?> ->
104
- ref.applyProps(changed)
105
- }
106
- AsyncFunction("queryFeatures") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
107
- ref.queryFeatures(query)
108
- }
109
- AsyncFunction("queryFeatureCount") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
110
- ref.queryFeatureCount(query)
111
- }
112
- AsyncFunction("queryExtent") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?>? ->
113
- ref.queryExtent(query)
114
- }
115
- AsyncFunction("queryStatistics") Coroutine { ref: FeatureLayerRef, query: Map<String, Any?> ->
116
- ref.queryStatistics(query)
117
- }
118
- AsyncFunction("queryFeatureTemplates") Coroutine { ref: FeatureLayerRef ->
119
- ref.queryFeatureTemplates()
120
- }
121
- AsyncFunction("addFeature") Coroutine { ref: FeatureLayerRef, attributes: Map<String, Any?>, geometry: Map<String, Any?>? ->
122
- ref.addFeature(attributes, geometry)
123
- }
124
- AsyncFunction("updateFeature") Coroutine { ref: FeatureLayerRef, objectId: Long, changes: Map<String, Any?> ->
125
- ref.updateFeature(objectId, changes)
126
- }
127
- AsyncFunction("deleteFeature") Coroutine { ref: FeatureLayerRef, objectId: Long ->
128
- ref.deleteFeature(objectId)
129
- }
130
- }
99
+ // FeatureLayerRef is registered on the ExpoArcgisGeometry module to keep this module's
100
+ // definition() under the Android JVM 64 KB method-size limit (SharedObjects cross modules).
131
101
 
132
102
  Class(TiledLayerRef::class) {
133
103
  Constructor { props: Map<String, Any?> ->
@@ -358,6 +328,10 @@ class ExpoArcgisModule : Module() {
358
328
  AsyncFunction("associations") Coroutine { ref: UtilityNetworkRef, tableName: String, whereClause: String ->
359
329
  ref.associations(tableName, whereClause)
360
330
  }
331
+ AsyncFunction("getState") Coroutine { ref: UtilityNetworkRef -> ref.getState() }
332
+ Function("validateNetworkTopology") { ref: UtilityNetworkRef, extent: Map<String, Any?> ->
333
+ ref.validateNetworkTopology(extent)
334
+ }
361
335
  }
362
336
 
363
337
  // Interactive GeometryEditor โ€” bound to a <MapView> for sketching; emits onGeometryChange.
@@ -401,6 +375,10 @@ class ExpoArcgisModule : Module() {
401
375
  view.identify(screenPoint, options, promise)
402
376
  }
403
377
 
378
+ AsyncFunction("identifyPopups") { view: ExpoArcgisMapView, screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise ->
379
+ view.identifyPopups(screenPoint, options, promise)
380
+ }
381
+
404
382
  AsyncFunction("retryLoad") { view: ExpoArcgisMapView, promise: Promise ->
405
383
  view.retryLoad(promise)
406
384
  }
@@ -446,6 +424,10 @@ class ExpoArcgisModule : Module() {
446
424
  AsyncFunction("identify") { view: ExpoArcgisSceneView, screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise ->
447
425
  view.identify(screenPoint, options, promise)
448
426
  }
427
+
428
+ AsyncFunction("identifyPopups") { view: ExpoArcgisSceneView, screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise ->
429
+ view.identifyPopups(screenPoint, options, promise)
430
+ }
449
431
  }
450
432
  }
451
433
  }
@@ -101,6 +101,22 @@ class ExpoArcgisSceneView(context: Context, appContext: AppContext) : ExpoView(c
101
101
  }
102
102
  }
103
103
 
104
+ /** Identifies popups under a screen point โ€” evaluates each and returns `{ title, fields }`. */
105
+ fun identifyPopups(screenPoint: Map<String, Any?>, options: Map<String, Any?>?, promise: Promise) {
106
+ val x = (screenPoint["x"] as? Number)?.toDouble() ?: 0.0
107
+ val y = (screenPoint["y"] as? Number)?.toDouble() ?: 0.0
108
+ val tolerance = (options?.get("tolerance") as? Number)?.toDouble() ?: 12.0
109
+ val maxResults = (options?.get("maxResults") as? Number)?.toInt() ?: 1
110
+ scope.launch {
111
+ try {
112
+ val results = sceneView.identifyLayers(ScreenCoordinate(x, y), tolerance, false, maxResults).getOrThrow()
113
+ promise.resolve(serializePopups(results))
114
+ } catch (e: Exception) {
115
+ promise.reject("IDENTIFY_ERROR", e.message ?: "Identify failed", e)
116
+ }
117
+ }
118
+ }
119
+
104
120
  /** Retries loading the scene (Loadable pattern) โ€” useful after a network outage. Re-emits the result. */
105
121
  fun retryLoad(promise: Promise) {
106
122
  val scene = sceneView.scene ?: run { promise.resolve(null); return }
@@ -25,9 +25,19 @@ private fun locatorTask(params: Map<String, Any?>): LocatorTask {
25
25
  return locators.getOrPut(url) { LocatorTask(url) }
26
26
  }
27
27
 
28
- internal suspend fun geocode(searchText: String, params: Map<String, Any?>): List<Map<String, Any?>> =
29
- locatorTask(params).geocode(searchText, buildGeocodeParameters(params)).getOrThrow()
30
- .map { serializeGeocodeResult(it) }
28
+ internal suspend fun geocode(searchText: String, params: Map<String, Any?>): List<Map<String, Any?>> {
29
+ val locator = locatorTask(params)
30
+ val parameters = buildGeocodeParameters(params)
31
+ @Suppress("UNCHECKED_CAST")
32
+ val searchValues = (params["searchValues"] as? Map<*, *>)
33
+ ?.mapNotNull { (k, v) -> if (k is String && v is String) k to v else null }
34
+ ?.toMap()
35
+ return if (!searchValues.isNullOrEmpty()) {
36
+ locator.geocode(searchValues, parameters).getOrThrow()
37
+ } else {
38
+ locator.geocode(searchText, parameters).getOrThrow()
39
+ }.map { serializeGeocodeResult(it) }
40
+ }
31
41
 
32
42
  internal suspend fun reverseGeocode(point: Map<String, Any?>, params: Map<String, Any?>): List<Map<String, Any?>> {
33
43
  val location = geometryFromDict(point) as? Point ?: return emptyList()
@@ -3,7 +3,9 @@ package expo.modules.arcgis
3
3
  import com.arcgismaps.geometry.Envelope
4
4
  import com.arcgismaps.geometry.Geometry
5
5
  import com.arcgismaps.geometry.GeometryEngine
6
+ import com.arcgismaps.geometry.Multipart
6
7
  import com.arcgismaps.geometry.Point
8
+ import com.arcgismaps.geometry.Polygon
7
9
  import com.arcgismaps.geometry.Polyline
8
10
 
9
11
  /**
@@ -104,6 +106,39 @@ internal fun geCut(g: Map<String, Any?>, cutter: Map<String, Any?>): List<Map<St
104
106
  internal fun geConvexHull(g: Map<String, Any?>): Map<String, Any?>? =
105
107
  parseGeo(g)?.let { encode(GeometryEngine.convexHullOrNull(it)) }
106
108
 
109
+ internal fun geLabelPoint(g: Map<String, Any?>): Map<String, Any?>? =
110
+ (parseGeo(g) as? Polygon)?.let { encode(GeometryEngine.labelPointOrNull(it)) }
111
+
112
+ internal fun geNormalizeCentralMeridian(g: Map<String, Any?>): Map<String, Any?>? =
113
+ parseGeo(g)?.let { encode(GeometryEngine.normalizeCentralMeridian(it)) }
114
+
115
+ internal fun geReshape(g: Map<String, Any?>, reshaper: Map<String, Any?>): Map<String, Any?>? {
116
+ val multipart = parseGeo(g) as? Multipart ?: return null
117
+ val line = parseGeo(reshaper) as? Polyline ?: return null
118
+ return encode(GeometryEngine.reshape(multipart, line))
119
+ }
120
+
121
+ internal fun geIntersections(a: Map<String, Any?>, b: Map<String, Any?>): List<Map<String, Any?>> {
122
+ val g1 = parseGeo(a) ?: return emptyList()
123
+ val g2 = parseGeo(b) ?: return emptyList()
124
+ return GeometryEngine.tryIntersections(g1, g2).mapNotNull { encode(it) }
125
+ }
126
+
127
+ internal fun geExtend(p: Map<String, Any?>, extender: Map<String, Any?>): Map<String, Any?>? {
128
+ val polyline = parseGeo(p) as? Polyline ?: return null
129
+ val ext = parseGeo(extender) as? Polyline ?: return null
130
+ return encode(GeometryEngine.extend(polyline, ext, emptySet()))
131
+ }
132
+
133
+ internal fun geAutoComplete(
134
+ existing: List<Map<String, Any?>>,
135
+ boundaries: List<Map<String, Any?>>,
136
+ ): List<Map<String, Any?>> {
137
+ val polygons = existing.mapNotNull { parseGeo(it) as? Polygon }
138
+ val lines = boundaries.mapNotNull { parseGeo(it) as? Polyline }
139
+ return GeometryEngine.tryAutoComplete(polygons, lines).mapNotNull { encode(it) }
140
+ }
141
+
107
142
  internal fun geBoundary(g: Map<String, Any?>): Map<String, Any?>? =
108
143
  parseGeo(g)?.let { encode(GeometryEngine.boundaryOrNull(it)) }
109
144
 
@@ -15,8 +15,11 @@ import com.arcgismaps.mapping.symbology.SimpleFillSymbol
15
15
  import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
16
16
  import com.arcgismaps.mapping.symbology.SimpleLineSymbol
17
17
  import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
18
+ import com.arcgismaps.mapping.symbology.DistanceCompositeSceneSymbol
19
+ import com.arcgismaps.mapping.symbology.DistanceSymbolRange
18
20
  import com.arcgismaps.mapping.symbology.SimpleMarkerSceneSymbol
19
21
  import com.arcgismaps.mapping.symbology.SimpleMarkerSceneSymbolStyle
22
+ import com.arcgismaps.mapping.symbology.PictureFillSymbol
20
23
  import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
21
24
  import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
22
25
  import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
@@ -188,6 +191,28 @@ private fun buildSymbol(s: Map<*, *>): Symbol? = when (s["type"]) {
188
191
  (s["height"] as? Number)?.toFloat()?.let { height = it }
189
192
  }
190
193
  }
194
+ "picture-fill" -> (s["url"] as? String)?.let { url ->
195
+ PictureFillSymbol(url).apply {
196
+ (s["width"] as? Number)?.toFloat()?.let { width = it }
197
+ (s["height"] as? Number)?.toFloat()?.let { height = it }
198
+ outline = outlineOf(s["outline"])
199
+ }
200
+ }
201
+ "distance-composite-scene" -> {
202
+ val composite = DistanceCompositeSceneSymbol()
203
+ val rangeList = s["ranges"] as? List<*> ?: emptyList<Any>()
204
+ for (rd in rangeList) {
205
+ val rdMap = rd as? Map<*, *> ?: continue
206
+ val sym = (rdMap["symbol"] as? Map<*, *>)?.let(::buildSymbol) ?: continue
207
+ val range = DistanceSymbolRange(
208
+ sym,
209
+ (rdMap["minDistance"] as? Number)?.toDouble(),
210
+ (rdMap["maxDistance"] as? Number)?.toDouble(),
211
+ )
212
+ composite.ranges.add(range)
213
+ }
214
+ composite
215
+ }
191
216
  else -> null
192
217
  }
193
218