From 829a61ef078be323e97830566e1d36136e6a142d Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Thu, 29 May 2025 11:01:04 -0600 Subject: [PATCH 01/21] feat: Adds an AI Navigator demo * Uses Firebase ai to send prompts to GenAI module to let the user navigate using a set of built in commands. --- Maps3DSamples/advanced/app/build.gradle.kts | 5 + .../advanced/app/src/main/AndroidManifest.xml | 6 + .../advancedmaps3dsamples/MainActivity.kt | 2 + .../Maps3DAdvancedApplication.kt | 1 + .../ainavigator/AiNavigatorActivity.kt | 158 ++++++++++++++++++ .../ainavigator/AiNavigatorViewModel.kt | 57 +++++++ .../ainavigator/data/NavigatorService.kt | 48 ++++++ .../ainavigator/data/Prompts.kt | 131 +++++++++++++++ .../scenarios/Animations.kt | 8 +- .../scenarios/ScenariosViewModel.kt | 9 +- .../app/src/main/res/values/strings.xml | 1 + Maps3DSamples/advanced/build.gradle.kts | 1 + .../advanced/gradle/libs.versions.toml | 9 +- 13 files changed, 429 insertions(+), 7 deletions(-) create mode 100644 Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt create mode 100644 Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt create mode 100644 Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt create mode 100644 Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt diff --git a/Maps3DSamples/advanced/app/build.gradle.kts b/Maps3DSamples/advanced/app/build.gradle.kts index 9d72f83..1e12cb8 100644 --- a/Maps3DSamples/advanced/app/build.gradle.kts +++ b/Maps3DSamples/advanced/app/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.ksp) alias(libs.plugins.hilt.android) alias(libs.plugins.secrets.gradle.plugin) + id("com.google.gms.google-services") } android { @@ -78,6 +79,10 @@ dependencies { implementation(libs.maps.utils.ktx) implementation(libs.androidx.material.icons.extended) + + implementation(platform(libs.firebase.bom)) +// implementation(libs.firebase.vertexai) + implementation(libs.firebase.ai) } secrets { diff --git a/Maps3DSamples/advanced/app/src/main/AndroidManifest.xml b/Maps3DSamples/advanced/app/src/main/AndroidManifest.xml index c10de60..07a3c16 100644 --- a/Maps3DSamples/advanced/app/src/main/AndroidManifest.xml +++ b/Maps3DSamples/advanced/app/src/main/AndroidManifest.xml @@ -51,6 +51,12 @@ android:name=".scenarios.ScenariosActivity" android:exported="true" /> + + + \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/MainActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/MainActivity.kt index 29a5ba2..58f8ec0 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/MainActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/MainActivity.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.example.advancedmaps3dsamples.ainavigator.AiNavigatorActivity import com.example.advancedmaps3dsamples.scenarios.ScenariosActivity import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme import dagger.hilt.android.AndroidEntryPoint @@ -48,6 +49,7 @@ data class MapSample(@StringRes val label: Int, val clazz: Class<*>) private val samples = listOf( MapSample(R.string.map_sample_scenarios, ScenariosActivity::class.java), + MapSample(R.string.map_sample_ai_navigator, AiNavigatorActivity::class.java), ) @OptIn(ExperimentalMaterial3Api::class) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/Maps3DAdvancedApplication.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/Maps3DAdvancedApplication.kt index 22bb6c1..8086853 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/Maps3DAdvancedApplication.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/Maps3DAdvancedApplication.kt @@ -18,6 +18,7 @@ import android.app.Application import android.content.pm.PackageManager import android.util.Log import android.widget.Toast +import com.google.firebase.FirebaseApp import dagger.hilt.android.HiltAndroidApp import java.util.Objects diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt new file mode 100644 index 0000000..b976338 --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -0,0 +1,158 @@ +package com.example.advancedmaps3dsamples.ainavigator + +import android.os.Bundle +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Stop +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat +import com.example.advancedmaps3dsamples.scenarios.ThreeDMap +import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme +import com.google.android.gms.maps3d.Map3DOptions +import com.google.android.gms.maps3d.model.Map3DMode +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +@OptIn(ExperimentalMaterial3Api::class) +class AiNavigatorActivity : ComponentActivity() { + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.light(android.graphics.Color.TRANSPARENT, android.graphics.Color.TRANSPARENT), + navigationBarStyle = SystemBarStyle.light(android.graphics.Color.TRANSPARENT, android.graphics.Color.TRANSPARENT) + ) + + // Hide the system bars + hideSystemUI() + + // Prevent screen from dimming + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + + val latitude = 40.02196731315463 + val longitude = -105.25453645683653 + val altitude = 1616.0 + + val heading = 0.0 + val tilt = 0.0 + val range = 10_000_000.0 + val roll = 0.0 + + val options = Map3DOptions( + defaultUiDisabled = true, + centerLat = latitude, + centerLng = longitude, + centerAlt = altitude, + heading = heading, + tilt = tilt, + roll = roll, + range = range, + minHeading = 0.0, + maxHeading = 360.0, + minTilt = 0.0, + maxTilt = 90.0, + bounds = null, + mapMode = Map3DMode.SATELLITE, + mapId = null, + ) + + setContent { + AdvancedMaps3DSamplesTheme { + Column(modifier = Modifier.fillMaxSize()) { + ThreeDMap( + modifier = Modifier.weight(1f), + options = options, + onMap3dViewReady = { viewModel.setGoogleMap3D(it) }, + onReleaseMap = { viewModel.releaseGoogleMap3D() }, + ) + + Column( + modifier = Modifier + .padding(16.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + var userInput by rememberSaveable { mutableStateOf("") } + val requestIsActive by viewModel.isRequestInflight.collectAsState() + + OutlinedTextField( + value = userInput, + onValueChange = { userInput = it }, + label = { Text("Where are you going?") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + trailingIcon = { + IconButton(onClick = { userInput = "" }) { + Icon(Icons.Filled.Clear, contentDescription = "Clear") + } + } + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + if (requestIsActive) { + IconButton( + onClick = { + viewModel.cancelRequest() + }, + modifier = Modifier + .padding(8.dp) // Add some padding around the spinner + ) { + // Replace with your preferred spinner/loading indicator + Icon(Icons.Filled.Stop, contentDescription = "Cancel") + } + } else { + Button( + onClick = { viewModel.processUserRequest(userInput) }, + ) { + Text("Submit") + } + } + } + } + } + } + } + } + + private fun hideSystemUI() { + WindowCompat.setDecorFitsSystemWindows(window, false) + WindowInsetsControllerCompat(window, window.decorView).let { controller -> + controller.hide(WindowInsetsCompat.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + } +} + diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt new file mode 100644 index 0000000..59d2a34 --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -0,0 +1,57 @@ +package com.example.advancedmaps3dsamples.ainavigator + +import android.util.Log +import androidx.lifecycle.viewModelScope +import com.example.advancedmaps3dsamples.ainavigator.data.NavigatorService +import com.example.advancedmaps3dsamples.common.Map3dViewModel +import com.example.advancedmaps3dsamples.scenarios.AnimationStep +import com.example.advancedmaps3dsamples.scenarios.ScenarioBaseViewModel +import com.example.advancedmaps3dsamples.scenarios.toAnimation +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AiNavigatorViewModel @Inject constructor( + private val navigatorService: NavigatorService +) : Map3dViewModel(), ScenarioBaseViewModel { + override val TAG = this::class.java.simpleName + private var animationJob: Job? = null + + private val _isRequestInflight = MutableStateFlow(false) + val isRequestInflight: StateFlow = _isRequestInflight + + fun processUserRequest(userInput: String) { + viewModelScope.launch { + _isRequestInflight.value = true + try { + val animationString = navigatorService.getAnimationString(userInput) + Log.w(TAG, "Got animationString: $animationString") + + val animation = animationString.toAnimation() + + playAnimation(animation) + } catch (e: Exception) { + Log.e(TAG, "Error processing user request", e) + } finally { + _isRequestInflight.value = false + } + } + } + + private fun playAnimation(animation: List) { + animationJob?.cancel() + + animationJob = viewModelScope.launch { + animation.forEach { step -> step(this@AiNavigatorViewModel) } + } + } + + fun cancelRequest() { + animationJob?.cancel() + _isRequestInflight.value = false + } +} diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt new file mode 100644 index 0000000..e258bae --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt @@ -0,0 +1,48 @@ +package com.example.advancedmaps3dsamples.ainavigator.data + +import android.util.Log +import com.google.firebase.Firebase +import com.google.firebase.ai.ai +import com.google.firebase.ai.type.GenerativeBackend +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NavigatorService @Inject constructor( +) { + val TAG = this::class.java.simpleName + + val model by lazy { + Firebase.ai(backend = GenerativeBackend.googleAI()) + .generativeModel("gemini-2.5-flash-preview-05-20") + } + + suspend fun getAnimationString(userInput: String): String { + Log.d(TAG, "Calling Firebase Vertex AI: Fetching animationString for user input: $userInput") + + try { + // --- Use Firebase SDK's generateContent --- + val response = model.generateContent(prompt + userInput) + // ----------------------------------------- + Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") + // Clean potential markdown code blocks from riddle response as well + val cleanedText = response.text?.sanitize()?.removePrefix("animationString=")?.removeSurrounding("\"") + Log.d(TAG, "Firebase Vertex AI cleaned response: $cleanedText") + return cleanedText ?: "" // TODO: default animation? Do a barrel roll...? + } catch (e: Exception) { + // TODO: Handle specific Firebase/Vertex AI exceptions if needed + Log.e(TAG, "Error getting animation from Firebase Vertex AI for $userInput", e) + throw GameRepositoryException("Unable to get animation: ${e.message}", e) + } + } +} + +// Define a custom exception class for clarity (Optional) +class GameRepositoryException(message: String, cause: Throwable? = null) : Exception(message, cause) + +private fun String.sanitize(): String { + return trim() + .removeSurrounding("```json", "```").trim() + .removeSurrounding("```", "```").trim() + .removeSurrounding("`") +} \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt new file mode 100644 index 0000000..e9e6dee --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -0,0 +1,131 @@ +package com.example.advancedmaps3dsamples.ainavigator.data + +val prompt = """ + You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. + + The `animationString` is a sequence of camera manipulation commands separated by semicolons (`;`). + The available commands are: + + 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. + * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` + * `lat`: Latitude of the camera's center of focus (-90 to 90). + * `lng`: Longitude of the camera's center of focus (-180 to 180). + * `alt`: Altitude of the camera's center of focus in meters above sea level (ASL). Consider the ground elevation of the target. + * `hdg`: Heading/bearing in degrees (0-360, where 0 is North, 90 is East). This is the direction the camera points. + * `tilt`: Tilt in degrees (0-90, where 0 is looking straight down, 90 is looking at the horizon). + * `range`: Distance in meters from the camera to its center of focus. **Crucially, this should be appropriate for the scale of the target.** + * `dur`: Duration of the fly-to animation in milliseconds. + + 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. + * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` + * The `lat`, `lng`, `alt`, `hdg`, `tilt`, `range` parameters define the *center point* of the orbit and the camera's *initial* orientation and distance relative to this center *at the start* of the fly-around. The camera will maintain this `range` and `tilt` relative to the center throughout the orbit, while its `heading` changes. + * `dur`: Total duration of the fly-around animation in milliseconds. + * `count`: Number of full 360-degree rounds to complete (can be fractional, e.g., 0.5 for 180 degrees, or negative for opposite direction). + + 3. **`delay`**: Pauses the animation sequence. + * Format: `delay=dur=` + * `dur`: Duration of the delay in milliseconds. + + **Important Constraints & Guidelines:** + * The `roll` parameter for the camera is **always 0**. Do **not** include `roll` in the `animationString`. + * Ensure latitude is between -90 and 90. + * Ensure longitude is between -180 and 180. + * Ensure heading is between 0 and 360 (it will wrap, but try to keep it canonical). + * Ensure tilt is between 0 and 90. + * **Scale-Appropriate Range and Altitude:** + * Adjust the `range` and `alt` parameters intelligently based on the scale and type of the target. + * For **vast natural areas** (e.g., Grand Canyon, a mountain range) or **entire cities**, use a larger `range` (e.g., 5,000m - 50,000m, or even more for very large areas) and a higher `alt` to provide a good overview. + * For **individual buildings, specific landmarks, or smaller areas**, use a smaller `range` (e.g., 100m - 2,000m) and a correspondingly appropriate `alt` to allow clear viewing of details. + * Avoid making the camera too close to the ground or the object if it obscures the view or feels unnatural for the requested perspective (e.g., a "satellite view" should have a very large range). + * **Animation Simplicity:** + * For simple requests like "fly me to [location]", "show me [location]", or "go to [location]", the `animationString` should ideally consist of a **single `flyTo` command** to position the camera appropriately for viewing that location. + * Only generate multi-step animations (using multiple `flyTo`, `flyAround`, `delay`) if the user explicitly asks for a "tour", "sequence", "showcase", "orbit", "helicopter view of...", "fly along...", or implies multiple viewpoints or actions through their phrasing. + * Use realistic and smooth `dur` values for animations (e.g., 2000-10000ms for significant camera moves, shorter for minor adjustments). + * If the user asks for specific locations, try to find reasonable geographic coordinates for them. + * Be creative in interpreting the user's request to generate an engaging camera experience when a tour or sequence is implied. + + **Your output MUST be a single string assigned to the variable `animationString`, like this:** + `animationString="command1_params;command2_params;command3_params"` + + **Examples:** + + User Request: "Show me the Eiffel Tower from above, then slowly zoom out." + Expected Output: + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=350,hdg=0,tilt=20,range=600,dur=3000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=350,hdg=0,tilt=30,range=2000,dur=4000"` + + User Request: "Fly me to the Grand Canyon." + Expected Output: + `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2800,hdg=0,tilt=45,range=25000,dur=6000"` + + User Request: "Give me a quick fly-around of Mount Fuji, Japan." + Expected Output: + `animationString="flyTo=lat=35.3606,lng=138.7274,alt=4000,hdg=0,tilt=45,range=5000,dur=5000;flyAround=lat=35.3606,lng=138.7274,alt=3800,hdg=0,tilt=45,range=5000,dur=10000,count=1;delay=dur=1000;flyTo=lat=35.3606,lng=138.7274,alt=5000,hdg=0,tilt=20,range=20000,dur=3000"` + + User Request: "I want a helicopter tour of the Grand Canyon, starting near the South Rim visitor center, flying towards Mather Point, then doing a slow circle around Yavapai Point." + Expected Output: + `animationString="flyTo=lat=36.0592,lng=-112.1096,alt=2200,hdg=45,tilt=60,range=1500,dur=6000;delay=dur=2000;flyTo=lat=36.0620,lng=-112.1068,alt=2250,hdg=70,tilt=55,range=1200,dur=5000;delay=dur=2000;flyAround=lat=36.0658,lng=-112.1156,alt=2200,hdg=0,tilt=65,range=1000,dur=15000,count=1.2;delay=dur=1000;flyTo=lat=36.0658,lng=-112.1156,alt=2500,hdg=270,tilt=40,range=5000,dur=4000"` + + User Request: "Show me New York City from high above." + Expected Output: + `animationString="flyTo=lat=40.7128,lng=-74.0060,alt=5000,hdg=0,tilt=30,range=40000,dur=5000"` + + Now, process the following user request and generate the `animationString`: + """.trimIndent() + +val prompt1 = """ + You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. + + The `animationString` is a sequence of camera manipulation commands separated by semicolons (`;`). + The available commands are: + + 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. + * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` + * `lat`: Latitude of the camera's center of focus (-90 to 90). + * `lng`: Longitude of the camera's center of focus (-180 to 180). + * `alt`: Altitude of the camera's center of focus in meters above sea level (ASL). + * `hdg`: Heading/bearing in degrees (0-360, where 0 is North, 90 is East). This is the direction the camera points. + * `tilt`: Tilt in degrees (0-90, where 0 is looking straight down, 90 is looking at the horizon). + * `range`: Distance in meters from the camera to its center of focus. + * `dur`: Duration of the fly-to animation in milliseconds. + + 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. + * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` + * The `lat`, `lng`, `alt`, `hdg`, `tilt`, `range` parameters define the *center point* of the orbit and the camera's *initial* orientation and distance relative to this center *at the start* of the fly-around. The camera will maintain this `range` and `tilt` relative to the center throughout the orbit, while its `heading` changes. + * `dur`: Total duration of the fly-around animation in milliseconds. + * `count`: Number of full 360-degree rounds to complete (can be fractional, e.g., 0.5 for 180 degrees, or negative for opposite direction). + + 3. **`delay`**: Pauses the animation sequence. + * Format: `delay=dur=` + * `dur`: Duration of the delay in milliseconds. + + **Important Constraints & Guidelines:** + * The `roll` parameter for the camera is **always 0**. Do **not** include `roll` in the `animationString`. + * Ensure latitude is between -90 and 90. + * Ensure longitude is between -180 and 180. + * Ensure heading is between 0 and 360 (it will wrap, but try to keep it canonical). + * Ensure tilt is between 0 and 90. + * Choose appropriate altitudes (ASL) and ranges based on the user's request (e.g., a "helicopter tour" should have lower altitudes and ranges than a "satellite view"). + * Use realistic and smooth `dur` values for animations (e.g., 2000-10000ms for significant camera moves). + * For tours, use multiple `flyTo`, `flyAround`, and `delay` steps to create a compelling sequence. + * If the user asks for specific locations, try to find reasonable geographic coordinates for them. + * Be creative in interpreting the user's request to generate an engaging camera experience. + + **Your output MUST be a single string assigned to the variable `animationString`, like this:** + `animationString="command1_params;command2_params;command3_params"` + + **Examples:** + + User Request: "Show me the Eiffel Tower from above, then slowly zoom out." + Expected Output: + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=300,hdg=0,tilt=10,range=500,dur=3000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=300,hdg=0,tilt=30,range=2000,dur=4000"` + + User Request: "Give me a quick fly-around of Mount Fuji, Japan." + Expected Output: + `animationString="flyTo=lat=35.3606,lng=138.7274,alt=4000,hdg=0,tilt=45,range=5000,dur=5000;flyAround=lat=35.3606,lng=138.7274,alt=3800,hdg=0,tilt=45,range=5000,dur=10000,count=1;delay=dur=1000;flyTo=lat=35.3606,lng=138.7274,alt=5000,hdg=0,tilt=20,range=20000,dur=3000"` + + User Request: "I want a helicopter tour of the Grand Canyon, starting near the South Rim visitor center, flying towards Mather Point, then doing a slow circle around Yavapai Point." + Expected Output: + `animationString="flyTo=lat=36.0592,lng=-112.1096,alt=2200,hdg=45,tilt=60,range=1500,dur=6000;delay=dur=2000;flyTo=lat=36.0620,lng=-112.1068,alt=2250,hdg=70,tilt=55,range=1200,dur=5000;delay=dur=2000;flyAround=lat=36.0658,lng=-112.1156,alt=2200,hdg=0,tilt=65,range=1000,dur=15000,count=1.2;delay=dur=1000;flyTo=lat=36.0658,lng=-112.1156,alt=2500,hdg=270,tilt=40,range=5000,dur=4000"` + + Now, process the following user request and generate the `animationString`: + """.trimIndent() diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt index 0b20c32..69e6627 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt @@ -19,23 +19,23 @@ import com.google.android.gms.maps3d.model.FlyToOptions import kotlinx.coroutines.delay sealed interface AnimationStep { - suspend operator fun invoke(viewModel: ScenariosViewModel) + suspend operator fun invoke(viewModel: ScenarioBaseViewModel) } data class DelayStep(val durationMillis: Long) : AnimationStep { - override suspend operator fun invoke(viewModel: ScenariosViewModel) { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { delay(durationMillis) } } data class FlyToStep(val flyToOptions: FlyToOptions) : AnimationStep { - override suspend operator fun invoke(viewModel: ScenariosViewModel) { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { viewModel.awaitFlyTo(flyToOptions) } } data class FlyAroundStep(val flyAroundOptions: FlyAroundOptions) : AnimationStep { - override suspend operator fun invoke(viewModel: ScenariosViewModel) { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { viewModel.awaitFlyAround(flyAroundOptions) } } diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt index fa8c50d..6e279d9 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt @@ -37,6 +37,8 @@ import com.example.advancedmaps3dsamples.utils.DEFAULT_ROLL import com.example.advancedmaps3dsamples.utils.toCameraString import com.example.advancedmaps3dsamples.R import com.example.advancedmaps3dsamples.utils.copy +import com.google.android.gms.maps3d.model.FlyAroundOptions +import com.google.android.gms.maps3d.model.FlyToOptions import com.google.android.gms.maps3d.model.flyAroundOptions enum class CameraAttribute(val labelId: Int) { @@ -67,8 +69,13 @@ private val NEUSCHWANSTEIN_CAMERA = camera { roll = 0.0 } +interface ScenarioBaseViewModel { + suspend fun awaitFlyTo(flyToOptions: FlyToOptions) + suspend fun awaitFlyAround(flyAroundOptions: FlyAroundOptions) +} + @HiltViewModel -class ScenariosViewModel @Inject constructor() : Map3dViewModel() { +class ScenariosViewModel @Inject constructor() : Map3dViewModel(), ScenarioBaseViewModel { override val TAG = this::class.java.simpleName private val _viewState = MutableStateFlow(ScenarioViewState()) val viewState = _viewState as StateFlow diff --git a/Maps3DSamples/advanced/app/src/main/res/values/strings.xml b/Maps3DSamples/advanced/app/src/main/res/values/strings.xml index 177aa55..fa8b66b 100644 --- a/Maps3DSamples/advanced/app/src/main/res/values/strings.xml +++ b/Maps3DSamples/advanced/app/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ 3D Map Samples Scenarios (video visuals) + AI Navigator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c4a603d --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..c4a603d --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 4293905..0000000 --- a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 4293905..0000000 --- a/Maps3DSamples/advanced/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78ecd372343283f4157dcfd918ec5165bb3..b8b1c030d522d594923cad7248298ad2a5038023 100644 GIT binary patch literal 5210 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 U{1*j2mmf5yQ}Fpk`9Fmc02_+@GXMYp literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..b7a98bf2b625c227cbcb2935c1eabb9c0de1be37 GIT binary patch literal 15128 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 GaRmS?Sq0kw literal 0 HcmV?d00001 diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..eb3b3f4760735647aa6b97a5c816a8906328f0fb 100644 GIT binary patch literal 7704 zcmV+z9_QgwNk&Ex9smGWMM6+kP&iBj9smF@N5ByfO)!WgDGsx<1?2N@n0Scj{{(PU zQDD@VUD_n=oP0?;=j1s7e^1&0pzQ?Ig`&2R9PEBYf+*~d+Dm6Ok9WSkZ^|72du!4QIsOa` z0Dv2XBLM19(6)^f^M}3N10fwk zXB|NhXeojnfO23muVN@ymLhn8l;3WdvdCwTGdV%XlrMM7qzse*LkJ3>O$C6V$YZl3 zXsZJ0Aa)K)00TG?$WkCTmCE2{p)_Q+sQ`|t0A#i)a4Ff=o<@@HFPVy&nYmU#Q46SH zEwVeO4b$8~&zPCHgB+GcxwdW9jx+ap?~97LdR(Re+_sW%&#_299Vbx=!PsJECeeHM z(6w#bv~ATn_rA7m+qP}|!M*X#96Po$+t#yf+vd^Qb54NWY}*#ea;pAfXzuCm=^mal zJahPnnVFfHnI&HVFf&_>%gl6`&rtVtPjh#1)&EzOAc+<$A&y~PF`1jf+CqS$s$WBXiepyAQTKofDtGP z#UNJ^tSeWhOkGMqzH(rf$(CWwwjM>2q)(EON4O_9^Dx5-W=YJPQ;kcTzsw9p$r0i1 zMw0JHwpH7oht{#_u^Ln6fdm3`-4^f&ZA-8rZQxt)=7~Ap&)_1Y{dX z%27sSX5YDbvyE5(Eur;{EGl#?N>~5wFtna|nYAam*}>Yzt1pgY>%Jrsz@49IP+kAq ztYU6tr>c)>yEcxsDmr-WA5jCa&in7gt1nJs>;7A*ryZHn#*3363K;#_<2IFM9j=;j ziXLYA^j6W)PFh%LWI`D$g{U&&FVk#(RGvStZqME}yc$pSw)hRs=%joUN&r7uGMwi(X={SPMlS0256SNRv>-3L|Ww8c#|9u6U_< z>92?D*3BXkeqZ<6mC)R&cxEslHiOGi5Jk0F&Sj92Yt z*txPs%j-l{%#PjZH-YljP&!ciC|^@D7v_vDiI7@wO|-p1p)*V*bTENs1ya$8x19CE zcEp(_BNkCx{6lv-#P`jFt?|uu)2VgR|CYBJl46~0+;a1({oy_sI!lKH2-_-}su3C_Wxgt#K?{M;U5Bl5-;6h__U*2vm$SCC>o!=X4nxMcL-(eg=IcRvgLW{AS*`VR&*?V3vC@7}GeaF^ z#X5?ZXbNR7ShK6qTrDAW+cB}K1}4nJYby6qfF2f2SLQQ?98W8z%HBFjeL$RG0;+du) zg|xCYNBEFcW8Ao|h)*%$0Pj%+&kF*bEJfunVtvLdpZ4_?563G0bIVV6EjaS(877>; zNm1A6YXYFBBMh)Qal9pEnOS_=kh=?&<5U=sItH1b9pkJy_#r_BT!rxUIYwI1tVtMf zBFWm4w3G}*J0!zvlY*LW8}&2OqmB;flRCt^NX@0kJv#$HJF^*egBK= z{%~O1a%MeL53bEqoFBaGWU9W#0Nf-p1eWN~MEm>JSO-Fq4h-l@RD>5h8mhv8-;ta2 z3eQ9s>~NKTp@BWmffa1v+=_5JG)390%QSg$J~WEuA+GrIY?I}mrk?-X_3Jfb=h-(Y z5SxSV;K5#u|0m9B?P+z@|8^}low?R{!&*)q{^XhP!pi<4(XIno?9@KaD12QINdRt_ zPRCyNoaY@5lh`myQjqkL`$m?dCj6fZ_nR27EW80%+_Cjp?CM`zJ7?yk$FBPF+~ck6 z51dgo-LR8@gD5umcCpIef4Jt;|J=8keQrkKi4HMKO!HZ-_aZh@P{q_{IPbUj9Q(qr z?ie?;{cz?m$NttFIy(2#eBRlwgJmG&Vo}l{&8k2qX!cU|7jD>AdUxl8vSd|`g(dfgSp@hL^pn+cSiH|Qg>*({n!3r|Efp5T9|)>ew@+Ab#MQg zm;Le7=&O^PKaZ^re(Bovky|QHr{Ft;8rC+ZqTM41HJr${vU5dnY^+22tso23cZRkJ zfe^ts6TD#!pgRd8pjR1y#w|Z0j5^G9sFl#0&Ee1D z{!oGpJzh!IIGQ`K;OxUZkx2?N7aRR}5T;d2+2rxWlaBf1i|Wtq`awV}7F|;j6Te3+ z6KzDwYlNv~=?BQ=ozJqic4-@g5Sc6{@a08sMdM_zUlv8!frZfxk|76b%C#5h<|2 zXfBv+nVKq{?vA<1n7P9~_oB5}L5)wGVgGUXAtHkl2rH9ImmA#;)=}h(ZoL$HaKTR= z@abbP7PdJi73+xzi$y0Ib=ox-_NXBLy0Hg#!PfrtfAeW&sN0nUH7uBdYHB5GS9B{o zppow;!*>4YBCV_0c{RxW)b5d(bCMSKFy+4zuJ!7*$ zQzpFI)TWX47^CA(*^g{HlZSv9b4Xz&zJPB>VM)fZ#l z^Gsgn+?nh3EK$y?%HVL(@n+qGiVFE~yj#Q~RHeA8X|4uI!`P@Vn(@MtuE;CY6V@Qe zh*)e7(}!-AZetgVWI~S}F9(VPa0N_+zEZ|tSh~egt3E2rL$7}s`iFPt?sZ}`XI#82 z!4qf+V-X2ze1x{mseX30xlUW}+V-tLcBrS?hq82*D`qmv=b7htUa=lWsoFB9$*W#L zAA-;}+60yG{p@3)!o?nFB1$@Hn9mh&CoFpGonrhrP&@6Y3CHo2lfypRo41)8QNxB_7`6jyEX1FLWg96kgD|K)? zza?Mjb}K{;PHU3Uw;s9>Az0W}N4R@`)OmA*7tZuXSZ0iE;C)^E8d5mXwhxN$0#E0N zLB0AvAi9h4k@wzCb8)Tb)ib3#A_6?>_q~Fd*Y5xOHQbbZP>U{1V0f~TIYof+(BNa@ z?h;wbF&w0fJ8>U;Qg?O>hHd_KrE2lW%$Y-#sYKu%X~5fvRc>7jn_N1@9H*|=2e`e3 zIK9^f(>m|W&7=;R0ygxZ&=}u1o-Nvo@|dYu7Tz>F&D;=y*Y!GN8`x*O+FWHc2f&d# ziRcGm#Ps&yLvyA4AUM!PxZ15J70*uLGpqfq2Prvkk^hx}+|Jgo9 zbz?&TnZ2DzT?P%%BoIh%OKufbZ46`ge7v*$2~GFO&4cXjacZEnaP1At?wp7llmg+Z zlnR|*#9xpIzamNn5Y0pj{sqe%+B7ya1WYDzwBPj!B8psSsch)RX%XuYNVwnds>WJ9 zK)L~Qlmk>%yjzS?63TJ@f>8y3_^kkI4Ue&~*dCL;eQDw+{AQzflbA_^#CDzF}RA_6%Wtx_d7 zYnT?X=YdtFBT)7x+6e$r%dubh1D-C^^I--(P4$z)JWd*^h@aWxl(@eH#Ns6b^dvY`;1M9GQc&;O*ET8I%vVpGHAkA zOcp@9qDsVx2I0p=0s(rMz%!f}6!@Liu&>h62O=b9xU3TeBBQw-{QOUDI%V??vz3(3 zDwULiG?ZduT)DY5vcgGAj`~7l+g!`7R622j!(qFskq69LC_#5Pg#Lb-m(VUhlFJXU z9wl&S_1COlWb1P1ILUsAxaFP0s-V$>q=|r#(_FbB>NTV}9?+L>Z&*J^r#jY+RCru>k0HLzE5o3Uj{VoJ4oT*8f z&D7+oTrHz?AxvP0W^mx@;RkQN#m%%Kt-;(uNb7yRS$U(4SKZnBf~ zKdvP2>2;j)i3jh{NE0BlDe9*->D9E3jJ<13J4`*MI@RjFZyTb?H>Fv=IUxPZ3QKv4 zN-U8Fb^K4>)u5;GL88l$e(|#seaY1y$nq0e<+Iy5-o`M~*@3PB%3Vzd2mF~sv>B#g zo=4n(tRvA9=l?e9`g(u=FWyZ+{44$jL>SZjpKR+VM8?hqxN_9tHYJl-d;VO6$jIps zS}67uFwMdAo*y@^Gyl!q{M}N_gaB4cv$UX6HYt!x`W%#|`Bo%jsy7zIvpe0=#|N~H z-G7dRlLH_Htc~$Yz2AjUMQQ>D!U}TbW;P3wLCslduGfX7t)z9~e1$)GdFEz^tvJLl zX~69#Qk?S@@Xh7B+H`$*#_~cpLQ-X4f5kIaDKyeoUWQKSU!u9N1&Ei9sj&Zp{@ zIbax9Q^5N=l%`uG*9s!D^^LP$4_=?KPf(fCN$py^BOeS`0wRGxt^ynYH^6~OxD;1Y zK}kDR$GKPQz)km=eZL9fo5qFSem9ED)z=9{+tvpFI5?H8=cI6`E6dy029@}#egOD} zkPy(Ax}Y$1!>?Y&7Y1NiW(n1l$f6^lURqkL>1tJVw-sCsQmgoR#MgKk_j#T@zS06? z6nwd%j@o};2hxY@t9*{+v zC~qK2fRlDKaj$1%UZ7Iv*KiDQhn<)7AS`d12pUZUWI!hu#+rHeL5XEqDy2oKz%BEp zP@@J~C7GP=pmmNiaNc(RiNX_w!cc{85eJYH@sqTVMU40*=HW^@Ob8lALp9WtU)6e> zOXe2a;d~!+EHcEK{u(bE9N_P?p5GNe;MrorMSt}Ev|5JNl25KmdGEx9EbJp>O5G7D zAOQ*SOUlZ1|Gx1o*6%CkEbFsuO_dx+XPLz`CWcv5!ERD5^)I(Qew-+U=@%z7N+>e@ z6`q-%6<;QdfP_WZ3D#hi$ttPu0?mNxnde;RoiY7lNOz51@VAs{ZLD;`hF(iCp(>ED z=u$I`kv;%pn_K!2S?}EI0u%8lJf<5P9+HSbc~F!j3jm;#C;~-zF|5exlEL-nVd$JY z{_7!|%1W(QQJZaYH?=W~7662#{bG zcNsuFB7gbu&>dt#)jU$!!Bt?_A|!1Fg(|ppK}#=l`o)3ULUzTNq>sWA$bdHz!k^+q zjDQ&&lMNW4t9Z*Su?<-V{@WpW@RNNtWeA8zqBa1uzH?M*JmmAauWc&;duvaVop23L zg{plS>nZCk>lWLm)L9y0$-5YD|)?;?&72$+pPJytqmy` zzll4IG~tdcVUDJS*!`K8Q);0$rls zg*D;yY2oMF>gBhVQ1T^BgWa;d+%=V@Ar90q3?nsd-q?BIF-NA;++q5dT_rs=DJDjA zSvJ8Y{X8x)Yl_j!q4LYAd!F61_-dGZgg7_HgT&XtC6G5NqFS6K%VChV_33kqHIoPI zCij{Pv4hY?UN0fWn9janM1pRMqXn?Il!8x$qPGv-ihTRH)E1lRIMbYRQWW%oymaU5 z!Za=##=^*o%kiZZmLN!hRHnDSr}k_IQ{SJFfvBei?9(SeVL7H6gscN>ud3FTa21NWmV%iMb zFloa|O`DNg+rLF?9&l(;P10w@v*NiTtwkaJ6Ney&4-K1HZd%P64}_h$!$NY$-xN#d ztZIzc-=H{=tW2WW@RQ+!wF{Pogd7%s%suk(drp!tBqAUIt?sFoDBfP!Cp9sKCaxcK zMx5dLoqazv%#J>NKgTcI!F_8~Ve!3F#qjtZ?P8Wh!!T(P59ppHc{24>F9#5pO zn>&mT2z2pO>?PzwMVer6Yg@EP_<^FjJt&R(Rz96NOMMHcl=I&qQ@aMr5hm{?gb`^#B2y=5}xJMTv zX~e&Kqkgy=7Ht;RBcsRkHla6Coc>Qch+KFwGvjfv##ZzIiosLwQTX6~>X>H1!9^jW z#00ocv$@qQ-*kg$j`qB`ZmjMcLkz1kV!!wx?G()c(O68v0U(Y_n#;q%s;>QiU8F9w zxetx^OFz08d%O*P{aSf%O1}TYvbHLxqh{H5ZEiijW&NN@7l;aZ2q@ny)UY|}O)?2& z08op|u-lK(3xVdScd%3Mk~jyUG)1LEEUq*S7HKISiDLs#rSn<2zvxTNsK#PjcA4Td ze{s$)&RyW&zg+0}d)&?KhV5k0^-1J!5pFbIDND{-*aW-nCSlsi+O%;VL=2fpFnV?G zT0obira`8cS2ZmRl*l#__ru`B7pnQfn9auWd)_Skmyz%A3tu1YuDKd&}RUs~hDf1+FhmUahx zSk$BI<-!Ubo=xq=wC$s9A=X*VIv?l2Y@ye|f3k+RH-&};%R2S{lcvWES2s6EzEZLm zrResKk4~|E|2#Y#0K=-Lv`q+)3=C!NzBDn0XrA3`V6EN%)-8XU(fr{S<)$GAiU|;n zBRYP#4BZg&($WN6f?sZsSTjOvEtpH>PLrRt@R^ez3;)@nsnxtMYwqug&+3d}&CNc( zo3)X}u@#2mhP;(;yWMF#M6l=k&c+fos}TjIm8s=zSMMIWbh_v6|L!s&!~86xTSLeV zRnH-%51@~QcIZyrmbnt)=S#N&Ns|J*x&c;VZzu}(2=Yyfj= z_PL9LOb=Nx8tdOznT8pK3I7c@vvCupZ7$W)Mc;t^wLK8+8d(yRU^}^~Q05xj7?Im!@hL+Qfda+r{_W(ZDeezflzc7bJMXARk&0mvJH=m+x&&qF z)uX9@@%{el=>}hTOL}QlKBYLO$5oNxAesHl>=WCQbpim24dkQ!#Y zW7@h*+%Y=Gy^fc*fjA8|@V*4`T*Q;6Z%}9FM7_L9%^NMPM7Mxx*ZZxz0?Br*LEpm@5`EP>h% zGGLClSm9XAEB(#>f*v_*C57NDzqTapt4#f}7;-RO72k<>Bc6+RHs6^d)$AX5&OkvAc2n1ih4VkW zuyJ?mnvEa)c%Y!i-}D1<;~zgRx1W31pk*`Ho1Ulg zq@m*IOQkw%fPzIdFRqrLz#j~Ms> literal 2898 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 diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..2cef6a93fdb8e42a88ff93aab984334b9dfedea4 100644 GIT binary patch literal 2996 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

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TYrq;1O(vpvgZObc48|Fz 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 literal 1772 zcmVQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a3070fe34c611c42c0d3ad3013a0dce358be0..67961f213681e7f2465f7a2bd2b70150890b7fae 100644 GIT binary patch literal 8648 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 diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..f6ef56134101647860af5cd9ce42bce36f46f5fa GIT binary patch literal 25182 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 literal 0 HcmV?d00001 diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..8485e540c6c378fb50464b34767bc28ac52313e5 100644 GIT binary patch literal 12350 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

=?6AZ9%oPSF9kBHDnUs}mj7XvA1!Zj}kH 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 literal 3918 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 literal 2884 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 z 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 literal 0 HcmV?d00001 diff --git a/Maps3DSamples/advanced/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Maps3DSamples/advanced/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 9287f5083623b375139afb391af71cc533a7dd37..468f03e8944b168ed276d8ef5ae9c87c6745938d 100644 GIT binary patch literal 23820 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNih$)=iUG! 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 literal 7778 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s From 6024611088374bcd63886f7cce18a70544bca3c6 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Thu, 29 May 2025 17:45:00 -0600 Subject: [PATCH 07/21] feat: Implement "What am I looking at?" feature This commit introduces the "What am I looking at?" functionality to the AI Navigator. When the user taps the corresponding button, the current camera parameters (latitude, longitude, altitude, heading, tilt, and range) are sent to a Vertex AI model. The model uses these parameters, along with a detailed prompt, to identify the likely subject of the user's view (e.g., landmark, city, geographical feature) and returns a concise, 1-2 sentence description. This description is then displayed to the user as a snackbar message. Key changes: - Added `whatAmILookingAtPrompt` to `Prompts.kt` to guide the AI in interpreting camera parameters and generating descriptions. - Implemented `whatAmILookingAt()` method in `NavigatorService.kt` to call the Vertex AI model with the camera parameters and prompt. - Added `whatAmILookingAt()` function in `AiNavigatorViewModel.kt` to handle the request, manage loading state, and send the AI's response to the UI. - Updated `AiNavigatorActivity.kt` to trigger the `whatAmILookingAt()` ViewModel function when the "What am I looking at?" button is pressed. - Added a utility function `toCameraString()` to format `CameraPosition` into the string expected by the AI prompt. --- .../ainavigator/AiNavigatorActivity.kt | 11 +- .../ainavigator/AiNavigatorViewModel.kt | 21 ++++ .../ainavigator/data/NavigatorService.kt | 14 +++ .../ainavigator/data/Prompts.kt | 103 ++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 8fdad33..24f3122 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -38,6 +38,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect @@ -118,7 +119,11 @@ class AiNavigatorActivity : ComponentActivity() { LaunchedEffect(viewModel.userMessage) { scope.launch { viewModel.userMessage.collect { message -> - snackbarHostState.showSnackbar(message) + if (message.length > 50) { + snackbarHostState.showSnackbar(message, duration = SnackbarDuration.Long) + } else { + snackbarHostState.showSnackbar(message) + } } } } @@ -182,9 +187,7 @@ class AiNavigatorActivity : ComponentActivity() { } IconButton( onClick = { - scope.launch { - snackbarHostState.showSnackbar("\"What am I looking at?\" functionality coming soon!") - } + viewModel.whatAmILookingAt() }, colors = iconButtonColors( containerColor = MaterialTheme.colorScheme.primary, diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt index af5d938..ac7b396 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -8,6 +8,7 @@ import com.example.advancedmaps3dsamples.common.Map3dViewModel import com.example.advancedmaps3dsamples.scenarios.AnimationStep import com.example.advancedmaps3dsamples.scenarios.ScenarioBaseViewModel import com.example.advancedmaps3dsamples.scenarios.toAnimation +import com.example.advancedmaps3dsamples.utils.toCameraString import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -108,4 +109,24 @@ class AiNavigatorViewModel @Inject constructor( } fun getRandomPrompt(): String = allPrompts.random() + + fun whatAmILookingAt() { + val cameraString = currentCamera.value.toCameraString() + Log.w(TAG, "What am I looking at? cameraString: $cameraString") + + viewModelScope.launch { + _isRequestInflight.value = true + try { + val whatAmILookingAt = navigatorService.whatAmILookingAt(cameraString) + Log.w(TAG, "Got whatAmILookingAt: $whatAmILookingAt") + if (whatAmILookingAt.isNotEmpty()) { + _userMessage.send(whatAmILookingAt) + } + } catch (e: Exception) { + Log.e(TAG, "Error getting whatAmILookingAt", e) + _userMessage.send("Error getting whatAmILookingAt: ${e.localizedMessage}") + } + _isRequestInflight.value = false + } + } } diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt index 18e1d8e..bdb9b9e 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt @@ -53,6 +53,20 @@ class NavigatorService @Inject constructor( throw GameRepositoryException("Unable to get prompts: ${e.message}", e) } } + + suspend fun whatAmILookingAt(cameraParams: String): String { + Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") + try { + val response = model.generateContent(whatAmILookingAtPrompt.replace("", cameraParams)) + Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") + val cleanedText = response.text?.sanitize() + Log.d(TAG, "Firebase Vertex AI cleaned response: $cleanedText") + return cleanedText ?: "" + } catch (e: Exception) { + Log.e(TAG, "Error getting whatAmILookingAt from Firebase Vertex AI for $cameraParams", e) + throw GameRepositoryException("Unable to get whatAmILookingAt: ${e.message}", e) + } + } } // Define a custom exception class for clarity (Optional) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index 18f590c..decffa1 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -78,6 +78,109 @@ val prompt = """ Now, process the following user request and generate the `animationString`: """.trimIndent() +val whatAmILookingAtPrompt = """ + You are an AI assistant with expertise in geography and interpreting 3D map views. Your task is to provide a concise and informative blurb (1-2 sentences) describing what the user is likely looking at, based on the provided camera parameters for a 3D map. + + **Input You Will Receive (Appended to this prompt):** + The current camera parameters will be provided in the following structured format: + + camera { + center = latLngAltitude { + latitude = + longitude = + altitude = // This is the altitude of the camera's focal point in meters ASL + } + heading = // Degrees, 0 is North + tilt = // Degrees, 0 is straight down, 90 is horizon + range = // Meters from camera to focal point + // Note: Roll is always 0 and may not be present. + } + + **Your Task:** + Based on these camera parameters, determine the most prominent or interesting landmark, geographical feature, city, or area that would be the focus of the user's view. Then, generate a short, engaging blurb about it. + + **Guidelines for Your Blurb:** + + 1. **Identify the Subject:** + * Use the `latitude` and `longitude` from the `center` object to identify the general location. + * Consider the `altitude` of the `center` (focal point altitude), `range`, and `tilt` to understand the scale and perspective. + * A low `range` (e.g., < 2000m) and `tilt` > 45 degrees often means focusing on a specific building or street-level feature. + * A high `range` (e.g., > 10000m) and low `tilt` might indicate an overview of a city, region, or large natural feature. + * The focal point `altitude` is crucial: if it's high, the focus is likely on something tall or an elevated view. + * The `heading` indicates the direction the camera is pointing from its focal point, which can help refine what's in the center of the view. + + 2. **Conciseness:** The blurb should be 1-2 sentences maximum. Aim for informative but brief. + + 3. **Engaging Tone:** Make it sound interesting, like a mini-fact or a quick observation. + + 4. **Specificity (if possible):** + * If a famous landmark is clearly identifiable (e.g., Eiffel Tower, Mount Everest), name it. + * If it's a general area, describe it (e.g., "the bustling downtown of [City]", "the rugged peaks of the [Mountain Range]", "a coastal view of the [Ocean/Sea]"). + * If the view is very generic (e.g., looking at a random patch of forest from high up with a wide range), it's okay to be more general (e.g., "a forested region from above," "an aerial view of rolling hills"). + + 5. **No Technical Jargon:** Do not mention the camera parameters (`latitude`, `longitude`, `range`, etc.) or their values in your blurb. The user only cares about what they are seeing. + + 6. **Focus on the Visual:** Describe what is *seen*, not the history or abstract facts, unless it's a very brief, well-known tidbit that enhances the visual understanding (e.g., "The Colosseum, ancient Roman amphitheater"). + + **Output Format:** + A single, short blurb as plain text. + + **Example Scenarios:** + + * **If camera parameters are (example):** + ``` + camera { + center = latLngAltitude { + latitude = 48.8584 + longitude = 2.2945 + altitude = 150.0 // Focal point is 150m up the tower + } + heading = 45.0 + tilt = 60.0 + range = 500.0 + } + ``` + `Output: You're looking at the iconic Eiffel Tower in Paris, a marvel of 19th-century engineering.` + + * **If camera parameters are (example):** + ``` + camera { + center = latLngAltitude { + latitude = 36.1069 + longitude = -112.1124 + altitude = 2100.0 // Focal point near the rim of the canyon + } + heading = 0.0 + tilt = 45.0 + range = 25000.0 + } + ``` + `Output: This is an expansive aerial view of the Grand Canyon, showcasing its immense scale and layered rock formations.` + + * **If camera parameters are (example):** + ``` + camera { + center = latLngAltitude { + latitude = 34.0522 + longitude = -118.2437 + altitude = 50.0 // Focal point relatively low for a general residential area + } + heading = 180.0 + tilt = 30.0 + range = 3000.0 + } + ``` + `Output: You're viewing a residential neighborhood from above, with its network of streets and houses.` + + --- + **Current Camera View Parameters:** + + --- + + Based on the camera parameters above, what is the user likely looking at? + +""".trimIndent() + val examplePrompts = listOf( "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", "Start with a wide shot of the Golden Gate Bridge, then fly underneath it from the ocean side towards San Francisco.", From 3da6763c21cc0e24f42237858fea1ab133890e7c Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 11:00:14 -0600 Subject: [PATCH 08/21] feat: update AiNavigator sample to use Gemini API for multimodal prompts This commit updates the AiNavigator sample to: - Pass a screenshot of the 3D map view to the Gemini API. - Update prompts for multimodal input. - Remove the `firebase-vertexai` dependency as it has been replaced with `firebase-ai`. --- Maps3DSamples/advanced/app/build.gradle.kts | 2 - .../ainavigator/AiNavigatorActivity.kt | 36 +++-- .../ainavigator/AiNavigatorViewModel.kt | 57 ++++++- .../ainavigator/data/NavigatorService.kt | 24 ++- .../ainavigator/data/Prompts.kt | 146 +++++++++++------- .../advanced/gradle/libs.versions.toml | 1 - 6 files changed, 190 insertions(+), 76 deletions(-) diff --git a/Maps3DSamples/advanced/app/build.gradle.kts b/Maps3DSamples/advanced/app/build.gradle.kts index 1e12cb8..724ce32 100644 --- a/Maps3DSamples/advanced/app/build.gradle.kts +++ b/Maps3DSamples/advanced/app/build.gradle.kts @@ -49,7 +49,6 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -81,7 +80,6 @@ dependencies { implementation(libs.androidx.material.icons.extended) implementation(platform(libs.firebase.bom)) -// implementation(libs.firebase.vertexai) implementation(libs.firebase.ai) } diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 24f3122..5d2c1d5 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -18,18 +18,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.NavigateBefore -import androidx.compose.material.icons.automirrored.filled.NavigateNext import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Shuffle import androidx.compose.material.icons.filled.Stop import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults.iconButtonColors @@ -37,8 +33,8 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect @@ -51,23 +47,23 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.layer.drawLayer +import androidx.compose.ui.graphics.rememberGraphicsLayer import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.example.advancedmaps3dsamples.ainavigator.data.examplePrompts import com.example.advancedmaps3dsamples.scenarios.ThreeDMap import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme import com.google.android.gms.maps3d.Map3DOptions import com.google.android.gms.maps3d.model.Map3DMode import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.seconds @AndroidEntryPoint -@OptIn(ExperimentalMaterial3Api::class) class AiNavigatorActivity : ComponentActivity() { private val viewModel by viewModels() @@ -113,6 +109,8 @@ class AiNavigatorActivity : ComponentActivity() { setContent { val scope = rememberCoroutineScope() + val coroutineScope = rememberCoroutineScope() + val graphicsLayer = rememberGraphicsLayer() val snackbarHostState = remember { SnackbarHostState() } @@ -139,7 +137,17 @@ class AiNavigatorActivity : ComponentActivity() { ) { Box(modifier = Modifier.weight(1f)) { ThreeDMap( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .drawWithContent { // This bit facilitates taking screenshots + // call record to capture the content in the graphics layer + graphicsLayer.record { + // draw the contents of the composable into the graphics layer + this@drawWithContent.drawContent() + } + // draw the graphics layer on the visible canvas + drawLayer(graphicsLayer) + }, options = options, onMap3dViewReady = { viewModel.setGoogleMap3D(it) }, onReleaseMap = { viewModel.releaseGoogleMap3D() }, @@ -187,7 +195,10 @@ class AiNavigatorActivity : ComponentActivity() { } IconButton( onClick = { - viewModel.whatAmILookingAt() + coroutineScope.launch { + val bitmap = graphicsLayer.toImageBitmap() + viewModel.whatAmILookingAt(bitmap) + } }, colors = iconButtonColors( containerColor = MaterialTheme.colorScheme.primary, @@ -317,4 +328,3 @@ class AiNavigatorActivity : ComponentActivity() { } } } - diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt index ac7b396..b04bdbd 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -1,6 +1,13 @@ package com.example.advancedmaps3dsamples.ainavigator +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Environment import android.util.Log +import androidx.compose.ui.graphics.ImageBitmap import androidx.lifecycle.viewModelScope import com.example.advancedmaps3dsamples.ainavigator.data.NavigatorService import com.example.advancedmaps3dsamples.ainavigator.data.examplePrompts @@ -16,7 +23,10 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.suspendCancellableCoroutine +import java.io.File import javax.inject.Inject +import kotlin.coroutines.resume @HiltViewModel class AiNavigatorViewModel @Inject constructor( @@ -110,14 +120,14 @@ class AiNavigatorViewModel @Inject constructor( fun getRandomPrompt(): String = allPrompts.random() - fun whatAmILookingAt() { + fun whatAmILookingAt(bitmap: ImageBitmap) { val cameraString = currentCamera.value.toCameraString() Log.w(TAG, "What am I looking at? cameraString: $cameraString") viewModelScope.launch { _isRequestInflight.value = true try { - val whatAmILookingAt = navigatorService.whatAmILookingAt(cameraString) + val whatAmILookingAt = navigatorService.whatAmILookingAt(cameraString, bitmap) Log.w(TAG, "Got whatAmILookingAt: $whatAmILookingAt") if (whatAmILookingAt.isNotEmpty()) { _userMessage.send(whatAmILookingAt) @@ -130,3 +140,46 @@ class AiNavigatorViewModel @Inject constructor( } } } + +suspend fun Bitmap.saveToDisk(context: Context): Uri { + val file = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + "screenshot-${System.currentTimeMillis()}.png" + ) + + file.writeBitmap(this, Bitmap.CompressFormat.PNG, 100) + + return scanFilePath(context, file.path) ?: throw Exception("File could not be saved") +} + +private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) { + outputStream().use { out -> + bitmap.compress(format, quality, out) + out.flush() + } +} + +private suspend fun scanFilePath(context: Context, filePath: String): Uri? { + return suspendCancellableCoroutine { continuation -> + MediaScannerConnection.scanFile( + context, + arrayOf(filePath), + arrayOf("image/png") + ) { _, scannedUri -> + if (scannedUri == null) { + continuation.cancel(Exception("File $filePath could not be scanned")) + } else { + continuation.resume(scannedUri) + } + } + } +} + +private fun shareImage(uri: Uri, context: Context) { + val intent = Intent(Intent.ACTION_SEND).apply { + type = "image/jpeg" + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(Intent.createChooser(intent, "Share Image")) +} diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt index bdb9b9e..4544f77 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt @@ -1,9 +1,12 @@ package com.example.advancedmaps3dsamples.ainavigator.data import android.util.Log +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asAndroidBitmap import com.google.firebase.Firebase import com.google.firebase.ai.ai import com.google.firebase.ai.type.GenerativeBackend +import com.google.firebase.ai.type.content import javax.inject.Inject import javax.inject.Singleton @@ -57,7 +60,26 @@ class NavigatorService @Inject constructor( suspend fun whatAmILookingAt(cameraParams: String): String { Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") try { - val response = model.generateContent(whatAmILookingAtPrompt.replace("", cameraParams)) + val response = model.generateContent(whatAmILookingAtPromptOld.replace("", cameraParams)) + Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") + val cleanedText = response.text?.sanitize() + Log.d(TAG, "Firebase Vertex AI cleaned response: $cleanedText") + return cleanedText ?: "" + } catch (e: Exception) { + Log.e(TAG, "Error getting whatAmILookingAt from Firebase Vertex AI for $cameraParams", e) + throw GameRepositoryException("Unable to get whatAmILookingAt: ${e.message}", e) + } + } + + suspend fun whatAmILookingAt(cameraParams: String, bitmap: ImageBitmap): String { + Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") + try { + val inputContent = content { + image(bitmap.asAndroidBitmap()) + text(whatAmILookingAtPrompt.replace("", cameraParams) + "") + } + + val response = model.generateContent(inputContent) Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") val cleanedText = response.text?.sanitize() Log.d(TAG, "Firebase Vertex AI cleaned response: $cleanedText") diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index decffa1..8278a82 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -78,7 +78,7 @@ val prompt = """ Now, process the following user request and generate the `animationString`: """.trimIndent() -val whatAmILookingAtPrompt = """ +val whatAmILookingAtPromptOld = """ You are an AI assistant with expertise in geography and interpreting 3D map views. Your task is to provide a concise and informative blurb (1-2 sentences) describing what the user is likely looking at, based on the provided camera parameters for a 3D map. **Input You Will Receive (Appended to this prompt):** @@ -181,6 +181,94 @@ val whatAmILookingAtPrompt = """ """.trimIndent() +val whatAmILookingAtPrompt = """ + You are an AI assistant with expertise in geography and interpreting 3D map views. Your task is to provide a concise and informative blurb (1-2 sentences) describing what the user is likely looking at. You will be given a **screenshot** of a photorealistic 3D map view and the **camera parameters** that correspond to that screenshot. + + **Inputs You Will Receive (Appended to this prompt):** + + 1. **Screenshot:** An image depicting the 3D map view from the user's perspective. This is the primary visual evidence. + 2. **Camera Parameters:** The camera's geospatial position, orientation, and zoom level that *produced the provided screenshot*. This data will be in the following structured format: + ``` + camera { + center = latLngAltitude { + latitude = + longitude = + altitude = // This is the altitude of the camera's focal point in meters ASL + } + heading = // Degrees, 0 is North + tilt = // Degrees, 0 is straight down, 90 is horizon + range = // Meters from camera to focal point + // Note: Roll is always 0 and may not be present. + } + ``` + + **Your Task:** + Based on **both the visual information in the screenshot and the provided camera parameters**, determine the most prominent or interesting landmark, geographical feature, city, or area. Then, generate a short, engaging blurb about it. + + **Guidelines for Your Blurb:** + + 1. **Prioritize the Screenshot:** The image is what the user is directly seeing. Analyze its visual content: + * Look for recognizable buildings, structures, or monuments. + * Identify natural features like mountains, rivers, coastlines, forests, deserts, etc. + * Observe urban patterns (street grids, density) or rural landscapes. + * Note any specific types of terrain or land use. + + 2. **Use Camera Parameters for Context and Confirmation:** + * The `latitude` and `longitude` from the `center` object confirm the geographic location shown in the screenshot. + * The `altitude` of the `center` (focal point altitude), `range`, and `tilt` help interpret the scale, perspective, and elevation of the view in the screenshot. + * A low `range` (e.g., < 2000m) and `tilt` > 45 degrees in the screenshot often means focusing on a specific building or street-level feature. + * A high `range` (e.g., > 10000m) and low `tilt` might indicate an overview of a city, region, or large natural feature as seen in the image. + * The `heading` indicates the direction the camera is pointing, which can help refine what's in the center of the screenshot. + + 3. **Correlate Image and Parameters:** The visual elements in the screenshot should be consistent with the location and perspective defined by the camera parameters. Use both to build a confident description. + + 4. **Conciseness:** The blurb should be 1-2 sentences maximum. Aim for informative but brief. + + 5. **Engaging Tone:** Make it sound interesting, like a mini-fact or a quick observation about what's visible. + + 6. **Specificity (if possible):** + * If a famous landmark is clearly identifiable in the screenshot (e.g., Eiffel Tower, Mount Everest), name it. + * If it's a general area, describe what's visually apparent (e.g., "the bustling downtown skyscrapers of [City] visible in the image," "the rugged, snow-capped peaks of the [Mountain Range] dominating the view," "a coastal scene showing the [Ocean/Sea] meeting the land"). + * If the view in the screenshot is very generic (e.g., a random patch of forest from high up), it's okay to be more general (e.g., "a forested region seen from above," "an aerial perspective of rolling hills"). + + 7. **No Technical Jargon:** Do not mention the camera parameters (`latitude`, `longitude`, `range`, etc.) or their values in your blurb. The user only cares about what they are seeing in the image. + + 8. **Focus on the Visual:** Describe what is *seen in the screenshot*. Brief, well-known tidbits that enhance visual understanding are okay (e.g., "The Colosseum, an ancient Roman amphitheater, clearly visible here."). + + **Output Format:** + A single, short blurb as plain text. + + **Example Scenarios:** + (Imagine each of these also has a clear screenshot corresponding to the parameters) + + * **If screenshot shows the Eiffel Tower and camera parameters are (example):** + ``` + camera { + center = latLngAltitude { latitude = 48.8584, longitude = 2.2945, altitude = 150.0 } + heading = 45.0, tilt = 60.0, range = 500.0 + } + ``` + `Output: You're looking at the iconic Eiffel Tower in Paris, a marvel of 19th-century engineering.` + + * **If screenshot shows a vast canyon and camera parameters are (example):** + ``` + camera { + center = latLngAltitude { latitude = 36.1069, longitude = -112.1124, altitude = 2100.0 } + heading = 0.0, tilt = 45.0, range = 25000.0 + } + ``` + `Output: This is an expansive aerial view of the Grand Canyon, showcasing its immense scale and layered rock formations as seen in the image.` + + --- + **See the accompanying Screenshot of the 3D Map View:** + + **Current Camera View Parameters (corresponding to the screenshot above):** + + --- + + Based on the screenshot and camera parameters above, what is the user likely looking at? +""".trimIndent() + val examplePrompts = listOf( "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", "Start with a wide shot of the Golden Gate Bridge, then fly underneath it from the ocean side towards San Francisco.", @@ -216,62 +304,6 @@ val examplePrompts = listOf( "Show me an isolated lighthouse on a rocky coast during a storm." ) -//val promptGeneratorPrompt = """ -// You are an AI assistant specialized in crafting diverse and effective user prompts for a sophisticated 3D map camera animation system. Your task is to generate a list of example user prompts. These prompts will be shown to users of an application that takes their natural language input and, using another AI, converts it into a precise `animationString` for camera movements. -// -// The example prompts you generate MUST adhere to the following rules, which are the same rules the animation-generating AI follows: -// -// **Rules for Example User Prompts You Generate:** -// -// 1. **Earth-Focused Content:** -// * Prompts must request views of Earth's surface, specific landmarks, geographical features, or cities. -// * Prompts **MUST NOT** request views primarily of the sky, celestial events (like auroras), specific weather phenomena (like storms), or imply specific times of day that would require different environmental lighting (e.g., "sunset," "city at night with bright lights"). The system cannot render these. -// -// 2. **Variety in Request Type:** Generate prompts that cover: -// * Specific, well-known landmarks (e.g., "Eiffel Tower," "Pyramids of Giza"). -// * Requests for "tours," "exploration," or "scenic views" of areas (e.g., "helicopter tour of the Grand Canyon," "explore the Amazon rainforest"). -// * More abstract or thematic requests that are still grounded in terrestrial views (e.g., "dramatic reveal of a mountain," "show me ancient ruins"). -// * Simple, direct requests for a view of a single location (e.g., "fly me to Tokyo," "show me Mount Fuji"). -// -// 3. **Variety in Implied Animation Complexity:** -// * Some prompts should be simple and likely result in a single camera movement (a single `flyTo`). -// * Others should naturally imply a multi-step sequence or tour, involving several camera movements, orbits (`flyAround`), or pauses. -// -// 4. **Geographic Diversity:** Aim for prompts that cover different continents, countries, and types of geographical features (mountains, cities, coasts, historical sites, deserts, jungles, etc.). -// -// 5. **Natural Language:** Prompts should sound like something a real user would type or say – they can be conversational, direct, or a bit creative. -// -// 6. **Implicit Scale Awareness:** Craft prompts that naturally suggest different scales of view. For example: -// * A request for "the Amazon rainforest" implies a wider, higher view than "the top of the Eiffel Tower." -// * "A street-level view of Times Square" implies a close-up, low-altitude perspective. -// -// 7. **Implied Camera Actions (Optional but good for variety):** -// * Some prompts can suggest actions like "fly along," "circle around," "zoom in/out," "look up at," "start wide and then focus on," etc. -// -// 8. **No Technical Camera Jargon:** Users will not specify `lat`, `lng`, `alt`, `hdg`, `tilt`, `range`, or `dur`. Your generated prompts should be purely natural language. The other AI handles the technical conversion. -// -// **Examples of the *kind of user prompts* you should generate:** -// -// * "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above." -// * "Take me on a scenic helicopter tour over the Na Pali Coast in Kauai, highlighting the cliffs and valleys." -// * "Give me a dramatic reveal of Mount Everest, starting from a low angle looking up." -// * "Position the camera for a nice view of Niagara Falls." -// * "Show me the world's great deserts. Start with the Sahara, then give me a glimpse of the Gobi." -// * "Start with a very high altitude view of North America, then rapidly zoom into Central Park in New York City." -// * "I want to see the Christ the Redeemer statue in Rio de Janeiro, with the city and Sugarloaf Mountain in the background." -// * "Let's explore the ancient city of Petra in Jordan, focusing on the Treasury." -// -// **Output Format (Strict):** -// **Your output MUST be plain text. Each example user prompt MUST be on its own separate line. There should be NO additional formatting, NO numbering, NO bullet points, NO introductory or concluding text, and NO code block markers (like ```). Just the prompts, one per line.** -// -// **Example of *Correct Output Format* if asked for 3 prompts:** -// Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above. -// Take me on a scenic helicopter tour over the Na Pali Coast in Kauai, highlighting the cliffs and valleys. -// Give me a dramatic reveal of Mount Everest, starting from a low angle looking up. -// -// Now, please generate 10 example user prompts based on these guidelines. -//""".trimIndent() - val promptGeneratorPrompt = """ You are an AI assistant specialized in crafting diverse and effective user prompts for a sophisticated 3D map camera animation system. Your task is to generate a list of example user prompts. These prompts will be shown to users of an application that takes their natural language input and, using another AI, converts it into a precise `animationString` for camera movements. diff --git a/Maps3DSamples/advanced/gradle/libs.versions.toml b/Maps3DSamples/advanced/gradle/libs.versions.toml index 2a090dc..d51f87c 100644 --- a/Maps3DSamples/advanced/gradle/libs.versions.toml +++ b/Maps3DSamples/advanced/gradle/libs.versions.toml @@ -45,7 +45,6 @@ maps-utils-ktx = { module = "com.google.maps.android:maps-utils-ktx", version.re androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } -firebase-vertexai = { module = "com.google.firebase:firebase-vertexai" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 99f8b7ec5c9a8a53e44a0db8a0f0858981fc46e5 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 11:49:05 -0600 Subject: [PATCH 09/21] feat: add compass to AI Navigator This commit introduces a compass to the AI Navigator UI. The compass displays the current heading of the map and updates as the camera moves. --- .../ainavigator/AiNavigatorActivity.kt | 94 +++++++++++++++---- .../ainavigator/AiNavigatorViewModel.kt | 2 - 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 5d2c1d5..a883775 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.SystemBarStyle +import androidx.compose.foundation.background import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels @@ -16,15 +17,25 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.East import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.North +import androidx.compose.material.icons.filled.Navigation +import androidx.compose.material.icons.filled.NorthEast +import androidx.compose.material.icons.filled.NorthWest import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Shuffle +import androidx.compose.material.icons.filled.South +import androidx.compose.material.icons.filled.SouthEast +import androidx.compose.material.icons.filled.SouthWest import androidx.compose.material.icons.filled.Stop +import androidx.compose.material.icons.filled.West import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -37,6 +48,7 @@ import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -46,14 +58,17 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.rememberGraphicsLayer +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.advancedmaps3dsamples.scenarios.ThreeDMap import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme import com.google.android.gms.maps3d.Map3DOptions @@ -80,24 +95,15 @@ class AiNavigatorActivity : ComponentActivity() { // Prevent screen from dimming window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - val latitude = 40.02196731315463 - val longitude = -105.25453645683653 - val altitude = 1616.0 - - val heading = 0.0 - val tilt = 0.0 - val range = 10_000_000.0 - val roll = 0.0 - val options = Map3DOptions( defaultUiDisabled = true, - centerLat = latitude, - centerLng = longitude, - centerAlt = altitude, - heading = heading, - tilt = tilt, - roll = roll, - range = range, + centerLat = 40.02196731315463, + centerLng = -105.25453645683653, + centerAlt = 1616.0, + heading = 0.0, + tilt = 0.0, + roll = 0.0, + range = 10_000_000.0, minHeading = 0.0, maxHeading = 360.0, minTilt = 0.0, @@ -114,6 +120,8 @@ class AiNavigatorActivity : ComponentActivity() { val snackbarHostState = remember { SnackbarHostState() } + val camera by viewModel.currentCamera.collectAsStateWithLifecycle() + LaunchedEffect(viewModel.userMessage) { scope.launch { viewModel.userMessage.collect { message -> @@ -153,6 +161,13 @@ class AiNavigatorActivity : ComponentActivity() { onReleaseMap = { viewModel.releaseGoogleMap3D() }, ) + Compass( + heading = camera.heading ?: 0.0, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(16.dp) + ) + Row( modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) @@ -246,7 +261,9 @@ class AiNavigatorActivity : ComponentActivity() { val requestIsActive by viewModel.isRequestInflight.collectAsState() // Always reserve space for the progress indicator, but only show it if the requestIsActive - Box(modifier = Modifier.fillMaxWidth().padding(top = 0.dp, bottom = 4.dp)) { + Box(modifier = Modifier + .fillMaxWidth() + .padding(top = 0.dp, bottom = 4.dp)) { if (requestIsActive) { LinearProgressIndicator( modifier = Modifier.fillMaxWidth(), @@ -258,7 +275,9 @@ class AiNavigatorActivity : ComponentActivity() { value = userInput, onValueChange = { userInput = it }, label = { Text("Where would you like to go today?") }, - modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp), singleLine = false, maxLines = 3, trailingIcon = { @@ -328,3 +347,42 @@ class AiNavigatorActivity : ComponentActivity() { } } } + +@Composable +private fun Compass( + heading: Double, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.background(MaterialTheme.colorScheme.primary, CircleShape), + horizontalAlignment = CenterHorizontally + ) { + val (icon, abbreviation) = heading.toCardinalDirection() + + Icon( + imageVector = icon, + contentDescription = "Compass", + tint = MaterialTheme.colorScheme.onPrimary, + modifier = Modifier.padding(top = 8.dp, start = 8.dp, end = 8.dp, bottom = 0.dp) + ) + Text( + text = abbreviation, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onPrimary, + modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) + ) + } +} + +private fun Double.toCardinalDirection(): Pair { + return when (this) { + in 337.5..360.0, in 0.0..22.5 -> Pair(Icons.Filled.North, "N") + in 22.5..67.5 -> Pair(Icons.Filled.NorthEast, "NE") // Placeholder, adjust as needed + in 67.5..112.5 -> Pair(Icons.Filled.East, "E") + in 112.5..157.5 -> Pair(Icons.Filled.SouthEast, "SE") // Placeholder + in 157.5..202.5 -> Pair(Icons.Filled.South, "S") + in 202.5..247.5 -> Pair(Icons.Filled.SouthWest, "SW") // Placeholder + in 247.5..292.5 -> Pair(Icons.Filled.West, "W") + else -> Pair(Icons.Filled.NorthWest, "NW") // Placeholder for 292.5..337.5 + } +} diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt index b04bdbd..81edcf4 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -79,8 +79,6 @@ class AiNavigatorViewModel @Inject constructor( if (isAnimating && animationJob?.isActive == true) { animationJob?.cancel() isAnimating = false - // Call stopAnimations or similar function if needed when animation is stopped by user - // For example: stopAnimations() } } From e6aacd9def0d5bdafeba05bb6540632631b73d60 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 12:45:40 -0600 Subject: [PATCH 10/21] feat: Improve AI Navigator functionality and UI This commit introduces several enhancements to the AI Navigator feature: - **Contextual AI Prompts:** The AI now receives current camera parameters, enabling it to generate animations relative to the user's current view (e.g., "show me nearby castles"). This allows for more intuitive and contextual interactions. - **Image Scaling for AI:** Images captured for the "What am I looking at?" feature are now scaled down by 50% before being sent to the AI. This reduces payload size and processing time without significant loss of information for the AI's understanding. - **Enhanced Compass UI:** Replaced the previous compass with a new "Whiskey Compass" style display. This compass shows the precise numerical heading and cardinal direction fixed in the center, with a rotating dial of tick marks behind it, offering a more professional and informative look. - **Updated Prompts:** The AI prompt has been significantly updated to guide the AI in utilizing the new current camera parameters and to provide more nuanced instructions on altitude selection for camera focus, tile loading delays, and avoiding animations that are not renderable (e.g., sky, specific times of day). - **Unit Tests:** Added unit tests for the new cardinal direction conversion logic used in the Whiskey Compass. Overall, these changes aim to make the AI Navigator more intelligent, responsive, and visually appealing. --- .../ainavigator/AiNavigatorActivity.kt | 12 +- .../ainavigator/AiNavigatorViewModel.kt | 4 +- .../ainavigator/AviationCompass.kt | 188 ++++++++++++++++++ .../ainavigator/data/NavigatorService.kt | 22 +- .../ainavigator/data/Prompts.kt | 100 ++++++++++ .../ainavigator/AviationCompassUnitTest.kt | 26 +++ 6 files changed, 341 insertions(+), 11 deletions(-) create mode 100644 Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt create mode 100644 Maps3DSamples/advanced/app/src/test/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompassUnitTest.kt diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index a883775..ed4d1ff 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -26,7 +26,6 @@ import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Public import androidx.compose.material.icons.filled.North -import androidx.compose.material.icons.filled.Navigation import androidx.compose.material.icons.filled.NorthEast import androidx.compose.material.icons.filled.NorthWest import androidx.compose.material.icons.filled.Refresh @@ -71,6 +70,7 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.advancedmaps3dsamples.scenarios.ThreeDMap import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme +import com.example.advancedmaps3dsamples.utils.toCameraString import com.google.android.gms.maps3d.Map3DOptions import com.google.android.gms.maps3d.model.Map3DMode import dagger.hilt.android.AndroidEntryPoint @@ -161,11 +161,11 @@ class AiNavigatorActivity : ComponentActivity() { onReleaseMap = { viewModel.releaseGoogleMap3D() }, ) - Compass( - heading = camera.heading ?: 0.0, + WhiskeyCompass( modifier = Modifier - .align(Alignment.TopEnd) - .padding(16.dp) + .align(Alignment.TopCenter) + .padding(top = 48.dp), + heading = (camera.heading ?: 0.0).toFloat() ) Row( @@ -325,7 +325,7 @@ class AiNavigatorActivity : ComponentActivity() { Button( onClick = { - viewModel.processUserRequest(userInput) + viewModel.processUserRequest(userInput, camera.toCameraString()) }, enabled = !requestIsActive ) { diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt index 81edcf4..55e25d5 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -45,11 +45,11 @@ class AiNavigatorViewModel @Inject constructor( val allPrompts = examplePrompts.toMutableList() - fun processUserRequest(userInput: String) { + fun processUserRequest(userInput: String, cameraString: String) { viewModelScope.launch { _isRequestInflight.value = true try { - val animationString = navigatorService.getAnimationString(userInput) + val animationString = navigatorService.getAnimationString(userInput, cameraString) Log.w(TAG, "Got animationString: $animationString") currentAnimation = animationString.toAnimation() diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt new file mode 100644 index 0000000..4881b8f --- /dev/null +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt @@ -0,0 +1,188 @@ +package com.example.advancedmaps3dsamples.ainavigator + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.center +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlin.math.roundToInt + +/** + * A composable that displays a refined aviation-style "whiskey" compass. + * + * This version displays the precise heading numerically and as a cardinal direction, + * fixed in the center. Behind this, a dial with tick marks rotates to align with the + * current heading. + * + * @param heading The current heading in degrees (0-360). + * @param modifier The modifier to be applied to the compass. + * @param backgroundColor The background color of the compass strip. + * @param foregroundColor The color of the ticks. + * @param labelColor The color of the static heading and cardinal text. + * @param lubberLineColor The color of the central indicator line. + * @param pixelsPerDegree Controls the visual spacing between degree markers. + */ +@Composable +fun WhiskeyCompass( + heading: Float, + modifier: Modifier = Modifier, + backgroundColor: Color = MaterialTheme.colorScheme.primary, + foregroundColor: Color = MaterialTheme.colorScheme.onPrimary, + labelColor: Color = MaterialTheme.colorScheme.onPrimary, + lubberLineColor: Color = Color.Red, + pixelsPerDegree: Float = 15f +) { + + Box( + modifier = modifier.background(backgroundColor), + contentAlignment = Alignment.Center + ) { + // The rotating compass card with only tick marks + Canvas(modifier = Modifier.fillMaxWidth()) { + val canvasWidth = size.width + val center = size.center + + // The horizontal offset for the entire compass card, + // creating the rotation effect. + val xOffset = center.x - (heading * pixelsPerDegree) + + translate(left = xOffset) { + // Draw the compass ticks twice to handle wrap-around seamlessly + for (repetition in -1..1) { + val repetitionOffset = repetition * 360 * pixelsPerDegree + // Draw from 0 to 360 degrees + for (degree in 0 until 360) { + val xPos = degree * pixelsPerDegree + repetitionOffset + + // Only draw elements that might be visible to save resources + if (xPos > -xOffset - 50 && xPos < -xOffset + canvasWidth + 50) { + // Major ticks every 10 degrees + if (degree % 10 == 0) { + drawLine( + color = foregroundColor, + start = Offset(x = xPos, y = 0f), + end = Offset(x = xPos, y = 25f), + strokeWidth = 4f + ) + } + // Minor ticks every 5 degrees + else if (degree % 5 == 0) { + drawLine( + color = foregroundColor, + start = Offset(x = xPos, y = 0f), + end = Offset(x = xPos, y = 15f), + strokeWidth = 2f + ) + } + } + } + } + } + } + + // Static elements that do not move + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Cardinal direction text (e.g., NE) + Text( + text = heading.toCardinalDirection(), + color = labelColor, + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold + ) + + // The fixed central lubber line + Box( + modifier = Modifier + .fillMaxWidth() + .height(40.dp) // Height of the tick area + ) { + Canvas(modifier = Modifier.fillMaxWidth()) { + drawLine( + color = lubberLineColor, + start = Offset(x = center.x, y = 0f), + end = Offset(x = center.x, y = size.height), + strokeWidth = 4f + ) + } + } + + // Numeric heading text (e.g., 45) + Text( + text = heading.roundToInt().toString(), + color = labelColor, + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + } + } +} + +/** + * Converts a heading in degrees to its corresponding cardinal or intercardinal direction. + */ +fun Float.toCardinalDirection(): String { + val directions = listOf("N", "NE", "E", "SE", "S", "SW", "W", "NW", "N") + return directions[((this % 360) / 45).roundToInt()] +} + + +@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC) +@Composable +private fun UpdatedWhiskeyCompassPreview() { + // A list of example headings to display + val exampleHeadings = listOf( + 0f, // North + 45f, // North-East + 168f, // A non-cardinal direction + 180f, // South + 225f, // South-West + 358f // Near the 360/0 wrap-around + ) + + Column( + modifier = Modifier + .background(Color.DarkGray) + .padding(16.dp) + .verticalScroll(rememberScrollState()), // Make the column scrollable + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + exampleHeadings.forEach { heading -> + // Label for the compass example + Text( + text = "Heading: ${heading.toInt()}° (${heading.toCardinalDirection()})", + color = Color.White, + fontFamily = FontFamily.Monospace + ) + + // The compass composable itself + WhiskeyCompass( + heading = heading, + modifier = Modifier + .fillMaxWidth() + .height(100.dp) + ) + } + } +} \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt index 4544f77..876d367 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt @@ -1,5 +1,6 @@ package com.example.advancedmaps3dsamples.ainavigator.data +import android.graphics.Bitmap import android.util.Log import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asAndroidBitmap @@ -9,6 +10,7 @@ import com.google.firebase.ai.type.GenerativeBackend import com.google.firebase.ai.type.content import javax.inject.Inject import javax.inject.Singleton +import androidx.core.graphics.scale @Singleton class NavigatorService @Inject constructor( @@ -20,12 +22,12 @@ class NavigatorService @Inject constructor( .generativeModel("gemini-2.5-flash-preview-05-20") } - suspend fun getAnimationString(userInput: String): String { + suspend fun getAnimationString(userInput: String, cameraString: String): String { Log.d(TAG, "Calling Firebase Vertex AI: Fetching animationString for user input: $userInput") try { // --- Use Firebase SDK's generateContent --- - val response = model.generateContent(prompt + userInput) + val response = model.generateContent(promptWithCamera + "\n" + userInput + "\n" + cameraString) // ----------------------------------------- Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") // Clean potential markdown code blocks from riddle response as well @@ -74,8 +76,22 @@ class NavigatorService @Inject constructor( suspend fun whatAmILookingAt(cameraParams: String, bitmap: ImageBitmap): String { Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") try { + // Convert the Jetpack Compose ImageBitmap to an Android Bitmap + val originalBitmap = bitmap.asAndroidBitmap() + + // --- Start: Image Scaling Logic --- + // Calculate the new dimensions (50% of the original) + val newWidth = originalBitmap.width / 2 + val newHeight = originalBitmap.height / 2 + + // Create a new bitmap scaled to the new dimensions. + // The 'true' flag enables filtering for better quality. + val scaledBitmap = originalBitmap.scale(newWidth, newHeight) + // --- End: Image Scaling Logic --- + val inputContent = content { - image(bitmap.asAndroidBitmap()) + // Use the newly created scaledBitmap in the request + image(scaledBitmap) text(whatAmILookingAtPrompt.replace("", cameraParams) + "") } diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index 8278a82..1df4f8d 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -1,5 +1,105 @@ package com.example.advancedmaps3dsamples.ainavigator.data +val promptWithCamera = """ + You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** + + **Input You Will Receive:** + 1. **User Request:** The user's natural language query. + 2. **Current Camera Parameters (if provided):** You *may* also receive the camera's current state, which represents what the user is looking at *before* your animation begins. If provided, it will be in this format: + ``` + currentCameraParams = camera { + center = latLngAltitude { + latitude = + longitude = + altitude = // Altitude of the camera's focal point in meters ASL + } + heading = // Degrees, 0 is North + tilt = // Degrees, 0 is straight down, 90 is horizon + range = // Meters from camera to focal point + // Roll is always 0 and may not be present + } + ``` + + The `animationString` is a sequence of camera manipulation commands separated by semicolons (`;`). + The available commands are: + + 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. + * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` + * `lat`: Latitude of the camera's center of focus (-90 to 90). + * `lng`: Longitude of the camera's center of focus (-180 to 180). + * `alt`: Altitude of the camera's **center of focus** in meters above sea level (ASL). **This altitude should be thoughtfully chosen: for buildings, consider mid-height or top; for natural features like canyons or mountains, choose an altitude significantly above the base or near a key viewpoint (e.g., rim, peak). Avoid setting this too low, as it can result in the camera looking at the ground or the base of tall objects.** + * `hdg`: Heading/bearing in degrees (0-360, where 0 is North, 90 is East). This is the direction the camera points. + * `tilt`: Tilt in degrees (0-90, where 0 is looking straight down, 90 is looking at the horizon). + * `range`: Distance in meters from the camera to its center of focus. Must be between 0 and 63,170,000. **Crucially, this should be appropriate for the scale of the target.** + * `dur`: Duration of the fly-to animation in milliseconds. + + 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. + * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` + * The `lat`, `lng`, `alt` parameters define the **center point** of the orbit. The `alt` for this center point should be chosen carefully as per the `flyTo` altitude guidelines. + * `hdg`, `tilt`, `range` define the camera's *initial* orientation and distance relative to this center. The `range` must be between 0 and 63,170,000. + * `dur`: Total duration of the fly-around animation in milliseconds. + * `count`: Number of full 360-degree rounds to complete. + + 3. **`delay`**: Pauses the animation sequence. + * Format: `delay=dur=` + * `dur`: Duration of the delay in milliseconds. + + 4. **`message`**: Displays a short text message to the user. + * Format: `message=""` + * ``: A short, quoted string. + + **How to Use Current Camera Parameters (if provided):** + * **Relative User Requests:** If the user's request seems relative to their current view (e.g., "show me nearby castles," "explore this area more," "what's over that hill from here?"), you **MUST** use the `currentCameraParams` as the starting point or primary reference for your animation. + * The `lat`, `lng`, and `alt` from `currentCameraParams.center` should inform the `lat`, `lng`, `alt` of your first `flyTo` or the `center` of your `flyAround`. + * You might adjust `hdg`, `tilt`, and `range` for the new relative target, but start your calculations or perspective from what `currentCameraParams` describe. + * **Absolute User Requests:** If the user makes an absolute request (e.g., "fly me to Tokyo," "show me the Pyramids of Giza"), the `currentCameraParams` are less critical for the *final destination*. + * You should prioritize reaching the user's specified absolute location. + * However, you *can* use `currentCameraParams` to make the *beginning* of the journey feel like a departure from the current view, making the transition smoother, before flying to the distant absolute target. + * **Do not let `currentCameraParams` override a clear, absolute request to go to a specific, distant location.** + * **If `currentCameraParams` are not provided, or if the user's request is unequivocally absolute and a contextual start offers no benefit, generate the animation string based solely on the user's textual request as before.** + * Your primary goal is to fulfill the user's request. Use `currentCameraParams` to enhance the experience when it makes sense for contextual or relative queries. + + **Important Constraints & Guidelines (Always Apply):** + * **Focus on Earth's Surface:** The generated animations should focus on terrestrial features, landmarks, and geography. **Avoid requests that primarily involve looking at the sky, atmospheric effects (auroras, storms), or specific times of day that imply lighting changes (e.g., "sunset," "city at night") as these cannot be accurately represented.** + * The `roll` parameter for the camera is **always 0**. + * Validate parameter ranges: lat (-90 to 90), lng (-180 to 180), hdg (0-360), tilt (0-90), **range (0 to 63,170,000)**. + * **Scale-Appropriate Range and Altitude of Camera Focus (`alt` parameter):** + * Adjust `range` and `alt` based on the target's scale (vast areas/cities: larger range/alt, e.g., 5km-50km range, focal `alt` well above ground; individual buildings: smaller range/alt, e.g., 100m-2km range, focal `alt` could be mid-height or top of building). + * **Crucially, ensure the `alt` for the camera's focal point is not too low.** + * **Animation Simplicity & Pacing:** + * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. + * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. + * **Tile Loading & Viewing Delay (Crucial):** + * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=4000` command.** + * **Optionally, after this 4000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. + * Do *not* add the 4000ms tile loading delay for minor adjustments at the *same general location* or if the animation starts from `currentCameraParams` and explores a *very nearby* feature without significant travel. + * **Messages:** + * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. + * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). + * If the user asks for specific locations, try to find reasonable geographic coordinates and appropriate viewing altitudes for the focal point. + + **Your output MUST be a single string assigned to the variable `animationString`, like this:** + `animationString="command1_params;command2_params;command3_params"` + + **Examples (Note `alt` adjustments and longer/additional delays):** + + User Request: "Show me the Eiffel Tower from above, then slowly zoom out." + Expected Output (assuming `currentCameraParams` is not relevant or provided): + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=4000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=4000"` + + User Request: "Fly me to the Grand Canyon." + Expected Output (assuming `currentCameraParams` is not relevant or provided): + `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=4000;delay=dur=1500"` + + User Request: "Show me some interesting spots near my current view." + (Assuming `currentCameraParams="camera { center = latLngAltitude { latitude = 40.7128, longitude = -74.0060, altitude = 50.0 }, heading = 180.0, tilt = 45.0, range = 1000.0 }"` i.e., looking south in Lower Manhattan from 1km range) + Expected Output (example, AI might choose different nearby spots): + `animationString="flyTo=lat=40.7061,lng=-74.0088,alt=30,hdg=0,tilt=60,range=500,dur=3000;message=\"Battery Park waterfront\";delay=dur=4000;delay=dur=1000;flyTo=lat=40.7100,lng=-74.0135,alt=150,hdg=270,tilt=50,range=800,dur=3000;message=\"One World Trade Center from nearby\";delay=dur=4000;delay=dur=1000"` + + Now, process the following user request and generate the `animationString`: +""".trimIndent() + + val prompt = """ You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** diff --git a/Maps3DSamples/advanced/app/src/test/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompassUnitTest.kt b/Maps3DSamples/advanced/app/src/test/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompassUnitTest.kt new file mode 100644 index 0000000..c982ba9 --- /dev/null +++ b/Maps3DSamples/advanced/app/src/test/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompassUnitTest.kt @@ -0,0 +1,26 @@ +package com.example.advancedmaps3dsamples.ainavigator + +import org.junit.Test + +import org.junit.Assert.* + +class FloatToCardinalDirectionTest { + @Test + fun toCardinalDirection_convertsCorrectly() { + assertEquals("N", 0f.toCardinalDirection()) + assertEquals("N", 359f.toCardinalDirection()) + assertEquals("N", 22.4f.toCardinalDirection()) // Edge case, still N + assertEquals("NE", 22.5f.toCardinalDirection()) // Edge case, becomes NE + assertEquals("NE", 45f.toCardinalDirection()) + assertEquals("NE", (45f + 22f).toCardinalDirection()) + assertEquals("E", (45f + 23f).toCardinalDirection()) + assertEquals("E", 90f.toCardinalDirection()) + assertEquals("SE", 135f.toCardinalDirection()) + assertEquals("S", 180f.toCardinalDirection()) + assertEquals("SW", 225f.toCardinalDirection()) + assertEquals("W", 270f.toCardinalDirection()) + assertEquals("NW", 315f.toCardinalDirection()) + assertEquals("N", 337.5f.toCardinalDirection()) // Boundary for N from NW + assertEquals("NW", 337.4f.toCardinalDirection()) // Boundary for NW + } +} From b60c4792c9270407be0e2d686987d1592ba15ccc Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 12:50:55 -0600 Subject: [PATCH 11/21] feat: enhance and integrate WhiskeyCompass This commit significantly enhances the `WhiskeyCompass` composable and integrates it into the `AiNavigatorActivity`. Key changes: - **Customization:** `WhiskeyCompass` now offers extensive customization options, including text styles for cardinal and numeric headings, tick sizes (major/minor height), tick area height, and stroke widths for ticks and the lubber line. - **Styling in `AiNavigatorActivity`:** The compass in `AiNavigatorActivity` is now styled for a more compact and integrated look. It's made slightly transparent, positioned considering the status bar, and uses smaller text and denser tick marks. - **Improved Preview:** The `WhiskeyCompassPreview` has been updated to showcase various styling options, including default, custom scaled/styled, and a minimalist small version. - **Cardinal Direction Logic:** Refined the logic in `toCardinalDirection()` for more accurate centering of the 45-degree segments. - **Drawing Optimization:** The compass drawing logic now draws tick marks across a wider range (-360 to 720 degrees internally) to ensure seamless wrapping and visibility. - **Layout Adjustments:** Minor layout adjustments in `AiNavigatorActivity` to accommodate the styled compass. --- .../ainavigator/AiNavigatorActivity.kt | 19 +- .../ainavigator/AviationCompass.kt | 192 ++++++++++++------ 2 files changed, 149 insertions(+), 62 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index ed4d1ff..bbee505 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape @@ -59,6 +60,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.rememberGraphicsLayer @@ -162,10 +164,21 @@ class AiNavigatorActivity : ComponentActivity() { ) WhiskeyCompass( + heading = camera.heading?.toFloat() ?: 0f, modifier = Modifier - .align(Alignment.TopCenter) - .padding(top = 48.dp), - heading = (camera.heading ?: 0.0).toFloat() + .fillMaxWidth() + .padding(top = 36.dp) // Adjusted for status bar + .alpha(0.6f) // Make it somewhat transparent + .height(80.dp), // Compact + pixelsPerDegree = 10f, // Denser + cardinalTextStyle = MaterialTheme.typography.labelSmall, + numericTextStyle = MaterialTheme.typography.titleSmall, + majorTickHeight = 15.dp, + minorTickHeight = 8.dp, + tickAreaHeight = 25.dp, + majorTickStrokeWidth = 1.5.dp, + minorTickStrokeWidth = 0.5.dp, + lubberLineStrokeWidth = 2.dp ) Row( diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt index 4881b8f..24265ec 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt @@ -1,10 +1,10 @@ package com.example.advancedmaps3dsamples.ainavigator - import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -19,37 +19,55 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.center import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlin.math.roundToInt /** - * A composable that displays a refined aviation-style "whiskey" compass. + * A composable that displays a customizable aviation-style "whiskey" compass. * - * This version displays the precise heading numerically and as a cardinal direction, - * fixed in the center. Behind this, a dial with tick marks rotates to align with the - * current heading. + * This version allows detailed control over visual elements including text styles, + * tick sizes, and stroke widths. * * @param heading The current heading in degrees (0-360). * @param modifier The modifier to be applied to the compass. * @param backgroundColor The background color of the compass strip. - * @param foregroundColor The color of the ticks. - * @param labelColor The color of the static heading and cardinal text. + * @param tickColor The color of the tick marks on the rotating dial. + * @param labelColor The default color for the cardinal and numeric heading text, + * used if the provided TextStyles don't specify a color. * @param lubberLineColor The color of the central indicator line. - * @param pixelsPerDegree Controls the visual spacing between degree markers. + * @param pixelsPerDegree Controls the horizontal spacing between degree markers on the dial. + * @param cardinalTextStyle TextStyle for the cardinal direction (e.g., "N", "NE"). + * @param numericTextStyle TextStyle for the numeric heading (e.g., "45"). + * @param majorTickHeight Height of the major tick marks. + * @param minorTickHeight Height of the minor tick marks. + * @param tickAreaHeight The height of the central area where the lubber line and ticks are displayed. + * @param majorTickStrokeWidth Stroke width for major ticks. + * @param minorTickStrokeWidth Stroke width for minor ticks. + * @param lubberLineStrokeWidth Stroke width for the lubber line. */ @Composable fun WhiskeyCompass( heading: Float, modifier: Modifier = Modifier, backgroundColor: Color = MaterialTheme.colorScheme.primary, - foregroundColor: Color = MaterialTheme.colorScheme.onPrimary, + tickColor: Color = MaterialTheme.colorScheme.onPrimary, labelColor: Color = MaterialTheme.colorScheme.onPrimary, lubberLineColor: Color = Color.Red, - pixelsPerDegree: Float = 15f + pixelsPerDegree: Float = 15f, + cardinalTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, + numericTextStyle: TextStyle = MaterialTheme.typography.headlineSmall, + majorTickHeight: Dp = 25.dp, + minorTickHeight: Dp = 15.dp, + tickAreaHeight: Dp = 40.dp, // Defines the central band for ticks and lubber line + majorTickStrokeWidth: Dp = 2.dp, + minorTickStrokeWidth: Dp = 1.dp, + lubberLineStrokeWidth: Dp = 3.dp ) { Box( @@ -59,38 +77,38 @@ fun WhiskeyCompass( // The rotating compass card with only tick marks Canvas(modifier = Modifier.fillMaxWidth()) { val canvasWidth = size.width - val center = size.center + val canvasCenterY = center.y // Center Y of the entire canvas + val xOffset = size.center.x - (heading * pixelsPerDegree) - // The horizontal offset for the entire compass card, - // creating the rotation effect. - val xOffset = center.x - (heading * pixelsPerDegree) + // Calculate Y positions for ticks to be somewhat centered if canvas is taller than tickAreaHeight + // Ticks will be drawn from the top edge of the tick drawing area. + val tickDrawingAreaStartY = canvasCenterY - (tickAreaHeight.toPx() / 2f) translate(left = xOffset) { - // Draw the compass ticks twice to handle wrap-around seamlessly + // Draw from -360 to 0, then 0 to 360, and 360 to 720 for robust wrapping. for (repetition in -1..1) { val repetitionOffset = repetition * 360 * pixelsPerDegree - // Draw from 0 to 360 degrees for (degree in 0 until 360) { val xPos = degree * pixelsPerDegree + repetitionOffset - // Only draw elements that might be visible to save resources - if (xPos > -xOffset - 50 && xPos < -xOffset + canvasWidth + 50) { + // Only draw elements that might be visible + if (xPos > -xOffset - (pixelsPerDegree * 10) && xPos < -xOffset + canvasWidth + (pixelsPerDegree * 10)) { // Major ticks every 10 degrees if (degree % 10 == 0) { drawLine( - color = foregroundColor, - start = Offset(x = xPos, y = 0f), - end = Offset(x = xPos, y = 25f), - strokeWidth = 4f + color = tickColor, + start = Offset(x = xPos, y = tickDrawingAreaStartY), + end = Offset(x = xPos, y = tickDrawingAreaStartY + majorTickHeight.toPx()), + strokeWidth = majorTickStrokeWidth.toPx() ) } // Minor ticks every 5 degrees else if (degree % 5 == 0) { drawLine( - color = foregroundColor, - start = Offset(x = xPos, y = 0f), - end = Offset(x = xPos, y = 15f), - strokeWidth = 2f + color = tickColor, + start = Offset(x = xPos, y = tickDrawingAreaStartY), + end = Offset(x = xPos, y = tickDrawingAreaStartY + minorTickHeight.toPx()), + strokeWidth = minorTickStrokeWidth.toPx() ) } } @@ -106,23 +124,23 @@ fun WhiskeyCompass( // Cardinal direction text (e.g., NE) Text( text = heading.toCardinalDirection(), - color = labelColor, - fontSize = 20.sp, - fontWeight = FontWeight.SemiBold + style = cardinalTextStyle.copy( + color = if (cardinalTextStyle.color == Color.Unspecified) labelColor else cardinalTextStyle.color + ) ) - // The fixed central lubber line + // The fixed central lubber line area Box( modifier = Modifier .fillMaxWidth() - .height(40.dp) // Height of the tick area + .height(tickAreaHeight) ) { - Canvas(modifier = Modifier.fillMaxWidth()) { + Canvas(modifier = Modifier.matchParentSize()) { // Use matchParentSize to fill the Box drawLine( color = lubberLineColor, start = Offset(x = center.x, y = 0f), - end = Offset(x = center.x, y = size.height), - strokeWidth = 4f + end = Offset(x = center.x, y = size.height), // spans full height of this Box + strokeWidth = lubberLineStrokeWidth.toPx() ) } } @@ -130,59 +148,115 @@ fun WhiskeyCompass( // Numeric heading text (e.g., 45) Text( text = heading.roundToInt().toString(), - color = labelColor, - fontSize = 24.sp, - fontWeight = FontWeight.Bold + style = numericTextStyle.copy( + color = if (numericTextStyle.color == Color.Unspecified) labelColor else numericTextStyle.color + ) ) } } } + /** * Converts a heading in degrees to its corresponding cardinal or intercardinal direction. */ -fun Float.toCardinalDirection(): String { +private fun Float.toCardinalDirection(): String { + // Ensure heading is within 0-360 range for calculations + val normalizedHeading = (this % 360 + 360) % 360 val directions = listOf("N", "NE", "E", "SE", "S", "SW", "W", "NW", "N") - return directions[((this % 360) / 45).roundToInt()] + // Add 22.5 for correct centering of 45 degree segments, then divide by 45 + return directions[((normalizedHeading + 22.5f) / 45f).toInt() % 8] } - -@Preview(showBackground = true, backgroundColor = 0xFFCCCCCC) +@Preview(showBackground = true, backgroundColor = 0xFF222222) @Composable -private fun UpdatedWhiskeyCompassPreview() { - // A list of example headings to display +private fun FullyCustomizableWhiskeyCompassPreview() { val exampleHeadings = listOf( - 0f, // North - 45f, // North-East - 168f, // A non-cardinal direction - 180f, // South - 225f, // South-West - 358f // Near the 360/0 wrap-around + 0f, 45f, 168f, 225f, 358f ) Column( modifier = Modifier .background(Color.DarkGray) .padding(16.dp) - .verticalScroll(rememberScrollState()), // Make the column scrollable + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(16.dp) + verticalArrangement = Arrangement.spacedBy(24.dp) ) { + Text("Default Styling", color = Color.White, style = MaterialTheme.typography.titleMedium) + WhiskeyCompass( + heading = 45f, + modifier = Modifier + .fillMaxWidth() + .height(120.dp) // Give it some space + ) + + Spacer(modifier = Modifier.height(16.dp)) + Text("Custom Scaled & Styled", color = Color.White, style = MaterialTheme.typography.titleMedium) + WhiskeyCompass( + heading = 123f, + modifier = Modifier + .fillMaxWidth() + .height(150.dp), // Taller + backgroundColor = Color(0xFF303030), + tickColor = Color.Cyan, + labelColor = Color.Yellow, + lubberLineColor = Color.Magenta, + pixelsPerDegree = 20f, // More spread out + cardinalTextStyle = MaterialTheme.typography.headlineMedium.copy( + fontFamily = FontFamily.Serif, + fontWeight = FontWeight.Bold + ), + numericTextStyle = MaterialTheme.typography.displaySmall.copy( + fontFamily = FontFamily.Monospace, + letterSpacing = 2.sp + ), + majorTickHeight = 35.dp, // Taller ticks + minorTickHeight = 20.dp, + tickAreaHeight = 60.dp, // Larger central band + majorTickStrokeWidth = 3.dp, + minorTickStrokeWidth = 1.5.dp, + lubberLineStrokeWidth = 4.dp + ) + + Spacer(modifier = Modifier.height(16.dp)) + Text("Minimalist Small Version", color = Color.White, style = MaterialTheme.typography.titleMedium) + WhiskeyCompass( + heading = 270f, + modifier = Modifier + .fillMaxWidth() + .height(80.dp), // Compact + pixelsPerDegree = 10f, // Denser + cardinalTextStyle = MaterialTheme.typography.labelSmall, + numericTextStyle = MaterialTheme.typography.titleSmall, + majorTickHeight = 15.dp, + minorTickHeight = 8.dp, + tickAreaHeight = 25.dp, + majorTickStrokeWidth = 1.5.dp, + minorTickStrokeWidth = 0.5.dp, + lubberLineStrokeWidth = 2.dp + ) + + exampleHeadings.forEach { heading -> - // Label for the compass example + Spacer(Modifier.height(8.dp)) Text( - text = "Heading: ${heading.toInt()}° (${heading.toCardinalDirection()})", - color = Color.White, - fontFamily = FontFamily.Monospace + text = "Test Heading: ${heading.toInt()}° (${heading.toCardinalDirection()})", + color = Color.LightGray, + fontFamily = FontFamily.Monospace, + fontSize = 12.sp ) - - // The compass composable itself WhiskeyCompass( heading = heading, modifier = Modifier .fillMaxWidth() - .height(100.dp) + .height(100.dp), + cardinalTextStyle = MaterialTheme.typography.bodySmall, // Smaller for these examples + numericTextStyle = MaterialTheme.typography.bodyLarge, + tickAreaHeight = 30.dp, + majorTickHeight = 20.dp, + minorTickHeight = 10.dp ) } } -} \ No newline at end of file +} From 987f5ded256626c86d7506502e3c0e3d6b7c7e50 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 14:09:03 -0600 Subject: [PATCH 12/21] feat: enhance and integrate WhiskeyCompass This commit significantly enhances the `WhiskeyCompass` composable and integrates it into the `AiNavigatorActivity`. Key changes: - **Customization:** `WhiskeyCompass` now offers extensive customization options, including text styles for cardinal and numeric headings, tick sizes (major/minor height), tick area height, and stroke widths for ticks and the lubber line. - **Styling in `AiNavigatorActivity`:** The compass in `AiNavigatorActivity` is now styled for a more compact and integrated look. It's made slightly transparent, positioned considering the status bar, and uses smaller text and denser tick marks. - **Improved Preview:** The `WhiskeyCompassPreview` has been updated to showcase various styling options, including default, custom scaled/styled, and a minimalist small version. - **Cardinal Direction Logic:** Refined the logic in `toCardinalDirection()` for more accurate centering of the 45-degree segments. - **Drawing Optimization:** The compass drawing logic now draws tick marks across a wider range (-360 to 720 degrees internally) to ensure seamless wrapping and visibility. - **Layout Adjustments:** Minor layout adjustments in `AiNavigatorActivity` to accommodate the styled compass. --- .../ainavigator/AiNavigatorActivity.kt | 18 +- .../ainavigator/AviationCompass.kt | 340 ++++++++++-------- .../ainavigator/data/Prompts.kt | 28 +- 3 files changed, 215 insertions(+), 171 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index bbee505..3a45495 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -165,20 +165,10 @@ class AiNavigatorActivity : ComponentActivity() { WhiskeyCompass( heading = camera.heading?.toFloat() ?: 0f, - modifier = Modifier - .fillMaxWidth() - .padding(top = 36.dp) // Adjusted for status bar - .alpha(0.6f) // Make it somewhat transparent - .height(80.dp), // Compact - pixelsPerDegree = 10f, // Denser - cardinalTextStyle = MaterialTheme.typography.labelSmall, - numericTextStyle = MaterialTheme.typography.titleSmall, - majorTickHeight = 15.dp, - minorTickHeight = 8.dp, - tickAreaHeight = 25.dp, - majorTickStrokeWidth = 1.5.dp, - minorTickStrokeWidth = 0.5.dp, - lubberLineStrokeWidth = 2.dp + modifier = Modifier.fillMaxWidth(), + stripHeight = 90.dp, + pixelsPerDegree = 7f, + degreeLabelInterval = 30 // Less frequent degree labels for clarity ) Row( diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt index 24265ec..c97ef67 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt @@ -1,52 +1,63 @@ -package com.example.advancedmaps3dsamples.ainavigator +package com.example.advancedmaps3dsamples.ainavigator // Keep your package structure + import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Column // Keep for Preview if needed, not for main compass +import androidx.compose.foundation.layout.Spacer // Keep for Preview import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.layout.padding // Keep for Preview +import androidx.compose.foundation.rememberScrollState // Keep for Preview +import androidx.compose.foundation.verticalScroll // Keep for Preview import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.material3.Text // Keep for Preview import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.center import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextMeasurer import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.drawText +import androidx.compose.ui.text.font.FontFamily // Keep for Preview +import androidx.compose.ui.text.font.FontWeight // Keep for Preview +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.sp // Keep for Preview import kotlin.math.roundToInt /** * A composable that displays a customizable aviation-style "whiskey" compass. - * - * This version allows detailed control over visual elements including text styles, - * tick sizes, and stroke widths. + * This version renders a flat, scrollable strip with ticks, degree labels, and cardinal directions. * * @param heading The current heading in degrees (0-360). * @param modifier The modifier to be applied to the compass. + * @param stripHeight The total visual height of the compass strip. * @param backgroundColor The background color of the compass strip. * @param tickColor The color of the tick marks on the rotating dial. - * @param labelColor The default color for the cardinal and numeric heading text, - * used if the provided TextStyles don't specify a color. * @param lubberLineColor The color of the central indicator line. * @param pixelsPerDegree Controls the horizontal spacing between degree markers on the dial. - * @param cardinalTextStyle TextStyle for the cardinal direction (e.g., "N", "NE"). - * @param numericTextStyle TextStyle for the numeric heading (e.g., "45"). + * + * @param showDegreeLabels Whether to display numeric degree labels on the strip. + * @param degreeLabelInterval Interval for numeric degree labels (e.g., every 15 degrees). + * @param degreeLabelTextStyle TextStyle for the numeric degree labels on the strip. + * @param degreeLabelVerticalOffset Vertical offset of degree labels from the bottom of the ticks. + * + * @param showCardinalLabels Whether to display cardinal direction labels (N, NE, E, etc.) on the strip. + * @param cardinalLabelTextStyle TextStyle for the cardinal direction labels on the strip. + * @param cardinalLabelVerticalOffset Vertical offset of cardinal labels from the top of the ticks. + * * @param majorTickHeight Height of the major tick marks. * @param minorTickHeight Height of the minor tick marks. - * @param tickAreaHeight The height of the central area where the lubber line and ticks are displayed. * @param majorTickStrokeWidth Stroke width for major ticks. * @param minorTickStrokeWidth Stroke width for minor ticks. * @param lubberLineStrokeWidth Stroke width for the lubber line. @@ -55,60 +66,138 @@ import kotlin.math.roundToInt fun WhiskeyCompass( heading: Float, modifier: Modifier = Modifier, - backgroundColor: Color = MaterialTheme.colorScheme.primary, - tickColor: Color = MaterialTheme.colorScheme.onPrimary, - labelColor: Color = MaterialTheme.colorScheme.onPrimary, + stripHeight: Dp = 80.dp, + backgroundColor: Color = MaterialTheme.colorScheme.primaryContainer, + tickColor: Color = MaterialTheme.colorScheme.onPrimaryContainer, lubberLineColor: Color = Color.Red, - pixelsPerDegree: Float = 15f, - cardinalTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, - numericTextStyle: TextStyle = MaterialTheme.typography.headlineSmall, + pixelsPerDegree: Float = 10f, + + showDegreeLabels: Boolean = true, + degreeLabelInterval: Int = 15, + degreeLabelTextStyle: TextStyle = MaterialTheme.typography.labelSmall.copy(textAlign = TextAlign.Center), + degreeLabelVerticalOffset: Dp = 4.dp, + + showCardinalLabels: Boolean = true, + cardinalLabelTextStyle: TextStyle = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), + cardinalLabelVerticalOffset: Dp = 4.dp, + majorTickHeight: Dp = 25.dp, minorTickHeight: Dp = 15.dp, - tickAreaHeight: Dp = 40.dp, // Defines the central band for ticks and lubber line majorTickStrokeWidth: Dp = 2.dp, minorTickStrokeWidth: Dp = 1.dp, - lubberLineStrokeWidth: Dp = 3.dp + lubberLineStrokeWidth: Dp = 2.dp ) { + val textMeasurer = rememberTextMeasurer() + val density = LocalDensity.current + + // Memoize measured cardinal labels for performance + val measuredCardinalLabels = remember(cardinalLabelTextStyle, density) { + // Pass density to measure if text style uses Dp for size, though typically it's Sp. + // For safety or if a custom TextStyle using Dp might be passed. + with(density) { + mapOf( + 0 to textMeasurer.measure("N", style = cardinalLabelTextStyle), + 45 to textMeasurer.measure("NE", style = cardinalLabelTextStyle), + 90 to textMeasurer.measure("E", style = cardinalLabelTextStyle), + 135 to textMeasurer.measure("SE", style = cardinalLabelTextStyle), + 180 to textMeasurer.measure("S", style = cardinalLabelTextStyle), + 225 to textMeasurer.measure("SW", style = cardinalLabelTextStyle), + 270 to textMeasurer.measure("W", style = cardinalLabelTextStyle), + 315 to textMeasurer.measure("NW", style = cardinalLabelTextStyle) + ) + } + } + Box( - modifier = modifier.background(backgroundColor), + modifier = modifier + .height(stripHeight) + .background(backgroundColor), contentAlignment = Alignment.Center ) { - // The rotating compass card with only tick marks - Canvas(modifier = Modifier.fillMaxWidth()) { + // Canvas 1: Scrolling Compass Strip (ticks, degree labels, cardinal labels) + Canvas(modifier = Modifier.matchParentSize()) { + // Convert Dp to Px here, inside DrawScope where density is implicitly available + val majorTickHeightPx = majorTickHeight.toPx() + val minorTickHeightPx = minorTickHeight.toPx() + val majorTickStrokeWidthPx = majorTickStrokeWidth.toPx() + val minorTickStrokeWidthPx = minorTickStrokeWidth.toPx() + val degreeLabelVerticalOffsetPx = degreeLabelVerticalOffset.toPx() + val cardinalLabelVerticalOffsetPx = cardinalLabelVerticalOffset.toPx() + val canvasWidth = size.width - val canvasCenterY = center.y // Center Y of the entire canvas - val xOffset = size.center.x - (heading * pixelsPerDegree) + val canvasCenterY = center.y // Vertical center of the strip + + // xOffset determines how much the strip is shifted horizontally + val xOffset = center.x - (heading * pixelsPerDegree) - // Calculate Y positions for ticks to be somewhat centered if canvas is taller than tickAreaHeight - // Ticks will be drawn from the top edge of the tick drawing area. - val tickDrawingAreaStartY = canvasCenterY - (tickAreaHeight.toPx() / 2f) + // The Y position where the center line of the ticks should be. + // Labels will be drawn above/below this. + val tickCenterY = canvasCenterY translate(left = xOffset) { - // Draw from -360 to 0, then 0 to 360, and 360 to 720 for robust wrapping. + // Loop through repetitions to ensure seamless wrapping for (repetition in -1..1) { - val repetitionOffset = repetition * 360 * pixelsPerDegree - for (degree in 0 until 360) { - val xPos = degree * pixelsPerDegree + repetitionOffset + val repetitionBaseDegree = repetition * 360 + for (degreeInRepetition in 0 until 360) { + val absoluteDegree = repetitionBaseDegree + degreeInRepetition + val xPos = absoluteDegree * pixelsPerDegree + + // Optimization: Only draw if potentially visible + val visibilityMargin = canvasWidth // Generous margin + if (xPos < -xOffset + canvasWidth + visibilityMargin && xPos > -xOffset - visibilityMargin) { + + val isMajorTickEquivalent = degreeInRepetition % 10 == 0 + val isMinorTickEquivalent = degreeInRepetition % 5 == 0 && !isMajorTickEquivalent - // Only draw elements that might be visible - if (xPos > -xOffset - (pixelsPerDegree * 10) && xPos < -xOffset + canvasWidth + (pixelsPerDegree * 10)) { - // Major ticks every 10 degrees - if (degree % 10 == 0) { + // Draw Major Ticks (every 10 degrees) + if (isMajorTickEquivalent) { + val tickTopY = tickCenterY - majorTickHeightPx / 2f + val tickBottomY = tickCenterY + majorTickHeightPx / 2f drawLine( color = tickColor, - start = Offset(x = xPos, y = tickDrawingAreaStartY), - end = Offset(x = xPos, y = tickDrawingAreaStartY + majorTickHeight.toPx()), - strokeWidth = majorTickStrokeWidth.toPx() + start = Offset(x = xPos, y = tickTopY), + end = Offset(x = xPos, y = tickBottomY), + strokeWidth = majorTickStrokeWidthPx ) + + // Draw Cardinal Labels above major ticks + if (showCardinalLabels && measuredCardinalLabels.containsKey(degreeInRepetition)) { + val measuredText = measuredCardinalLabels.getValue(degreeInRepetition) + drawText( + textLayoutResult = measuredText, + topLeft = Offset( + x = xPos - measuredText.size.width / 2f, + y = tickTopY - measuredText.size.height - cardinalLabelVerticalOffsetPx + ) + ) + } } - // Minor ticks every 5 degrees - else if (degree % 5 == 0) { + // Draw Minor Ticks (every 5 degrees, not overlapping major) + else if (isMinorTickEquivalent) { + val tickTopY = tickCenterY - minorTickHeightPx / 2f + val tickBottomY = tickCenterY + minorTickHeightPx / 2f drawLine( color = tickColor, - start = Offset(x = xPos, y = tickDrawingAreaStartY), - end = Offset(x = xPos, y = tickDrawingAreaStartY + minorTickHeight.toPx()), - strokeWidth = minorTickStrokeWidth.toPx() + start = Offset(x = xPos, y = tickTopY), + end = Offset(x = xPos, y = tickBottomY), + strokeWidth = minorTickStrokeWidthPx + ) + } + + // Draw Degree Labels below ticks at specified intervals + if (showDegreeLabels && degreeInRepetition % degreeLabelInterval == 0) { + val tickBottomY = tickCenterY + (if (isMajorTickEquivalent) majorTickHeightPx else if (isMinorTickEquivalent) minorTickHeightPx else 0f) / 2f + val labelText = degreeInRepetition.toString() + // It's good practice to use density for text measurement if TextStyle might involve Dp. + // For standard Sp, it's usually handled, but explicit density in remember is safer. + val measuredText = textMeasurer.measure(labelText, style = degreeLabelTextStyle) + drawText( + textLayoutResult = measuredText, + topLeft = Offset( + x = xPos - measuredText.size.width / 2f, + y = tickBottomY + degreeLabelVerticalOffsetPx + ) ) } } @@ -117,145 +206,110 @@ fun WhiskeyCompass( } } - // Static elements that do not move - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - // Cardinal direction text (e.g., NE) - Text( - text = heading.toCardinalDirection(), - style = cardinalTextStyle.copy( - color = if (cardinalTextStyle.color == Color.Unspecified) labelColor else cardinalTextStyle.color - ) - ) - - // The fixed central lubber line area - Box( - modifier = Modifier - .fillMaxWidth() - .height(tickAreaHeight) - ) { - Canvas(modifier = Modifier.matchParentSize()) { // Use matchParentSize to fill the Box - drawLine( - color = lubberLineColor, - start = Offset(x = center.x, y = 0f), - end = Offset(x = center.x, y = size.height), // spans full height of this Box - strokeWidth = lubberLineStrokeWidth.toPx() - ) - } - } - - // Numeric heading text (e.g., 45) - Text( - text = heading.roundToInt().toString(), - style = numericTextStyle.copy( - color = if (numericTextStyle.color == Color.Unspecified) labelColor else numericTextStyle.color - ) + // Canvas 2: Fixed Lubber Line + Canvas(modifier = Modifier.matchParentSize()) { + // Draw the lubber line vertically centered, spanning a good portion of the strip height + val lubberLineVisualPadding = stripHeight.toPx() * 0.05f // Uses DrawScope.toPx() implicitly + val currentLubberLineStrokeWidthPx = lubberLineStrokeWidth.toPx() // Uses DrawScope.toPx() implicitly + drawLine( + color = lubberLineColor, + start = Offset(x = center.x, y = lubberLineVisualPadding), + end = Offset(x = center.x, y = size.height - lubberLineVisualPadding), + strokeWidth = currentLubberLineStrokeWidthPx ) } } } - /** * Converts a heading in degrees to its corresponding cardinal or intercardinal direction. + * (This is not used by the compass itself anymore but kept for previews/external use) */ private fun Float.toCardinalDirection(): String { - // Ensure heading is within 0-360 range for calculations val normalizedHeading = (this % 360 + 360) % 360 val directions = listOf("N", "NE", "E", "SE", "S", "SW", "W", "NW", "N") - // Add 22.5 for correct centering of 45 degree segments, then divide by 45 return directions[((normalizedHeading + 22.5f) / 45f).toInt() % 8] } @Preview(showBackground = true, backgroundColor = 0xFF222222) @Composable -private fun FullyCustomizableWhiskeyCompassPreview() { +private fun FlatWhiskeyCompassPreview() { val exampleHeadings = listOf( - 0f, 45f, 168f, 225f, 358f + 0f, 10f, 22.5f, 45f, 168f, 270f, 358f ) Column( modifier = Modifier + .fillMaxWidth() .background(Color.DarkGray) .padding(16.dp) .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(24.dp) + verticalArrangement = Arrangement.spacedBy(16.dp) ) { - Text("Default Styling", color = Color.White, style = MaterialTheme.typography.titleMedium) + Text("Default Flat Compass Strip", color = Color.White, style = MaterialTheme.typography.titleMedium) WhiskeyCompass( heading = 45f, - modifier = Modifier - .fillMaxWidth() - .height(120.dp) // Give it some space + modifier = Modifier.fillMaxWidth(), + stripHeight = 100.dp, + pixelsPerDegree = 8f // Make it a bit denser for preview ) Spacer(modifier = Modifier.height(16.dp)) - Text("Custom Scaled & Styled", color = Color.White, style = MaterialTheme.typography.titleMedium) + Text("Customized Labels & Ticks", color = Color.White, style = MaterialTheme.typography.titleMedium) WhiskeyCompass( heading = 123f, - modifier = Modifier - .fillMaxWidth() - .height(150.dp), // Taller - backgroundColor = Color(0xFF303030), - tickColor = Color.Cyan, - labelColor = Color.Yellow, - lubberLineColor = Color.Magenta, - pixelsPerDegree = 20f, // More spread out - cardinalTextStyle = MaterialTheme.typography.headlineMedium.copy( - fontFamily = FontFamily.Serif, - fontWeight = FontWeight.Bold - ), - numericTextStyle = MaterialTheme.typography.displaySmall.copy( - fontFamily = FontFamily.Monospace, - letterSpacing = 2.sp - ), - majorTickHeight = 35.dp, // Taller ticks - minorTickHeight = 20.dp, - tickAreaHeight = 60.dp, // Larger central band - majorTickStrokeWidth = 3.dp, - minorTickStrokeWidth = 1.5.dp, - lubberLineStrokeWidth = 4.dp + modifier = Modifier.fillMaxWidth(), + stripHeight = 120.dp, + backgroundColor = Color(0xFF1A237E), // Dark Blue + tickColor = Color(0xFFB0BEC5), // Blue Grey + lubberLineColor = Color(0xFFFFD600), // Amber + pixelsPerDegree = 12f, + degreeLabelInterval = 10, + degreeLabelTextStyle = MaterialTheme.typography.bodySmall.copy(color = Color(0xFF81D4FA)), // Light Blue + cardinalLabelTextStyle = MaterialTheme.typography.labelLarge.copy(color = Color.White, fontWeight = FontWeight.Bold), + majorTickHeight = 30.dp, + minorTickHeight = 18.dp, + degreeLabelVerticalOffset = 6.dp, + cardinalLabelVerticalOffset = 6.dp, + majorTickStrokeWidth = 2.5.dp + ) + + Spacer(modifier = Modifier.height(16.dp)) + Text("No Cardinal Labels", color = Color.White, style = MaterialTheme.typography.titleMedium) + WhiskeyCompass( + heading = 210f, + modifier = Modifier.fillMaxWidth(), + stripHeight = 70.dp, + showCardinalLabels = false, + pixelsPerDegree = 6f ) Spacer(modifier = Modifier.height(16.dp)) - Text("Minimalist Small Version", color = Color.White, style = MaterialTheme.typography.titleMedium) + Text("No Degree Labels", color = Color.White, style = MaterialTheme.typography.titleMedium) WhiskeyCompass( - heading = 270f, - modifier = Modifier - .fillMaxWidth() - .height(80.dp), // Compact - pixelsPerDegree = 10f, // Denser - cardinalTextStyle = MaterialTheme.typography.labelSmall, - numericTextStyle = MaterialTheme.typography.titleSmall, - majorTickHeight = 15.dp, - minorTickHeight = 8.dp, - tickAreaHeight = 25.dp, - majorTickStrokeWidth = 1.5.dp, - minorTickStrokeWidth = 0.5.dp, - lubberLineStrokeWidth = 2.dp + heading = 300f, + modifier = Modifier.fillMaxWidth(), + stripHeight = 70.dp, + showDegreeLabels = false, + pixelsPerDegree = 6f ) - exampleHeadings.forEach { heading -> - Spacer(Modifier.height(8.dp)) + exampleHeadings.forEach { currentHeading -> + Spacer(Modifier.height(12.dp)) Text( - text = "Test Heading: ${heading.toInt()}° (${heading.toCardinalDirection()})", + text = "Test Heading: ${currentHeading.roundToInt()}°", color = Color.LightGray, fontFamily = FontFamily.Monospace, fontSize = 12.sp ) WhiskeyCompass( - heading = heading, - modifier = Modifier - .fillMaxWidth() - .height(100.dp), - cardinalTextStyle = MaterialTheme.typography.bodySmall, // Smaller for these examples - numericTextStyle = MaterialTheme.typography.bodyLarge, - tickAreaHeight = 30.dp, - majorTickHeight = 20.dp, - minorTickHeight = 10.dp + heading = currentHeading, + modifier = Modifier.fillMaxWidth(), + stripHeight = 90.dp, + pixelsPerDegree = 7f, + degreeLabelInterval = 30 // Less frequent degree labels for clarity ) } } diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index 1df4f8d..e8352ff 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -70,9 +70,9 @@ val promptWithCamera = """ * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. * **Tile Loading & Viewing Delay (Crucial):** - * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=4000` command.** - * **Optionally, after this 4000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. - * Do *not* add the 4000ms tile loading delay for minor adjustments at the *same general location* or if the animation starts from `currentCameraParams` and explores a *very nearby* feature without significant travel. + * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=5000` command.** + * **Optionally, after this 5000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. + * Do *not* add the 5000ms tile loading delay for minor adjustments at the *same general location* or if the animation starts from `currentCameraParams` and explores a *very nearby* feature without significant travel. * **Messages:** * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). @@ -85,16 +85,16 @@ val promptWithCamera = """ User Request: "Show me the Eiffel Tower from above, then slowly zoom out." Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=4000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=4000"` + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=5000"` User Request: "Fly me to the Grand Canyon." Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=4000;delay=dur=1500"` + `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=5000;delay=dur=1500"` User Request: "Show me some interesting spots near my current view." (Assuming `currentCameraParams="camera { center = latLngAltitude { latitude = 40.7128, longitude = -74.0060, altitude = 50.0 }, heading = 180.0, tilt = 45.0, range = 1000.0 }"` i.e., looking south in Lower Manhattan from 1km range) Expected Output (example, AI might choose different nearby spots): - `animationString="flyTo=lat=40.7061,lng=-74.0088,alt=30,hdg=0,tilt=60,range=500,dur=3000;message=\"Battery Park waterfront\";delay=dur=4000;delay=dur=1000;flyTo=lat=40.7100,lng=-74.0135,alt=150,hdg=270,tilt=50,range=800,dur=3000;message=\"One World Trade Center from nearby\";delay=dur=4000;delay=dur=1000"` + `animationString="flyTo=lat=40.7061,lng=-74.0088,alt=30,hdg=0,tilt=60,range=500,dur=3000;message=\"Battery Park waterfront\";delay=dur=5000;delay=dur=1000;flyTo=lat=40.7100,lng=-74.0135,alt=150,hdg=270,tilt=50,range=800,dur=3000;message=\"One World Trade Center from nearby\";delay=dur=4000;delay=dur=1000"` Now, process the following user request and generate the `animationString`: """.trimIndent() @@ -142,9 +142,9 @@ val prompt = """ * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. * **Tile Loading & Viewing Delay (Crucial):** - * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=4000` command.** - * **Optionally, after this 4000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. - * Do *not* add the 4000ms tile loading delay for minor adjustments at the *same general location*. + * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=5000` command.** + * **Optionally, after this 5000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. + * Do *not* add the 5000ms tile loading delay for minor adjustments at the *same general location*. * **Messages:** * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). @@ -157,23 +157,23 @@ val prompt = """ User Request: "Show me the Eiffel Tower from above, then slowly zoom out." Expected Output: - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=4000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=4000"` + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=5000"` User Request: "Fly me to the Grand Canyon." Expected Output: - `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=4000;delay=dur=1500"` + `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=5000;delay=dur=1500"` User Request: "Give me a quick fly-around of Mount Fuji, Japan, then take me to Tokyo Tower." Expected Output: - `animationString="flyTo=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=5000;message=\"Mount Fuji\";delay=dur=4000;delay=dur=1000;flyAround=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=10000,count=1;delay=dur=1000;flyTo=lat=35.6586,lng=139.7454,alt=250,hdg=0,tilt=60,range=1000,dur=7000;message=\"Tokyo Tower\";delay=dur=4000;delay=dur=1500"` + `animationString="flyTo=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=5000;message=\"Mount Fuji\";delay=dur=5000;delay=dur=1000;flyAround=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=10000,count=1;delay=dur=1000;flyTo=lat=35.6586,lng=139.7454,alt=250,hdg=0,tilt=60,range=1000,dur=7000;message=\"Tokyo Tower\";delay=dur=5000;delay=dur=1500"` User Request: "I want a helicopter tour of the Grand Canyon, starting near the South Rim visitor center, flying towards Mather Point, then doing a slow circle around Yavapai Point." Expected Output: - `animationString="flyTo=lat=36.0592,lng=-112.1096,alt=2150,hdg=45,tilt=60,range=1500,dur=6000;message=\"Grand Canyon South Rim\";delay=dur=4000;delay=dur=1000;flyTo=lat=36.0620,lng=-112.1068,alt=2180,hdg=70,tilt=55,range=1200,dur=5000;message=\"Approaching Mather Point\";delay=dur=2000;flyTo=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=2000;message=\"Yavapai Point\";delay=dur=1000;flyAround=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=15000,count=1.2;delay=dur=1000;flyTo=lat=36.0658,lng=-112.1156,alt=2200,hdg=270,tilt=40,range=5000,dur=4000"` + `animationString="flyTo=lat=36.0592,lng=-112.1096,alt=2150,hdg=45,tilt=60,range=1500,dur=6000;message=\"Grand Canyon South Rim\";delay=dur=5000;delay=dur=1000;flyTo=lat=36.0620,lng=-112.1068,alt=2180,hdg=70,tilt=55,range=1200,dur=5000;message=\"Approaching Mather Point\";delay=dur=2000;flyTo=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=2000;message=\"Yavapai Point\";delay=dur=1000;flyAround=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=15000,count=1.2;delay=dur=1000;flyTo=lat=36.0658,lng=-112.1156,alt=2200,hdg=270,tilt=40,range=5000,dur=5000"` User Request: "Show me New York City from high above." Expected Output: - `animationString="flyTo=lat=40.7128,lng=-74.0060,alt=800,hdg=0,tilt=30,range=40000,dur=5000;message=\"New York City Overview\";delay=dur=4000;delay=dur=1500"` + `animationString="flyTo=lat=40.7128,lng=-74.0060,alt=800,hdg=0,tilt=30,range=50000,dur=5000;message=\"New York City Overview\";delay=dur=5000;delay=dur=1500"` Now, process the following user request and generate the `animationString`: """.trimIndent() From 4a8002d290ceccb06b54717c0f5c6227d0f1055b Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 14:13:16 -0600 Subject: [PATCH 13/21] feat: ensure WhiskeyCompass respects safe drawing insets The WhiskeyCompass will now correctly account for system elements like display cutouts by applying `safeDrawingPadding`. Additionally, its opacity has been reduced to `0.55f`. --- .../ainavigator/AiNavigatorActivity.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 3a45495..a728906 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape @@ -165,7 +166,10 @@ class AiNavigatorActivity : ComponentActivity() { WhiskeyCompass( heading = camera.heading?.toFloat() ?: 0f, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .alpha(0.55f) + .safeDrawingPadding(), // Apply padding for system elements like cutouts stripHeight = 90.dp, pixelsPerDegree = 7f, degreeLabelInterval = 30 // Less frequent degree labels for clarity From b7b69ff44fd3f97fcc81cdb1b921a510737fbd2a Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 14:29:39 -0600 Subject: [PATCH 14/21] feat: added fade animation for compass in AiNavigator This commit introduces a fade animation for the compass in the AiNavigator feature. The compass will now fade to a lower opacity after 2 seconds of inactivity (no change in camera heading) and will return to its original opacity when the camera heading changes again. The following changes were made: - Added `Animatable` to control the compass alpha. - Implemented `LaunchedEffect` to trigger the fade animation after 2 seconds of inactivity and reset the alpha when the camera heading changes. - Updated the `Compass` composable to use the animated alpha value. --- .../ainavigator/AiNavigatorActivity.kt | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index a728906..9926eb9 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -4,10 +4,13 @@ import android.os.Bundle import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.SystemBarStyle -import androidx.compose.foundation.background import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,9 +18,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -25,11 +27,11 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.East import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Public import androidx.compose.material.icons.filled.North import androidx.compose.material.icons.filled.NorthEast import androidx.compose.material.icons.filled.NorthWest +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Public import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Shuffle import androidx.compose.material.icons.filled.South @@ -125,6 +127,8 @@ class AiNavigatorActivity : ComponentActivity() { val camera by viewModel.currentCamera.collectAsStateWithLifecycle() + val compassAlpha = remember { Animatable(0.55f) } + LaunchedEffect(viewModel.userMessage) { scope.launch { viewModel.userMessage.collect { message -> @@ -137,6 +141,24 @@ class AiNavigatorActivity : ComponentActivity() { } } + // Start a timer when the camera heading stops changing. + // After 2 seconds, fade the compass alpha. + // If the camera heading changes again, reset the timer and set the compass alpha back to 55%. + LaunchedEffect(camera.heading) { + compassAlpha.snapTo(0.55f) // Reset alpha to 55% when heading changes + val job = scope.launch { + delay(2.seconds) // Wait for 2 seconds + compassAlpha.animateTo( + targetValue = 0.3f, + animationSpec = tween(durationMillis = 500, easing = LinearEasing) + ) + } + // Cancel the previous job if heading changes before 2 seconds + // This ensures the fade-out only happens if the heading is stable for 2 seconds + job.invokeOnCompletion { if (it is kotlinx.coroutines.CancellationException) scope.launch { compassAlpha.snapTo(0.55f) } } + } + + AdvancedMaps3DSamplesTheme { Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) } @@ -168,11 +190,11 @@ class AiNavigatorActivity : ComponentActivity() { heading = camera.heading?.toFloat() ?: 0f, modifier = Modifier .fillMaxWidth() - .alpha(0.55f) + .alpha(compassAlpha.value) .safeDrawingPadding(), // Apply padding for system elements like cutouts stripHeight = 90.dp, pixelsPerDegree = 7f, - degreeLabelInterval = 30 // Less frequent degree labels for clarity + degreeLabelInterval = 30, // Less frequent degree labels for clarity ) Row( From 09e2b22a91ad57f7cd79d636eb80b97a6d15c1b0 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 14:33:25 -0600 Subject: [PATCH 15/21] Refactor: Improve coroutine scope management in AiNavigatorActivity This commit refactors the coroutine scope management in `AiNavigatorActivity.kt` for better clarity and robustness. Specifically: - Uses a consistent `scope` (rememberCoroutineScope()) for UI-triggered actions. - Leverages `LaunchedEffect`'s own scope for operations tied to its lifecycle (e.g., collecting `userMessage`, animating `compassAlpha`). This ensures these operations are automatically cancelled and restarted when the `LaunchedEffect`'s keys change, preventing potential leaks or unintended behavior. - Removes the unused `Compass` composable and its related `toCardinalDirection` helper function. - Simplifies comments related to system UI and screen dimming. - Uses `collectAsStateWithLifecycle()` for `viewModel.isRequestInflight` for better lifecycle awareness. --- .../ainavigator/AiNavigatorActivity.kt | 105 +++++------------- 1 file changed, 25 insertions(+), 80 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 9926eb9..29fe2fd 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -10,7 +10,6 @@ import androidx.activity.viewModels import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -21,24 +20,15 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear -import androidx.compose.material.icons.filled.East import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.North -import androidx.compose.material.icons.filled.NorthEast -import androidx.compose.material.icons.filled.NorthWest import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Public import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Shuffle -import androidx.compose.material.icons.filled.South -import androidx.compose.material.icons.filled.SouthEast -import androidx.compose.material.icons.filled.SouthWest import androidx.compose.material.icons.filled.Stop -import androidx.compose.material.icons.filled.West import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -51,9 +41,7 @@ import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -61,13 +49,11 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.rememberGraphicsLayer -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -94,10 +80,7 @@ class AiNavigatorActivity : ComponentActivity() { navigationBarStyle = SystemBarStyle.light(android.graphics.Color.TRANSPARENT, android.graphics.Color.TRANSPARENT) ) - // Hide the system bars hideSystemUI() - - // Prevent screen from dimming window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) val options = Map3DOptions( @@ -119,18 +102,17 @@ class AiNavigatorActivity : ComponentActivity() { ) setContent { - val scope = rememberCoroutineScope() - val coroutineScope = rememberCoroutineScope() + val scope = rememberCoroutineScope() // This scope can be used for actions tied to UI events val graphicsLayer = rememberGraphicsLayer() - val snackbarHostState = remember { SnackbarHostState() } - val camera by viewModel.currentCamera.collectAsStateWithLifecycle() - val compassAlpha = remember { Animatable(0.55f) } LaunchedEffect(viewModel.userMessage) { - scope.launch { + // Use a new coroutine scope for collecting user messages + // to avoid being cancelled by other LaunchedEffects. + // This scope is tied to this LaunchedEffect instance. + launch { viewModel.userMessage.collect { message -> if (message.length > 50) { snackbarHostState.showSnackbar(message, duration = SnackbarDuration.Long) @@ -141,21 +123,25 @@ class AiNavigatorActivity : ComponentActivity() { } } - // Start a timer when the camera heading stops changing. - // After 2 seconds, fade the compass alpha. - // If the camera heading changes again, reset the timer and set the compass alpha back to 55%. + // This LaunchedEffect controls the compass alpha based on camera heading changes. LaunchedEffect(camera.heading) { - compassAlpha.snapTo(0.55f) // Reset alpha to 55% when heading changes - val job = scope.launch { - delay(2.seconds) // Wait for 2 seconds + // When camera.heading changes, this LaunchedEffect is cancelled and restarted. + // Any coroutine launched within its scope (like the one below) is also cancelled. + + // Reset alpha to initial state and stop any ongoing animation on compassAlpha. + compassAlpha.snapTo(0.55f) + + // Launch a new coroutine within this LaunchedEffect's scope. + // This coroutine will handle the delay and subsequent fade-out animation. + // If camera.heading changes again before this completes, this coroutine will be cancelled. + launch { + delay(2.seconds) // Wait for 2 seconds of stable heading + // If this point is reached, it means camera.heading was stable for 2 seconds. compassAlpha.animateTo( targetValue = 0.3f, animationSpec = tween(durationMillis = 500, easing = LinearEasing) ) } - // Cancel the previous job if heading changes before 2 seconds - // This ensures the fade-out only happens if the heading is stable for 2 seconds - job.invokeOnCompletion { if (it is kotlinx.coroutines.CancellationException) scope.launch { compassAlpha.snapTo(0.55f) } } } @@ -172,13 +158,10 @@ class AiNavigatorActivity : ComponentActivity() { ThreeDMap( modifier = Modifier .fillMaxSize() - .drawWithContent { // This bit facilitates taking screenshots - // call record to capture the content in the graphics layer + .drawWithContent { graphicsLayer.record { - // draw the contents of the composable into the graphics layer this@drawWithContent.drawContent() } - // draw the graphics layer on the visible canvas drawLayer(graphicsLayer) }, options = options, @@ -191,10 +174,10 @@ class AiNavigatorActivity : ComponentActivity() { modifier = Modifier .fillMaxWidth() .alpha(compassAlpha.value) - .safeDrawingPadding(), // Apply padding for system elements like cutouts + .safeDrawingPadding(), stripHeight = 90.dp, pixelsPerDegree = 7f, - degreeLabelInterval = 30, // Less frequent degree labels for clarity + degreeLabelInterval = 30, ) Row( @@ -239,7 +222,8 @@ class AiNavigatorActivity : ComponentActivity() { } IconButton( onClick = { - coroutineScope.launch { + // Use the general 'scope' for UI triggered actions + scope.launch { val bitmap = graphicsLayer.toImageBitmap() viewModel.whatAmILookingAt(bitmap) } @@ -260,6 +244,7 @@ class AiNavigatorActivity : ComponentActivity() { onClick = { viewModel.nextMapMode() mapModeButtonEnabled = false + // Use the general 'scope' for UI triggered actions scope.launch { delay(2.seconds) mapModeButtonEnabled = true @@ -287,9 +272,8 @@ class AiNavigatorActivity : ComponentActivity() { horizontalAlignment = Alignment.CenterHorizontally ) { var userInput by rememberSaveable { mutableStateOf("") } - val requestIsActive by viewModel.isRequestInflight.collectAsState() + val requestIsActive by viewModel.isRequestInflight.collectAsStateWithLifecycle() // Recommended - // Always reserve space for the progress indicator, but only show it if the requestIsActive Box(modifier = Modifier .fillMaxWidth() .padding(top = 0.dp, bottom = 4.dp)) { @@ -376,42 +360,3 @@ class AiNavigatorActivity : ComponentActivity() { } } } - -@Composable -private fun Compass( - heading: Double, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier.background(MaterialTheme.colorScheme.primary, CircleShape), - horizontalAlignment = CenterHorizontally - ) { - val (icon, abbreviation) = heading.toCardinalDirection() - - Icon( - imageVector = icon, - contentDescription = "Compass", - tint = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier.padding(top = 8.dp, start = 8.dp, end = 8.dp, bottom = 0.dp) - ) - Text( - text = abbreviation, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) - ) - } -} - -private fun Double.toCardinalDirection(): Pair { - return when (this) { - in 337.5..360.0, in 0.0..22.5 -> Pair(Icons.Filled.North, "N") - in 22.5..67.5 -> Pair(Icons.Filled.NorthEast, "NE") // Placeholder, adjust as needed - in 67.5..112.5 -> Pair(Icons.Filled.East, "E") - in 112.5..157.5 -> Pair(Icons.Filled.SouthEast, "SE") // Placeholder - in 157.5..202.5 -> Pair(Icons.Filled.South, "S") - in 202.5..247.5 -> Pair(Icons.Filled.SouthWest, "SW") // Placeholder - in 247.5..292.5 -> Pair(Icons.Filled.West, "W") - else -> Pair(Icons.Filled.NorthWest, "NW") // Placeholder for 292.5..337.5 - } -} From c466e92e134bf2edf1e7fd463ce007177144208d Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 16:06:25 -0600 Subject: [PATCH 16/21] feat(ainavigator): Add marker and polyline support This commit introduces the following changes: - **New AI Navigator Commands:** - `addMarker`: Allows the AI to place markers on the map. - `addPolyline`: Enables the AI to draw polylines (routes, paths) on the map using Google Encoded Polyline Algorithm strings. - **UI Enhancement:** - Added a "Clear Map Objects" button to the AI Navigator interface, allowing users to remove all markers and polylines added by the AI. - **Prompt Updates:** - The AI prompt (`promptWithCamera`) has been updated to include instructions and examples for using the new `addMarker` and `addPolyline` commands. - Expanded the list of example prompts to include scenarios utilizing markers and polylines. - **ViewModel and Scenario Integration:** - `AiNavigatorViewModel` now includes a `clearMapObjects` function to handle the removal of map objects. - `ScenariosViewModel` interface and `ScenarioBaseViewModel` are updated to support adding markers and polylines. - New `AnimationStep` types (`AddMarkerStep`, `AddPolylineStep`) and corresponding parsing logic in `ScenarioMapper.kt` have been implemented to handle these commands from the AI's animation string. - **Utility Function:** - Made `Float.toCardinalDirection()` in `AviationCompass.kt` public for potential external use. --- .../ainavigator/AiNavigatorActivity.kt | 14 ++ .../ainavigator/AiNavigatorViewModel.kt | 12 ++ .../ainavigator/AviationCompass.kt | 2 +- .../ainavigator/data/Prompts.kt | 170 +++++------------- .../scenarios/Animations.kt | 14 ++ .../scenarios/ScenarioMapper.kt | 71 ++++++++ .../scenarios/ScenariosViewModel.kt | 5 + 7 files changed, 158 insertions(+), 130 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt index 29fe2fd..3985ba3 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorActivity.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.LayersClear import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Public import androidx.compose.material.icons.filled.Refresh @@ -239,6 +240,19 @@ class AiNavigatorActivity : ComponentActivity() { ) } + IconButton( + onClick = { viewModel.clearMapObjects() }, + colors = iconButtonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) + ) { + Icon( + imageVector = Icons.Filled.LayersClear, + contentDescription = "Clear Map Objects" + ) + } + var mapModeButtonEnabled by remember { mutableStateOf(true) } IconButton( onClick = { diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt index 55e25d5..eec9c0a 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AiNavigatorViewModel.kt @@ -137,6 +137,18 @@ class AiNavigatorViewModel @Inject constructor( _isRequestInflight.value = false } } + + fun clearMapObjects() { + viewModelScope.launch { + // Stop any ongoing animation that might be adding objects + stopAnimation() + // Call the clearObjects method from the parent Map3dViewModel + // This removes objects from the map and clears internal tracking lists. + super.clearObjects() + // Optionally, send a message to the user + _userMessage.send("Map objects cleared.") + } + } } suspend fun Bitmap.saveToDisk(context: Context): Uri { diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt index c97ef67..0828224 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/AviationCompass.kt @@ -225,7 +225,7 @@ fun WhiskeyCompass( * Converts a heading in degrees to its corresponding cardinal or intercardinal direction. * (This is not used by the compass itself anymore but kept for previews/external use) */ -private fun Float.toCardinalDirection(): String { +fun Float.toCardinalDirection(): String { val normalizedHeading = (this % 360 + 360) % 360 val directions = listOf("N", "NE", "E", "SE", "S", "SW", "W", "NW", "N") return directions[((normalizedHeading + 22.5f) / 45f).toInt() % 8] diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index e8352ff..7522a39 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -1,5 +1,6 @@ package com.example.advancedmaps3dsamples.ainavigator.data +// promptWithCamera (updated) val promptWithCamera = """ You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** @@ -25,155 +26,64 @@ val promptWithCamera = """ 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` - * `lat`: Latitude of the camera's center of focus (-90 to 90). - * `lng`: Longitude of the camera's center of focus (-180 to 180). - * `alt`: Altitude of the camera's **center of focus** in meters above sea level (ASL). **This altitude should be thoughtfully chosen: for buildings, consider mid-height or top; for natural features like canyons or mountains, choose an altitude significantly above the base or near a key viewpoint (e.g., rim, peak). Avoid setting this too low, as it can result in the camera looking at the ground or the base of tall objects.** - * `hdg`: Heading/bearing in degrees (0-360, where 0 is North, 90 is East). This is the direction the camera points. - * `tilt`: Tilt in degrees (0-90, where 0 is looking straight down, 90 is looking at the horizon). - * `range`: Distance in meters from the camera to its center of focus. Must be between 0 and 63,170,000. **Crucially, this should be appropriate for the scale of the target.** - * `dur`: Duration of the fly-to animation in milliseconds. + * `lat`, `lng`, `alt`: Define the camera's focal point. Choose `alt` thoughtfully. + * `hdg`, `tilt`, `range`: Define camera orientation and distance. + * `dur`: Animation duration. 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` - * The `lat`, `lng`, `alt` parameters define the **center point** of the orbit. The `alt` for this center point should be chosen carefully as per the `flyTo` altitude guidelines. - * `hdg`, `tilt`, `range` define the camera's *initial* orientation and distance relative to this center. The `range` must be between 0 and 63,170,000. - * `dur`: Total duration of the fly-around animation in milliseconds. - * `count`: Number of full 360-degree rounds to complete. + * `lat`, `lng`, `alt`: Define the orbit's center point. + * `hdg`, `tilt`, `range`: Define initial camera relative to the center. + * `dur`, `count`: Animation duration and number of rounds. 3. **`delay`**: Pauses the animation sequence. * Format: `delay=dur=` - * `dur`: Duration of the delay in milliseconds. + * `dur`: Duration. 4. **`message`**: Displays a short text message to the user. * Format: `message=""` - * ``: A short, quoted string. + * ``: Quoted string. + + 5. **`addMarker`**: Adds a visual marker to the map. + * Format: `addMarker=id=,lat=,lng=,alt=,label="",altMode=` + * `id`: Unique identifier. + * `lat`, `lng`, `alt`: Marker position. + * `label`: Marker text. + * `altMode`: `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. + + 6. **`addPolyline`**: Adds a line (route, path) to the map. + * Format: `addPolyline=id=,encodedPoints="",color="<#AARRGGBB_or_#RRGGBB>",width=,altMode=` + * `id`: A unique string identifier for the polyline (e.g., "route_1", "boston_marathon"). + * `encodedPoints`: **Crucial:** This MUST be a Google Encoded Polyline Algorithm string. Do NOT provide a list of lat/lng pairs. + * `color`: Hex color string for the polyline (e.g., "#FF0000" for red, "#800000FF" for semi-transparent blue). Defaults to blue if invalid. + * `width`: Width of the polyline in screen pixels (e.g., 5.0). Defaults to 5.0. + * `altMode`: Specifies how altitude of points (if any in encoding, usually 0) is interpreted. `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. `clampToGround` is a good default for routes on terrain. **How to Use Current Camera Parameters (if provided):** - * **Relative User Requests:** If the user's request seems relative to their current view (e.g., "show me nearby castles," "explore this area more," "what's over that hill from here?"), you **MUST** use the `currentCameraParams` as the starting point or primary reference for your animation. - * The `lat`, `lng`, and `alt` from `currentCameraParams.center` should inform the `lat`, `lng`, `alt` of your first `flyTo` or the `center` of your `flyAround`. - * You might adjust `hdg`, `tilt`, and `range` for the new relative target, but start your calculations or perspective from what `currentCameraParams` describe. - * **Absolute User Requests:** If the user makes an absolute request (e.g., "fly me to Tokyo," "show me the Pyramids of Giza"), the `currentCameraParams` are less critical for the *final destination*. - * You should prioritize reaching the user's specified absolute location. - * However, you *can* use `currentCameraParams` to make the *beginning* of the journey feel like a departure from the current view, making the transition smoother, before flying to the distant absolute target. - * **Do not let `currentCameraParams` override a clear, absolute request to go to a specific, distant location.** - * **If `currentCameraParams` are not provided, or if the user's request is unequivocally absolute and a contextual start offers no benefit, generate the animation string based solely on the user's textual request as before.** - * Your primary goal is to fulfill the user's request. Use `currentCameraParams` to enhance the experience when it makes sense for contextual or relative queries. + * (Same as before) **Important Constraints & Guidelines (Always Apply):** - * **Focus on Earth's Surface:** The generated animations should focus on terrestrial features, landmarks, and geography. **Avoid requests that primarily involve looking at the sky, atmospheric effects (auroras, storms), or specific times of day that imply lighting changes (e.g., "sunset," "city at night") as these cannot be accurately represented.** - * The `roll` parameter for the camera is **always 0**. - * Validate parameter ranges: lat (-90 to 90), lng (-180 to 180), hdg (0-360), tilt (0-90), **range (0 to 63,170,000)**. - * **Scale-Appropriate Range and Altitude of Camera Focus (`alt` parameter):** - * Adjust `range` and `alt` based on the target's scale (vast areas/cities: larger range/alt, e.g., 5km-50km range, focal `alt` well above ground; individual buildings: smaller range/alt, e.g., 100m-2km range, focal `alt` could be mid-height or top of building). - * **Crucially, ensure the `alt` for the camera's focal point is not too low.** - * **Animation Simplicity & Pacing:** - * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. - * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. - * **Tile Loading & Viewing Delay (Crucial):** - * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=5000` command.** - * **Optionally, after this 5000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. - * Do *not* add the 5000ms tile loading delay for minor adjustments at the *same general location* or if the animation starts from `currentCameraParams` and explores a *very nearby* feature without significant travel. - * **Messages:** - * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. - * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). - * If the user asks for specific locations, try to find reasonable geographic coordinates and appropriate viewing altitudes for the focal point. + * (Same as before, but add a note for polylines) + * **Polylines (`addPolyline`):** + * Use `addPolyline` to draw routes, paths, or boundaries. + * **The `encodedPoints` value must be a valid Google Encoded Polyline string.** + * Ensure `id` is unique for each polyline in an animation sequence. + * `altMode=clampToGround` is generally best for drawing routes on the map surface. **Your output MUST be a single string assigned to the variable `animationString`, like this:** `animationString="command1_params;command2_params;command3_params"` **Examples (Note `alt` adjustments and longer/additional delays):** - - User Request: "Show me the Eiffel Tower from above, then slowly zoom out." - Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=5000"` - - User Request: "Fly me to the Grand Canyon." + User Request: "Show me the Eiffel Tower from above, add a marker, then draw a line from there to Arc de Triomphe." + (Assume Arc de Triomphe is at lat=48.8738,lng=2.2950. Encoded polyline from Eiffel to Arc: `y~syHkbtM?_@`) Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=5000;delay=dur=1500"` - - User Request: "Show me some interesting spots near my current view." - (Assuming `currentCameraParams="camera { center = latLngAltitude { latitude = 40.7128, longitude = -74.0060, altitude = 50.0 }, heading = 180.0, tilt = 45.0, range = 1000.0 }"` i.e., looking south in Lower Manhattan from 1km range) - Expected Output (example, AI might choose different nearby spots): - `animationString="flyTo=lat=40.7061,lng=-74.0088,alt=30,hdg=0,tilt=60,range=500,dur=3000;message=\"Battery Park waterfront\";delay=dur=5000;delay=dur=1000;flyTo=lat=40.7100,lng=-74.0135,alt=150,hdg=270,tilt=50,range=800,dur=3000;message=\"One World Trade Center from nearby\";delay=dur=4000;delay=dur=1000"` - - Now, process the following user request and generate the `animationString`: -""".trimIndent() - - -val prompt = """ - You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** - - The `animationString` is a sequence of camera manipulation commands separated by semicolons (`;`). - The available commands are: - - 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. - * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` - * `lat`: Latitude of the camera's center of focus (-90 to 90). - * `lng`: Longitude of the camera's center of focus (-180 to 180). - * `alt`: Altitude of the camera's **center of focus** in meters above sea level (ASL). **This altitude should be thoughtfully chosen: for buildings, consider mid-height or top; for natural features like canyons or mountains, choose an altitude significantly above the base or near a key viewpoint (e.g., rim, peak). Avoid setting this too low, as it can result in the camera looking at the ground or the base of tall objects.** - * `hdg`: Heading/bearing in degrees (0-360, where 0 is North, 90 is East). This is the direction the camera points. - * `tilt`: Tilt in degrees (0-90, where 0 is looking straight down, 90 is looking at the horizon). - * `range`: Distance in meters from the camera to its center of focus. Must be between 0 and 63,170,000. **Crucially, this should be appropriate for the scale of the target.** - * `dur`: Duration of the fly-to animation in milliseconds. - - 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. - * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` - * The `lat`, `lng`, `alt` parameters define the **center point** of the orbit. The `alt` for this center point should be chosen carefully as per the `flyTo` altitude guidelines. - * `hdg`, `tilt`, `range` define the camera's *initial* orientation and distance relative to this center. The `range` must be between 0 and 63,170,000. - * `dur`: Total duration of the fly-around animation in milliseconds. - * `count`: Number of full 360-degree rounds to complete. + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;addMarker=id=eiffel_marker,lat=48.8584,lng=2.2945,alt=0,label=\"Eiffel Tower\",altMode=clampToGround;message=\"Eiffel Tower\";delay=dur=5000;addPolyline=id=eiffel_to_arc,encodedPoints=\"y~syHkbtM?_@\",color=\"#FF0000FF\",width=5.0,altMode=clampToGround;delay=dur=1000;flyTo=lat=48.865,lng=2.295,alt=150,hdg=315,tilt=45,range=2000,dur=4000"` - 3. **`delay`**: Pauses the animation sequence. - * Format: `delay=dur=` - * `dur`: Duration of the delay in milliseconds. - 4. **`message`**: Displays a short text message to the user. - * Format: `message=""` - * ``: A short, quoted string. - - **Important Constraints & Guidelines:** - * **Focus on Earth's Surface:** The generated animations should focus on terrestrial features, landmarks, and geography. **Avoid requests that primarily involve looking at the sky, atmospheric effects (auroras, storms), or specific times of day that imply lighting changes (e.g., "sunset," "city at night") as these cannot be accurately represented.** - * The `roll` parameter for the camera is **always 0**. - * Validate parameter ranges: lat (-90 to 90), lng (-180 to 180), hdg (0-360), tilt (0-90), **range (0 to 63,170,000)**. - * **Scale-Appropriate Range and Altitude of Camera Focus (`alt` parameter):** - * Adjust `range` and `alt` based on the target's scale (vast areas/cities: larger range/alt, e.g., 5km-50km range, focal `alt` well above ground; individual buildings: smaller range/alt, e.g., 100m-2km range, focal `alt` could be mid-height or top of building). - * **Crucially, ensure the `alt` for the camera's focal point is not too low.** - * **Animation Simplicity & Pacing:** - * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. - * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. - * **Tile Loading & Viewing Delay (Crucial):** - * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=5000` command.** - * **Optionally, after this 5000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. - * Do *not* add the 5000ms tile loading delay for minor adjustments at the *same general location*. - * **Messages:** - * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. - * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). - * If the user asks for specific locations, try to find reasonable geographic coordinates and appropriate viewing altitudes for the focal point. - - **Your output MUST be a single string assigned to the variable `animationString`, like this:** - `animationString="command1_params;command2_params;command3_params"` - - **Examples (Note `alt` adjustments and longer/additional delays):** - - User Request: "Show me the Eiffel Tower from above, then slowly zoom out." - Expected Output: - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;delay=dur=1000;flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=30,range=2000,dur=5000"` - - User Request: "Fly me to the Grand Canyon." - Expected Output: - `animationString="flyTo=lat=36.1069,lng=-112.1124,alt=2100,hdg=0,tilt=45,range=25000,dur=6000;message=\"The Grand Canyon\";delay=dur=5000;delay=dur=1500"` - - User Request: "Give me a quick fly-around of Mount Fuji, Japan, then take me to Tokyo Tower." - Expected Output: - `animationString="flyTo=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=5000;message=\"Mount Fuji\";delay=dur=5000;delay=dur=1000;flyAround=lat=35.3606,lng=138.7274,alt=3000,hdg=0,tilt=45,range=5000,dur=10000,count=1;delay=dur=1000;flyTo=lat=35.6586,lng=139.7454,alt=250,hdg=0,tilt=60,range=1000,dur=7000;message=\"Tokyo Tower\";delay=dur=5000;delay=dur=1500"` - - User Request: "I want a helicopter tour of the Grand Canyon, starting near the South Rim visitor center, flying towards Mather Point, then doing a slow circle around Yavapai Point." - Expected Output: - `animationString="flyTo=lat=36.0592,lng=-112.1096,alt=2150,hdg=45,tilt=60,range=1500,dur=6000;message=\"Grand Canyon South Rim\";delay=dur=5000;delay=dur=1000;flyTo=lat=36.0620,lng=-112.1068,alt=2180,hdg=70,tilt=55,range=1200,dur=5000;message=\"Approaching Mather Point\";delay=dur=2000;flyTo=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=2000;message=\"Yavapai Point\";delay=dur=1000;flyAround=lat=36.0658,lng=-112.1156,alt=2150,hdg=0,tilt=65,range=1000,dur=15000,count=1.2;delay=dur=1000;flyTo=lat=36.0658,lng=-112.1156,alt=2200,hdg=270,tilt=40,range=5000,dur=5000"` - - User Request: "Show me New York City from high above." - Expected Output: - `animationString="flyTo=lat=40.7128,lng=-74.0060,alt=800,hdg=0,tilt=30,range=50000,dur=5000;message=\"New York City Overview\";delay=dur=5000;delay=dur=1500"` + User Request: "Fly me to the start of the Boston Marathon route and draw the route." + (Assume Boston Marathon start: lat=42.2464,lng=-71.4606. Assume route is encoded as `gu_aFx_gcL...`) + Expected Output (assuming `currentCameraParams` is not relevant or provided): + `animationString="flyTo=lat=42.2464,lng=-71.4606,alt=150,hdg=45,tilt=50,range=3000,dur=5000;addPolyline=id=boston_marathon,encodedPoints=\"gu_aFx_gcL...placeholder_for_actual_encoding...\",color=\"#0000FF\",width=7.0,altMode=clampToGround;message=\"Boston Marathon Route\";delay=dur=5000;delay=dur=2000"` Now, process the following user request and generate the `animationString`: """.trimIndent() @@ -370,6 +280,8 @@ val whatAmILookingAtPrompt = """ """.trimIndent() val examplePrompts = listOf( + "Add a marker showing Crater Lake and fly to it. Then do a slow orbit around the lake.", + "build a tour of a few of the UNESCO world heritage sites. stay high above each and make a slow orbit before moving on", "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", "Start with a wide shot of the Golden Gate Bridge, then fly underneath it from the ocean side towards San Francisco.", "Show me Machu Picchu. Start far away to see the mountains, then zoom in to the main citadel.", @@ -450,5 +362,5 @@ val promptGeneratorPrompt = """ Fly low over the fjords of Western Norway, starting near Geiranger. Identify important stops along the historic Oregon Trail and then fly along them at slower speeds to impart how far it really is. - Now, please generate 10 example user prompts based on these guidelines. + Now, please generate 20 example user prompts based on these guidelines. """.trimIndent() diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt index 0488c34..2c58512 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt @@ -16,6 +16,8 @@ package com.example.advancedmaps3dsamples.scenarios import com.google.android.gms.maps3d.model.FlyAroundOptions import com.google.android.gms.maps3d.model.FlyToOptions +import com.google.android.gms.maps3d.model.MarkerOptions +import com.google.android.gms.maps3d.model.PolylineOptions import kotlinx.coroutines.delay sealed interface AnimationStep { @@ -45,3 +47,15 @@ data class MessageStep(val message: String) : AnimationStep { viewModel.showMessage(message) } } + +data class AddMarkerStep(val options: MarkerOptions) : AnimationStep { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { + viewModel.addMarker(this.options) + } +} + +data class AddPolylineStep(val options: PolylineOptions) : AnimationStep { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { + viewModel.addPolyline(this.options) + } +} diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt index ae0cf31..abecd82 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt @@ -47,6 +47,7 @@ import com.google.android.gms.maps3d.model.vector3D import com.google.maps.android.ktx.utils.toLatLngList import java.util.UUID import androidx.core.graphics.toColorInt +import com.google.maps.android.PolyUtil private const val TAG = "ScenarioMapper" @@ -155,6 +156,68 @@ fun String.toMap3DMode(): Int { } } +fun Map.toMarkerOptions(): MarkerOptions { + val labelText = getString("label", "").trim('"') + val altModeString = getString("altMode", "clampToGround") // Default altMode + + return markerOptions { + position = this@toMarkerOptions.toLatLngAltitude() // Uses the existing LatLngAltitude parser + label = labelText + altitudeMode = parseAltitudeMode(altModeString) // Uses existing helper + isExtruded = true // Default + isDrawnWhenOccluded = true // Default + collisionBehavior = CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL // Default + zIndex = 1 // Default + } +} + +fun Map.toPolylineOptionsFromAi(): PolylineOptions { + val id = getString("id", "polyline_${UUID.randomUUID()}") + val encodedPointsStr = getString("encodedPoints", "").trim('"') + val colorStr = getString("color", "#0000FFFF").trim('"') // Default to blue + val width = getDouble("width", 5.0).toFloat() + val altModeString = getString("altMode", "clampToGround") + + val decodedLatLngs: List = + if (encodedPointsStr.isNotBlank()) { + try { + PolyUtil.decode(encodedPointsStr) + } catch (e: Exception) { + Log.e(TAG, "Failed to decode polyline string: '$encodedPointsStr'", e) + emptyList() + } + } else { + emptyList() + } + + val coordinates3D = decodedLatLngs.map { + latLngAltitude { + latitude = it.latitude + longitude = it.longitude + altitude = 0.0 // Altitude often ignored for clampToGround/Mesh, or could be part of a more complex encoding + } + } + + val parsedColor = try { + colorStr.toColorInt() + } catch (e: IllegalArgumentException) { + Log.w(TAG, "Invalid color string '$colorStr' for polyline $id, defaulting to blue.") + Color.BLUE + } + + return polylineOptions { + this.id = id + this.coordinates = coordinates3D + this.strokeColor = parsedColor + this.strokeWidth = width.toDouble() // PolylineOptions takes Double for strokeWidth + this.altitudeMode = parseAltitudeMode(altModeString) + this.zIndex = 5 // Default zIndex + // Add other properties like drawsOccludedSegments if needed + this.drawsOccludedSegments = true + } +} + + fun String.toAnimation(): List { val stepsString = this.trim().trimEnd(';') if (stepsString.isBlank()) { @@ -173,6 +236,14 @@ fun String.toAnimation(): List { Log.w(TAG, "Message: $value") add(MessageStep(value.trim('"'))) } + "addmarker" -> { // Added new case + val attributes = value.toAttributesMap() + add(AddMarkerStep(attributes.toMarkerOptions())) + } + "addpolyline" -> { // Added new case + val attributes = value.toAttributesMap() + add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) + } else -> Log.w(TAG, "Unsupported animation step type: $key") } } else { diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt index feaaf47..7227e9f 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt @@ -39,6 +39,9 @@ import com.example.advancedmaps3dsamples.R import com.example.advancedmaps3dsamples.utils.copy import com.google.android.gms.maps3d.model.FlyAroundOptions import com.google.android.gms.maps3d.model.FlyToOptions +import com.google.android.gms.maps3d.model.MarkerOptions +import com.google.android.gms.maps3d.model.Polyline +import com.google.android.gms.maps3d.model.PolylineOptions import com.google.android.gms.maps3d.model.flyAroundOptions enum class CameraAttribute(val labelId: Int) { @@ -73,6 +76,8 @@ interface ScenarioBaseViewModel { suspend fun awaitFlyTo(flyToOptions: FlyToOptions) suspend fun awaitFlyAround(flyAroundOptions: FlyAroundOptions) suspend fun showMessage(message: String) + fun addMarker(options: MarkerOptions) + fun addPolyline(polylineOptions: PolylineOptions) } @HiltViewModel From fc47ad37ab4ab4eb54a876ab6e0c49be31016ddd Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 16:45:09 -0600 Subject: [PATCH 17/21] Refactor: Improve AI navigator polyline handling This commit refactors the AI navigator's polyline functionality by: - Changing the `addPolyline` command to accept a list of points (`lat,lng;lat,lng;...`) instead of an encoded polyline string. This simplifies prompt generation and AI output. - Updating the `promptWithCamera` to reflect this change, including examples and constraints on the number of points. - Modifying `ScenarioMapper.kt` to: - Parse the new `points` format for `addPolyline`. - Improve the robustness of `toAttributesMap()` to correctly handle quoted values containing commas or semicolons, which is necessary for the new `points` format and `message` commands. - Enhance `toAnimation()` to correctly parse command strings, especially those with quoted values that might contain command delimiters (`;`). - Adjust `toPolylineOptionsFromAi()` to process the list of points and correctly set altitude mode. - Adding a new example prompt demonstrating the creation of a polyline with multiple markers. --- .../ainavigator/data/Prompts.kt | 51 ++- .../scenarios/ScenarioMapper.kt | 313 ++++++++++++++---- 2 files changed, 261 insertions(+), 103 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index 7522a39..720cb0a 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -1,6 +1,5 @@ package com.example.advancedmaps3dsamples.ainavigator.data -// promptWithCamera (updated) val promptWithCamera = """ You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** @@ -26,64 +25,51 @@ val promptWithCamera = """ 1. **`flyTo`**: Smoothly animates the camera to a new target position and orientation. * Format: `flyTo=lat=,lng=,alt=,hdg=,tilt=,range=,dur=` - * `lat`, `lng`, `alt`: Define the camera's focal point. Choose `alt` thoughtfully. - * `hdg`, `tilt`, `range`: Define camera orientation and distance. - * `dur`: Animation duration. 2. **`flyAround`**: Smoothly animates the camera in an orbit around a central point. * Format: `flyAround=lat=,lng=,alt=,hdg=,tilt=,range=,dur=,count=` - * `lat`, `lng`, `alt`: Define the orbit's center point. - * `hdg`, `tilt`, `range`: Define initial camera relative to the center. - * `dur`, `count`: Animation duration and number of rounds. 3. **`delay`**: Pauses the animation sequence. * Format: `delay=dur=` - * `dur`: Duration. 4. **`message`**: Displays a short text message to the user. * Format: `message=""` - * ``: Quoted string. 5. **`addMarker`**: Adds a visual marker to the map. * Format: `addMarker=id=,lat=,lng=,alt=,label="",altMode=` - * `id`: Unique identifier. - * `lat`, `lng`, `alt`: Marker position. - * `label`: Marker text. * `altMode`: `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. - 6. **`addPolyline`**: Adds a line (route, path) to the map. - * Format: `addPolyline=id=,encodedPoints="",color="<#AARRGGBB_or_#RRGGBB>",width=,altMode=` - * `id`: A unique string identifier for the polyline (e.g., "route_1", "boston_marathon"). - * `encodedPoints`: **Crucial:** This MUST be a Google Encoded Polyline Algorithm string. Do NOT provide a list of lat/lng pairs. - * `color`: Hex color string for the polyline (e.g., "#FF0000" for red, "#800000FF" for semi-transparent blue). Defaults to blue if invalid. + 6. **`addPolyline`**: Adds a line (route, path) to the map using a list of points. + * Format: `addPolyline=id=,points="",color="<#AARRGGBB_or_#RRGGBB>",width=,altMode=` + * `id`: A unique string identifier for the polyline. + * `points`: A string containing latitude,longitude pairs separated by semicolons. Each pair is comma-separated (e.g., "42.1,-71.2;42.2,-71.3"). + * **IMPORTANT: Limit the number of points to a maximum of 20-30 for a single polyline to keep the request practical.** For very long routes, break them into multiple `addPolyline` commands or simplify the route. Altitude for these points will be interpreted based on `altMode`. + * `color`: Hex color string for the polyline (e.g., "#FF0000" for red). Defaults to blue if invalid. * `width`: Width of the polyline in screen pixels (e.g., 5.0). Defaults to 5.0. - * `altMode`: Specifies how altitude of points (if any in encoding, usually 0) is interpreted. `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. `clampToGround` is a good default for routes on terrain. + * `altMode`: Specifies how altitude of points is interpreted. `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. `clampToGround` is a good default. **How to Use Current Camera Parameters (if provided):** * (Same as before) **Important Constraints & Guidelines (Always Apply):** - * (Same as before, but add a note for polylines) + * (Same as before) * **Polylines (`addPolyline`):** - * Use `addPolyline` to draw routes, paths, or boundaries. - * **The `encodedPoints` value must be a valid Google Encoded Polyline string.** - * Ensure `id` is unique for each polyline in an animation sequence. - * `altMode=clampToGround` is generally best for drawing routes on the map surface. + * The `points` value must be a string of "lat,lng" pairs separated by semicolons. + * **Strictly limit the number of points per polyline to a maximum of 20-30.** + * Ensure `id` is unique for each polyline. + * `altMode=clampToGround` is generally best for drawing routes on the map surface (point altitudes will be ignored, and the line will drape over terrain). If using `absolute` or `relativeTo...`, ensure point altitudes are considered if the source data has them, otherwise assume 0 for the third dimension if not provided in the `points` string format. (For simplicity, the parser will assume 0 altitude for each point in the `points` string and rely on `altMode` for rendering.) **Your output MUST be a single string assigned to the variable `animationString`, like this:** `animationString="command1_params;command2_params;command3_params"` **Examples (Note `alt` adjustments and longer/additional delays):** - User Request: "Show me the Eiffel Tower from above, add a marker, then draw a line from there to Arc de Triomphe." - (Assume Arc de Triomphe is at lat=48.8738,lng=2.2950. Encoded polyline from Eiffel to Arc: `y~syHkbtM?_@`) - Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;addMarker=id=eiffel_marker,lat=48.8584,lng=2.2945,alt=0,label=\"Eiffel Tower\",altMode=clampToGround;message=\"Eiffel Tower\";delay=dur=5000;addPolyline=id=eiffel_to_arc,encodedPoints=\"y~syHkbtM?_@\",color=\"#FF0000FF\",width=5.0,altMode=clampToGround;delay=dur=1000;flyTo=lat=48.865,lng=2.295,alt=150,hdg=315,tilt=45,range=2000,dur=4000"` - + User Request: "Show me the Eiffel Tower, then draw a short line east from it." + Expected Output: + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;addPolyline=id=eiffel_east_line,points=\"48.8584,2.2945;48.8584,2.2995\",color=\"#FF0000FF\",width=5.0,altMode=clampToGround;delay=dur=1000"` - User Request: "Fly me to the start of the Boston Marathon route and draw the route." - (Assume Boston Marathon start: lat=42.2464,lng=-71.4606. Assume route is encoded as `gu_aFx_gcL...`) - Expected Output (assuming `currentCameraParams` is not relevant or provided): - `animationString="flyTo=lat=42.2464,lng=-71.4606,alt=150,hdg=45,tilt=50,range=3000,dur=5000;addPolyline=id=boston_marathon,encodedPoints=\"gu_aFx_gcL...placeholder_for_actual_encoding...\",color=\"#0000FF\",width=7.0,altMode=clampToGround;message=\"Boston Marathon Route\";delay=dur=5000;delay=dur=2000"` + User Request: "Fly to Boston Common and show a simplified path towards the Public Garden." + Expected Output: + `animationString="flyTo=lat=42.3550,lng=-71.0657,alt=50,hdg=270,tilt=45,range=500,dur=4000;addPolyline=id=common_to_garden_path,points=\"42.3550,-71.0657;42.3552,-71.0670;42.3555,-71.0685;42.3558,-71.0700\",color=\"#00FF00\",width=6.0,altMode=clampToGround;message=\"Path from Boston Common to Public Garden\";delay=dur=5000"` Now, process the following user request and generate the `animationString`: """.trimIndent() @@ -280,6 +266,7 @@ val whatAmILookingAtPrompt = """ """.trimIndent() val examplePrompts = listOf( + "Create a polyline representing the freedom trail in Boston. Add markers for each important location. There should be no fewer than 10 location. Add the markers and the polyline first and only then start a tour in order of the stops.", "Add a marker showing Crater Lake and fly to it. Then do a slow orbit around the lake.", "build a tour of a few of the UNESCO world heritage sites. stay high above each and make a slow orbit before moving on", "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt index abecd82..2e7b7e7 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt @@ -52,23 +52,95 @@ import com.google.maps.android.PolyUtil private const val TAG = "ScenarioMapper" /** - * Parses a comma-separated string of "key=value" pairs into a map. Example: - * "lat=39.65,lng=-105.02,alt=550.2" -> {"lat": "39.65", "lng": "-105.02", "alt": "550.2"} + * Parses a comma-separated string of "key=value" pairs into a map. + * Handles quoted values that may contain commas or semicolons. + * Example: "id=foo,points="lat1,lng1;lat2,lng2",color=#FF0000" */ fun String.toAttributesMap(): Map { - // Trim whitespace around the string and commas/semicolons just in case - val trimmedString = this.trim().trimEnd(';').trimEnd(',') - if (trimmedString.isBlank()) { - return emptyMap() - } - return trimmedString - .split(",") - .map { it.trim() } - .filter { it.contains("=") } - .associate { part -> - val (key, value) = part.split("=", limit = 2) - key.trim() to value.trim() + val attributes = mutableMapOf() + var index = 0 + val length = this.length + + while (index < length) { + // Skip leading delimiters (comma or semicolon) for subsequent pairs + // and any leading whitespace for the key. + while (index < length && (this[index] == ',' || this[index] == ';' || this[index].isWhitespace())) { + index++ + } + if (index >= length) break + + // Parse key + val keyStart = index + while (index < length && this[index] != '=') { + index++ + } + if (index >= length || this[index] != '=') { + Log.w(TAG, "Malformed pair or end of string encountered parsing key at index $keyStart. Input: '$this'") + break // Malformed or end of string without a full pair + } + val key = this.substring(keyStart, index).trim() + index++ // Skip '=' + + if (index >= length) { + Log.w(TAG, "No value found for key '$key'. Input: '$this'") + if (key.isNotBlank()) attributes[key] = "" // Store empty value for key if key is valid + break + } + + // Skip whitespace before value + while (index < length && this[index].isWhitespace()) { + index++ + } + if (index >= length) { + Log.w(TAG, "No value found for key '$key' (after skipping whitespace). Input: '$this'") + if (key.isNotBlank()) attributes[key] = "" + break + } + + + // Parse value + val value: String + if (this[index] == '"') { // Quoted value + index++ // Skip opening quote + val valueStart = index + val sb = StringBuilder() + var escaped = false + var foundClosingQuote = false + while (index < length) { + val char = this[index] + if (escaped) { + sb.append(char) // Append the escaped character as is + escaped = false + } else if (char == '\\') { + escaped = true // Next character is escaped + } else if (char == '"') { + foundClosingQuote = true + index++ // Consume the closing quote + break // End of quoted value + } else { + sb.append(char) + } + index++ + } + value = sb.toString() // Content within quotes, escaped characters processed + if (!foundClosingQuote) { + Log.w(TAG, "Unclosed quote for key '$key'. Value parsed so far: '$value'. Input: '$this'") + } + } else { // Unquoted value + val valueStart = index + while (index < length && this[index] != ',' && this[index] != ';') { + index++ + } + value = this.substring(valueStart, index).trim() + } + + if (key.isNotBlank()) { + attributes[key] = value + } else { + Log.w(TAG, "Parsed a blank key. Value was '$value'. Input: '$this'") + } } + return attributes } /** Helper to safely get a Double value from the attributes map. */ @@ -173,85 +245,185 @@ fun Map.toMarkerOptions(): MarkerOptions { fun Map.toPolylineOptionsFromAi(): PolylineOptions { val id = getString("id", "polyline_${UUID.randomUUID()}") - val encodedPointsStr = getString("encodedPoints", "").trim('"') - val colorStr = getString("color", "#0000FFFF").trim('"') // Default to blue + val pointsStr = getString("points", "").trim('"') // Get the "lat1,lng1;lat2,lng2;..." string + val colorStr = getString("color", "#000000FF").trim('"') // Default to opaque blue val width = getDouble("width", 5.0).toFloat() val altModeString = getString("altMode", "clampToGround") - val decodedLatLngs: List = - if (encodedPointsStr.isNotBlank()) { - try { - PolyUtil.decode(encodedPointsStr) - } catch (e: Exception) { - Log.e(TAG, "Failed to decode polyline string: '$encodedPointsStr'", e) - emptyList() + val coordinates3D = mutableListOf() + if (pointsStr.isNotBlank()) { + val pointPairs = pointsStr.split(';') + for (pairStr in pointPairs) { + val latLngParts = pairStr.split(',') + if (latLngParts.size == 2) { + try { + val lat = latLngParts[0].trim().toDouble() + val lng = latLngParts[1].trim().toDouble() + // For simplicity, assume altitude 0 for points from AI string. + // `altMode` will determine how these are rendered. + coordinates3D.add(latLngAltitude { latitude = lat; longitude = lng; altitude = 0.0 }) + } catch (e: NumberFormatException) { + Log.w(TAG, "Invalid lat/lng in polyline points string: '$pairStr' for polyline $id") + } + } else { + Log.w(TAG, "Invalid point pair format in polyline points string: '$pairStr' for polyline $id") } - } else { - emptyList() } + } - val coordinates3D = decodedLatLngs.map { - latLngAltitude { - latitude = it.latitude - longitude = it.longitude - altitude = 0.0 // Altitude often ignored for clampToGround/Mesh, or could be part of a more complex encoding - } + if (coordinates3D.size < 2) { + Log.w(TAG, "Polyline $id has fewer than 2 valid points. It might not render.") } val parsedColor = try { colorStr.toColorInt() } catch (e: IllegalArgumentException) { Log.w(TAG, "Invalid color string '$colorStr' for polyline $id, defaulting to blue.") - Color.BLUE + android.graphics.Color.BLUE } return polylineOptions { this.id = id this.coordinates = coordinates3D this.strokeColor = parsedColor - this.strokeWidth = width.toDouble() // PolylineOptions takes Double for strokeWidth + this.strokeWidth = width.toDouble() this.altitudeMode = parseAltitudeMode(altModeString) - this.zIndex = 5 // Default zIndex - // Add other properties like drawsOccludedSegments if needed + this.zIndex = 5 this.drawsOccludedSegments = true } } - fun String.toAnimation(): List { - val stepsString = this.trim().trimEnd(';') - if (stepsString.isBlank()) { + val animationString = this.trim() + if (animationString.isBlank()) { return emptyList() } - return buildList { - stepsString.split(";").forEach { step -> - val trimmedStep = step.trim() - if (trimmedStep.contains('=')) { - val (key, value) = trimmedStep.split("=", limit = 2) - when (key.trim().lowercase()) { - "flyto" -> add(FlyToStep(value.toFlyTo())) - "delay" -> add(DelayStep(value.toDelay())) - "flyaround" -> add(FlyAroundStep(value.toFlyAround())) - "message" -> { - Log.w(TAG, "Message: $value") - add(MessageStep(value.trim('"'))) - } - "addmarker" -> { // Added new case - val attributes = value.toAttributesMap() - add(AddMarkerStep(attributes.toMarkerOptions())) - } - "addpolyline" -> { // Added new case - val attributes = value.toAttributesMap() - add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) - } - else -> Log.w(TAG, "Unsupported animation step type: $key") + + val steps = mutableListOf() + var currentIndex = 0 + val length = animationString.length + + while (currentIndex < length) { + // Skip leading semicolons and whitespace + while (currentIndex < length && (animationString[currentIndex] == ';' || animationString[currentIndex].isWhitespace())) { + currentIndex++ + } + if (currentIndex >= length) break + + // Find the command key + val commandKeyStart = currentIndex + while (currentIndex < length && animationString[currentIndex] != '=') { + currentIndex++ + } + + if (currentIndex >= length || animationString[currentIndex] != '=') { + Log.w(TAG, "Malformed command (missing '=') starting at index $commandKeyStart in animation string: '$animationString'") + break // or continue to next potential command if desired + } + val commandKey = animationString.substring(commandKeyStart, currentIndex).trim().lowercase() + currentIndex++ // Skip '=' + + if (currentIndex >= length) { + Log.w(TAG, "No value found for command '$commandKey' at end of animation string: '$animationString'") + break + } + + // Find the command value string (everything until the next unquoted semicolon or end of string) + val commandValueStart = currentIndex + val valueBuilder = StringBuilder() + var inQuotes = false + var foundEndOfValue = false + + while (currentIndex < length) { + val char = animationString[currentIndex] + if (char == '"') { + inQuotes = !inQuotes + } + if (!inQuotes && char == ';') { + foundEndOfValue = true + break // Found delimiter for the next command + } + valueBuilder.append(char) + currentIndex++ + } + + val commandValueString = valueBuilder.toString().trim() + + if (commandKey.isBlank()) { + Log.w(TAG, "Parsed a blank command key. Value string was '$commandValueString'. Full string: '$animationString'") + if (foundEndOfValue) currentIndex++ // Move past the semicolon if we stopped because of it + continue + } + + Log.d(TAG, "Parsing command: $commandKey with value: $commandValueString") + + try { + when (commandKey) { + "flyto" -> steps.add(FlyToStep(commandValueString.toFlyTo())) + "delay" -> steps.add(DelayStep(commandValueString.toDelay())) + "flyaround" -> steps.add(FlyAroundStep(commandValueString.toFlyAround())) + "message" -> { + // The value for message might be quoted itself. toAttributesMap is not ideal. + // The prompt is message="" -> commandValueString will be "\"\"" + val messageContent = commandValueString.removeSurrounding("\"") + steps.add(MessageStep(messageContent)) } - } else { - Log.w(TAG, "Ignoring invalid animation step format: $step") + "addmarker" -> { + val attributes = commandValueString.toAttributesMap() + steps.add(AddMarkerStep(attributes.toMarkerOptions())) + } + "addpolyline" -> { + val attributes = commandValueString.toAttributesMap() + steps.add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) + } + else -> Log.w(TAG, "Unsupported animation step type: $commandKey with value: $commandValueString") } + } catch (e: Exception) { + Log.e(TAG, "Error parsing command '$commandKey' with value '$commandValueString'. Error: ${e.message}", e) + } + + if (foundEndOfValue) { + // currentIndex is already at the semicolon, loop will skip it in the next iteration. } } + return steps } +// +// +//fun String.toAnimation(): List { +// val stepsString = this.trim().trimEnd(';') +// if (stepsString.isBlank()) { +// return emptyList() +// } +// return buildList { +// stepsString.split(";").forEach { step -> +// val trimmedStep = step.trim() +// if (trimmedStep.contains('=')) { +// val (key, value) = trimmedStep.split("=", limit = 2) +// when (key.trim().lowercase()) { +// "flyto" -> add(FlyToStep(value.toFlyTo())) +// "delay" -> add(DelayStep(value.toDelay())) +// "flyaround" -> add(FlyAroundStep(value.toFlyAround())) +// "message" -> { +// Log.w(TAG, "Message: $value") +// add(MessageStep(value.trim('"'))) +// } +// "addmarker" -> { // Added new case +// val attributes = value.toAttributesMap() +// add(AddMarkerStep(attributes.toMarkerOptions())) +// } +// "addpolyline" -> { // Added new case +// val attributes = value.toAttributesMap() +// add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) +// } +// else -> Log.w(TAG, "Unsupported animation step type: $key") +// } +// } else { +// Log.w(TAG, "Ignoring invalid animation step format: $step") +// } +// } +// } +//} fun String.toMaps3DOptions(): Map3DOptions { var camera: Camera? = null @@ -570,22 +742,21 @@ fun String.toPolyline(idp: String? = null): List { } // --- Helper Functions --- - @AltitudeMode -private fun parseAltitudeMode(altModeString: String): Int { - return when (altModeString.lowercase()) { +internal fun parseAltitudeMode(altModeString: String?): Int { // Made internal + return when (altModeString?.trim()?.lowercase()) { "absolute" -> AltitudeMode.ABSOLUTE + "relativetoground" -> AltitudeMode.RELATIVE_TO_GROUND // common typo fix "relative_to_ground" -> AltitudeMode.RELATIVE_TO_GROUND + "relativetomesh" -> AltitudeMode.RELATIVE_TO_MESH // common typo fix "relative_to_mesh" -> AltitudeMode.RELATIVE_TO_MESH + "clamptoground" -> AltitudeMode.CLAMP_TO_GROUND // common typo fix "clamp_to_ground" -> AltitudeMode.CLAMP_TO_GROUND else -> { - if (altModeString.isNotEmpty()) { - Log.w( - TAG, - "Ignoring unrecognized altitude mode '$altModeString', defaulting to CLAMP_TO_GROUND", - ) + if (!altModeString.isNullOrEmpty()) { + Log.w(TAG, "Ignoring unrecognized altitude mode '$altModeString', defaulting to CLAMP_TO_GROUND.") } AltitudeMode.CLAMP_TO_GROUND } } -} +} \ No newline at end of file From 4640c5293b8f214fc7cd2a73175517ce9601004e Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 17:18:07 -0600 Subject: [PATCH 18/21] feat: Add `addPolygon` command for AI Navigator This commit introduces a new `addPolygon` command to the AI Navigator functionality. Key changes include: - **New `addPolygon` command:** Allows AI to request drawing filled polygons on the map. - Supports `id`, `outerPoints`, `fillColor`, `strokeColor`, `strokeWidth`, and `altMode` parameters. - Limits outer points to a maximum of 100. - Inner holes are not currently supported by this command. - **Updated `promptWithCamera`:** - Includes detailed instructions and examples for using the new `addPolygon` command. - Specifies point limits for polygons. - Updates polyline point limit to 100 (previously 20-30). - Default polyline color changed to opaque blue. - **`ScenarioMapper` enhancements:** - Added `toPolygonOptionsFromAi()` to parse polygon parameters from the AI's string. - `toAnimation()` now correctly parses `addPolygon` commands. - The animation string parser was improved to handle quoted values within commands more robustly. - **New `AddPolygonStep` animation class:** Handles the logic for adding a polygon to the map via the view model. - **`ScenariosViewModel` interface updated:** Added `addPolygon` function. - **Removed `whatAmILookingAtPromptOld`:** The older, non-image-based "what am I looking at" prompt has been removed as it's superseded by the image-based version. - **Removed unused `android.graphics.Bitmap` import** in `NavigatorService`. --- .../ainavigator/data/NavigatorService.kt | 15 -- .../ainavigator/data/Prompts.kt | 140 +++--------------- .../scenarios/Animations.kt | 7 + .../scenarios/ScenarioMapper.kt | 118 +++++++++------ .../scenarios/ScenariosViewModel.kt | 2 + 5 files changed, 102 insertions(+), 180 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt index 876d367..54c8242 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/NavigatorService.kt @@ -1,6 +1,5 @@ package com.example.advancedmaps3dsamples.ainavigator.data -import android.graphics.Bitmap import android.util.Log import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asAndroidBitmap @@ -59,20 +58,6 @@ class NavigatorService @Inject constructor( } } - suspend fun whatAmILookingAt(cameraParams: String): String { - Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") - try { - val response = model.generateContent(whatAmILookingAtPromptOld.replace("", cameraParams)) - Log.d(TAG, "Firebase Vertex AI raw response: ${response.text}") - val cleanedText = response.text?.sanitize() - Log.d(TAG, "Firebase Vertex AI cleaned response: $cleanedText") - return cleanedText ?: "" - } catch (e: Exception) { - Log.e(TAG, "Error getting whatAmILookingAt from Firebase Vertex AI for $cameraParams", e) - throw GameRepositoryException("Unable to get whatAmILookingAt: ${e.message}", e) - } - } - suspend fun whatAmILookingAt(cameraParams: String, bitmap: ImageBitmap): String { Log.d(TAG, "Calling Firebase Vertex AI: Fetching whatAmILookingAt for cameraParams: $cameraParams") try { diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index 720cb0a..f58d4ac 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -1,5 +1,6 @@ package com.example.advancedmaps3dsamples.ainavigator.data +// promptWithCamera (updated) val promptWithCamera = """ You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** @@ -41,142 +42,43 @@ val promptWithCamera = """ 6. **`addPolyline`**: Adds a line (route, path) to the map using a list of points. * Format: `addPolyline=id=,points="",color="<#AARRGGBB_or_#RRGGBB>",width=,altMode=` - * `id`: A unique string identifier for the polyline. - * `points`: A string containing latitude,longitude pairs separated by semicolons. Each pair is comma-separated (e.g., "42.1,-71.2;42.2,-71.3"). - * **IMPORTANT: Limit the number of points to a maximum of 20-30 for a single polyline to keep the request practical.** For very long routes, break them into multiple `addPolyline` commands or simplify the route. Altitude for these points will be interpreted based on `altMode`. - * `color`: Hex color string for the polyline (e.g., "#FF0000" for red). Defaults to blue if invalid. - * `width`: Width of the polyline in screen pixels (e.g., 5.0). Defaults to 5.0. + * `points`: String of "lat,lng" pairs separated by semicolons. Max 100 points. + * `altMode`: `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. + + 7. **`addPolygon`**: Adds a filled polygon area to the map. + * Format: `addPolygon=id=,outerPoints="",fillColor="<#AARRGGBB>",strokeColor="<#AARRGGBB>",strokeWidth=,altMode=` + * `id`: A unique string identifier for the polygon. + * `outerPoints`: A string containing latitude,longitude pairs for the polygon's outer boundary, separated by semicolons. The polygon will be closed automatically (do not repeat the first point at the end). **Minimum 3 points.** + * **IMPORTANT: Limit the number of points to a maximum of 100 for a single polygon.** + * `fillColor`: Hex color string for the polygon's fill (e.g., "#80FF0000" for semi-transparent red). Defaults to semi-transparent blue. + * `strokeColor`: Hex color string for the polygon's outline. Defaults to opaque blue. + * `strokeWidth`: Width of the polygon's outline in screen pixels (e.g., 3.0). Defaults to 3.0. * `altMode`: Specifies how altitude of points is interpreted. `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. `clampToGround` is a good default. + * **Note: Inner holes are not supported by this command version.** **How to Use Current Camera Parameters (if provided):** * (Same as before) **Important Constraints & Guidelines (Always Apply):** * (Same as before) - * **Polylines (`addPolyline`):** - * The `points` value must be a string of "lat,lng" pairs separated by semicolons. - * **Strictly limit the number of points per polyline to a maximum of 20-30.** - * Ensure `id` is unique for each polyline. - * `altMode=clampToGround` is generally best for drawing routes on the map surface (point altitudes will be ignored, and the line will drape over terrain). If using `absolute` or `relativeTo...`, ensure point altitudes are considered if the source data has them, otherwise assume 0 for the third dimension if not provided in the `points` string format. (For simplicity, the parser will assume 0 altitude for each point in the `points` string and rely on `altMode` for rendering.) + * **Polygons (`addPolygon`):** + * The `outerPoints` value must be a string of "lat,lng" pairs (minimum 3) separated by semicolons. + * **Strictly limit the number of points per polygon's outer boundary to a maximum of 100.** + * Ensure `id` is unique. + * `altMode=clampToGround` is generally best for drawing areas on the map surface. + * The parser will assume 0 altitude for each point in the `outerPoints` string and rely on `altMode` for rendering. **Your output MUST be a single string assigned to the variable `animationString`, like this:** `animationString="command1_params;command2_params;command3_params"` **Examples (Note `alt` adjustments and longer/additional delays):** - User Request: "Show me the Eiffel Tower, then draw a short line east from it." - Expected Output: - `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";delay=dur=5000;addPolyline=id=eiffel_east_line,points=\"48.8584,2.2945;48.8584,2.2995\",color=\"#FF0000FF\",width=5.0,altMode=clampToGround;delay=dur=1000"` - - User Request: "Fly to Boston Common and show a simplified path towards the Public Garden." + User Request: "Fly to Central Park in NYC and draw a green rectangle representing its approximate area." Expected Output: - `animationString="flyTo=lat=42.3550,lng=-71.0657,alt=50,hdg=270,tilt=45,range=500,dur=4000;addPolyline=id=common_to_garden_path,points=\"42.3550,-71.0657;42.3552,-71.0670;42.3555,-71.0685;42.3558,-71.0700\",color=\"#00FF00\",width=6.0,altMode=clampToGround;message=\"Path from Boston Common to Public Garden\";delay=dur=5000"` + `animationString="flyTo=lat=40.7829,lng=-73.9654,alt=100,hdg=0,tilt=30,range=5000,dur=5000;addPolygon=id=central_park_area,outerPoints=\"40.7960,-73.9580;40.7639,-73.9720;40.7675,-73.9820;40.8000,-73.9670\",fillColor=\"#8000FF00\",strokeColor=\"#FF008000\",strokeWidth=2.0,altMode=clampToGround;message=\"Central Park Area\";delay=dur=5000"` Now, process the following user request and generate the `animationString`: """.trimIndent() -val whatAmILookingAtPromptOld = """ - You are an AI assistant with expertise in geography and interpreting 3D map views. Your task is to provide a concise and informative blurb (1-2 sentences) describing what the user is likely looking at, based on the provided camera parameters for a 3D map. - - **Input You Will Receive (Appended to this prompt):** - The current camera parameters will be provided in the following structured format: - - camera { - center = latLngAltitude { - latitude = - longitude = - altitude = // This is the altitude of the camera's focal point in meters ASL - } - heading = // Degrees, 0 is North - tilt = // Degrees, 0 is straight down, 90 is horizon - range = // Meters from camera to focal point - // Note: Roll is always 0 and may not be present. - } - - **Your Task:** - Based on these camera parameters, determine the most prominent or interesting landmark, geographical feature, city, or area that would be the focus of the user's view. Then, generate a short, engaging blurb about it. - - **Guidelines for Your Blurb:** - - 1. **Identify the Subject:** - * Use the `latitude` and `longitude` from the `center` object to identify the general location. - * Consider the `altitude` of the `center` (focal point altitude), `range`, and `tilt` to understand the scale and perspective. - * A low `range` (e.g., < 2000m) and `tilt` > 45 degrees often means focusing on a specific building or street-level feature. - * A high `range` (e.g., > 10000m) and low `tilt` might indicate an overview of a city, region, or large natural feature. - * The focal point `altitude` is crucial: if it's high, the focus is likely on something tall or an elevated view. - * The `heading` indicates the direction the camera is pointing from its focal point, which can help refine what's in the center of the view. - - 2. **Conciseness:** The blurb should be 1-2 sentences maximum. Aim for informative but brief. - - 3. **Engaging Tone:** Make it sound interesting, like a mini-fact or a quick observation. - - 4. **Specificity (if possible):** - * If a famous landmark is clearly identifiable (e.g., Eiffel Tower, Mount Everest), name it. - * If it's a general area, describe it (e.g., "the bustling downtown of [City]", "the rugged peaks of the [Mountain Range]", "a coastal view of the [Ocean/Sea]"). - * If the view is very generic (e.g., looking at a random patch of forest from high up with a wide range), it's okay to be more general (e.g., "a forested region from above," "an aerial view of rolling hills"). - - 5. **No Technical Jargon:** Do not mention the camera parameters (`latitude`, `longitude`, `range`, etc.) or their values in your blurb. The user only cares about what they are seeing. - - 6. **Focus on the Visual:** Describe what is *seen*, not the history or abstract facts, unless it's a very brief, well-known tidbit that enhances the visual understanding (e.g., "The Colosseum, ancient Roman amphitheater"). - - **Output Format:** - A single, short blurb as plain text. - - **Example Scenarios:** - - * **If camera parameters are (example):** - ``` - camera { - center = latLngAltitude { - latitude = 48.8584 - longitude = 2.2945 - altitude = 150.0 // Focal point is 150m up the tower - } - heading = 45.0 - tilt = 60.0 - range = 500.0 - } - ``` - `Output: You're looking at the iconic Eiffel Tower in Paris, a marvel of 19th-century engineering.` - - * **If camera parameters are (example):** - ``` - camera { - center = latLngAltitude { - latitude = 36.1069 - longitude = -112.1124 - altitude = 2100.0 // Focal point near the rim of the canyon - } - heading = 0.0 - tilt = 45.0 - range = 25000.0 - } - ``` - `Output: This is an expansive aerial view of the Grand Canyon, showcasing its immense scale and layered rock formations.` - - * **If camera parameters are (example):** - ``` - camera { - center = latLngAltitude { - latitude = 34.0522 - longitude = -118.2437 - altitude = 50.0 // Focal point relatively low for a general residential area - } - heading = 180.0 - tilt = 30.0 - range = 3000.0 - } - ``` - `Output: You're viewing a residential neighborhood from above, with its network of streets and houses.` - - --- - **Current Camera View Parameters:** - - --- - - Based on the camera parameters above, what is the user likely looking at? - -""".trimIndent() - val whatAmILookingAtPrompt = """ You are an AI assistant with expertise in geography and interpreting 3D map views. Your task is to provide a concise and informative blurb (1-2 sentences) describing what the user is likely looking at. You will be given a **screenshot** of a photorealistic 3D map view and the **camera parameters** that correspond to that screenshot. diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt index 2c58512..666511b 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/Animations.kt @@ -17,6 +17,7 @@ package com.example.advancedmaps3dsamples.scenarios import com.google.android.gms.maps3d.model.FlyAroundOptions import com.google.android.gms.maps3d.model.FlyToOptions import com.google.android.gms.maps3d.model.MarkerOptions +import com.google.android.gms.maps3d.model.PolygonOptions import com.google.android.gms.maps3d.model.PolylineOptions import kotlinx.coroutines.delay @@ -59,3 +60,9 @@ data class AddPolylineStep(val options: PolylineOptions) : AnimationStep { viewModel.addPolyline(this.options) } } + +data class AddPolygonStep(val options: PolygonOptions) : AnimationStep { + override suspend operator fun invoke(viewModel: ScenarioBaseViewModel) { + viewModel.addPolygon(this.options) + } +} diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt index 2e7b7e7..9d16187 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioMapper.kt @@ -246,7 +246,7 @@ fun Map.toMarkerOptions(): MarkerOptions { fun Map.toPolylineOptionsFromAi(): PolylineOptions { val id = getString("id", "polyline_${UUID.randomUUID()}") val pointsStr = getString("points", "").trim('"') // Get the "lat1,lng1;lat2,lng2;..." string - val colorStr = getString("color", "#000000FF").trim('"') // Default to opaque blue + val colorStr = getString("color", "#FF0000FF").trim('"') // Default to opaque blue val width = getDouble("width", 5.0).toFloat() val altModeString = getString("altMode", "clampToGround") @@ -293,6 +293,68 @@ fun Map.toPolylineOptionsFromAi(): PolylineOptions { } } +fun Map.toPolygonOptionsFromAi(): PolygonOptions { + val id = getString("id", "polygon_${UUID.randomUUID()}") + val outerPointsStr = getString("outerPoints", "").trim('"') + // Inner holes are not supported by AI command yet, so default to empty list + // val innerHolesStr = getString("innerHoles", "").trim('"') + + val fillColorStr = getString("fillColor", "#800000FF").trim('"') // Default to semi-transparent blue + val strokeColorStr = getString("strokeColor", "#FF0000FF").trim('"') // Default to opaque blue + val strokeWidth = getDouble("strokeWidth", 3.0).toFloat() + val altModeString = getString("altMode", "clampToGround") + + val outerCoordinates3D = mutableListOf() + if (outerPointsStr.isNotBlank()) { + val pointPairs = outerPointsStr.split(';') + for (pairStr in pointPairs) { + val latLngParts = pairStr.split(',') + if (latLngParts.size == 2) { + try { + val lat = latLngParts[0].trim().toDouble() + val lng = latLngParts[1].trim().toDouble() + outerCoordinates3D.add(latLngAltitude { latitude = lat; longitude = lng; altitude = 0.0 }) + } catch (e: NumberFormatException) { + Log.w(TAG, "Invalid lat/lng in polygon outerPoints: '$pairStr' for polygon $id") + } + } else { + Log.w(TAG, "Invalid point pair format in polygon outerPoints: '$pairStr' for polygon $id") + } + } + } + + if (outerCoordinates3D.size < 3) { + Log.w(TAG, "Polygon $id has fewer than 3 valid outer points. It might not render. Original points: '$outerPointsStr'") + // Potentially return a default or throw an error, but for now, allow creation of an invalid polygon that won't render. + } + + val parsedFillColor = try { + fillColorStr.toColorInt() + } catch (e: IllegalArgumentException) { + Log.w(TAG, "Invalid fill color string '$fillColorStr' for polygon $id, defaulting to semi-transparent blue.") + "#800000FF".toColorInt() // Default + } + + val parsedStrokeColor = try { + strokeColorStr.toColorInt() + } catch (e: IllegalArgumentException) { + Log.w(TAG, "Invalid stroke color string '$strokeColorStr' for polygon $id, defaulting to opaque blue.") + Color.BLUE // Default + } + + return polygonOptions { + this.id = id + this.outerCoordinates = outerCoordinates3D + // this.innerCoordinates = emptyList() // No inner holes from AI for now + this.fillColor = parsedFillColor + this.strokeColor = parsedStrokeColor + this.strokeWidth = strokeWidth.toDouble() + this.altitudeMode = parseAltitudeMode(altModeString) + this.zIndex = 3 // Default zIndex for polygons, can be adjusted + this.geodesic = false // Default + } +} + fun String.toAnimation(): List { val animationString = this.trim() if (animationString.isBlank()) { @@ -304,13 +366,12 @@ fun String.toAnimation(): List { val length = animationString.length while (currentIndex < length) { - // Skip leading semicolons and whitespace + // ... (logic for skipping delimiters and finding commandKey remains the same) ... while (currentIndex < length && (animationString[currentIndex] == ';' || animationString[currentIndex].isWhitespace())) { currentIndex++ } if (currentIndex >= length) break - // Find the command key val commandKeyStart = currentIndex while (currentIndex < length && animationString[currentIndex] != '=') { currentIndex++ @@ -318,7 +379,7 @@ fun String.toAnimation(): List { if (currentIndex >= length || animationString[currentIndex] != '=') { Log.w(TAG, "Malformed command (missing '=') starting at index $commandKeyStart in animation string: '$animationString'") - break // or continue to next potential command if desired + break } val commandKey = animationString.substring(commandKeyStart, currentIndex).trim().lowercase() currentIndex++ // Skip '=' @@ -328,7 +389,6 @@ fun String.toAnimation(): List { break } - // Find the command value string (everything until the next unquoted semicolon or end of string) val commandValueStart = currentIndex val valueBuilder = StringBuilder() var inQuotes = false @@ -341,7 +401,7 @@ fun String.toAnimation(): List { } if (!inQuotes && char == ';') { foundEndOfValue = true - break // Found delimiter for the next command + break } valueBuilder.append(char) currentIndex++ @@ -351,7 +411,7 @@ fun String.toAnimation(): List { if (commandKey.isBlank()) { Log.w(TAG, "Parsed a blank command key. Value string was '$commandValueString'. Full string: '$animationString'") - if (foundEndOfValue) currentIndex++ // Move past the semicolon if we stopped because of it + if (foundEndOfValue) currentIndex++ continue } @@ -363,8 +423,6 @@ fun String.toAnimation(): List { "delay" -> steps.add(DelayStep(commandValueString.toDelay())) "flyaround" -> steps.add(FlyAroundStep(commandValueString.toFlyAround())) "message" -> { - // The value for message might be quoted itself. toAttributesMap is not ideal. - // The prompt is message="" -> commandValueString will be "\"\"" val messageContent = commandValueString.removeSurrounding("\"") steps.add(MessageStep(messageContent)) } @@ -376,6 +434,10 @@ fun String.toAnimation(): List { val attributes = commandValueString.toAttributesMap() steps.add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) } + "addpolygon" -> { // Added new case + val attributes = commandValueString.toAttributesMap() + steps.add(AddPolygonStep(attributes.toPolygonOptionsFromAi())) + } else -> Log.w(TAG, "Unsupported animation step type: $commandKey with value: $commandValueString") } } catch (e: Exception) { @@ -383,47 +445,11 @@ fun String.toAnimation(): List { } if (foundEndOfValue) { - // currentIndex is already at the semicolon, loop will skip it in the next iteration. + // currentIndex is already at the semicolon } } return steps } -// -// -//fun String.toAnimation(): List { -// val stepsString = this.trim().trimEnd(';') -// if (stepsString.isBlank()) { -// return emptyList() -// } -// return buildList { -// stepsString.split(";").forEach { step -> -// val trimmedStep = step.trim() -// if (trimmedStep.contains('=')) { -// val (key, value) = trimmedStep.split("=", limit = 2) -// when (key.trim().lowercase()) { -// "flyto" -> add(FlyToStep(value.toFlyTo())) -// "delay" -> add(DelayStep(value.toDelay())) -// "flyaround" -> add(FlyAroundStep(value.toFlyAround())) -// "message" -> { -// Log.w(TAG, "Message: $value") -// add(MessageStep(value.trim('"'))) -// } -// "addmarker" -> { // Added new case -// val attributes = value.toAttributesMap() -// add(AddMarkerStep(attributes.toMarkerOptions())) -// } -// "addpolyline" -> { // Added new case -// val attributes = value.toAttributesMap() -// add(AddPolylineStep(attributes.toPolylineOptionsFromAi())) -// } -// else -> Log.w(TAG, "Unsupported animation step type: $key") -// } -// } else { -// Log.w(TAG, "Ignoring invalid animation step format: $step") -// } -// } -// } -//} fun String.toMaps3DOptions(): Map3DOptions { var camera: Camera? = null diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt index 7227e9f..26c04eb 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosViewModel.kt @@ -40,6 +40,7 @@ import com.example.advancedmaps3dsamples.utils.copy import com.google.android.gms.maps3d.model.FlyAroundOptions import com.google.android.gms.maps3d.model.FlyToOptions import com.google.android.gms.maps3d.model.MarkerOptions +import com.google.android.gms.maps3d.model.PolygonOptions import com.google.android.gms.maps3d.model.Polyline import com.google.android.gms.maps3d.model.PolylineOptions import com.google.android.gms.maps3d.model.flyAroundOptions @@ -78,6 +79,7 @@ interface ScenarioBaseViewModel { suspend fun showMessage(message: String) fun addMarker(options: MarkerOptions) fun addPolyline(polylineOptions: PolylineOptions) + fun addPolygon(polygonOptions: PolygonOptions) } @HiltViewModel From 9047a0ccc5c2b8f5b432fd202bab3c758dd45c38 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 30 May 2025 17:52:42 -0600 Subject: [PATCH 19/21] feat: enhance AI Navigator prompts and guidelines This commit refines the user prompts and the guidelines for the AI Navigator's animation generation. Key changes: * **Updated `promptWithCamera`:** * Clarified instructions for using `currentCameraParams` for relative vs. absolute user requests. * Introduced a crucial guideline: add map objects (`addMarker`, `addPolyline`, `addPolygon`) *before* camera movements (`flyTo`, `flyAround`) that focus on them, unless the prompt dictates otherwise. * Emphasized focusing on Earth's surface and avoiding sky/weather/time-of-day specific requests. * Revised delay logic for tile loading. * Updated `addPolygon` description for brevity. * Refreshed examples to reflect the new order of operations. * **Revised `examplePrompts` list:** * Updated the list of example user prompts to be more diverse and to better showcase the capabilities of drawing markers, polylines, and polygons in conjunction with camera movements. * **Refined `promptGeneratorPrompt`:** * Updated the prompt for generating example user prompts to emphasize the use of `addMarker`, `addPolyline`, and `addPolygon` commands. * Reinforced guidance on geographic diversity and avoiding overly common landmarks. * Updated example output to reflect new capabilities. --- .../ainavigator/data/Prompts.kt | 215 +++++++++++------- 1 file changed, 138 insertions(+), 77 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt index f58d4ac..9c05ce8 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/ainavigator/data/Prompts.kt @@ -1,6 +1,5 @@ package com.example.advancedmaps3dsamples.ainavigator.data -// promptWithCamera (updated) val promptWithCamera = """ You are a specialized AI assistant expert in 3D map camera choreography. Your primary function is to take a user's natural language description of a desired 3D map camera tour or positioning and convert it into a precise `animationString`. The camera will be viewing Earth's surface and its features; **do not generate animations that focus on the sky, celestial events, weather phenomena (like storms or auroras), or imply specific times of day that would require different lighting (e.g., "sunset," "night lights") as these cannot be rendered.** @@ -47,34 +46,73 @@ val promptWithCamera = """ 7. **`addPolygon`**: Adds a filled polygon area to the map. * Format: `addPolygon=id=,outerPoints="",fillColor="<#AARRGGBB>",strokeColor="<#AARRGGBB>",strokeWidth=,altMode=` - * `id`: A unique string identifier for the polygon. - * `outerPoints`: A string containing latitude,longitude pairs for the polygon's outer boundary, separated by semicolons. The polygon will be closed automatically (do not repeat the first point at the end). **Minimum 3 points.** - * **IMPORTANT: Limit the number of points to a maximum of 100 for a single polygon.** - * `fillColor`: Hex color string for the polygon's fill (e.g., "#80FF0000" for semi-transparent red). Defaults to semi-transparent blue. - * `strokeColor`: Hex color string for the polygon's outline. Defaults to opaque blue. - * `strokeWidth`: Width of the polygon's outline in screen pixels (e.g., 3.0). Defaults to 3.0. - * `altMode`: Specifies how altitude of points is interpreted. `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. `clampToGround` is a good default. - * **Note: Inner holes are not supported by this command version.** + * `outerPoints`: String of "lat,lng" pairs (min 3) separated by semicolons. Max 100 points. + * `altMode`: `absolute`, `relativeToGround`, `relativeToMesh`, `clampToGround`. **How to Use Current Camera Parameters (if provided):** - * (Same as before) + * **Relative User Requests:** If the user's request seems relative to their current view (e.g., "show me nearby castles," "explore this area more," "what's over that hill from here?"), you **MUST** use the `currentCameraParams` as the starting point or primary reference for your animation. + * The `lat`, `lng`, and `alt` from `currentCameraParams.center` should inform the `lat`, `lng`, `alt` of your first `flyTo` or the `center` of your `flyAround`. + * You might adjust `hdg`, `tilt`, and `range` for the new relative target, but start your calculations or perspective from what `currentCameraParams` describe. + * **Absolute User Requests:** If the user makes an absolute request (e.g., "fly me to Tokyo," "show me the Pyramids of Giza"), the `currentCameraParams` are less critical for the *final destination*. + * You should prioritize reaching the user's specified absolute location. + * However, you *can* use `currentCameraParams` to make the *beginning* of the journey feel like a departure from the current view, making the transition smoother, before flying to the distant absolute target. + * **Do not let `currentCameraParams` override a clear, absolute request to go to a specific, distant location.** + * **If `currentCameraParams` are not provided, or if the user's request is unequivocally absolute and a contextual start offers no benefit, generate the animation string based solely on the user's textual request as before.** + * Your primary goal is to fulfill the user's request. Use `currentCameraParams` to enhance the experience when it makes sense for contextual or relative queries. **Important Constraints & Guidelines (Always Apply):** - * (Same as before) + * **Order of Operations (Crucial New Guideline):** + * **Unless the user's prompt explicitly dictates a different order (e.g., "fly to Paris, *then* add a marker at the Eiffel Tower"), you should generally aim to add map objects (`addMarker`, `addPolyline`, `addPolygon`) *before* initiating camera movements (`flyTo`, `flyAround`) that focus on or relate to those objects.** This allows the objects to be visible as the camera arrives or tours the area. + * For example, if asked to "show the Golden Gate Bridge and mark its towers," it's better to `addMarker` for the towers first, then `flyTo` a viewpoint of the bridge. + * If a user requests a tour with multiple stops, and each stop should be marked, add the marker for a stop *before* the `flyTo` command for that stop. + * If the user prompt implies a sequence like "first show X, then draw Y", follow that sequence. + * **Focus on Earth's Surface:** The generated animations should focus on terrestrial features, landmarks, and geography. **Avoid requests that primarily involve looking at the sky, atmospheric effects (auroras, storms), or specific times of day that imply lighting changes (e.g., "sunset," "city at night") as these cannot be accurately represented.** + * The `roll` parameter for the camera is **always 0**. + * Validate parameter ranges: lat (-90 to 90), lng (-180 to 180), hdg (0-360), tilt (0-90), **range (0 to 63,170,000)**. + * **Scale-Appropriate Range and Altitude of Camera Focus (`alt` parameter for `flyTo`/`flyAround`):** + * Adjust `range` and `alt` based on the target's scale (vast areas/cities: larger range/alt, e.g., 5km-50km range, focal `alt` well above ground; individual buildings: smaller range/alt, e.g., 100m-2km range, focal `alt` could be mid-height or top of building). + * **Crucially, ensure the `alt` for the camera's focal point is not too low.** + * **Animation Simplicity & Pacing:** + * For simple requests ("fly me to [location]"), ideally use a `flyTo` -> `message` -> `delay` (for tile loading & viewing) sequence. + * Only generate multi-step animations if a tour or multiple viewpoints are explicitly implied. + * **Tile Loading & Viewing Delay (Crucial):** + * **After a `flyTo` command moves the camera to a *new, distinct, and geographically distant location*, and *after* any associated `message` for that location, insert a `delay=dur=5000` command.** + * **Optionally, after this 5000ms tile loading delay, consider an *additional* short pacing `delay=dur=1000` or `delay=dur=2000`** for user absorption before the next major camera movement. + * Do *not* add the 5000ms tile loading delay for minor adjustments at the *same general location* or if the animation starts from `currentCameraParams` and explores a *very nearby* feature without significant travel. + * **Messages:** + * Use the `message` command *after* a `flyTo` to a new location, or *before* a `flyAround`. Messages should be short and descriptive, appearing *before* subsequent delays at that location. + * **Markers (`addMarker`):** + * Ensure `id` is unique for each marker in an animation sequence. + * **Polylines (`addPolyline`):** + * The `points` value must be a string of "lat,lng" pairs separated by semicolons. + * **Strictly limit the number of points per polyline to a maximum of 100 points.** + * Ensure `id` is unique for each polyline. + * `altMode=clampToGround` is generally best for drawing routes on the map surface. Parser assumes 0 altitude for points, relying on `altMode`. * **Polygons (`addPolygon`):** * The `outerPoints` value must be a string of "lat,lng" pairs (minimum 3) separated by semicolons. * **Strictly limit the number of points per polygon's outer boundary to a maximum of 100.** * Ensure `id` is unique. - * `altMode=clampToGround` is generally best for drawing areas on the map surface. - * The parser will assume 0 altitude for each point in the `outerPoints` string and rely on `altMode` for rendering. + * `altMode=clampToGround` is generally best. Parser assumes 0 altitude for points. + * Use realistic `dur` values for camera movements (e.g., 2000-10000ms). + * If the user asks for specific locations, try to find reasonable geographic coordinates and appropriate viewing altitudes for the focal point. **Your output MUST be a single string assigned to the variable `animationString`, like this:** `animationString="command1_params;command2_params;command3_params"` - **Examples (Note `alt` adjustments and longer/additional delays):** - User Request: "Fly to Central Park in NYC and draw a green rectangle representing its approximate area." + **Examples (Illustrating new order of operations where applicable):** + + User Request: "Show me the Eiffel Tower from above, add a marker, then draw a line from there to Arc de Triomphe." + (This prompt implies an order: show Eiffel, then marker, then line, then camera move to view line/Arc) + Expected Output: + `animationString="flyTo=lat=48.8584,lng=2.2945,alt=200,hdg=0,tilt=20,range=600,dur=3000;message=\"Eiffel Tower\";addMarker=id=eiffel_marker,lat=48.8584,lng=2.2945,alt=0,label=\"Eiffel Tower\",altMode=clampToGround;delay=dur=1000;addPolyline=id=eiffel_to_arc,points=\"48.8584,2.2945;48.8738,2.2950\",color=\"#FF0000FF\",width=5.0,altMode=clampToGround;delay=dur=1000;flyTo=lat=48.865,lng=2.295,alt=150,hdg=315,tilt=45,range=2000,dur=4000;delay=dur=4000"` + + User Request: "Mark the start and end of the Boston Marathon route, draw the route, then fly to the start." + Expected Output: + `animationString="addMarker=id=bm_start,lat=42.2464,-71.4606,alt=0,label=\"Start\",altMode=clampToGround;addMarker=id=bm_finish,lat=42.3663,-71.0572,alt=0,label=\"Finish\",altMode=clampToGround;addPolyline=id=boston_marathon,points=\"42.2464,-71.4606;42.2708,-71.3942;42.3248,-71.2653;42.3489,-71.1390;42.3525,-71.0839;42.3663,-71.0572\",color=\"#0000FF\",width=7.0,altMode=clampToGround;flyTo=lat=42.2464,lng=-71.4606,alt=150,hdg=45,tilt=50,range=3000,dur=5000;message=\"Boston Marathon Route\";delay=dur=5000;delay=dur=2000"` + + User Request: "Outline Central Park in NYC with a green polygon, then fly to an overview." Expected Output: - `animationString="flyTo=lat=40.7829,lng=-73.9654,alt=100,hdg=0,tilt=30,range=5000,dur=5000;addPolygon=id=central_park_area,outerPoints=\"40.7960,-73.9580;40.7639,-73.9720;40.7675,-73.9820;40.8000,-73.9670\",fillColor=\"#8000FF00\",strokeColor=\"#FF008000\",strokeWidth=2.0,altMode=clampToGround;message=\"Central Park Area\";delay=dur=5000"` + `animationString="addPolygon=id=central_park_area,outerPoints=\"40.7960,-73.9580;40.7639,-73.9720;40.7675,-73.9820;40.8000,-73.9670\",fillColor=\"#8000FF00\",strokeColor=\"#FF008000\",strokeWidth=2.0,altMode=clampToGround;flyTo=lat=40.7829,lng=-73.9654,alt=100,hdg=0,tilt=30,range=5000,dur=5000;message=\"Central Park Area\";delay=dur=5000"` Now, process the following user request and generate the `animationString`: """.trimIndent() @@ -167,46 +205,69 @@ val whatAmILookingAtPrompt = """ Based on the screenshot and camera parameters above, what is the user likely looking at? """.trimIndent() +//val examplePrompts = listOf( +// "Create a polyline representing the freedom trail in Boston. Add markers for each important location. There should be no fewer than 10 location. Add the markers and the polyline first and only then start a tour in order of the stops.", +// "Add a marker showing Crater Lake and fly to it. Then do a slow orbit around the lake.", +// "build a tour of a few of the UNESCO world heritage sites. stay high above each and make a slow orbit before moving on", +// "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", +// "Start with a wide shot of the Golden Gate Bridge, then fly underneath it from the ocean side towards San Francisco.", +// "Show me Machu Picchu. Start far away to see the mountains, then zoom in to the main citadel.", +// "Take me on a scenic helicopter tour over the Na Pali Coast in Kauai, Hawaii, highlighting the cliffs and valleys.", +// "I want to explore the canals of Venice, Italy. Start at St. Mark's Square, then glide along the Grand Canal towards the Rialto Bridge.", +// "Imagine I'm a bird soaring over the Swiss Alps. Show me majestic peaks like the Matterhorn and some alpine villages.", +// "Let's go on an adventure through the Amazon rainforest. Show me a winding river and the dense canopy from just above the trees.", +// "Give me a dramatic reveal of Mount Everest, starting from a low angle looking up.", +// "Show me the world's great deserts. Start with the Sahara, then give me a glimpse of the Gobi. Then the Sahara. Finally, Antarctica", +// "I feel like seeing some ancient wonders. Maybe the Pyramids of Giza, then a quick hop to Stonehenge.", +// "Take me on a journey from the Earth's surface up into space, looking back at the blue marble.", +// "Position the camera for a nice view of Niagara Falls.", +// "Let's see the Sydney Opera House.", +// "Go to the top of Mount Kilimanjaro.", +// "Fly to the Burj Khalifa in Dubai.", +// "I want to see the Christ the Redeemer statue in Rio de Janeiro, with the city and Sugarloaf Mountain in the background.", +// "Take me to the Great Wall of China, and fly along a section of it, showing its scale winding through the mountains.", +// "Let's look down into the caldera of Mount St. Helens.", +// "Compare the views from the top of the Shard in London and then the top of One World Trade Center in New York.", +// "Show me a beautiful beach in the Maldives, then quickly jump to a rugged coastline in Big Sur, California.", +// "I'd like a quick tour of three famous European capitals: Paris (Eiffel Tower), Rome (Colosseum), and Berlin (Brandenburg Gate).", +// "Find a serene, hidden waterfall deep in a lush jungle.", +// "Show me the power of a large volcano, perhaps Kilauea in Hawaii during an eruption (simulated view if needed).", +// "Take me on a journey through time, from an ancient Roman forum to a futuristic cityscape.", +// "Show me a place that feels incredibly remote and untouched by humans.", +// "Start at a global view of Earth, then rapidly zoom into Central Park in New York City.", +// "Give me an ant's-eye view of a famous monument, like the Lincoln Memorial.", +// "Fly low and fast over the Bonneville Salt Flats.", +// "Circle around Neuschwanstein Castle in Germany, showing it from all sides, especially with the mountains behind it.", +// "What does Times Square look like from directly above, say 500 meters up?", +// "Let's visit the Hollywood Sign, then pan to show the view over Los Angeles.", +// "Show me an isolated lighthouse on a rocky coast during a storm." +//) + val examplePrompts = listOf( - "Create a polyline representing the freedom trail in Boston. Add markers for each important location. There should be no fewer than 10 location. Add the markers and the polyline first and only then start a tour in order of the stops.", - "Add a marker showing Crater Lake and fly to it. Then do a slow orbit around the lake.", - "build a tour of a few of the UNESCO world heritage sites. stay high above each and make a slow orbit before moving on", - "Fly me to the Colosseum in Rome, and give me a slow 360-degree view from above.", - "Start with a wide shot of the Golden Gate Bridge, then fly underneath it from the ocean side towards San Francisco.", - "Show me Machu Picchu. Start far away to see the mountains, then zoom in to the main citadel.", - "Take me on a scenic helicopter tour over the Na Pali Coast in Kauai, Hawaii, highlighting the cliffs and valleys.", - "I want to explore the canals of Venice, Italy. Start at St. Mark's Square, then glide along the Grand Canal towards the Rialto Bridge.", - "Imagine I'm a bird soaring over the Swiss Alps. Show me majestic peaks like the Matterhorn and some alpine villages.", - "Let's go on an adventure through the Amazon rainforest. Show me a winding river and the dense canopy from just above the trees.", - "Give me a dramatic reveal of Mount Everest, starting from a low angle looking up.", - "Show me the world's great deserts. Start with the Sahara, then give me a glimpse of the Gobi. Then the Sahara. Finally, Antarctica", - "I feel like seeing some ancient wonders. Maybe the Pyramids of Giza, then a quick hop to Stonehenge.", - "Take me on a journey from the Earth's surface up into space, looking back at the blue marble.", - "Position the camera for a nice view of Niagara Falls.", - "Let's see the Sydney Opera House.", - "Go to the top of Mount Kilimanjaro.", - "Fly to the Burj Khalifa in Dubai.", - "I want to see the Christ the Redeemer statue in Rio de Janeiro, with the city and Sugarloaf Mountain in the background.", - "Take me to the Great Wall of China, and fly along a section of it, showing its scale winding through the mountains.", - "Let's look down into the caldera of Mount St. Helens.", - "Compare the views from the top of the Shard in London and then the top of One World Trade Center in New York.", - "Show me a beautiful beach in the Maldives, then quickly jump to a rugged coastline in Big Sur, California.", - "I'd like a quick tour of three famous European capitals: Paris (Eiffel Tower), Rome (Colosseum), and Berlin (Brandenburg Gate).", - "Find a serene, hidden waterfall deep in a lush jungle.", - "Show me the power of a large volcano, perhaps Kilauea in Hawaii during an eruption (simulated view if needed).", - "Take me on a journey through time, from an ancient Roman forum to a futuristic cityscape.", - "Show me a place that feels incredibly remote and untouched by humans.", - "Start at a global view of Earth, then rapidly zoom into Central Park in New York City.", - "Give me an ant's-eye view of a famous monument, like the Lincoln Memorial.", - "Fly low and fast over the Bonneville Salt Flats.", - "Circle around Neuschwanstein Castle in Germany, showing it from all sides, especially with the mountains behind it.", - "What does Times Square look like from directly above, say 500 meters up?", - "Let's visit the Hollywood Sign, then pan to show the view over Los Angeles.", - "Show me an isolated lighthouse on a rocky coast during a storm." + "Show me the Freedom Trail in Boston: draw the route as a red line, add markers for at least 5 key locations, then fly along the trail.", + "Fly to Crater Lake, add a marker at Wizard Island, then do a slow orbit around the lake showing its blue water.", + "Take me on a tour of 3 UNESCO World Heritage sites: first the Pyramids of Giza, then Machu Picchu, and finally the Taj Mahal. Show each from above with a marker. Add the markers before starting the tour.", + "Fly me to the Colosseum in Rome, mark it, and give me a slow 360-degree view from above.", + "Start with a wide shot of the Golden Gate Bridge, draw a blue line across it, then fly underneath it from the ocean side towards San Francisco.", + "Show me Mount Everest. Add a marker at the summit. Start from far away to see the Himalayas, then zoom in dramatically to the peak.", + "Give me a tour of the Grand Canyon: fly along the Colorado River for a bit, then mark and circle Bright Angel Trailhead.", + "Outline the Pentagon building with a semi-transparent blue polygon and give me an overhead view.", + "Fly to Times Square in New York, then draw a polygon around the main square area and label it 'Times Square'.", + "Show the approximate route of the Nile River through Egypt with a blue polyline, then fly from Aswan to Cairo along it.", + "Mark the location of the Mariana Trench, then fly to it, and look straight down.", + "Take me to the Great Wall of China, draw a section of it as a polyline, and fly along it showing its scale.", + "Highlight the Bermuda Triangle with a semi-transparent red polygon, then fly over its center.", + "Tour of European capitals: Paris (Eiffel Tower), Rome (Colosseum), Berlin (Brandenburg Gate). Mark each and fly between them.", + "Start at a global view, then rapidly zoom into Central Park in New York City and draw its boundary as a green polygon.", + "Fly low and fast over the Bonneville Salt Flats, then add a marker where land speed records are set.", + "Circle Neuschwanstein Castle in Germany, mark its location, and show it from all sides with the mountains behind it.", + "Show me an isolated lighthouse on a rocky coast, mark its position, and then fly around it.", + "Draw the border of Vatican City as a polygon, then fly to St. Peter's Square and add a marker.", + "Take me to the Amazon rainforest, draw a 10-point polyline representing a winding river, then fly just above the canopy along that line." ) val promptGeneratorPrompt = """ - You are an AI assistant specialized in crafting diverse and effective user prompts for a sophisticated 3D map camera animation system. Your task is to generate a list of example user prompts. These prompts will be shown to users of an application that takes their natural language input and, using another AI, converts it into a precise `animationString` for camera movements. + You are an AI assistant specialized in crafting diverse and effective user prompts for a sophisticated 3D map camera animation system. Your task is to generate a list of example user prompts. These prompts will be shown to users of an application that takes their natural language input and, using another AI, converts it into a precise `animationString` for camera movements which can include `flyTo`, `flyAround`, `delay`, `message`, `addMarker`, `addPolyline`, and `addPolygon` commands. The example prompts you generate MUST adhere to the following rules, which are the same rules the animation-generating AI follows: @@ -216,40 +277,40 @@ val promptGeneratorPrompt = """ * Prompts must request views of Earth's surface, specific landmarks, geographical features, or cities. * Prompts **MUST NOT** request views primarily of the sky, celestial events (like auroras), specific weather phenomena (like storms), or imply specific times of day that would require different environmental lighting (e.g., "sunset," "city at night with bright lights"). The system cannot render these. - 2. **Variety in Request Type:** Generate prompts that cover: - * Specific landmarks (both world-famous and lesser-known but visually interesting). - * Requests for "tours," "exploration," or "scenic views" of areas. - * More abstract or thematic requests that are still grounded in terrestrial views. - * Simple, direct requests for a view of a single location. - - 3. **Variety in Implied Animation Complexity:** - * Some prompts should be simple and likely result in a single camera movement. - * Others should naturally imply a multi-step sequence or tour. - - 4. **Geographic and Thematic Diversity (Crucial for this task):** - * **Aim for a WIDE VARIETY of unique locations.** Actively avoid repeating the most common landmarks (e.g., Eiffel Tower, Pyramids, Grand Canyon) unless specifically varying the *type* of request for that landmark. - * **Seek out lesser-known but visually distinct and interesting places.** Think beyond typical tourist hotspots. Consider unique geological formations, diverse ecosystems, historically significant but less famous sites, varied architectural styles, etc. - * Cover different continents, countries, biomes (deserts, tundras, rainforests, coral reefs if viewable from above, mountain ranges, plains), and types of human settlements (ancient ruins, modern metropolises, remote villages, industrial areas if visually interesting). - * If you've already generated a prompt for a major city, try a different type of location (e.g., a national park, a specific natural wonder, an archaeological site) for the next few prompts. + 2. **Feature Usage Variety:** Generate prompts that naturally lead to the use of: + * Simple camera movements (`flyTo`, `flyAround`, `delay`). + * Adding markers (`addMarker`) to pinpoint locations. + * Drawing routes or paths (`addPolyline`). Remember polyline points are `lat,lng` pairs separated by semicolons. + * Highlighting areas (`addPolygon`). Remember polygon outer points are `lat,lng` pairs separated by semicolons. + * Combinations of these features (e.g., fly to a place, mark it, then draw a route from it). + + 3. **Variety in Request Type & Complexity:** + * Specific landmarks (e.g., "Show me the Space Needle, mark it, and circle it."). + * Requests for "tours," "exploration," or "scenic views" that might involve multiple steps and drawing elements (e.g., "Tour of National Mall: draw a line from Lincoln Memorial to Washington Monument to Capitol, marking each."). + * Simple, direct requests (e.g., "Fly to Mount Fuji."). + * Requests that imply drawing shapes (e.g., "Outline the area of Hyde Park, London with a polygon."). + + 4. **Geographic and Thematic Diversity:** + * **Aim for a WIDE VARIETY of unique locations.** Actively avoid repeating the most common landmarks unless specifically varying the *type* of request. + * **Seek out lesser-known but visually distinct and interesting places.** + * Cover different continents, countries, biomes, and types of human settlements. + * Include natural wonders, historical sites, urban areas, and routes. 5. **Natural Language:** Prompts should sound like something a real user would type or say. - 6. **Implicit Scale Awareness:** Craft prompts that naturally suggest different scales of view. + 6. **Implicit Scale & Detail:** + * Craft prompts that naturally suggest different scales of view. + * For polylines and polygons, prompts can imply a level of detail (e.g., "a simplified route" vs. "the detailed path"). The animation AI will try to provide a reasonable number of points (max 100). - 7. **Implied Camera Actions (Optional but good for variety):** - * Suggest actions like "fly along," "circle around," "zoom in/out," "look up at," "start wide and then focus on," etc. - - 8. **No Technical Camera Jargon:** Prompts should be purely natural language. - - 9. Consider chaining together interesting related places. Or multiple stops along a more famous route. But don't do this for every prompt. + 7. **No Technical Camera Jargon:** Prompts should be purely natural language. Avoid terms like "latitude," "longitude," "altitude mode," etc. **Output Format (Strict):** **Your output MUST be plain text. Each example user prompt MUST be on its own separate line. There should be NO additional formatting, NO numbering, NO bullet points, NO introductory or concluding text, and NO code block markers (like ```). Just the prompts, one per line.** **Example of *Correct Output Format* if asked for 3 prompts:** - Explore the salt flats of Salar de Uyuni in Bolivia after a rain. - Fly low over the fjords of Western Norway, starting near Geiranger. - Identify important stops along the historic Oregon Trail and then fly along them at slower speeds to impart how far it really is. + Outline the island of Manhattan with a blue polygon, then fly over it. + Show me the route of the Orient Express from Paris to Istanbul with a polyline and mark both cities. + Fly to the summit of Denali and place a marker there. Now, please generate 20 example user prompts based on these guidelines. """.trimIndent() From 77569302a3598f2489685d193469821164478a1e Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:58:54 -0600 Subject: [PATCH 20/21] feat: Add Boston scenario to advanced maps sample This commit introduces a new scenario titled "Lost in Boston" to the advanced maps sample application. --- .../scenarios/ScenarioData.kt | 36 ++++++++++++++++--- .../app/src/main/res/values/strings.xml | 1 + 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioData.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioData.kt index 6842e9e..acf6353 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioData.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenarioData.kt @@ -126,12 +126,21 @@ val hawaiiRoute = """ 21.26286, -157.80343 21.26289, -157.80359 21.26401, -157.80579 -""".trimIndent().split("\n") - .map { +""".trimIndent().coordinatesToEncodedPolyline() + +val bostonPolyline = """ + 42.35628586524052, -71.06225781918089 + 42.35583081188131, -71.06130009037366 + 42.355455527402505, -71.06161679863297 + 42.355449621886734, -71.06161403790463 +""".trimIndent().coordinatesToEncodedPolyline() + +private fun String.coordinatesToEncodedPolyline(): String { + return this.split("\n").map { val (lat, lng) = it.split(",").map(String::toDouble) LatLng(lat, lng) - } - .latLngListEncode() + }.latLngListEncode() +} // Encoded polyline // qj`aCj}nb]BDlAuAp@gAlEwIpAaDp@gBlAw@p@YtBq@vB}@v@U~Ao@dAk@dF_ErAcAbAm@~EmDxAqAhAyAx@wAdAiCdCqH~CuIt@mCrAiDnEwMfNea@TkAFu@BgA@_BpF{B|LqFrH_EpDmBxAu@bG{ChHwCpG}CrCuB~P}RhAmAnCeCbAcANIlTcJ~@YNMj@W|B{Bn@s@rAwBj@{Kt@_RH_F@qHM{AyA_Hi@wN?aBB_BFe@PiDLoD@cJQoE?g@Ju@DSRi@Zc@v@s@l@w@jAgCz@mAz@aAl@}@fAr@H?NI`@]LIb@OhAKx@ClCY|@KNCb@[d@E`@@XLFFJRl@~BhCfKjAtE@d@E^_FvL @@ -370,6 +379,23 @@ val scenarios = "flyTo=lat=39.7498,lng=-104.9535,alt=2000,hdg=200,tilt=60,range=1500,dur=2500;" + "delay=dur=1000", polygon = denverPolygon, - ) + ), + createScenario( + name = "boston-lost-man", + titleId = R.string.scenarios_lost_in_boston, + initialState = "mode=satellite;camera=lat=42.35628586524052,lng=-71.06225781918089,alt=10,tilt=10,hdg=121,range=1000", + animationString = "delay=dur=5000;" + + "flyTo=lat=42.35628586524052,lng=-71.06225781918089,alt=15,tilt=67,hdg=121,range=135,dur=2000;"+ + "flyTo=lat=42.355819,lng=-71.061274,alt=10,tilt=45,hdg=121,range=119,dur=2000;"+ + "flyTo=lat=42.355858,lng=-71.061279,alt=10,tilt=0,hdg=211,range=64,dur=2000;"+ + "flyTo=lat=42.355455,lng=-71.061608,alt=12,tilt=35,hdg=211,range=56,dur=2000;"+ + "delay=dur=2000;" + + "flyTo=lat=42.355475,lng=-71.062052,alt=53.5,tilt=5,hdg=216,range=524,dur=2000;"+ + "", + polylines = bostonPolyline, + markers = + "lat=42.35628586524052,lng=-71.06225781918089,alt=10,altMode=clamp_to_ground;" + + "lat=42.355449621886734,lng=-71.06161403790463,alt=10,altMode=clamp_to_ground;" + ), ).associateBy { it.name } \ No newline at end of file diff --git a/Maps3DSamples/advanced/app/src/main/res/values/strings.xml b/Maps3DSamples/advanced/app/src/main/res/values/strings.xml index fa8b66b..95cb89e 100644 --- a/Maps3DSamples/advanced/app/src/main/res/values/strings.xml +++ b/Maps3DSamples/advanced/app/src/main/res/values/strings.xml @@ -59,6 +59,7 @@ Polyline in Chicago Polyline in Honolulu Polygon in Denver + Lost in Boston Heading: %1$.0f Tilt: %1$.0f From 434449831b46981772d8eb556bbd5ef946cfa004 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:08:55 -0600 Subject: [PATCH 21/21] feat: enhance ScenariosActivity with immersive mode and compass This commit introduces several enhancements to the `ScenariosActivity`: - **Immersive Mode:** The activity now hides system UI bars (status and navigation) for a more immersive experience. The screen is also kept on. - **Whiskey Compass:** A compass UI element (`WhiskeyCompass`) is added to the map view. - The compass's alpha (transparency) fades in when the camera heading changes and fades out after a period of inactivity. - **Conditional Top App Bar:** The top app bar is now only displayed when no specific scenario is active (i.e., on the scenario selection screen). - Minor UI adjustments for the close button. --- .../scenarios/ScenariosActivity.kt | 95 ++++++++++++++++--- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosActivity.kt b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosActivity.kt index 85c0dbd..d14c627 100644 --- a/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosActivity.kt +++ b/Maps3DSamples/advanced/app/src/main/java/com/example/advancedmaps3dsamples/scenarios/ScenariosActivity.kt @@ -16,11 +16,16 @@ package com.example.advancedmaps3dsamples.scenarios import android.content.Intent import android.os.Bundle +import android.view.WindowManager import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -32,6 +37,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons @@ -46,21 +52,30 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults.topAppBarColors import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.rememberGraphicsLayer import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.advancedmaps3dsamples.R +import com.example.advancedmaps3dsamples.ainavigator.WhiskeyCompass import com.example.advancedmaps3dsamples.ui.theme.AdvancedMaps3DSamplesTheme import com.example.advancedmaps3dsamples.utils.DEFAULT_ROLL import com.example.advancedmaps3dsamples.utils.toHeading @@ -69,7 +84,10 @@ import com.example.advancedmaps3dsamples.utils.toRoll import com.example.advancedmaps3dsamples.utils.toTilt import com.google.android.gms.maps3d.model.Camera import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlin.time.Duration.Companion.seconds @AndroidEntryPoint @OptIn(ExperimentalMaterial3Api::class) @@ -85,7 +103,14 @@ class ScenariosActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.light(android.graphics.Color.TRANSPARENT, android.graphics.Color.TRANSPARENT), + navigationBarStyle = SystemBarStyle.light(android.graphics.Color.TRANSPARENT, android.graphics.Color.TRANSPARENT) + ) + + hideSystemUI() + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + setContent { val viewState by viewModel.viewState.collectAsStateWithLifecycle() val currentCamera by viewModel.currentCamera.collectAsStateWithLifecycle(Camera.DEFAULT_CAMERA) @@ -95,21 +120,47 @@ class ScenariosActivity : ComponentActivity() { val cameraAttribute by viewModel.trackedAttribute.collectAsStateWithLifecycle() + val camera by viewModel.currentCamera.collectAsStateWithLifecycle() + val compassAlpha = remember { Animatable(0.55f) } + + // This LaunchedEffect controls the compass alpha based on camera heading changes. + LaunchedEffect(camera.heading) { + // When camera.heading changes, this LaunchedEffect is cancelled and restarted. + // Any coroutine launched within its scope (like the one below) is also cancelled. + + // Reset alpha to initial state and stop any ongoing animation on compassAlpha. + compassAlpha.snapTo(0.55f) + + // Launch a new coroutine within this LaunchedEffect's scope. + // This coroutine will handle the delay and subsequent fade-out animation. + // If camera.heading changes again before this completes, this coroutine will be cancelled. + launch { + delay(2.seconds) // Wait for 2 seconds of stable heading + // If this point is reached, it means camera.heading was stable for 2 seconds. + compassAlpha.animateTo( + targetValue = 0.3f, + animationSpec = tween(durationMillis = 500, easing = LinearEasing) + ) + } + } + AdvancedMaps3DSamplesTheme( dynamicColor = false ) { Scaffold( modifier = Modifier.fillMaxSize(), topBar = { - CenterAlignedTopAppBar( - colors = topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.primary, - ), - title = { - Text(stringResource(viewState.scenario?.titleId ?: R.string.scenarios_none)) - } - ) + if (viewState.scenario == null) { + CenterAlignedTopAppBar( + colors = topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.primary, + ), + title = { + Text(stringResource(viewState.scenario?.titleId ?: R.string.scenarios_none)) + } + ) + } } ) { innerPadding -> val modifier = Modifier.padding(innerPadding) @@ -135,6 +186,17 @@ class ScenariosActivity : ComponentActivity() { onMap3dViewReady = { viewModel.setGoogleMap3D(it) }, onReleaseMap = { viewModel.releaseGoogleMap3D() }, ) + + WhiskeyCompass( + heading = camera.heading?.toFloat() ?: 0f, + modifier = Modifier + .fillMaxWidth() + .alpha(compassAlpha.value) + .safeDrawingPadding(), + stripHeight = 90.dp, + pixelsPerDegree = 7f, + degreeLabelInterval = 30, + ) } if (viewState.countDownVisible) { @@ -165,6 +227,14 @@ class ScenariosActivity : ComponentActivity() { } } } + + private fun hideSystemUI() { + WindowCompat.setDecorFitsSystemWindows(window, false) + WindowInsetsControllerCompat(window, window.decorView).let { controller -> + controller.hide(WindowInsetsCompat.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + } } /** @@ -256,7 +326,10 @@ fun FinishedOverlay( ) { // Close Button at the top end corner OverlayButton( - modifier = modifier.align(Alignment.TopEnd).offset(x = (-16).dp, y = 16.dp).size(size), + modifier = modifier + .align(Alignment.TopEnd) + .offset(x = (-16).dp, y = 16.dp) + .size(size), onExitClick = onCloseClick, imageVector = Icons.Default.Close, contentDescription = stringResource(R.string.close)