Skip to content

Commit 7ff9ec1

Browse files
hud10837puneet-pdx
andauthored
SceneView: AnalysisOverlayCollection (#269)
* create AnalysisOverlayCollection * mv factory for graphics overlay collection * add analysis to scene view * eof newlines * add doc * Fix doc that mentions graphics overlays Co-authored-by: Puneet Prakash <[email protected]> * mv factory function to SceneView file * address doc comments * mv updater to SceneView file * Update toolkit/geo-compose/src/main/java/com/arcgismaps/toolkit/geocompose/SceneView.kt Co-authored-by: Puneet Prakash <[email protected]> --------- Co-authored-by: Puneet Prakash <[email protected]>
1 parent bfb41ed commit 7ff9ec1

File tree

4 files changed

+183
-17
lines changed

4 files changed

+183
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2023 Esri
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package com.arcgismaps.toolkit.geocompose
19+
20+
import androidx.compose.runtime.Composable
21+
import androidx.compose.runtime.LaunchedEffect
22+
import androidx.compose.runtime.Stable
23+
import com.arcgismaps.mapping.view.AnalysisOverlay
24+
import com.arcgismaps.mapping.view.SceneView
25+
import kotlinx.coroutines.channels.BufferOverflow
26+
import kotlinx.coroutines.flow.MutableSharedFlow
27+
import kotlinx.coroutines.flow.SharedFlow
28+
import kotlinx.coroutines.flow.asSharedFlow
29+
30+
/**
31+
* A collection class to encapsulate the [AnalysisOverlay] list used by the [com.arcgismaps.toolkit.geocompose.SceneView]
32+
*
33+
* @since 200.4.0
34+
*/
35+
@Stable
36+
public class AnalysisOverlayCollection : Iterable<AnalysisOverlay> {
37+
38+
private val analysisOverlays = mutableListOf<AnalysisOverlay>()
39+
40+
private val _changed: MutableSharedFlow<ChangedEvent> = MutableSharedFlow(
41+
extraBufferCapacity = Int.MAX_VALUE,
42+
onBufferOverflow = BufferOverflow.DROP_OLDEST
43+
)
44+
45+
/**
46+
* [SharedFlow] used to emit changes made to the [analysisOverlays] list
47+
*/
48+
internal val changed: SharedFlow<ChangedEvent> = _changed.asSharedFlow()
49+
50+
override fun iterator(): Iterator<AnalysisOverlay> {
51+
return analysisOverlays.iterator()
52+
}
53+
54+
/**
55+
* Add a [analysisOverlay] to this AnalysisOverlayCollection.
56+
*
57+
* @return if the add operation succeeds, return true.
58+
* @since 200.4.0
59+
*/
60+
public fun add(analysisOverlay: AnalysisOverlay): Boolean {
61+
return if (analysisOverlays.add(analysisOverlay)) {
62+
_changed.tryEmit(ChangedEvent.Added(analysisOverlay))
63+
true
64+
} else false
65+
}
66+
67+
/**
68+
* Remove a [analysisOverlay] from this AnalysisOverlayCollection.
69+
*
70+
* @return if the remove operation succeeds, return true.
71+
* @since 200.4.0
72+
*/
73+
public fun remove(analysisOverlay: AnalysisOverlay): Boolean {
74+
return if (analysisOverlays.remove(analysisOverlay)) {
75+
_changed.tryEmit(ChangedEvent.Removed(analysisOverlay))
76+
true
77+
} else false
78+
}
79+
80+
/**
81+
* Returns the number of analysis overlays in this AnalysisOverlayCollection.
82+
*
83+
* @since 200.4.0
84+
*/
85+
public val size: Int
86+
get() = analysisOverlays.size
87+
88+
/**
89+
* Clears all analysis overlays from this AnalysisOverlayCollection.
90+
*
91+
* @since 200.4.0
92+
*/
93+
public fun clear() {
94+
analysisOverlays.clear()
95+
_changed.tryEmit(ChangedEvent.Cleared)
96+
}
97+
98+
/**
99+
* Sealed class used to notify the compose SceneView to update the AnalysisOverlays on the
100+
* type of [ChangedEvent].
101+
*
102+
* @since 200.4.0
103+
*/
104+
internal sealed class ChangedEvent() {
105+
class Added(val element: AnalysisOverlay) : ChangedEvent()
106+
class Removed(val element: AnalysisOverlay) : ChangedEvent()
107+
object Cleared : ChangedEvent()
108+
}
109+
}

toolkit/geo-compose/src/main/java/com/arcgismaps/toolkit/geocompose/GraphicsOverlayCollection.kt

+18
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package com.arcgismaps.toolkit.geocompose
2020
import androidx.compose.runtime.Composable
2121
import androidx.compose.runtime.LaunchedEffect
2222
import androidx.compose.runtime.Stable
23+
import androidx.compose.runtime.remember
2324
import com.arcgismaps.mapping.view.GeoView
2425
import com.arcgismaps.mapping.view.GraphicsOverlay
2526
import kotlinx.coroutines.channels.BufferOverflow
@@ -144,3 +145,20 @@ internal fun GraphicsOverlaysUpdater(
144145
}
145146
}
146147
}
148+
149+
/**
150+
* Create and [remember] a [GraphicsOverlayCollection].
151+
* [init] will be called when the [GraphicsOverlayCollection] is first created to configure its
152+
* initial state.
153+
*
154+
* @param key invalidates the remembered GraphicsOverlayCollection if different from the previous composition
155+
* @param init called when the [GraphicsOverlayCollection] is created to configure its initial state
156+
* @since 200.4.0
157+
*/
158+
@Composable
159+
public inline fun rememberGraphicsOverlayCollection(
160+
key: Any? = null,
161+
crossinline init: GraphicsOverlayCollection.() -> Unit = {}
162+
): GraphicsOverlayCollection = remember(key) {
163+
GraphicsOverlayCollection().apply(init)
164+
}

