Skip to content

Observe Map Events

Note

You can find the full source code of this example in ObserverActivity.kt of the MapLibreAndroidTestApp.

You can observe-low level map events that are happening by registering listeners to a MapView. Below you can see the the map events that are currently available.

override fun onPreCompileShader(id: Int, type: Int, defines: String) {
    shaderTimes["${id}-${type}-${defines}"] = TimeSource.Monotonic.markNow()
    Logger.i(TAG, "A new shader is being compiled, shaderID:${id}, backend type:${type}, program configuration:${defines}")
}

override fun onPostCompileShader(id: Int, type: Int, defines: String) {
    val startTime = shaderTimes.get("${id}-${type}-${defines}")
    if (startTime != null) {
        Logger.i(TAG, "A shader has been compiled in ${startTime.elapsedNow()}, shaderID:${id}, backend type:${type}, program configuration:${defines}")
    }
}

override fun onGlyphsRequested(fontStack: Array<String>, rangeStart: Int, rangeEnd: Int) {
    Logger.i(TAG, "Glyphs are being requested for the font stack $fontStack, ranging from $rangeStart to $rangeEnd")
}

override fun onGlyphsLoaded(fontStack: Array<String>, rangeStart: Int, rangeEnd: Int) {
    Logger.i(TAG, "Glyphs have been loaded for the font stack $fontStack, ranging from $rangeStart to $rangeEnd")
}

override fun onSpriteRequested(id: String, url: String) {
    Logger.i(TAG, "The sprite $id has been requested from $url")
}

override fun onSpriteLoaded(id: String, url: String) {
    Logger.i(TAG, "The sprite $id has been loaded from $url")
}

override fun onTileAction(op: TileOperation, x: Int, y: Int, z: Int, wrap: Int, overscaledZ: Int, sourceID: String) {
    val tile = "X:${x}, Y:${y}, Z:${z}, Wrap:${wrap}, OverscaledZ:${overscaledZ}, SourceID:${sourceID}"
    when (op) {
        TileOperation.RequestedFromCache -> Logger.i(TAG, "Requesting tile ${tile} from the cache")
        TileOperation.RequestedFromNetwork -> Logger.i(TAG, "Requesting tile ${tile} from the network")
        TileOperation.LoadFromCache -> Logger.i(TAG, "Loading tile ${tile}, requested from the cache")
        TileOperation.LoadFromNetwork -> Logger.i(TAG, "Loading tile ${tile}, requested from the network")
        TileOperation.StartParse -> Logger.i(TAG, "Parsing tile ${tile}")
        TileOperation.EndParse -> Logger.i(TAG, "Completed parsing tile ${tile}")
        TileOperation.Error -> Logger.e(TAG, "An error occured during proccessing for tile ${tile}")
        TileOperation.Cancelled -> Logger.i(TAG, "Pending work on tile ${tile} was cancelled")
        TileOperation.NullOp -> Logger.e(TAG, "An unknown tile operation was emitted for tile ${tile}")
    }
}

override fun onDidFinishRenderingFrame(fully: Boolean, stats: RenderingStats) {
    renderStatsTracker.addFrame(stats)
}

You need to register them with these APIs:

mapView.addOnPreCompileShaderListener(this)
mapView.addOnPostCompileShaderListener(this)
mapView.addOnTileActionListener(this)
mapView.addOnGlyphsLoadedListener(this)
mapView.addOnGlyphsRequestedListener(this)
mapView.addOnSpriteLoadedListener(this)
mapView.addOnSpriteRequestedListener(this)
mapView.addOnDidFinishRenderingFrameListener(this)

In this case we implement them by implementing the interfaces below in the activity class, but you could also use lambdas.

/**
 * Test activity showcasing logging observer actions from the core
 */
class ObserverActivity : AppCompatActivity(),
    MapView.OnPreCompileShaderListener,
    MapView.OnPostCompileShaderListener,
    MapView.OnTileActionListener,
    MapView.OnGlyphsLoadedListener,
    MapView.OnGlyphsRequestedListener,
    MapView.OnSpriteLoadedListener,
    MapView.OnSpriteRequestedListener,
    MapView.OnDidFinishRenderingFrameWithStatsListener {

ObserverActivity.onDidFinishRenderingFrame uses RenderStatsTracker as an example for tracking rendering statistics over time. This offers periodic reports of minimum, maximum, average values and callbacks when predefined thresholds are exceeded.

renderStatsTracker.setReportFields(listOf(
    "encodingTime",
    "renderingTime",
    "numDrawCalls",
    "numActiveTextures",
    "numBuffers",
    "memTextures",
    "memBuffers"
))

renderStatsTracker.setReportListener { _, _, avg ->
    Logger.i(TAG, "RenderStatsReport - avg - ${avg.nonZeroValuesString()}")
}

renderStatsTracker.setThresholds(hashMapOf(
    "numDrawCalls" to 1000,
    "totalBuffers" to 1000L
))

renderStatsTracker.setThresholdExceededListener { exceededValues, _ ->
    Logger.i(TAG, "Exceeded render values $exceededValues")
}

renderStatsTracker.startReports(10L)