Display Multiple Geometry Types Together
This tutorial shows how to display markers, polylines, polygons, and circles all on the same MapMetrics Android map.
Prerequisites
- Completed the Getting Started Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Combined Geometries
Add points, lines, polygons, and circles in one view:
kotlin
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.annotations.MarkerOptions
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapMetricsMap
import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.CircleLayer
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.*
import com.google.gson.JsonObject
import kotlin.math.*
class MultiGeometryActivity : 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 ->
addPolygonZone(style)
addRouteLines(style)
addPointMarkers(style)
addCircleRadius(style)
fitToAll()
}
}
}
// 1. Polygon — park/zone boundary
private fun addPolygonZone(style: Style) {
val parkBoundary = Polygon.fromLngLats(listOf(listOf(
Point.fromLngLat(2.3300, 48.8470),
Point.fromLngLat(2.3400, 48.8470),
Point.fromLngLat(2.3420, 48.8440),
Point.fromLngLat(2.3380, 48.8420),
Point.fromLngLat(2.3280, 48.8430),
Point.fromLngLat(2.3300, 48.8470),
)))
style.addSource(
GeoJsonSource("zone-source", Feature.fromGeometry(parkBoundary))
)
style.addLayer(
FillLayer("zone-fill", "zone-source")
.withProperties(
fillColor(Color.parseColor("#34A853")),
fillOpacity(0.2f)
)
)
style.addLayer(
LineLayer("zone-outline", "zone-source")
.withProperties(
lineColor(Color.parseColor("#34A853")),
lineWidth(2f)
)
)
}
// 2. Polylines — walking routes
private fun addRouteLines(style: Style) {
val route1 = Feature.fromGeometry(
LineString.fromLngLats(listOf(
Point.fromLngLat(2.3350, 48.8560),
Point.fromLngLat(2.3380, 48.8530),
Point.fromLngLat(2.3360, 48.8490),
Point.fromLngLat(2.3340, 48.8460),
)),
JsonObject().apply { addProperty("name", "Route A") }
)
val route2 = Feature.fromGeometry(
LineString.fromLngLats(listOf(
Point.fromLngLat(2.3200, 48.8550),
Point.fromLngLat(2.3250, 48.8520),
Point.fromLngLat(2.3300, 48.8500),
Point.fromLngLat(2.3350, 48.8480),
)),
JsonObject().apply { addProperty("name", "Route B") }
)
style.addSource(
GeoJsonSource("routes-source",
FeatureCollection.fromFeatures(listOf(route1, route2)))
)
style.addLayer(
LineLayer("routes-layer", "routes-source")
.withProperties(
lineColor(Color.parseColor("#4285F4")),
lineWidth(3f),
lineOpacity(0.8f)
)
)
}
// 3. Point markers — landmarks
private fun addPointMarkers(style: Style) {
val landmarks = listOf(
Triple(2.3376, 48.8606, "Louvre Museum"),
Triple(2.3499, 48.8530, "Notre-Dame"),
Triple(2.3266, 48.8600, "Musée d'Orsay"),
)
for ((lng, lat, name) in landmarks) {
map.addMarker(
MarkerOptions()
.position(LatLng(lat, lng))
.title(name)
)
}
}
// 4. Circle — radius zone around a point
private fun addCircleRadius(style: Style) {
val center = LatLng(48.8530, 2.3499) // Notre-Dame
val circlePolygon = createCirclePolygon(center, 300.0, 64)
style.addSource(
GeoJsonSource("circle-source", Feature.fromGeometry(circlePolygon))
)
style.addLayer(
FillLayer("circle-fill", "circle-source")
.withProperties(
fillColor(Color.parseColor("#FF6B35")),
fillOpacity(0.15f)
)
)
style.addLayer(
LineLayer("circle-outline", "circle-source")
.withProperties(
lineColor(Color.parseColor("#FF6B35")),
lineWidth(1.5f),
lineDasharray(arrayOf(2f, 1f))
)
)
}
private fun fitToAll() {
val bounds = LatLngBounds.Builder()
.include(LatLng(48.8606, 2.3376))
.include(LatLng(48.8420, 2.3280))
.include(LatLng(48.8600, 2.3266))
.include(LatLng(48.8560, 2.3350))
.build()
map.easeCamera(
CameraUpdateFactory.newLatLngBounds(bounds, 80),
1000
)
}
private fun createCirclePolygon(center: LatLng, radiusMeters: Double, steps: Int): Polygon {
val points = mutableListOf<Point>()
val earthRadius = 6371000.0
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)
}
}Layer Rendering Order
Layers render in the order they're added. The correct visual stacking:
Top → Annotation markers (always on top)
Symbol layers (icons, labels)
Circle layers (data points)
Line layers (routes, borders)
Bottom → Fill layers (zones, polygons)Use addLayerBelow or addLayerAbove for precise control:
kotlin
style.addLayerBelow(fillLayer, "routes-layer") // fills below lines
style.addLayerAbove(circleLayer, "routes-layer") // points above linesNext Steps
- Polygon Area — Draw filled shapes
- Polyline Route — Draw route lines
- Draw a Circle — Circle zones
- Multiple Data Sources — Combining data sources
Tip: Add fills first, then lines, then points. This ensures points remain clickable on top and lines are visible above filled zones.