Skip to content

Draw a Polyline Route

This tutorial shows how to draw polyline routes on your MapMetrics Android map using both the annotation API and GeoJSON line layers.

Prerequisites

Using a GeoJSON Line Layer

The recommended approach — add a styled line from GeoJSON data:

kotlin
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.camera.CameraPosition
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.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.LineString
import org.maplibre.geojson.Point

class PolylineActivity : 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 ->
                addRoute(style)
            }
        }
    }

    private fun addRoute(style: Style) {
        // Define route points
        val routePoints = listOf(
            Point.fromLngLat(2.3522, 48.8566),   // Paris
            Point.fromLngLat(4.3517, 50.8503),   // Brussels
            Point.fromLngLat(4.9041, 52.3676),   // Amsterdam
            Point.fromLngLat(9.9937, 53.5511),   // Hamburg
            Point.fromLngLat(13.4050, 52.5200),  // Berlin
        )

        // Create GeoJSON line feature
        val lineString = LineString.fromLngLats(routePoints)
        val feature = Feature.fromGeometry(lineString)
        val featureCollection = FeatureCollection.fromFeature(feature)

        // Add source
        style.addSource(GeoJsonSource("route-source", featureCollection))

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

        // Fit camera to route
        val bounds = LatLngBounds.Builder()
        for (point in routePoints) {
            bounds.include(LatLng(point.latitude(), point.longitude()))
        }
        map.easeCamera(
            CameraUpdateFactory.newLatLngBounds(bounds.build(), 80),
            1000
        )
    }

    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)
    }
}

Styled Route with Markers at Stops

Add start/end markers along the route:

kotlin
private fun addStyledRoute(style: Style) {
    val stops = listOf(
        Pair(Point.fromLngLat(2.3522, 48.8566), "Paris"),
        Pair(Point.fromLngLat(4.3517, 50.8503), "Brussels"),
        Pair(Point.fromLngLat(4.9041, 52.3676), "Amsterdam"),
        Pair(Point.fromLngLat(13.4050, 52.5200), "Berlin"),
    )

    val routePoints = stops.map { it.first }

    // Line source and layer
    val lineString = LineString.fromLngLats(routePoints)
    style.addSource(GeoJsonSource("route-source", Feature.fromGeometry(lineString)))

    style.addLayer(
        LineLayer("route-layer", "route-source").withProperties(
            lineColor(Color.parseColor("#FF6B35")),
            lineWidth(5f),
            lineOpacity(0.9f),
            lineJoin("round")
        )
    )

    // Add markers at each stop
    for ((point, name) in stops) {
        map.addMarker(
            org.maplibre.android.annotations.MarkerOptions()
                .position(LatLng(point.latitude(), point.longitude()))
                .title(name)
        )
    }
}

Dashed Line

Create a dashed polyline using lineDasharray:

kotlin
style.addLayer(
    LineLayer("dashed-route", "route-source").withProperties(
        lineColor(Color.parseColor("#333333")),
        lineWidth(3f),
        lineDasharray(arrayOf(2f, 1f))  // 2px dash, 1px gap
    )
)

Multiple Route Layers

Show different route options with different colors:

kotlin
private fun addMultipleRoutes(style: Style) {
    // Route 1 — fastest
    val fastRoute = listOf(
        Point.fromLngLat(2.3522, 48.8566),
        Point.fromLngLat(6.1296, 49.8153),
        Point.fromLngLat(13.4050, 52.5200),
    )

    // Route 2 — scenic
    val scenicRoute = listOf(
        Point.fromLngLat(2.3522, 48.8566),
        Point.fromLngLat(4.3517, 50.8503),
        Point.fromLngLat(4.9041, 52.3676),
        Point.fromLngLat(9.9937, 53.5511),
        Point.fromLngLat(13.4050, 52.5200),
    )

    // Fast route — blue, thicker
    style.addSource(
        GeoJsonSource("fast-source",
            Feature.fromGeometry(LineString.fromLngLats(fastRoute)))
    )
    style.addLayer(
        LineLayer("fast-layer", "fast-source").withProperties(
            lineColor(Color.parseColor("#4285F4")),
            lineWidth(5f),
            lineOpacity(0.9f)
        )
    )

    // Scenic route — green, thinner
    style.addSource(
        GeoJsonSource("scenic-source",
            Feature.fromGeometry(LineString.fromLngLats(scenicRoute)))
    )
    style.addLayer(
        LineLayer("scenic-layer", "scenic-source").withProperties(
            lineColor(Color.parseColor("#34A853")),
            lineWidth(3f),
            lineOpacity(0.7f)
        )
    )
}

Line Layer Properties

PropertyTypeDescription
lineColorColorLine color
lineWidthFloatWidth in pixels
lineOpacityFloatTransparency (0.0 - 1.0)
lineJoinString"round", "bevel", "miter"
lineCapString"round", "butt", "square"
lineDasharrayArrayDash and gap lengths

Next Steps


Tip: For route lines that pass beneath map labels, use style.addLayerBelow(layer, "labelLayerId") to insert the line layer below the label layer in the rendering stack.