toolkit/geo-compose/src/main/java/com/arcgismaps/toolkit/geocompose/MapView.kt

-17
Original file line numberDiff line numberDiff line change
@@ -400,20 +400,3 @@ public inline fun rememberLocationDisplay(
400400
LocationDisplay().apply(init)
401401
}
402402
}
403-
404-
/**
405-
* Create and [remember] a [GraphicsOverlayCollection].
406-
* [init] will be called when the [GraphicsOverlayCollection] is first created to configure its
407-
* initial state.
408-
*
409-
* @param key invalidates the remembered GraphicsOverlayCollection if different from the previous composition
410-
* @param init called when the [GraphicsOverlayCollection] is created to configure its initial state
411-
* @since 200.4.0
412-
*/
413-
@Composable
414-
public inline fun rememberGraphicsOverlayCollection(
415-
key: Any? = null,
416-
crossinline init: GraphicsOverlayCollection.() -> Unit = {}
417-
): GraphicsOverlayCollection = remember(key) {
418-
GraphicsOverlayCollection().apply(init)
419-
}

toolkit/geo-compose/src/main/java/com/arcgismaps/toolkit/geocompose/SceneView.kt

+56
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import java.time.Instant
7070
* @param selectionProperties the [SelectionProperties] used by the composable SceneView
7171
* @param attributionState specifies the attribution bar's visibility, text changed and layout changed events
7272
* @param cameraController the [CameraController] to manage the position, orientation, and movement of the camera
73+
* @param analysisOverlays a collection of analysis overlays that render the results of 3D visual analysis on the composable SceneView
7374
* @param atmosphereEffect the effect applied to the scene's atmosphere
7475
* @param timeExtent the [TimeExtent] used by the composable SceneView
7576
* @param onTimeExtentChanged lambda invoked when the composable SceneView's [TimeExtent] is changed
@@ -105,6 +106,7 @@ public fun SceneView(
105106
selectionProperties: SelectionProperties = SelectionProperties(),
106107
attributionState: AttributionState = AttributionState(),
107108
cameraController: CameraController = GlobeCameraController(),
109+
analysisOverlays: AnalysisOverlayCollection = rememberAnalysisOverlayCollection(),
108110
atmosphereEffect: AtmosphereEffect = AtmosphereEffect.HorizonOnly,
109111
timeExtent: TimeExtent? = null,
110112
onTimeExtentChanged: ((TimeExtent?) -> Unit)? = null,
@@ -163,6 +165,7 @@ public fun SceneView(
163165
ViewpointUpdater(sceneView, viewpointOperation)
164166

165167
GraphicsOverlaysUpdater(graphicsOverlays, sceneView)
168+
AnalysisOverlaysUpdater(analysisOverlays, sceneView)
166169

167170
AttributionStateHandler(sceneView, attributionState)
168171
ViewpointChangedStateHandler(sceneView, viewpointChangedState)
@@ -204,6 +207,42 @@ private fun ViewpointUpdater(
204207
}
205208
}
206209

210+
/**
211+
* Update the view-based [SceneView]'s analysisOverlays property to reflect changes made to the
212+
* [analysisOverlayCollection] based on the type of [AnalysisOverlayCollection.ChangedEvent]
213+
*
214+
* @since 200.4.0
215+
*/
216+
@Composable
217+
internal fun AnalysisOverlaysUpdater(
218+
analysisOverlayCollection: AnalysisOverlayCollection,
219+
sceneView: SceneView
220+
) {
221+
LaunchedEffect(analysisOverlayCollection) {
222+
// sync up the SceneView with the new analysis overlays
223+
sceneView.analysisOverlays.clear()
224+
analysisOverlayCollection.forEach {
225+
sceneView.analysisOverlays.add(it)
226+
}
227+
// start observing analysisOverlays for subsequent changes
228+
analysisOverlayCollection.changed.collect { changedEvent ->
229+
when (changedEvent) {
230+
// On AnalysisOverlay added:
231+
is AnalysisOverlayCollection.ChangedEvent.Added ->
232+
sceneView.analysisOverlays.add(changedEvent.element)
233+
234+
// On AnalysisOverlay removed:
235+
is AnalysisOverlayCollection.ChangedEvent.Removed ->
236+
sceneView.analysisOverlays.remove(changedEvent.element)
237+
238+
// On AnalysisOverlays cleared:
239+
is AnalysisOverlayCollection.ChangedEvent.Cleared ->
240+
sceneView.analysisOverlays.clear()
241+
}
242+
}
243+
}
244+
}
245+
207246
/**
208247
* Sets up the callbacks for all the view-based [sceneView] events.
209248
*/
@@ -327,3 +366,20 @@ private fun SceneViewEventHandler(
327366
}
328367
}
329368
}
369+
370+
/**
371+
* Create and [remember] a [AnalysisOverlayCollection].
372+
* [init] will be called when the [AnalysisOverlayCollection] is first created to configure its
373+
* initial state.
374+
*
375+
* @param key invalidates the remembered AnalysisOverlayCollection if different from the previous composition
376+
* @param init called when the [AnalysisOverlayCollection] is created to configure its initial state
377+
* @since 200.4.0
378+
*/
379+
@Composable
380+
public inline fun rememberAnalysisOverlayCollection(
381+
key: Any? = null,
382+
crossinline init: AnalysisOverlayCollection.() -> Unit = {}
383+
): AnalysisOverlayCollection = remember(key) {
384+
AnalysisOverlayCollection().apply(init)
385+
}

0 commit comments

Comments
 (0)