Draw a Circle on the Map
This tutorial shows how to draw circular areas on your MapMetrics Android map — useful for showing radius zones, geofences, or proximity areas.
Prerequisites
- Completed the Getting Started Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Draw a Circle Using Turf
Since there's no native circle geometry in GeoJSON, we approximate one using a polygon with many points:
kotlin
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapMetricsMap
import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.FillLayer
import org.maplibre.android.style.layers.LineLayer
import org.maplibre.android.style.layers.PropertyFactory.*
import org.maplibre.android.style.sources.GeoJsonSource
import org.maplibre.geojson.Feature
import org.maplibre.geojson.Point
import org.maplibre.geojson.Polygon
import kotlin.math.*
class DrawCircleActivity : AppCompatActivity() {
private lateinit var mapView: MapView
private lateinit var map: MapMetricsMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { mapMetricsMap ->
map = mapMetricsMap
map.setStyle(
Style.Builder().fromUri(
"https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY"
)
) { style ->
drawCircle(style, LatLng(48.8584, 2.2945), 500.0) // 500m radius
}
}
}
private fun drawCircle(style: Style, center: LatLng, radiusMeters: Double) {
val circlePolygon = createCirclePolygon(center, radiusMeters, 64)
style.addSource(
GeoJsonSource("circle-source", Feature.fromGeometry(circlePolygon))
)
// Fill
style.addLayer(
FillLayer("circle-fill", "circle-source")
.withProperties(
fillColor(Color.parseColor("#4285F4")),
fillOpacity(0.2f)
)
)
// Outline
style.addLayer(
LineLayer("circle-outline", "circle-source")
.withProperties(
lineColor(Color.parseColor("#4285F4")),
lineWidth(2f)
)
)
// Center camera
map.cameraPosition = CameraPosition.Builder()
.target(center)
.zoom(14.0)
.build()
}
/**
* Generate a polygon approximating a circle.
* @param center Center point
* @param radiusMeters Radius in meters
* @param steps Number of polygon vertices (more = smoother)
*/
private fun createCirclePolygon(
center: LatLng,
radiusMeters: Double,
steps: Int
): Polygon {
val points = mutableListOf<Point>()
val earthRadius = 6371000.0 // meters
for (i in 0..steps) {
val angle = Math.toRadians((360.0 / steps) * i)
val lat = Math.toRadians(center.latitude)
val lng = Math.toRadians(center.longitude)
val newLat = asin(
sin(lat) * cos(radiusMeters / earthRadius) +
cos(lat) * sin(radiusMeters / earthRadius) * cos(angle)
)
val newLng = lng + atan2(
sin(angle) * sin(radiusMeters / earthRadius) * cos(lat),
cos(radiusMeters / earthRadius) - sin(lat) * sin(newLat)
)
points.add(
Point.fromLngLat(
Math.toDegrees(newLng),
Math.toDegrees(newLat)
)
)
}
return Polygon.fromLngLats(listOf(points))
}
override fun onStart() { super.onStart(); mapView.onStart() }
override fun onResume() { super.onResume(); mapView.onResume() }
override fun onPause() { super.onPause(); mapView.onPause() }
override fun onStop() { super.onStop(); mapView.onStop() }
override fun onDestroy() { super.onDestroy(); mapView.onDestroy() }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView.onSaveInstanceState(outState)
}
}Multiple Radius Rings
Show concentric distance rings around a point:
kotlin
private fun drawRadiusRings(style: Style, center: LatLng) {
val rings = listOf(
Pair(250.0, "#4285F4"), // 250m — blue
Pair(500.0, "#34A853"), // 500m — green
Pair(1000.0, "#FF6B35"), // 1km — orange
)
for ((index, ring) in rings.withIndex()) {
val (radius, color) = ring
val polygon = createCirclePolygon(center, radius, 64)
val sourceId = "ring-source-$index"
style.addSource(GeoJsonSource(sourceId, Feature.fromGeometry(polygon)))
style.addLayer(
FillLayer("ring-fill-$index", sourceId)
.withProperties(
fillColor(Color.parseColor(color)),
fillOpacity(0.1f)
)
)
style.addLayer(
LineLayer("ring-outline-$index", sourceId)
.withProperties(
lineColor(Color.parseColor(color)),
lineWidth(2f),
lineDasharray(arrayOf(2f, 1f))
)
)
}
// Add center marker
map.addMarker(
org.maplibre.android.annotations.MarkerOptions()
.position(center)
.title("Center Point")
.snippet("250m / 500m / 1km radius")
)
}Circle on Tap
Let users tap the map to place a circle:
kotlin
private fun setupTapToCircle(style: Style) {
// Pre-create empty source and layers
style.addSource(GeoJsonSource("tap-circle-source"))
style.addLayer(
FillLayer("tap-circle-fill", "tap-circle-source")
.withProperties(
fillColor(Color.parseColor("#FF6B35")),
fillOpacity(0.2f)
)
)
style.addLayer(
LineLayer("tap-circle-outline", "tap-circle-source")
.withProperties(
lineColor(Color.parseColor("#FF6B35")),
lineWidth(2f)
)
)
map.addOnMapClickListener { latLng ->
val polygon = createCirclePolygon(latLng, 300.0, 64)
val source = style.getSource("tap-circle-source") as? GeoJsonSource
source?.setGeoJson(Feature.fromGeometry(polygon))
true
}
}Using Turf Library
If you include the Turf dependency, circle creation is simpler:
kotlin
import com.mapbox.turf.TurfTransformation
import com.mapbox.turf.TurfConstants
val center = Point.fromLngLat(2.2945, 48.8584)
val circlePolygon = TurfTransformation.circle(
center,
500.0,
64,
TurfConstants.UNIT_METRES
)Next Steps
- Polygon Area — Draw custom shapes
- Measure Distances — Distance calculations
- Circle Layer — Circle layer for point data
Tip: Use 64 steps for smooth circles. Below 32 you'll see visible edges. Above 128 adds points without visible improvement. The Turf library approach is recommended when available as it handles edge cases near the poles.