Annotation: Marker

This guide will show you how to add Markers in the map.

Annotation is an overlay on top of a Map. In package org.maplibre.android.annotations, it has the following subclasses:

  1. Marker
  2. Polyline
  3. Polygon

A Marker shows an icon image at a geographical location. By default, marker uses a provided image as its icon.

marker image

Or, the icon can be customized using IconFactory to generate an Icon using a provided image.

For more customization, please read the documentation about MarkerOptions.

In this showcase, we continue the code from the Quickstart, rename Activity into JsonApiActivity, and pull the GeoJSON data from a free and public API. Then add markers to the map with GeoJSON:

  1. In your module Gradle file (usually <project>/<app-module>/build.gradle), add okhttp to simplify code for making HTTP requests.
dependencies {
    ...
    implementation 'com.squareup.okhttp3:okhttp:4.10.0'
    ...
}
  1. Sync your Android project the with Gradle files.

  2. In JsonApiActivity we add a new variable for MapboxMap. It is used to add annotations to the map instance.

class JsonApiActivity : AppCompatActivity() {

    // Declare a variable for MapView
    private lateinit var mapView: MapView

    // Declare a variable for MapLibreMap
    private lateinit var maplibreMap: MapLibreMap
  1. Call mapview.getMapSync() in order to get a MapboxMap object. After maplibreMap is assigned, call the getEarthQuakeDataFromUSGS() method to make a HTTP request and transform data into the map annotations.
        mapView.getMapAsync { map ->
            maplibreMap = map

            maplibreMap.setStyle("https://demotiles.maplibre.org/style.json")

            // Fetch data from USGS
            getEarthQuakeDataFromUSGS()
        }
  1. Define a function getEarthQuakeDataFromUSGS() to fetch GeoJSON data from a public API. If we successfully get the response, call addMarkersToMap() on the UI thread.
    // Get Earthquake data from usgs.gov, read API doc at:
    // https://earthquake.usgs.gov/fdsnws/event/1/
    private fun getEarthQuakeDataFromUSGS() {
        val url = "https://earthquake.usgs.gov/fdsnws/event/1/query".toHttpUrl().newBuilder()
            .addQueryParameter("format", "geojson")
            .addQueryParameter("starttime", "2022-01-01")
            .addQueryParameter("endtime", "2022-12-31")
            .addQueryParameter("minmagnitude", "5.8")
            .addQueryParameter("latitude", "24")
            .addQueryParameter("longitude", "121")
            .addQueryParameter("maxradius", "1.5")
            .build()
        val request: Request = Request.Builder().url(url).build()

        OkHttpClient().newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Toast.makeText(this@JsonApiActivity, "Fail to fetch data", Toast.LENGTH_SHORT)
                    .show()
            }

            override fun onResponse(call: Call, response: Response) {
                val featureCollection = response.body?.string()
                    ?.let(FeatureCollection::fromJson)
                    ?: return
                // If FeatureCollection in response is not null
                // Then add markers to map
                runOnUiThread { addMarkersToMap(featureCollection) }
            }
        })
    }
  1. Now it is time to add markers into the map.
    • In the addMarkersToMap() method, we define two types of bitmap for the marker icon.
    • For each feature in the GeoJSON, add a marker with a snippet about earthquake details.
    • If the magnitude of an earthquake is bigger than 6.0, we use the red icon. Otherwise, we use the blue one.
    • Finally, move the camera to the bounds of the newly added markers
    private fun addMarkersToMap(data: FeatureCollection) {
        val bounds = mutableListOf<LatLng>()

        // Get bitmaps for marker icon
        val infoIconDrawable = ResourcesCompat.getDrawable(
            this.resources,
            // Intentionally specify package name
            // This makes copy from another project easier
            org.maplibre.android.R.drawable.maplibre_info_icon_default,
            theme
        )!!
        val bitmapBlue = infoIconDrawable.toBitmap()
        val bitmapRed = infoIconDrawable
            .mutate()
            .apply { setTint(Color.RED) }
            .toBitmap()

        // Add symbol for each point feature
        data.features()?.forEach { feature ->
            val geometry = feature.geometry()?.toJson() ?: return@forEach
            val point = Point.fromJson(geometry) ?: return@forEach
            val latLng = LatLng(point.latitude(), point.longitude())
            bounds.add(latLng)

            // Contents in InfoWindow of each marker
            val title = feature.getStringProperty("title")
            val epochTime = feature.getNumberProperty("time")
            val dateString = SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.TAIWAN).format(epochTime)

            // If magnitude > 6.0, show marker with red icon. If not, show blue icon instead
            val mag = feature.getNumberProperty("mag")
            val icon = IconFactory.getInstance(this)
                .fromBitmap(if (mag.toFloat() > 6.0) bitmapRed else bitmapBlue)

            // Use MarkerOptions and addMarker() to add a new marker in map
            val markerOptions = MarkerOptions()
                .position(latLng)
                .title(dateString)
                .snippet(title)
                .icon(icon)
            maplibreMap.addMarker(markerOptions)
        }

        // Move camera to newly added annotations
        maplibreMap.getCameraForLatLngBounds(LatLngBounds.fromLatLngs(bounds))?.let {
            val newCameraPosition = CameraPosition.Builder()
                .target(it.target)
                .zoom(it.zoom - 0.5)
                .build()
            maplibreMap.cameraPosition = newCameraPosition
        }
    }
  1. Here is the final result. For the full contents of JsonApiActivity, please visit source code of our Test App.
Screenshot with the map in demotile style