You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .gemini/skills/android-maps3d-sdk/SKILL.md
+77Lines changed: 77 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -147,6 +147,83 @@ suspend fun awaitCameraAnimation(map: GoogleMap3D) = suspendCancellableCoroutine
147
147
```
148
148
149
149
***Lifecycle:** You must pass lifecycle events down to `Map3DView`. In Compose, `factory` block takes care of instantiation and `onRelease` handles cleanup (`onDestroy()`). Ensure `onCreate` is called in the factory block.
150
+
**Critical Note:* The underlying `GoogleMap3D` engine instance is effectively created once per application lifecycle. If your `AndroidView` Composable leaves the composition and later returns (creating a new `Map3DView`), the underlying 3D engine may still retain previously added objects (like Polygons) from the destroyed view. You must manually clear or track your objects to avoid duplicates across recompositions or Navigation transitions.
151
+
***Initialization & Adding Objects:** Do **not** attempt to set the camera or add 3D objects (like Polygons) immediately after the `GoogleMap3D` reference is ready. The renderer needs time to warm up.
152
+
***Initial Camera:** Always set the initial camera position declaratively via `Map3DOptions` (passed into your container view) rather than imperatively moving the camera after the map loads. This avoids dizzying "flight" animations from coordinate `(0,0)` on startup.
153
+
***Adding Objects:** Only inject geometries into the scene after the map has signaled it is fully ready and stable. Typically, this means waiting for an `onMapSteady` callback.
154
+
***Updating Map Objects:** When updating an existing Map Object (e.g., `Polygon`, `Polyline`), do **not** use `remove()` and re-add a new one, as this causes flickering. Instead, use `getId()` from the existing object and pass it to a new `PolygonOptions` (or equivalent) builder, then call `addPolygon()` with those new options on the same `GoogleMap3D` instance. The SDK uses the matching ID to update the existing object gracefully without flickering.
155
+
***Nullable Camera Properties:** The 3D SDK's `Camera` object has 6 degrees of freedom. Properties like `heading`, `tilt`, `roll`, and `range` are returned as `Double?` (nullable) since the renderer does not always guarantee a value for every property. Handle these nulls defensively when extracting camera telemetry, especially when persisting position data.
156
+
***Parameter Validation:** The Maps 3D library will throw exceptions and crash if passed out-of-bounds telemetry for camera movements or locations. Standardize a validation/coercion layer (e.g., returning a `toValidCamera()` extension object) covering:
157
+
*`latitude`: clamped to `[-90.0, 90.0]`
158
+
*`longitude`: clamped to `[-180.0, 180.0]`
159
+
*`tilt`: clamped to `[0.0, 90.0]`
160
+
*`range`: clamped to `[0.0, 63170000.0]`
161
+
*`heading`: wrapped to `[0.0, 360.0]`
162
+
*`roll`: wrapped to `[-360.0, 360.0]`
163
+
*`altitude`: clamped to `[0.0, MAX_ALTITUDE_METERS]`
164
+
165
+
**Example Extension:**
166
+
```kotlin
167
+
/** Helper to wrap cyclic values like heading and roll */
168
+
fun Double.wrapIn(lower:Double, upper:Double): Double {
roll = source.roll?.toDouble()?.wrapIn(-360.0, 360.0) ?:0.0
187
+
range = source.range?.toDouble()?.coerceIn(0.0..63170000.0) ?:1500.0
188
+
}
189
+
}
190
+
```
191
+
192
+
***ImmutableUpdates (`copy` Extensions):**The 3D SDK builders (like `camera {}` or `latLngAltitude {}`) donot natively provide a `copy()` method like Kotlin data classes. To gracefully update a single property (like altitude) while retaining the rest of the object's complex state, implement custom `.copy()` extensions:
193
+
194
+
```kotlin
195
+
/** Extension to clone and modify a Camera */
196
+
fun Camera.copy(
197
+
center: LatLngAltitude? = null,
198
+
heading: Double? = null,
199
+
tilt: Double? = null,
200
+
range: Double? = null,
201
+
roll: Double? = null,
202
+
): Camera {
203
+
val objectToCopy = this
204
+
return camera {
205
+
this.center = center ?: objectToCopy.center
206
+
this.heading = heading ?: objectToCopy.heading
207
+
this.tilt = tilt ?: objectToCopy.tilt
208
+
this.range = range ?: objectToCopy.range
209
+
this.roll = roll ?: objectToCopy.roll
210
+
}
211
+
}
212
+
213
+
/** Extension to clone and modify a LatLngAltitude */
0 commit comments