Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ android {
compileSdk 34

defaultConfig {
applicationId "ru.dgis.sdk.demo"
applicationId "am.ggtaxi.main"
minSdkVersion 21
targetSdkVersion 34
versionCode 1
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ModelWithAnimationInMapActivity"
android:screenOrientation="portrait"
android:theme="@style/GenericActivityTheme"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".MainActivity"
android:screenOrientation="portrait"
Expand Down
Binary file added app/src/main/assets/blue_car.glb
Binary file not shown.
4 changes: 4 additions & 0 deletions app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class MainActivity : AppCompatActivity() {
private val RECORD_REQUEST_CODE = 101

private val pages = listOf(
Page("Issue with 3D model animation") {
val intent = Intent(this@MainActivity, ModelWithAnimationInMapActivity::class.java)
startActivity(intent)
},
Page("Compose Examples") {
val intent = Intent(this@MainActivity, ComposeActivity::class.java)
startActivity(intent)
Expand Down
162 changes: 162 additions & 0 deletions app/src/main/java/ru/dgis/sdk/demo/ModelWithAnimationInMapActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package ru.dgis.sdk.demo

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import ru.dgis.sdk.Context
import ru.dgis.sdk.Duration
import ru.dgis.sdk.coordinates.GeoPoint
import ru.dgis.sdk.geometry.GeoPointWithElevation
import ru.dgis.sdk.map.BearingSource
import ru.dgis.sdk.map.CameraPosition
import ru.dgis.sdk.map.Map
import ru.dgis.sdk.map.MapDirection
import ru.dgis.sdk.map.MapObjectManager
import ru.dgis.sdk.map.MapView
import ru.dgis.sdk.map.ModelMapObject
import ru.dgis.sdk.map.ModelMapObjectOptions
import ru.dgis.sdk.map.ModelScale
import ru.dgis.sdk.map.ModelSize
import ru.dgis.sdk.map.MyLocationControllerSettings
import ru.dgis.sdk.map.MyLocationMapObjectSource
import ru.dgis.sdk.map.Padding
import ru.dgis.sdk.map.Zoom
import ru.dgis.sdk.map.modelDataFromAsset

class ModelWithAnimationInMapActivity : AppCompatActivity() {
private val sdkContext: Context by lazy { application.sdkContext }
lateinit var mapSource: MyLocationMapObjectSource
private var mapObjectManager: MapObjectManager? = null

private var map: Map? = null
private var movingJob: Job? = null

private lateinit var mapView: MapView
private lateinit var root: View

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_model_with_animation)
root = findViewById(R.id.content)
mapView = findViewById<MapView>(R.id.mapView).also {
it.getMapAsync(this::onMapReady)
it.showApiVersionInCopyrightView = true
}
}

override fun onDestroy() {
super.onDestroy()
}

private fun onMapReady(map: Map) {
this.map = map
mapSource = MyLocationMapObjectSource(
sdkContext,
MyLocationControllerSettings(BearingSource.MAGNETIC)
)
//moving camera to car location
val screenHeight = root.height
val mapBottomPaddingOffset = (screenHeight * 0.4).roundToTop()
map.camera.padding = Padding(0, 0, 0, bottom = mapBottomPaddingOffset)
map.camera.move(
CameraPosition(
point = GeoPoint(latitude = 40.209339212102556, longitude = 44.51782621674358),
zoom = Zoom(40f),
), time = Duration.ofMilliseconds(300)
)
//loading 3d model
mapObjectManager = MapObjectManager(map)
val fileName = "blue_car.glb"
val location = GeoPointWithElevation(40.209339212102556, 44.51782621674358)
val modelData = modelDataFromAsset(sdkContext, fileName)
val modelObject = ModelMapObject(
ModelMapObjectOptions(
position = location,
data = modelData,
size = ModelSize(ModelScale(0.055f)),
)
)
map.addSource(mapSource)
//adding 3d model to map
mapObjectManager?.addObject(modelObject)
//animating model movements and rotation with animation
movingJob = lifecycleScope.launch(Dispatchers.Main) {
animateCarMovementAndRotation(modelObject, GeoPoint(40.20949240044926, 44.51422545018235))
}
}


fun Double.roundToTop(): Int {
val intPart = this.toInt()
val decimalPart = this - intPart
return if (decimalPart >= 0.1) intPart + 1 else intPart
}

private fun Double.normalizeAngle(): Double = (this % 360 + 360) % 360

suspend fun animateCarMovementAndRotation(
modelObject: ModelMapObject,
newLocation: GeoPoint,
): Unit = suspendCancellableCoroutine { continuation ->

// Target latitude and longitude for the animation.
val toLat: Double = newLocation.latitude.value
val toLng: Double = newLocation.longitude.value
val fromLat = 40.209339212102556
val fromLng = 44.51782621674358
modelObject.mapDirection = MapDirection(70.0)
val currentAngle = modelObject.mapDirection?.value ?: 0.0
val targetAngle = modelObject.mapDirection?.value?.normalizeAngle() ?: return@suspendCancellableCoroutine
val angleDelta = ((targetAngle - currentAngle + 540) % 360) - 180 // Shortest way

val rotationAnimator = ValueAnimator.ofFloat(0f, angleDelta.toFloat()).apply {
duration = 4000L
interpolator = LinearInterpolator()
// Listener to update the model's direction during animation.
addUpdateListener { animator ->
val animatedDelta = animator.animatedValue as Float
val newAngle = (currentAngle + animatedDelta).normalizeAngle()
modelObject.mapDirection = MapDirection(newAngle)
}
}

// Calculate the distance to determine animation duration.
val movementAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 4000L
interpolator = LinearInterpolator()
addUpdateListener { animation ->
// Interpolate the position of the modelObject based on animation progress.
val t = animation.animatedFraction
modelObject.position = GeoPointWithElevation(
latitude = (1 - t) * fromLat + t * toLat,
longitude = (1 - t) * fromLng + t * toLng
)
}
}

AnimatorSet().apply {
playTogether(rotationAnimator, movementAnimator)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
start()
}
})
start()
}.also { animator ->
continuation.invokeOnCancellation {
animator.cancel()
}
}
}
}
38 changes: 38 additions & 0 deletions app/src/main/res/layout/activity_model_with_animation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GenericMapActivity">

<ru.dgis.sdk.map.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ru.dgis.sdk.map.ZoomControl
android:id="@+id/zoomControl"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ru.dgis.sdk.map.MyLocationControl
android:id="@+id/locationControl"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ru.dgis.sdk.map.MapView>
</FrameLayout>