Skip to content

feat: experimental Kotlin Multiplatform support (clustering, library, heatmaps) - #1774

Draft
kikoso wants to merge 9 commits into
mainfrom
feat/experimental-kmp-clustering
Draft

kikoso wants to merge 9 commits into
mainfrom
feat/experimental-kmp-clustering

Conversation

@kikoso

@kikoso kikoso commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Experimental Kotlin Multiplatform support for android-maps-utils, so the platform-independent utilities can be used from common code and from the KMP module of android-maps-compose (googlemaps/android-maps-compose#927). Now up to date with 6.0.0.

  • New maps-model module: common LatLng / CameraPosition. On Android they are actual typealiases to the Play Services classes, so the Android API is unchanged. On iOS they are value holders replicating the GMS clamping, wrapping and equality semantics.
  • clustering: the algorithm layer (quadtree, geometry, Mercator projection, all algorithms) is in commonMain and compiles for Android, iosArm64, iosSimulatorArm64 and iosX64. ClusterManager, the renderers and the ktx flows stay Android-only.
  • library: PolyUtil, SphericalUtil, MathUtil in commonMain. The Maps SDK extensions (ktx), Street View, collections and the attribution initializer stay in androidMain.
  • heatmaps: WeightedLatLng, Gradient and a common ColorUtils (matching Android's HSV conversion) in commonMain; HeatmapTileProvider stays Android-only.
  • JVM-only constructs replaced by multiplatform equivalents: PlatformLock (ReentrantLock / NSRecursiveLock), kotlin.math, stdlib collections.

Merge with 6.0.0

Merged main (ktx migration, explicit API, binary compatibility validation, flexible polyline, z-index). Files main added in the old src/main / src/test layout were moved into androidMain / androidHostTest, and Android-only files git had placed in commonMain were moved out.

Publishing and tooling (previously the known gaps)

  • KmpPublishingConventionPlugin: explicit API, Dokka, Kover, lint and Vanniktech KotlinMultiplatform publishing under the existing android-maps-utils-* artifactIds and POM (shared with PublishingConventionPlugin via MapsUtilsPublishing.kt).
  • API compatibility: the Android API of the KMP modules is checked against the existing api/<module>.api files and is byte-for-byte unchanged. klib validation is enabled for the iOS ABI (api/<module>.klib.api).
  • Published Android artifact still ships lint.jar (lint-checks), the consumer keep rules and the attribution startup provider.
  • Coverage: Kover debug variant, so koverXmlReportDebug and reportDebug.xml keep feeding the coverage history. Numbers match main (library 87.3%, clustering 33.2%, heatmaps 81.8%).
  • Lint: com.android.lint gives the KMP modules lint tasks; lint-report lints library again.
  • CI: publish.yml now runs on macos-latest (iOS klibs can only be built on macOS). New manual publish-snapshot.yml publishes a -SNAPSHOT version to the Maven Central snapshot repository.

Remaining limitations

  • PreCachingAlgorithmDecorator stays Android-only (Executors-based; candidate for a coroutines rewrite).
  • External subclasses of NonHierarchicalDistanceBasedAlgorithm that did their own synchronized(mQuadTree) no longer synchronize with the internal lock.
  • data (GeoJSON/KML) and ui stay Android-only by design.
  • clustering and heatmaps have no commonTest yet, so nothing of theirs runs on iOS.
  • The snapshot workflow needs snapshot publishing enabled for the com.google.maps.android namespace in the Central Portal.

Test plan

Locally on macOS:

  • ./gradlew build apiCheck koverXmlReportDebug (what test.yml runs) passes, including iOS compilation and tests.
  • Android host tests: library 245, clustering 83, heatmaps 30, the same counts as main, all green.
  • :library:iosSimulatorArm64Test: 36 shared geometry tests green on the iOS simulator.
  • :data, :ui, :maps-utils and :demo (app and test APK) build against the KMP modules.
  • publishToMavenLocal (scratch repo) produces the root, -android and three iOS artifacts per module, with sources and javadoc jars. A 6.1.0-SNAPSHOT build also publishes.

Introduces a maps-model multiplatform module with common LatLng and
CameraPosition types (typealiased to the Play Services classes on
Android, plain value holders on iOS) and converts the clustering module
to Kotlin Multiplatform: the full algorithm layer (quadtree, geometry,
projection, all clustering algorithms) now lives in commonMain and
compiles for Android and iOS, while ClusterManager and the renderers
remain Android-only in androidMain.

JVM-only constructs in common code were replaced with multiplatform
equivalents: an expect/actual PlatformLock replaces synchronized blocks
and ReentrantReadWriteLock, java.util collections were swapped for
Kotlin stdlib ones, and Math.* calls for kotlin.math. The three
remaining Java test files were converted to Kotlin because KMP
compilations do not compile Java host-test sources.

Known gaps (prototype): publishing, jacoco, lint-checks and consumer
proguard rules are not yet wired for the KMP module layout.

Claude-Session: https://claude.ai/code/session_01225X6MnAqkyCF7Xones6WY
Apply maven-publish to maps-model and clustering so their multiplatform
publications can be published to mavenLocal for consumption by the
android-maps-compose KMP branch. Bump AGP 9.3.1 -> 9.4.0: AGP forbids
mixing versions across composite builds, and android-maps-compose is
already on 9.4.0.

Claude-Session: https://claude.ai/code/session_01225X6MnAqkyCF7Xones6WY
Publishing the KMP clustering module as com.google.maps.android:clustering
gave the same classes a second module identity next to the
android-maps-utils-clustering AAR on Maven Central, producing duplicate
class errors in apps that pull both (e.g. android-maps-compose's
maps-app). Remap the publication artifactIds to the repo's public
android-maps-utils-<module> scheme so both dependency paths
conflict-resolve to a single module.

Claude-Session: https://claude.ai/code/session_01225X6MnAqkyCF7Xones6WY
Converts the library module (PolyUtil, SphericalUtil, MathUtil in
commonMain; StreetView utilities, collections managers and the
attribution initializer in androidMain) and the heatmaps module
(WeightedLatLng, Gradient and shared constants in commonMain; the
Bitmap/Tile-based HeatmapTileProvider in androidMain) following the
pattern established by the clustering migration.

Gradient's android.graphics.Color usage is replaced by a common
ColorUtils that reproduces the Android/Skia RGB<->HSV conversions
exactly; GradientTest's hardcoded Android color values verify parity.
The AttributionId codegen task is ported into the KMP build and wired
into androidMain. Math.toRadians/toDegrees become common helpers.

The Java test suites (PolyUtilTest, SphericalUtilTest, MathUtilTest,
heatmaps UtilTest) are converted to Kotlin; the three math suites move
to commonTest and now also run on iOS (36 tests green on the iOS
simulator, 60 android host tests for library, 24 for heatmaps).
robolectric.properties pins sdk=28 for host tests as Robolectric does
not support targetSdk 37.

Claude-Session: https://claude.ai/code/session_01225X6MnAqkyCF7Xones6WY
The KMP Android library plugin does not create lintDebug/SARIF reporting
tasks, so :library:lintDebug no longer exists after the multiplatform
migration. Lint data, ui and demo instead; KMP-module lint reporting is
tracked as a known gap of the migration.
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@googlemaps-bot

googlemaps-bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage

Overall Project 57.22% -0.1% 🍏
Files changed 95.85% 🍏

Module Coverage
Kover Gradle Plugin XML report for :library 89.62% 🍏
Kover Gradle Plugin XML report for :heatmaps 89.02% -1.65% 🍏
Kover Gradle Plugin XML report for :clustering 32.95% -0.01% 🍏
Files
Module File Coverage
Kover Gradle Plugin XML report for :library SupportStreetViewPanoramaFragment.kt 100% 🍏
MapView.kt 100% 🍏
MapFragment.kt 100% 🍏
SupportMapFragment.kt 100% 🍏
StreetViewPanoramaFragment.kt 100% 🍏
Polyline.kt 100% 🍏
LatLng.kt 100% 🍏
AttributionIdInitializer.kt 100% 🍏
SupportStreetViewPanoramaFragment.kt 100% 🍏
MapView.kt 100% 🍏
SupportMapFragment.kt 100% 🍏
Polyline.kt 100% 🍏
LatLng.kt 100% 🍏
StreetViewJavaHelper.kt 100% 🍏
StreetViewPanoramaView.kt 100% 🍏
MapFragment.kt 100% 🍏
AngleConversions.kt 100% 🍏
MapsInitializer.kt 100% 🍏
MathUtil.kt 100% 🍏
StreetViewPanoramaFragment.kt 100% 🍏
PolylineOptions.kt 100% 🍏
MarkerOptions.kt 100% 🍏
PolygonOptions.kt 100% 🍏
CircleOptions.kt 100% 🍏
StreetViewPanoramaOrientation.kt 100% 🍏
CameraPosition.kt 100% 🍏
GroundOverlayOptions.kt 100% 🍏
TileOverlayOptions.kt 100% 🍏
StreetViewPanoramaCamera.kt 100% 🍏
PolylineOptions.kt 100% 🍏
MarkerOptions.kt 100% 🍏
PolygonOptions.kt 100% 🍏
CircleOptions.kt 100% 🍏
StreetViewPanoramaOrientation.kt 100% 🍏
CameraPosition.kt 100% 🍏
GroundOverlayOptions.kt 100% 🍏
TileOverlayOptions.kt 100% 🍏
StreetViewPanoramaCamera.kt 100% 🍏
SphericalUtil.kt 99.71% 🍏
PolyUtil.kt 99.37% 🍏
FlexiblePolyline.kt 97.84% 🍏
MapObjectManager.kt 94.05% 🍏
MarkerManager.kt 91.98% 🍏
MarkerManagerFlows.kt 91.2% 🍏
PolygonManager.kt 87.84% 🍏
CircleManager.kt 87.84% 🍏
GroundOverlayManager.kt 87.84% 🍏
PolylineManager.kt 87.84% 🍏
FusedLocationProvider.kt 84.62% 🍏
LocationManager.kt 84.62% 🍏
GoogleMap.kt 83.83% 🍏
Polygon.kt 80.95% 🍏
PolylineManagerFlows.kt 78% 🍏
GroundOverlayManagerFlows.kt 78% 🍏
PolygonManagerFlows.kt 78% 🍏
CircleManagerFlows.kt 78% 🍏
Polygon.kt 70.73% 🍏
MapsInitializer.kt 66.67% 🍏
MarkerManager.kt 60% 🍏
FusedLocationProvider.kt 38.46% 🍏
LocationManager.kt 35.71% 🍏
StreetViewUtil.kt 34.02% 🍏
PolygonManager.kt 33.33% 🍏
CircleManager.kt 33.33% 🍏
GroundOverlayManager.kt 33.33% 🍏
PolylineManager.kt 33.33% 🍏
GoogleMap.kt 28.44% 🍏
StreetViewPanoramaView.kt 27.27% 🍏
Kover Gradle Plugin XML report for :heatmaps WeightedLatLng.kt 100% 🍏
HeatmapTileProvider.kt 92.61% 🍏
ColorUtils.kt 90.77% -9.23% 🍏
Gradient.kt 88.89% -1.82% 🍏
Heatmap.kt 15.15% 🍏
Heatmap.kt 13.73% 🍏
Kover Gradle Plugin XML report for :clustering ClusterManagerFlows.kt 100% 🍏
ClusterManager.kt 100% 🍏
Point.kt 100% 🍏
SphericalMercatorProjection.kt 100% 🍏
CentroidNonHierarchicalDistanceBasedAlgorithm.kt 100% 🍏
NonHierarchicalViewBasedAlgorithm.kt 100% 🍏
AbstractAlgorithm.kt 100% 🍏
PlatformLock.kt 100% 🍏
ScreenBasedAlgorithmAdapter.kt 100% 🍏
GridBasedAlgorithm.kt 100% 🍏
PreCachingAlgorithmDecorator.kt 100% 🍏
PlatformLock.android.kt 100% 🍏
StaticCluster.kt 100% 🍏
Point.kt 100% 🍏
PointExtensions.kt 100% 🍏
Bounds.kt 100% 🍏
ContinuousZoomEuclideanCentroidAlgorithm.kt 99.61% 🍏
NonHierarchicalDistanceBasedAlgorithm.kt 99.39% 🍏
Point.kt 97.18% -1.41% 🍏
PointQuadTree.kt 94.71% 🍏
ClusterManager.kt 71.59% 🍏
DefaultClusterRenderer.kt 17.25% 🍏
ClusterRendererMultipleItems.kt 0.15% 🍏
DefaultAdvancedMarkersClusterRenderer.kt 0% 🍏

kikoso added 4 commits October 1, 2026 15:24
Brings the experimental KMP work up to date with 6.0.0: the ktx migration,
explicit API mode, binary compatibility validation, flexible polyline and
the z-index work.

- Files main added under the old src/main and src/test layout are moved
  into androidMain and androidHostTest. ClusterManagerFlows and Heatmap
  (Android-only) are moved out of commonMain, where git's directory
  rename detection had put them; PointExtensions stays common.
- Conflicts resolved by keeping the multiplatform code and adding main's
  explicit public modifiers. maps-model gets explicit visibility, and
  HeatmapConstants becomes internal so it is not new public API.
- New KmpPublishingConventionPlugin replaces the prototype maven-publish
  setup: explicit API, Kover (with a debug variant so koverXmlReportDebug
  and reportDebug.xml keep working), Dokka, lint via com.android.lint,
  and Vanniktech KotlinMultiplatform publishing under the existing
  android-maps-utils-* artifactIds. Shared POM and Maven Central setup
  moved to MapsUtilsPublishing.kt for both plugins.
- The Android API of the KMP modules is checked against the existing
  api/<module>.api files and is unchanged. klib validation is enabled
  for the iOS ABI (api/<module>.klib.api).
- Consumer keep rules and lint-checks are still published with the
  Android artifact.
The Kotlin Multiplatform modules publish iOS klibs, which can only be
built on macOS, so publish.yml moves to macos-latest. Its sed calls use
-i.bak, which works with both GNU and BSD sed.

publish-snapshot.yml is a manual workflow that publishes a -SNAPSHOT
version of every module to the Maven Central snapshot repository. It
rejects versions that do not end with -SNAPSHOT and does not tag or
commit anything.
The multiplatform library module gets lint tasks from com.android.lint,
so lint-report goes back to linting library and demo, with :library:lint
instead of :library:lintDebug.
README: which artifacts are multiplatform, what is shared and what stays
Android-only, and the snapshot repository. AGENTS.md: maps-model, the
source set layout, API files and the macOS requirement for iOS targets.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants