Skip to content

Draw a Polygon Area

This tutorial shows how to draw filled polygon shapes on your MapMetrics Android map — useful for highlighting zones, districts, or geofences.

Prerequisites

Basic Polygon with Fill

Add a filled polygon using a GeoJSON source and fill layer:

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.FeatureCollection
import org.maplibre.geojson.Point
import org.maplibre.geojson.Polygon

class PolygonActivity : 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 ->
                addPolygon(style)
            }
        }
    }

    private fun addPolygon(style: Style) {
        // Define polygon vertices (must form a closed ring)
        val polygonPoints = listOf(
            listOf(
                Point.fromLngLat(2.3200, 48.8700),
                Point.fromLngLat(2.3800, 48.8700),
                Point.fromLngLat(2.3800, 48.8400),
                Point.fromLngLat(2.3200, 48.8400),
                Point.fromLngLat(2.3200, 48.8700)  // Close the ring
            )
        )

        val polygon = Polygon.fromLngLats(polygonPoints)
        val feature = Feature.fromGeometry(polygon)

        // Add GeoJSON source
        style.addSource(GeoJsonSource("polygon-source", feature))

        // Add fill layer
        style.addLayer(
            FillLayer("polygon-fill", "polygon-source").withProperties(
                fillColor(Color.parseColor("#4285F4")),
                fillOpacity(0.3f)
            )
        )

        // Add outline layer
        style.addLayer(
            LineLayer("polygon-outline", "polygon-source").withProperties(
                lineColor(Color.parseColor("#4285F4")),
                lineWidth(2f),
                lineOpacity(0.8f)
            )
        )

        // Center camera on polygon
        map.cameraPosition = CameraPosition.Builder()
            .target(LatLng(48.855, 2.350))
            .zoom(13.0)
            .build()
    }

    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 Colored Zones

Display several zones with different colors:

kotlin
private fun addMultipleZones(style: Style) {
    data class Zone(
        val id: String,
        val points: List<Point>,
        val color: String,
        val name: String
    )

    val zones = listOf(
        Zone(
            "zone-a",
            listOf(
                Point.fromLngLat(2.32, 48.87),
                Point.fromLngLat(2.35, 48.87),
                Point.fromLngLat(2.35, 48.855),
                Point.fromLngLat(2.32, 48.855),
                Point.fromLngLat(2.32, 48.87),
            ),
            "#FF6B35", "District A"
        ),
        Zone(
            "zone-b",
            listOf(
                Point.fromLngLat(2.35, 48.87),
                Point.fromLngLat(2.38, 48.87),
                Point.fromLngLat(2.38, 48.855),
                Point.fromLngLat(2.35, 48.855),
                Point.fromLngLat(2.35, 48.87),
            ),
            "#34A853", "District B"
        ),
        Zone(
            "zone-c",
            listOf(
                Point.fromLngLat(2.32, 48.855),
                Point.fromLngLat(2.38, 48.855),
                Point.fromLngLat(2.38, 48.84),
                Point.fromLngLat(2.32, 48.84),
                Point.fromLngLat(2.32, 48.855),
            ),
            "#9C27B0", "District C"
        ),
    )

    for (zone in zones) {
        val polygon = Polygon.fromLngLats(listOf(zone.points))
        val feature = Feature.fromGeometry(polygon)

        style.addSource(GeoJsonSource("${zone.id}-source", feature))

        style.addLayer(
            FillLayer("${zone.id}-fill", "${zone.id}-source").withProperties(
                fillColor(Color.parseColor(zone.color)),
                fillOpacity(0.3f)
            )
        )

        style.addLayer(
            LineLayer("${zone.id}-outline", "${zone.id}-source").withProperties(
                lineColor(Color.parseColor(zone.color)),
                lineWidth(2f)
            )
        )
    }
}

Clickable Polygon

Show info when the user taps inside a polygon:

kotlin
private fun addClickablePolygon(style: Style) {
    addPolygon(style) // Add polygon first

    map.addOnMapClickListener { latLng ->
        // Query the polygon layer at the tap point
        val screenPoint = map.projection.toScreenLocation(latLng)
        val features = map.queryRenderedFeatures(screenPoint, "polygon-fill")

        if (features.isNotEmpty()) {
            android.widget.Toast.makeText(
                this,
                "Tapped inside polygon!",
                android.widget.Toast.LENGTH_SHORT
            ).show()
        }
        true
    }
}

Fill Layer Properties

PropertyTypeDescription
fillColorColor/ExpressionFill color
fillOpacityFloatFill transparency (0.0 - 1.0)
fillOutlineColorColorBorder color (1px only)
fillAntialiasBooleanSmooth edges (default: true)

Next Steps


Tip: Always close polygon rings — the first and last point must be identical. For outlines thicker than 1px, add a separate LineLayer on top of the FillLayer as shown above.