Skip to main content

Display Current Street Info

Last updated: June 19, 2026 | 4 minutes read

This example demonstrates how to display the current street name, city and speed limit using the improved position data provided by the SDK. On startup, an info dialog explains how to make the information appear: use a mock-location app or physically move along the streets so the position keeps updating. The map follows the current GPS position; as it updates, the street name and city are shown in a panel displayed at the bottom of the screen, and the road speed limit is shown in a speed-limit sign in the top-right corner. When the user pans away from the position, a button appears to recenter the map back onto the current location.

Startup info: how to make the current street info appear
Current street name, city and speed limit are displayed

Listening for Position Updates

A DataSourceListener is attached to a live data source for EDataType.ImprovedPosition. Each time new GPS data arrives, the road address (street name and city) and the road speed limit are extracted from an ImprovedPositionData wrapper. The speed limit is converted from m/s to km/h. When no city is reported by the GPS fix, the nearest address is queried as a fallback to provide a "Near <city>" label. The app only displays street info while it is following the current position.

MainActivity.ktView on Github
// Fires whenever GPS data arrives; extracts street/city/speed info and posts it to the UI.
private val dataSourceListener = object : DataSourceListener() {
override fun onNewData(data: SenseData) {
val mapView = binding.gemSurface.mapView ?: return

if (!mapView.isFollowingPosition()) {
runOnAliveUi { handleCurrentStreetNameInfo("", "", "") }
return
}

val improvedPositionData = ImprovedPositionData(data)
val speedLimitInt = (improvedPositionData.roadSpeedLimit * 3.6).toInt()
val roadAddress = improvedPositionData.roadAddress
val speedLimit = if (speedLimitInt > 0) "$speedLimitInt" else ""
var streetName = roadAddress?.getField(EAddressField.StreetName) ?: ""
var cityName = roadAddress?.getField(EAddressField.City) ?: ""

// If no city from the GPS fix, query the nearest address for a fallback city name.
if (cityName.isEmpty()) {
mapView.getClosestAddress(improvedPositionData.coordinates, 10000, true)
?.addressInfo?.getField(EAddressField.City)?.let { city ->
cityName = "Near $city"
}
}

runOnAliveUi { handleCurrentStreetNameInfo(streetName, cityName, speedLimit) }
}
}

Starting the Position Listener

Once the default map view is created, the map starts following the current position. If location permissions are granted and location services are enabled, a live data source is produced and the listener is registered for improved-position updates.

MainActivity.ktView on Github
private fun startImprovedPositionListener() {
if (dataSource == null) {
dataSource = DataSourceFactory.produceLive()
dataSource?.addListener(dataSourceListener, EDataType.ImprovedPosition)
}
}

Updating the Street Info Panel

handleCurrentStreetNameInfo updates the on-screen panel. The street name is shown, the city is shown below it when available, and the speed-limit sign is shown only when a valid speed limit is reported. Longer speed-limit values are rendered at a smaller text size so they fit inside the sign.

MainActivity.ktView on Github
private fun handleCurrentStreetNameInfo(streetName: String, cityName: String, speedLimit: String) {
binding.apply {
if (streetName.isNotEmpty()) {
if (streetName != currentStreetText.text) {
currentStreetText.text = streetName
}

if (cityName.isNotEmpty()) {
currentCityText.visibility = View.VISIBLE
if (cityName != currentCityText.text) {
currentCityText.text = cityName
}
(currentStreetText.layoutParams as ConstraintLayout.LayoutParams).bottomMargin = getSizeInPixels(1)
} else {
currentCityText.visibility = View.GONE
(currentStreetText.layoutParams as ConstraintLayout.LayoutParams).bottomMargin =
resources.getDimension(R.dimen.text_padding).toInt()
}

currentStreetTextContainer.visibility = View.VISIBLE
} else {
currentStreetTextContainer.visibility = View.GONE
}

speedLimitSign.isVisible = speedLimit.isNotEmpty()
if (speedLimit.isNotEmpty()) {
val defaultTextSize = resources.getDimensionPixelSize(R.dimen.speed_panel_text_size).toFloat()
val textSize = if (speedLimit.length >= 3) defaultTextSize * 0.8f else defaultTextSize
currentSpeedLimit.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
currentSpeedLimit.text = speedLimit
}
}
}

Recentering on the Current Position

When the user pans away from the followed position, a button is shown to return to it. Tapping the button re-enables position following.

MainActivity.ktView on Github
private fun enableGPSButton() {
binding.gemSurface.mapView?.apply {
onExitFollowingPosition = { binding.followCursorButton.isVisible = true }
onEnterFollowingPosition = { binding.followCursorButton.isVisible = false }
binding.followCursorButton.setOnClickListener {
SdkCall.execute { followPosition() }
}
}
}