Following this blogpost, i have created a Kotlin version.
I implemented it as a Polygon
extension.
package com.example.diman.googlemapsplayground
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.android.gms.maps.model.Polygon
import com.google.maps.android.PolyUtil
import com.google.maps.android.SphericalUtil
/**
* Find approximate center of a polygon.
* Reference: https://moduscreate.com/blog/placing-markers-inside-polygons-with-google-maps/
*
* Algorithm:
* If the bounding box center point is inside the polygon, return center point.
* Otherwise, sample points at fixed percentages of the bounding box’s height North and South of the center point.
* Sample points at fixed percentages of the bounding box’s width East and West of the center point.
* Stop when we find the first point that is inside the area of the polygon, and drop the marker there.
*/
fun Polygon.getApproximateCenter(): LatLng {
// Calc bounds first
val boundsBuilder = LatLngBounds.builder()
points.forEach {
boundsBuilder.include(it)
}
val polygonBounds = boundsBuilder.build()
val centerPoint = polygonBounds.center
// Center point is inside the polygon, return it
if (PolyUtil.containsLocation(centerPoint, points, true)) {
return centerPoint
}
// Center point is out of bounds
// Sample points at fixed percentages of the bounding box’s width East and West of the center point.
val maxSearchSteps = 10
var testPos: LatLng = centerPoint
// Calculate NorthWest point so we can work out height of polygon NW->SE
val northWest = LatLng(polygonBounds.northeast.latitude, polygonBounds.southwest.longitude)
// Work out how tall and wide the bounds are and what our search
// increment will be
val boundsHeight = SphericalUtil.computeDistanceBetween(northWest, polygonBounds.southwest)
val heightIncr = boundsHeight / maxSearchSteps
val boundsWidth = SphericalUtil.computeDistanceBetween(northWest, polygonBounds.northeast)
val widthIncr = boundsWidth / maxSearchSteps
// Expand out from Centroid and find a point within polygon at
// 0, 90, 180, 270 degrees
for (n in 1..maxSearchSteps) {
// Test point North of Centroid
testPos = SphericalUtil.computeOffset(
centerPoint,
(heightIncr * n),
0.0
)
if (PolyUtil.containsLocation(testPos, points, true)) {
break
}
// Test point East of Centroid
testPos = SphericalUtil.computeOffset(
centerPoint,
(widthIncr * n),
90.0
)
if (PolyUtil.containsLocation(testPos, points, true)) {
break
}
// Test point South of Centroid
testPos = SphericalUtil.computeOffset(
centerPoint,
(heightIncr * n),
180.0
)
if (PolyUtil.containsLocation(testPos, points, true)) {
break
}
// Test point West of Centroid
testPos = SphericalUtil.computeOffset(
centerPoint,
(widthIncr * n),
270.0
)
if (PolyUtil.containsLocation(testPos, points, true)) {
break
}
}
return testPos
}
Note, add PolyUtil
and SphericalUtil
by adding to gradle:
implementation 'com.google.maps.android:android-maps-utils:0.4.3'