Show Polygon Info on Click
This tutorial shows how to display information about a polygon zone when users tap inside it on your MapMetrics Android map.
Prerequisites
- Completed the Getting Started Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Clickable Polygons with Info
Create named zones and show details on tap:
kotlin
import android.graphics.Color
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.gson.JsonObject
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.expressions.Expression.*
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.*
class PolygonInfoActivity : AppCompatActivity() {
private lateinit var mapView: MapView
private lateinit var map: MapMetricsMap
private lateinit var infoPanel: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_polygon_info)
infoPanel = findViewById(R.id.tvInfo)
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 ->
addZones(style)
setupClickHandler()
}
}
}
private fun addZones(style: Style) {
val zones = listOf(
createZoneFeature(
"zone-1", "Le Marais",
"Historic district, popular for art galleries and cafés",
"#FF6B35",
listOf(
Point.fromLngLat(2.3500, 48.8600),
Point.fromLngLat(2.3650, 48.8600),
Point.fromLngLat(2.3650, 48.8530),
Point.fromLngLat(2.3500, 48.8530),
Point.fromLngLat(2.3500, 48.8600),
)
),
createZoneFeature(
"zone-2", "Saint-Germain",
"Left Bank intellectual quarter with bookshops and bistros",
"#4285F4",
listOf(
Point.fromLngLat(2.3200, 48.8550),
Point.fromLngLat(2.3400, 48.8550),
Point.fromLngLat(2.3400, 48.8480),
Point.fromLngLat(2.3200, 48.8480),
Point.fromLngLat(2.3200, 48.8550),
)
),
createZoneFeature(
"zone-3", "Montmartre",
"Hilltop village known for Sacré-Cœur and street artists",
"#34A853",
listOf(
Point.fromLngLat(2.3300, 48.8900),
Point.fromLngLat(2.3500, 48.8900),
Point.fromLngLat(2.3500, 48.8830),
Point.fromLngLat(2.3300, 48.8830),
Point.fromLngLat(2.3300, 48.8900),
)
),
)
style.addSource(
GeoJsonSource("zones-source", FeatureCollection.fromFeatures(zones))
)
// Fill layer — colored by feature property
style.addLayer(
FillLayer("zones-fill", "zones-source")
.withProperties(
fillColor(get("color")),
fillOpacity(0.25f)
)
)
// Outline
style.addLayer(
LineLayer("zones-outline", "zones-source")
.withProperties(
lineColor(get("color")),
lineWidth(2f)
)
)
// Camera
map.cameraPosition = CameraPosition.Builder()
.target(LatLng(48.865, 2.340))
.zoom(12.5)
.build()
}
private fun createZoneFeature(
id: String,
name: String,
description: String,
color: String,
points: List<Point>
): Feature {
val props = JsonObject().apply {
addProperty("id", id)
addProperty("name", name)
addProperty("description", description)
addProperty("color", color)
}
return Feature.fromGeometry(
Polygon.fromLngLats(listOf(points)),
props,
id
)
}
private fun setupClickHandler() {
map.addOnMapClickListener { latLng ->
val screenPoint = map.projection.toScreenLocation(latLng)
val features = map.queryRenderedFeatures(screenPoint, "zones-fill")
if (features.isNotEmpty()) {
val feature = features[0]
val name = feature.getStringProperty("name") ?: "Unknown"
val description = feature.getStringProperty("description") ?: ""
// Show in info panel
infoPanel.text = "$name\n$description"
infoPanel.visibility = android.view.View.VISIBLE
// Highlight the tapped zone
highlightZone(feature.getStringProperty("id"))
} else {
infoPanel.visibility = android.view.View.GONE
clearHighlight()
}
true
}
}
private fun highlightZone(zoneId: String?) {
val layer = map.style?.getLayer("zones-fill") as? FillLayer
layer?.setProperties(
fillOpacity(
match(
get("id"),
literal(0.15f), // default: dim
stop(zoneId ?: "", literal(0.5f)) // highlighted: bright
)
)
)
}
private fun clearHighlight() {
val layer = map.style?.getLayer("zones-fill") as? FillLayer
layer?.setProperties(fillOpacity(0.25f))
}
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)
}
}Layout (res/layout/activity_polygon_info.xml):
xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.maplibre.android.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tvInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#DD000000"
android:padding="16dp"
android:textColor="#FFFFFF"
android:textSize="14sp"
android:visibility="gone" />
</FrameLayout>Next Steps
- Polygon Area — Drawing polygon shapes
- Filter Features — Data-driven filtering
- Map Click Events — General click handling
Tip: Use queryRenderedFeatures with the specific layer ID ("zones-fill") to only detect polygon taps. Without a layer filter, it would also match road labels, building polygons, and other style features under the tap point.