diff --git a/example-app/src/main/java/com/mapconductor/example/DemoAppScreen.kt b/example-app/src/main/java/com/mapconductor/example/DemoAppScreen.kt index 0eb14b27..e1efae26 100644 --- a/example-app/src/main/java/com/mapconductor/example/DemoAppScreen.kt +++ b/example-app/src/main/java/com/mapconductor/example/DemoAppScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Place import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -18,6 +19,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import androidx.lifecycle.viewmodel.compose.viewModel import com.mapconductor.example.navigation.NavigationViewModel +import com.mapconductor.example.pages.animation.AnimationMapPage import com.mapconductor.example.pages.circle.CircleMapPage import com.mapconductor.example.pages.groundimage.GroundImageMapPage import com.mapconductor.example.pages.groundimage.GroundImageResources @@ -87,6 +89,12 @@ fun DemoAppScreen() { icon = Icons.Default.PlayArrow, route = "polyline", ), + SidebarItem( + id = "animation", + title = "Animation ", + icon = Icons.Default.Place, + route = "animation", + ), SidebarItem( id = "mapDesign", title = "MapDesign Demo", @@ -133,6 +141,11 @@ fun DemoAppScreen() { onToggleSidebar = navigationViewModel::toggleSidebar, ) } + "animation" -> { + AnimationMapPage( + onToggleSidebar = navigationViewModel::toggleSidebar, + ) + } "groundImage" -> { GroundImageMapPage( groundImageResources = groundImageResources, diff --git a/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapComponent.kt b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapComponent.kt new file mode 100644 index 00000000..8fccfe6d --- /dev/null +++ b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapComponent.kt @@ -0,0 +1,36 @@ +package com.mapconductor.example.pages.animation + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.mapconductor.core.circle.OnCircleEventHandler +import com.mapconductor.core.map.MapViewState +import com.mapconductor.core.map.OnMapEventHandler +import com.mapconductor.core.marker.Marker +import com.mapconductor.core.marker.OnMarkerEventHandler +import com.mapconductor.example.MapViewContainer + +@Composable +fun AnimationMapComponent( + mapViewState: MapViewState<*>?, + viewModel: AnimationPageViewModel, + modifier: Modifier = Modifier, + onMapClick: OnMapEventHandler = {}, + onMarkerClick: OnMarkerEventHandler = {}, + onCircleClick: OnCircleEventHandler = {}, + onMarkerDrag: OnMarkerEventHandler = {}, +) { + mapViewState?.let { + MapViewContainer( + modifier = modifier, + state = it, + onMapClick = onMapClick, + onMarkerClick = onMarkerClick, + onCircleClick = onCircleClick, + onMarkerDrag = onMarkerDrag, + ) { + viewModel.allMarkers.forEach { marker -> + Marker(marker) + } + } + } +} diff --git a/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapPage.kt b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapPage.kt new file mode 100644 index 00000000..d404c60e --- /dev/null +++ b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationMapPage.kt @@ -0,0 +1,68 @@ +package com.mapconductor.example.pages.animation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.mapconductor.example.ui.DemoMapPageScaffold +import com.mapconductor.example.ui.MessageCard + +@Composable +fun AnimationMapPage( + viewModel: AnimationPageViewModel = AnimationPageViewModelImpl(), + onToggleSidebar: () -> Unit = {}, +) { + DemoMapPageScaffold( + initCameraPosition = viewModel.initCameraPosition, + onToggleSidebar = onToggleSidebar, + onMapViewStateChanged = viewModel::onMapViewChanged, + ) { paddingValues -> + val mapViewState = viewModel.mapViewState.collectAsState() + + AnimationMapComponent( + mapViewState = mapViewState.value, + viewModel = viewModel, + onMarkerClick = viewModel::onMarkerClick, + ) + + // Message Card + MessageCard( + modifier = + Modifier + .align(Alignment.BottomStart) + .padding( + bottom = paddingValues.calculateBottomPadding() + 16.dp, + start = paddingValues.calculateStartPadding(LayoutDirection.Ltr) + 16.dp, + end = paddingValues.calculateEndPadding(LayoutDirection.Ltr) + 16.dp, + ), + title = "Animation list", + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + viewModel.allMarkers.forEach { marker -> + Row { + Button( + modifier = Modifier.weight(1f), + onClick = { viewModel.onMarkerClick(marker) }, + ) { + Text(viewModel.getSpotName(marker.id)) + } + } + } + } + } + } +} diff --git a/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationPageViewModel.kt b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationPageViewModel.kt new file mode 100644 index 00000000..33dd422b --- /dev/null +++ b/example-app/src/main/java/com/mapconductor/example/pages/animation/AnimationPageViewModel.kt @@ -0,0 +1,107 @@ +package com.mapconductor.example.pages.animation + +import androidx.compose.runtime.getValue +import androidx.lifecycle.ViewModel +import com.mapconductor.core.features.GeoPoint +import com.mapconductor.core.map.MapCameraPosition +import com.mapconductor.core.map.MapViewState +import com.mapconductor.core.marker.DefaultIcon +import com.mapconductor.core.marker.MarkerAnimation +import com.mapconductor.core.marker.MarkerState +import com.mapconductor.example.toast.ToastMessage +import java.util.NoSuchElementException +import android.util.Log +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +private data class Spot( + val id: String, + val name: String, + val animation: MarkerAnimation, + val point: GeoPoint, +) + +private val exampleSpots = + listOf( + Spot( + id = "s1", + name = "Bounce", + animation = MarkerAnimation.Bounce, + point = GeoPoint.fromLatLong(21.3069, -157.8583), + ), + Spot( + id = "s2", + name = "Drop", + animation = MarkerAnimation.Drop, + point = GeoPoint.fromLatLong(21.4513, -158.0152), + ), + ) + +interface AnimationPageViewModel { + val initCameraPosition: MapCameraPosition + val mapViewState: StateFlow?> + val messages: StateFlow> + val allMarkers: List + + val circleCenter: GeoPoint + + fun getSpotName(markerId: String): String + + fun onMapViewChanged(state: MapViewState<*>) + + fun onMarkerClick(clicked: MarkerState) +} + +class AnimationPageViewModelImpl : + ViewModel(), + AnimationPageViewModel { + private val _messages: MutableStateFlow> = MutableStateFlow(emptyList()) + override val messages: StateFlow> = _messages.asStateFlow() + + override val initCameraPosition = + MapCameraPosition( + position = + GeoPoint.fromLatLong( + latitude = 21.382314, + longitude = -157.933097, + ), + zoom = 9.0, + bearing = 0.0, + tilt = 0.0, + paddings = null, + ) + + override val circleCenter = GeoPoint.fromLatLong(21.382314, -157.933097) + + private val markers: Map = + exampleSpots.associate { spot -> + spot.id to + MarkerState( + id = spot.id, + position = spot.point, + icon = DefaultIcon(label = spot.name), + animation = null, + ) + } + override val allMarkers: List get() = markers.values.toList() + + private val _mapViewState = MutableStateFlow?>(null) + override val mapViewState: StateFlow?> = _mapViewState.asStateFlow() + + override fun getSpotName(markerId: String): String = + exampleSpots.firstOrNull { it.id == markerId }?.name + ?: throw NoSuchElementException("Spot not found: $markerId") + + override fun onMapViewChanged(state: MapViewState<*>) { + this._mapViewState.value = state + } + + override fun onMarkerClick(clicked: MarkerState) { + // When you want to activate the marker, set the animation for the marker. + Log.i("AnimationPageViewModelImpl", "onMarkerClick: ${clicked.id}") + + val spot = exampleSpots.firstOrNull { it.id == clicked.id } + clicked.animation = spot?.animation + } +} diff --git a/example-app/src/main/java/com/mapconductor/example/pages/mapDesign/MapDesignPageViewModel.kt b/example-app/src/main/java/com/mapconductor/example/pages/mapDesign/MapDesignPageViewModel.kt index a0f5ed07..495a8b02 100644 --- a/example-app/src/main/java/com/mapconductor/example/pages/mapDesign/MapDesignPageViewModel.kt +++ b/example-app/src/main/java/com/mapconductor/example/pages/mapDesign/MapDesignPageViewModel.kt @@ -179,10 +179,16 @@ class MapDesignPageViewModelImpl : MapDesignOptions(label = "HumanGeographyLabels", design = ArcGISDesign.Companion.HumanGeographyLabels), MapDesignOptions(label = "HumanGeographyDark", design = ArcGISDesign.Companion.HumanGeographyDark), MapDesignOptions(label = "HumanGeographyDarkBase", design = ArcGISDesign.Companion.HumanGeographyDarkBase), - MapDesignOptions(label = "HumanGeographyDarkDetail", design = - ArcGISDesign.Companion.HumanGeographyDarkDetail), - MapDesignOptions(label = "HumanGeographyDarkLabels", design = - ArcGISDesign.Companion.HumanGeographyDarkLabels), + MapDesignOptions( + label = "HumanGeographyDarkDetail", + design = + ArcGISDesign.Companion.HumanGeographyDarkDetail, + ), + MapDesignOptions( + label = "HumanGeographyDarkLabels", + design = + ArcGISDesign.Companion.HumanGeographyDarkLabels, + ), MapDesignOptions(label = "Outdoor", design = ArcGISDesign.Companion.Outdoor), MapDesignOptions(label = "OsmStandard", design = ArcGISDesign.Companion.OsmStandard), MapDesignOptions(label = "OsmStandardRelief", design = ArcGISDesign.Companion.OsmStandardRelief), diff --git a/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt b/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt index e22ccd79..109c5438 100644 --- a/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt +++ b/mapconductor-core/src/main/java/com/mapconductor/core/map/MapViewState.kt @@ -32,9 +32,7 @@ interface MapViewState { fun resetInitState() - fun changeMapDesignType( - value: ActualMapDesignType - ) + fun changeMapDesignType(value: ActualMapDesignType) fun moveCameraTo( cameraPosition: MapCameraPosition, diff --git a/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderer.kt b/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderer.kt index 2bc17899..346faa16 100644 --- a/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderer.kt +++ b/mapconductor-core/src/main/java/com/mapconductor/core/marker/MarkerRenderer.kt @@ -226,7 +226,7 @@ abstract class AbstractMarkerRenderer : MarkerRenderer val startLatLng = holder.fromScreenOffset(startPoint) ?: return@onEach - val lng = target.longitude + val lng = t * target.longitude + (1f - t) * startLatLng.longitude val lat = t * target.latitude + (1f - t) * startLatLng.latitude // 現在の座標をマーカーに適用 diff --git a/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt b/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt index 939789fb..558976e8 100644 --- a/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt +++ b/mapconductor-core/src/main/java/com/mapconductor/settings/Settings.kt @@ -13,7 +13,7 @@ sealed class Settings( ) { object Default : Settings( tapTolerance = 14.dp, - markerDropAnimateDuration = 100, + markerDropAnimateDuration = 300, markerBounceAnimateDuration = 2000, iconSize = MarkerIconSize.Regular, iconStroke = 1.dp, diff --git a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt index f982460d..3a672bc7 100644 --- a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt +++ b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGISMapViewController.kt @@ -1,5 +1,6 @@ package com.mapconductor.arcgis +import com.arcgismaps.mapping.Basemap import com.arcgismaps.mapping.view.Graphic import com.arcgismaps.mapping.view.GraphicsOverlay import com.arcgismaps.mapping.view.LongPressEvent @@ -44,7 +45,6 @@ import android.view.MotionEvent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import com.arcgismaps.mapping.Basemap interface IArcGISMapViewController : MapViewController< @@ -53,9 +53,7 @@ interface IArcGISMapViewController : ArcGISActualPolyline, ArcGISActualPolygon, > { - fun changeMapDesign( - value: String - ) + fun changeMapDesign(value: String) fun moveCamera( dstPosition: MapCameraPosition, @@ -344,16 +342,14 @@ class ArcGISMapViewController( override suspend fun updateCircle(state: CircleState) = circleOverlayManager.updateCircle(state) - override fun changeMapDesign( - value: String - ){ + override fun changeMapDesign(value: String) { coroutine.launch { holder.map.scene!!.setBasemap( Basemap( ArcGISDesign.toBasemapStyle( - ArcGISDesign(value) - ) - ) + ArcGISDesign(value), + ), + ), ) } } diff --git a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGisMapViewState.kt b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGisMapViewState.kt index 63e58f6e..01739a96 100644 --- a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGisMapViewState.kt +++ b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/ArcGisMapViewState.kt @@ -9,7 +9,6 @@ import com.mapconductor.core.map.BaseMapViewSaver import com.mapconductor.core.map.IMapCameraPosition import com.mapconductor.core.map.InitState import com.mapconductor.core.map.MapCameraPosition -import com.mapconductor.core.map.MapDesignType import com.mapconductor.core.map.MapPaddings import com.mapconductor.core.map.MapPaddingsImpl import com.mapconductor.core.map.MapViewState @@ -27,8 +26,7 @@ class ArcGISMapViewState( override val id: String, override val initCameraPosition: MapCameraPosition, override var mapDesignType: ArcGISDesign, -) : MapViewStateImpl() - { +) : MapViewStateImpl() { // Map padding private val _padding = MutableStateFlow(MapPaddingsImpl.Zeros) val padding: StateFlow = _padding.asStateFlow() diff --git a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt index 8fb2203c..45759e64 100644 --- a/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt +++ b/mapconductor-for-arcgis/src/main/java/com/mapconductor/arcgis/marker/ArcGISMarkerRenderer.kt @@ -50,7 +50,9 @@ class ArcGISMarkerRenderer( markerEntity: MarkerEntity, position: GeoPoint, ) { - markerEntity.marker.geometry = position.toPoint(holder.map.scene?.spatialReference) + coroutine.launch { + markerEntity.marker.geometry = position.toPoint(holder.map.scene?.spatialReference) + } } override suspend fun addIcons(newMarkers: List>): List { diff --git a/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt b/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt index c103e27c..3151b6fc 100644 --- a/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt +++ b/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewController.kt @@ -56,9 +56,7 @@ import kotlinx.coroutines.launch interface IGoogleMapViewController : MapViewController, GroundImageCapable { - fun changeMapDesign( - value: Int - ) + fun changeMapDesign(value: Int) fun moveCamera( dstPosition: MapCameraPosition, @@ -86,10 +84,10 @@ class GoogleMapViewController( private val circleRendererFactory: CircleRendererFactory = DefaultGoogleMapCircleRenderer(), private val groundImageController: GroundImageController, ) : BaseMapViewController< - ActualGoogleMapMarker, - ActualGoogleMapCircle, - ActualGoogleMapPolyline, - ActualGoogleMapPolygon, + ActualGoogleMapMarker, + ActualGoogleMapCircle, + ActualGoogleMapPolyline, + ActualGoogleMapPolygon, >(), IGoogleMapViewController, OnCameraMoveStartedListener, @@ -180,9 +178,7 @@ class GoogleMapViewController( holder.map.setOnMarkerDragListener(this) } - override fun changeMapDesign( - value: Int - ){ + override fun changeMapDesign(value: Int) { coroutine.launch { holder.map.mapType = value } diff --git a/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewState.kt b/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewState.kt index ebd9fb1b..d1da3939 100644 --- a/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewState.kt +++ b/mapconductor-for-googlemaps/src/main/java/com/mapconductor/googlemaps/GoogleMapViewState.kt @@ -12,7 +12,6 @@ import com.mapconductor.core.map.MapCameraPosition import com.mapconductor.core.map.MapPaddingsImpl import com.mapconductor.core.map.MapViewState import com.mapconductor.core.map.MapViewStateImpl -import com.mapconductor.core.state.StateOrValue import java.util.UUID import android.os.Bundle import kotlinx.coroutines.flow.MutableStateFlow @@ -37,9 +36,7 @@ class GoogleMapViewState( internal var controller: IGoogleMapViewController? = null - override fun changeMapDesignType( - value: GoogleMapDesign, - ){ + override fun changeMapDesignType(value: GoogleMapDesign) { this.mapDesignType = value this.controller?.changeMapDesign(value.getValue()) } diff --git a/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt b/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt index 14fe1520..b8f839a7 100644 --- a/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt +++ b/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewController.kt @@ -56,9 +56,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch interface IHereMapViewController : MapViewController { - fun changeMapDesign( - value: MapScheme - ) + fun changeMapDesign(value: MapScheme) fun moveCamera( dstPosition: MapCameraPosition, @@ -200,11 +198,9 @@ class HereMapViewController( holder.mapView.gestures.longPressListener = this } - override fun changeMapDesign( - value: MapScheme - ){ + override fun changeMapDesign(value: MapScheme) { coroutine.launch { - holder.mapView.mapScene.loadScene(value){} + holder.mapView.mapScene.loadScene(value) {} } } diff --git a/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewState.kt b/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewState.kt index dab6aed3..397257be 100644 --- a/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewState.kt +++ b/mapconductor-for-here/src/main/java/com/mapconductor/here/HereMapViewState.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -import com.here.sdk.mapview.MapScheme import com.mapconductor.core.features.GeoPoint import com.mapconductor.core.map.BaseMapViewSaver import com.mapconductor.core.map.IMapCameraPosition @@ -38,9 +37,7 @@ class HereMapViewState( private val _cameraPosition = MutableStateFlow(initCameraPosition) override val cameraPosition: StateFlow = _cameraPosition.asStateFlow() - override fun changeMapDesignType( - value: HereMapDesignType - ) { + override fun changeMapDesignType(value: HereMapDesignType) { this.mapDesignType = value this.controller?.changeMapDesign(value.getValue()) } diff --git a/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewController.kt b/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewController.kt index 6c61525f..94f9cbd4 100644 --- a/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewController.kt +++ b/mapconductor-for-mapbox/src/main/java/com/mapconductor/mapbox/MapboxMapViewController.kt @@ -66,10 +66,7 @@ interface IMapboxMapViewController : MapboxActualPolyline, MapboxActualPolygon, > { - - fun changeMapDesign( - value: String - ) + fun changeMapDesign(value: String) fun moveCamera( dstPosition: MapCameraPosition, @@ -265,9 +262,7 @@ internal class MapboxMapViewController( } } - override fun changeMapDesign( - value: String - ){ + override fun changeMapDesign(value: String) { coroutine.launch { holder.mapView.mapboxMap.loadStyle(value) {} }