Skip to content

SceneView: AnalysisOverlayCollection #269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright 2023 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.arcgismaps.toolkit.geocompose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the unused import

import com.arcgismaps.mapping.view.AnalysisOverlay
import com.arcgismaps.mapping.view.SceneView
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow

/**
* A collection class to encapsulate the [AnalysisOverlay] list used by the [com.arcgismaps.toolkit.geocompose.SceneView]
*
* @since 200.4.0
*/
@Stable
public class AnalysisOverlayCollection : Iterable<AnalysisOverlay> {

private val analysisOverlays = mutableListOf<AnalysisOverlay>()

private val _changed: MutableSharedFlow<ChangedEvent> = MutableSharedFlow(
extraBufferCapacity = Int.MAX_VALUE,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)

/**
* [SharedFlow] used to emit changes made to the [analysisOverlays] list
*/
internal val changed: SharedFlow<ChangedEvent> = _changed.asSharedFlow()

override fun iterator(): Iterator<AnalysisOverlay> {
return analysisOverlays.iterator()
}

/**
* Add a [analysisOverlay] to this AnalysisOverlayCollection.
*
* @return if the add operation succeeds, return true.
* @since 200.4.0
*/
public fun add(analysisOverlay: AnalysisOverlay): Boolean {
return if (analysisOverlays.add(analysisOverlay)) {
_changed.tryEmit(ChangedEvent.Added(analysisOverlay))
true
} else false
}

/**
* Remove a [analysisOverlay] from this AnalysisOverlayCollection.
*
* @return if the remove operation succeeds, return true.
* @since 200.4.0
*/
public fun remove(analysisOverlay: AnalysisOverlay): Boolean {
return if (analysisOverlays.remove(analysisOverlay)) {
_changed.tryEmit(ChangedEvent.Removed(analysisOverlay))
true
} else false
}

/**
* Returns the number of analysis overlays in this AnalysisOverlayCollection.
*
* @since 200.4.0
*/
public val size: Int
get() = analysisOverlays.size

/**
* Clears all analysis overlays from this AnalysisOverlayCollection.
*
* @since 200.4.0
*/
public fun clear() {
analysisOverlays.clear()
_changed.tryEmit(ChangedEvent.Cleared)
}

/**
* Sealed class used to notify the compose SceneView to update the AnalysisOverlays on the
* type of [ChangedEvent].
*
* @since 200.4.0
*/
internal sealed class ChangedEvent() {
class Added(val element: AnalysisOverlay) : ChangedEvent()
class Removed(val element: AnalysisOverlay) : ChangedEvent()
object Cleared : ChangedEvent()
}
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary newline

/**
* Update the view-based [geoView]'s analysisOverlays property to reflect changes made to the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Update the view-based [geoView]'s analysisOverlays property to reflect changes made to the
* Update the view-based [GeoView]'s analysisOverlays property to reflect changes made to the

* [analysisOverlayCollection] based on the type of [AnalysisOverlayCollection.ChangedEvent]
*
* @since 200.4.0
*/
@Composable
internal fun AnalysisOverlaysUpdater(
analysisOverlayCollection: AnalysisOverlayCollection,
sceneView: SceneView
) {
LaunchedEffect(analysisOverlayCollection) {
// sync up the GeoView with the new graphics overlays
sceneView.analysisOverlays.clear()
analysisOverlayCollection.forEach {
sceneView.analysisOverlays.add(it)
}
// start observing analysisOverlays for subsequent changes
analysisOverlayCollection.changed.collect { changedEvent ->
when (changedEvent) {
// On AnalysisOverlay added:
is AnalysisOverlayCollection.ChangedEvent.Added ->
sceneView.analysisOverlays.add(changedEvent.element)

// On AnalysisOverlay removed:
is AnalysisOverlayCollection.ChangedEvent.Removed ->
sceneView.analysisOverlays.remove(changedEvent.element)

// On AnalysisOverlays cleared:
is AnalysisOverlayCollection.ChangedEvent.Cleared ->
sceneView.analysisOverlays.clear()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.arcgismaps.toolkit.geocompose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import com.arcgismaps.mapping.view.GeoView
import com.arcgismaps.mapping.view.GraphicsOverlay
import kotlinx.coroutines.channels.BufferOverflow
Expand Down Expand Up @@ -144,3 +145,20 @@ internal fun GraphicsOverlaysUpdater(
}
}
}

/**
* Create and [remember] a [GraphicsOverlayCollection].
* [init] will be called when the [GraphicsOverlayCollection] is first created to configure its
* initial state.
*
* @param key invalidates the remembered GraphicsOverlayCollection if different from the previous composition
* @param init called when the [GraphicsOverlayCollection] is created to configure its initial state
* @since 200.4.0
*/
@Composable
public inline fun rememberGraphicsOverlayCollection(
key: Any? = null,
crossinline init: GraphicsOverlayCollection.() -> Unit = {}
): GraphicsOverlayCollection = remember(key) {
GraphicsOverlayCollection().apply(init)
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,20 +400,3 @@ public inline fun rememberLocationDisplay(
LocationDisplay().apply(init)
}
}

/**
* Create and [remember] a [GraphicsOverlayCollection].
* [init] will be called when the [GraphicsOverlayCollection] is first created to configure its
* initial state.
*
* @param key invalidates the remembered GraphicsOverlayCollection if different from the previous composition
* @param init called when the [GraphicsOverlayCollection] is created to configure its initial state
* @since 200.4.0
*/
@Composable
public inline fun rememberGraphicsOverlayCollection(
key: Any? = null,
crossinline init: GraphicsOverlayCollection.() -> Unit = {}
): GraphicsOverlayCollection = remember(key) {
GraphicsOverlayCollection().apply(init)
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import kotlinx.coroutines.launch
* @param selectionProperties the [SelectionProperties] used by the composable SceneView
* @param attributionState specifies the attribution bar's visibility, text changed and layout changed events
* @param cameraController the [CameraController] to manage the position, orientation, and movement of the camera
* @param analysisOverlays a collection of analysis overlays that render the results of 3D visual analysis on the SceneView
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param analysisOverlays a collection of analysis overlays that render the results of 3D visual analysis on the SceneView
* @param analysisOverlays a collection of analysis overlays that render the results of 3D visual analysis on the composable SceneView

* @param timeExtent the [TimeExtent] used by the composable SceneView
* @param onTimeExtentChanged lambda invoked when the composable SceneView's [TimeExtent] is changed
* @param onNavigationChanged lambda invoked when the navigation status of the composable SceneView has changed
Expand Down Expand Up @@ -97,6 +98,7 @@ public fun SceneView(
selectionProperties: SelectionProperties = SelectionProperties(),
attributionState: AttributionState = AttributionState(),
cameraController: CameraController = GlobeCameraController(),
analysisOverlays: AnalysisOverlayCollection = rememberAnalysisOverlayCollection(),
timeExtent: TimeExtent? = null,
onTimeExtentChanged: ((TimeExtent?) -> Unit)? = null,
onNavigationChanged: ((isNavigating: Boolean) -> Unit)? = null,
Expand Down Expand Up @@ -148,6 +150,7 @@ public fun SceneView(
ViewpointUpdater(sceneView, viewpointOperation)

GraphicsOverlaysUpdater(graphicsOverlays, sceneView)
AnalysisOverlaysUpdater(analysisOverlays, sceneView)

AttributionStateHandler(sceneView, attributionState)
ViewpointChangedStateHandler(sceneView, viewpointChangedState)
Expand Down Expand Up @@ -304,3 +307,20 @@ private fun SceneViewEventHandler(
}
}
}

/**
* Create and [remember] a [AnalysisOverlayCollection].
* [init] will be called when the [AnalysisOverlayCollection] is first created to configure its
* initial state.
*
* @param key invalidates the remembered AnalysisOverlayCollection if different from the previous composition
* @param init called when the [AnalysisOverlayCollection] is created to configure its initial state
* @since 200.4.0
*/
@Composable
public inline fun rememberAnalysisOverlayCollection(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent, should the function be moved into the AnalysisOverlayCollection file?

key: Any? = null,
crossinline init: AnalysisOverlayCollection.() -> Unit = {}
): AnalysisOverlayCollection = remember(key) {
AnalysisOverlayCollection().apply(init)
}