[](https://maven-badges.herokuapp.com/maven-central/com.google.maps.android/android-maps-utils)


[](https://github.com/googlemaps/android-maps-utils/actions/workflows/test.yml)

[][license]
[](https://stackoverflow.com/questions/tagged/google-maps)
[][Discord server]
# Maps SDK for Android Utility Library
## Description
This open-source library contains utilities that are useful for a wide
range of applications using the [Google Maps SDK for Android][maps-sdk].
As of version 5.0.0, the library has been rewritten in Kotlin and split into
focused submodules so you can depend only on what you need. If you are
upgrading from 4.x, see the [migration guide](MIGRATION.md).
- **Marker animation** - animates a marker from one position to another
- **Marker clustering** — handles the display of a large number of points
- **Marker icons** — display text on your Markers
- **Heatmaps** — display a large number of points as a heat map
- **Import KML** — displays KML data on the map
- **Import GeoJSON** — displays and styles GeoJSON data on the map
- **Polyline encoding and decoding** — compact encoding for paths,
interoperability with Maps API web services
- **Spherical geometry** — for example: computeDistance, computeHeading,
computeArea
- **Street View metadata** — checks if a Street View panorama exists at a given location
- **Reactive Kotlin Extensions & Builders** — coroutine suspensions (`awaitMap()`), reactive `Flow` observers (`mapClickEvents()`), and option builder DSLs (`addMarker { ... }`) consolidated directly into `com.google.maps.android.*`.
> [!IMPORTANT]
> **KTX Consolidation Notice (`v6.0.0+`)**: All Kotlin extensions (`maps-ktx` and `maps-utils-ktx` from `android-maps-ktx`) are now built directly into `android-maps-utils` under the canonical `com.google.maps.android.*` packages. Separate dependencies on `android-maps-ktx` or `maps-utils-ktx` are no longer needed and should be removed. Legacy calls to `com.google.maps.android.ktx.*` packages remain supported via `@Deprecated(level = DeprecationLevel.WARNING)` bridges that forward directly to canonical implementations.
<p align="center"><img width="90%" vspace="20" src="https://cloud.githubusercontent.com/assets/1950036/6629704/f57bc6d8-c908-11e4-815a-0d909fe02f99.gif"></p>
## Requirements
* Android API level 24+
* [Sign up with Google Maps Platform]
* A Google Maps Platform [project] with the **Maps SDK for Android** enabled
- An [API key] associated with the project above ... follow the [API key instructions] if you're new to the process
## Installation
```kotlin
dependencies {
// Utilities and consolidated Kotlin Extensions for Maps SDK for Android
// The aggregator artifact transitively pulls in all submodules below.
implementation("com.google.maps.android:android-maps-utils:6.1.0") // x-release-please-version
}
```
Alternatively, depend only on the submodules your app needs:
```kotlin
dependencies {
// Base utilities: PolyUtil, SphericalUtil, collection managers, Street View metadata
implementation("com.google.maps.android:android-maps-utils-core:6.1.0") // x-release-please-version
// Marker clustering
implementation("com.google.maps.android:android-maps-utils-clustering:6.1.0") // x-release-please-version
// KML and GeoJSON import
implementation("com.google.maps.android:android-maps-utils-data:6.1.0") // x-release-please-version
// Heatmaps
implementation("com.google.maps.android:android-maps-utils-heatmaps:6.1.0") // x-release-please-version
// Marker icons and animation
implementation("com.google.maps.android:android-maps-utils-ui:6.1.0") // x-release-please-version
}
```
### Compatibility with Google Navigation SDK for Android
If your project integrates the [Google Navigation SDK for Android](https://developers.google.com/maps/documentation/navigation/android-sdk), you must exclude the transitive `play-services-maps` dependency to prevent duplicate class conflicts:
```kotlin
configurations.all {
if (name.contains("navigation", ignoreCase = true)) {
exclude(group = "com.google.android.gms", module = "play-services-maps")
}
}
```
## Sample App
<img src="https://developers.google.com/maps/documentation/android-sdk/images/utility-markercluster.png" width="150" align=right>
This repository includes a [sample app](demo) that illustrates the use of this library.
To run the demo app, ensure you've met the requirements above then:
1. Clone the repository
1. Add a file `secrets.properties` in the root project (this file should *NOT* be under version control to protect your API key)
1. Add the following keys to `secrets.properties`:
- `MAPS_API_KEY`: **Required**. Your Google Maps API Key. Ensure it has the **Maps SDK for Android** enabled.
- `PLACES_API_KEY`: **Optional**. Required for demos using the Places API (e.g., Heatmaps with Places). Ensure the **Places API** is enabled.
- `MAP_ID`: **Optional**. Required for demos using Advanced Markers or Cloud-based Map Styling.
For example:
```properties
MAPS_API_KEY=YOUR_MAPS_API_KEY
PLACES_API_KEY=YOUR_PLACES_API_KEY
MAP_ID=YOUR_MAP_ID
```
1. Build and run the `debug` variant for the Maps SDK for Android version
### Setting up the Map ID
Some of the features in the demo app, such as Advanced Markers, require a Map ID. You can learn more about map IDs in the [official documentation](https://developers.google.com/maps/documentation/android-sdk/map-ids/mapid-over). You can set the Map ID in one of the following ways:
1. **`secrets.properties`:** Add a line to your `secrets.properties` file with your Map ID:
```properties
MAP_ID=YOUR_MAP_ID
```
2. **`strings.xml`:** Alternatively, you can set the Map ID in the `demo/src/main/res/values/strings.xml` file:
```xml
<string name="map_id">YOUR_MAP_ID</string>
```
3. **XML Layout Files:** You can also hardcode the Map ID directly in the XML layout files where a map is defined, by setting the `map:mapId` attribute:
```xml
<androidx.fragment.app.FragmentContainerView
map:mapId="YOUR_MAP_ID"
/>
```
## Documentation
See the [documentation] for a full list of classes and their methods.
Full guides for using the utilities are published in
[Google Maps Platform documentation](https://developers.google.com/maps/documentation/android-sdk/utility).
## Usage
<details>
<summary>Marker utilities</summary>
### Marker utilities
- Marker animation [source](https://github.com/googlemaps/android-maps-utils/blob/main/ui/src/main/java/com/google/maps/android/ui/AnimationUtil.kt), [sample code](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/AnimationUtilDemoActivity.java)
- Marker clustering [source](https://github.com/googlemaps/android-maps-utils/tree/main/clustering/src/main/java/com/google/maps/android/clustering), [guide](https://developers.google.com/maps/documentation/android-sdk/utility/marker-clustering)
- Advanced Markers clustering [source](https://github.com/googlemaps/android-maps-utils/tree/main/clustering/src/main/java/com/google/maps/android/clustering), [sample code](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/CustomAdvancedMarkerClusteringDemoActivity.java)
- SuperCluster mega-scale clustering (100k+ markers) [source](https://github.com/googlemaps/android-maps-utils/blob/main/clustering/src/main/java/com/google/maps/android/clustering/algo/SuperClusterAlgorithm.kt), [sample code](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/SuperCluster100kDemoActivity.kt)
- Marker icons [source](https://github.com/googlemaps/android-maps-utils/blob/main/ui/src/main/java/com/google/maps/android/ui/IconGenerator.kt), [sample code](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/IconGeneratorDemoActivity.java)
</details>
<details>
<summary>Data visualization utilities</summary>
### Data visualization utilities
- Display heat maps [source](https://github.com/googlemaps/android-maps-utils/tree/main/heatmaps/src/main/java/com/google/maps/android/heatmaps), [guide](https://developers.google.com/maps/documentation/android-sdk/utility/heatmap)
- Import GeoJSON [source](https://github.com/googlemaps/android-maps-utils/tree/main/data/src/main/java/com/google/maps/android/data/geojson), [guide](https://developers.google.com/maps/documentation/android-sdk/utility/geojson)
- Import KML [source](https://github.com/googlemaps/android-maps-utils/tree/main/data/src/main/java/com/google/maps/android/data/kml), [guide](https://developers.google.com/maps/documentation/android-sdk/utility/kml)
</details>
<details>
<summary>Polyline and spherical geometry utilities</summary>
### Additional utilities
- Polyline encoding and decoding [source](https://github.com/googlemaps/android-maps-utils/blob/main/library/src/main/java/com/google/maps/android/PolyUtil.kt), [encoding sample](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/PolySimplifyDemoActivity.java), [decoding sample](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/PolyDecodeDemoActivity.java)
- Spherical geometry [source](https://github.com/googlemaps/android-maps-utils/blob/main/library/src/main/java/com/google/maps/android/SphericalUtil.kt), [compute distance sample](https://github.com/googlemaps/android-maps-utils/blob/main/demo/src/main/java/com/google/maps/android/utils/demo/DistanceDemoActivity.java)
</details>
<details>
<summary>Reactive Kotlin Extensions & Builders (Consolidated in v6.0.0)</summary>
### Reactive Kotlin Extensions & Builders
All Kotlin extensions formerly provided by `android-maps-ktx` (`maps-ktx` and `maps-utils-ktx`) are now integrated into `android-maps-utils` (`v6.0.0+`) under canonical packages (`com.google.maps.android.*`, `com.google.maps.android.clustering.*`, etc.).
#### 1. Coroutine Suspensions (`awaitMapsSdkInitialized()`, `awaitMap()`, `awaitAnimateCamera()`)
```kotlin
import com.google.android.gms.maps.MapsInitializer
import com.google.maps.android.awaitMapsSdkInitialized
import com.google.maps.android.awaitMap
import com.google.maps.android.awaitAnimateCamera
// Suspend until Maps SDK is initialized
val renderer: MapsInitializer.Renderer = context.awaitMapsSdkInitialized(MapsInitializer.Renderer.LATEST)
// Suspend until GoogleMap is ready on MapView / MapFragment
val googleMap: GoogleMap = mapView.awaitMap()
// Suspend until camera animation completes
googleMap.awaitAnimateCamera(CameraUpdateFactory.newLatLngZoom(sydney, 12f))
```
#### 2. Option Builders DSL (`addMarker`, `addPolyline`, `addPolygon`)
```kotlin
import com.google.maps.android.addMarker
import com.google.maps.android.addCircle
googleMap.addMarker {
position(LatLng(-33.852, 151.211))
title("Sydney Opera House")
}
googleMap.addCircle {
center(LatLng(-33.870, 151.200))
radius(500.0)
strokeWidth(2f)
}
```
#### 3. Reactive `Flow` Observers (`mapClickEvents`, `cameraMoveEvents`)
```kotlin
import com.google.maps.android.mapClickEvents
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
googleMap.mapClickEvents().collect { latLng ->
Log.d("MapClick", "Clicked: $latLng")
}
}
}
```
#### Backward Compatibility & Deprecation
Existing references to `com.google.maps.android.ktx.*` continue to work through `@Deprecated(level = DeprecationLevel.WARNING)` forwarding wrappers. You can safely migrate your code incrementally to `com.google.maps.android.*`.
</details>
<details>
<summary>Street View metadata utility</summary>
### Street View metadata utility
The StreetViewUtil class provides functionality to check whether a location is supported in StreetView. You can avoid errors when [adding a Street View panorama](https://developers.google.com/maps/documentation/android-sdk/streetview) to an Android app by calling this metadata utility and only adding a Street View panorama if the response is `OK`.
```kotlin
StreetViewUtils.fetchStreetViewData(LatLng(8.1425918, 11.5386121), BuildConfig.MAPS_API_KEY,Source.DEFAULT)
```
`fetchStreetViewData` will return `NOT_FOUND`, `OK`, `ZERO_RESULTS` or `REQUEST_DENIED`, depending on the response.
By default, the `Source` is set to `Source.DEFAULT`, but you can also specify `Source.OUTDOOR` to request outdoor Street View panoramas.
</details>
## Clustering Performance Benchmarks (SuperCluster vs. Traditional)
For large datasets (10,000 to 100,000+ points), `SuperClusterAlgorithm` replaces on-the-fly quadtree traversal with a static hierarchical zoom pyramid backed by primitive flat arrays (`FlatKdTree`). During camera panning and zooming, viewport queries execute in sub-milliseconds without triggering garbage collection pauses.
### Empirical Benchmarks across 10k, 50k, and 100k Points
| Algorithm | 10k Points (Query) | 50k Points (Query) | 100k Points (Query) | 100k Build Time | Scaling Verdict |
| :--- | :---: | :---: | :---: | :---: | :--- |
| **`SuperClusterAlgorithm` (Viewport)** | **1.07 ms** | **0.23 ms** | **0.22 ms** | **786 ms** | **~2,800x faster**. Sub-millisecond camera moves. |
| **`SuperClusterAlgorithm` (Unbounded)** | **5.88 ms** | **10.28 ms** | **11.86 ms** | **807 ms** | **~52x faster** querying the entire globe simultaneously. |
| `NonHierarchicalViewBasedAlgorithm` | 4.22 ms | 8.68 ms | 17.35 ms | 0 ms | Performs well at high zoom; slower at low zoom. |
| `NonHierarchicalDistanceBasedAlgorithm` (Default) | 55.70 ms | 336.68 ms | 617.17 ms | 0 ms | Severe camera panning stutter (~150 ms/frame). |
| `GridBasedAlgorithm` | 54.55 ms | 598.14 ms | 2,047.27 ms | 0 ms | Unusable at scale (2+ second freeze). |
```kotlin
// Usage with ClusterManager:
val clusterManager = ClusterManager<MyItem>(context, map)
val algorithm = SuperClusterAlgorithm<MyItem>(
minZoom = 0,
maxZoom = 16,
radius = 64.0,
extent = 512.0,
viewWidth = screenWidthDp,
viewHeight = screenHeightDp,
)
clusterManager.setAlgorithm(algorithm)
clusterManager.addItems(largeDataset) // 100,000+ items
clusterManager.cluster()
```
## Internal usage attribution ID
This library calls the `addInternalUsageAttributionId` method, which helps Google understand which libraries and samples are helpful to developers and is optional. Instructions for opting out of the identifier are provided below.
If you wish to disable this, you can do so by removing the initializer in your `AndroidManifest.xml` using the `tools:node="remove"` attribute:
```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.google.maps.android.utils.attribution.AttributionIdInitializer"
tools:node="remove" />
</provider>
```
## Contributing
Contributions are welcome and encouraged! If you'd like to contribute, send us a [pull request] and refer to our [code of conduct] and [contributing guide].
### Using AI
This repository provides an official Gemini Skill and an `llm-integration-prompt.md` to help AI agents navigate the codebase and provide assistance. Refer to the [contributing guide] for more details on AI usage.
## Terms of Service
This library uses Google Maps Platform services. Use of Google Maps Platform services through this library is subject to the Google Maps Platform [Terms of Service].
If your billing address is in the European Economic Area, effective on 8 July 2025, the [Google Maps Platform EEA Terms of Service](https://cloud.google.com/terms/maps-platform/eea) will apply to your use of the Services. Functionality varies by region. [Learn more](https://developers.google.com/maps/comms/eea/faq).
This library is not a Google Maps Platform Core Service. Therefore, the Google Maps Platform Terms of Service (e.g. Technical Support Services, Service Level Agreements, and Deprecation Policy) do not apply to the code in this library.
## Support
This library is offered via an open source [license]. It is not governed by the Google Maps Platform Support [Technical Support Services Guidelines], the [SLA], or the [Deprecation Policy]. However, any Google Maps Platform services used by the library remain subject to the Google Maps Platform Terms of Service.
This library adheres to [semantic versioning] to indicate when backwards-incompatible changes are introduced. Accordingly, while the library is in version 0.x, backwards-incompatible changes may be introduced at any time.
If you find a bug, or have a feature request, please [file an issue] on GitHub. If you would like to get answers to technical questions from other Google Maps Platform developers, ask through one of our [developer community channels]. If you'd like to contribute, please check the [contributing guide].
You can also discuss this library on our [Discord server].
[API key]: https://developers.google.com/maps/documentation/android-sdk/get-api-key
[API key instructions]: https://developers.google.com/maps/documentation/android-sdk/config#step_3_add_your_api_key_to_the_project
[maps-sdk]: https://developers.google.com/maps/documentation/android-sdk
[documentation]: https://googlemaps.github.io/android-maps-utils
[android-maps-ktx]: https://github.com/googlemaps/android-maps-ktx
[code of conduct]: ?tab=coc-ov-file#readme
[contributing guide]: CONTRIBUTING.md
[Deprecation Policy]: https://cloud.google.com/maps-platform/terms
[developer community channels]: https://developers.google.com/maps/developer-community
[Discord server]: https://discord.gg/hYsWbmk
[file an issue]: https://github.com/googlemaps/android-maps-utils/issues/new/choose
[license]: LICENSE
[project]: https://developers.google.com/maps/documentation/android-sdk/cloud-setup
[pull request]: https://github.com/googlemaps/android-maps-utils/compare
[semantic versioning]: https://semver.org
[Sign up with Google Maps Platform]: https://console.cloud.google.com/google/maps-apis/start
[similar inquiry]: https://github.com/googlemaps/android-maps-utils/issues
[SLA]: https://cloud.google.com/maps-platform/terms/sla
[Technical Support Services Guidelines]: https://cloud.google.com/maps-platform/terms/tssg
[Terms of Service]: https://cloud.google.com/maps-platform/terms