Yatmo Android SDK

A Kotlin library that puts the Yatmo map in an Activity, a Fragment or a Compose screen, with a coroutine client for the neighbourhood data. Built on MapLibre Android, no Google Maps key needed.

Requirements

Install

// settings.gradle.kts: JitPack builds the library from the GitHub release tag
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

// app/build.gradle.kts
dependencies {
    implementation("com.github.Yatmo:yatmo-sdk-android:v1.0.0")
}

Source and releases: github.com/yatmo/yatmo-sdk-android.

Only the INTERNET permission is needed; the library declares it in its manifest.

Configure

One Yatmo instance per app, created in your Application class or your dependency injection graph:

import com.yatmo.sdk.*

class MyApp : Application() {
    lateinit var yatmo: Yatmo

    override fun onCreate() {
        super.onCreate()
        yatmo = Yatmo(this, YatmoConfiguration(
            licenseKey = "YOUR_FRONTEND_KEY",
            country = Country.BE,
            language = Language.FR
        ))
    }
}

The app id is read from context.packageName, which is your applicationId including any debug suffix. The constructor also initialises MapLibre (MapLibre.getInstance), so create it before inflating any map.

The map (XML / Activity)

<com.yatmo.sdk.YatmoMapView
    android:id="@+id/yatmoMap"
    android:layout_width="match_parent"
    android:layout_height="320dp" />
class ListingActivity : AppCompatActivity() {
    private lateinit var map: YatmoMapView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_listing)
        map = findViewById(R.id.yatmoMap)
        map.onCreate(savedInstanceState)
        map.attach((application as MyApp).yatmo, style = YatmoMapStyle.LIBERTY)
        map.showProperty(latitude = 50.8520525, longitude = 4.3442926, zoom = 15.0)
        map.showIsochrones(TravelMode.WALKING)          // optional
        map.onPoiSelected = { poi -> Log.d("Yatmo", "${poi.name} (${poi.type})") }
    }

    // MapLibre needs the lifecycle forwarded
    override fun onStart() { super.onStart(); map.onStart() }
    override fun onResume() { super.onResume(); map.onResume() }
    override fun onPause() { map.onPause(); super.onPause() }
    override fun onStop() { map.onStop(); super.onStop() }
    override fun onLowMemory() { super.onLowMemory(); map.onLowMemory() }
    override fun onDestroy() { map.onDestroy(); super.onDestroy() }
    override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState); map.onSaveInstanceState(outState) }
}

The underlying MapLibre MapView is available as map.mapView, and the MapLibreMap through map.getMapAsync { }.

Jetpack Compose

@Composable
fun ListingMap(yatmo: Yatmo) {
    YatmoMap(
        yatmo = yatmo,
        latitude = 50.8520525,
        longitude = 4.3442926,
        zoom = 15.0,
        style = YatmoMapStyle.LIBERTY,
        isochrones = TravelMode.WALKING,
        onPoiSelected = { poi -> /* show a bottom sheet */ },
        modifier = Modifier.fillMaxWidth().height(320.dp)
    )
}

YatmoMap is an AndroidView wrapper that forwards the lifecycle for you.

Map options

Name In Type Description
style YatmoMapStyle LIBERTY, BASIC, BRIGHT, THREE_D, POSITRON, DARK, LIBERTY_STONEHEDGE. Default: LIBERTY.
showProperty(latitude, longitude, zoom) method Centres the map and drops the property pin.
poiTypeIds List<Long>? Restrict the POIs to some categories, ids from client.simplifiedCategories(). Default: null (all).
poiIconSizeDp Int Marker size in dp (24 or 32). Default: 24.
showIsochrones(mode) / hideIsochrones() method 5 / 10 / 20 minute areas for WALKING, BICYCLING, DRIVING or TRANSIT.
fitIsochrones / isochronePaddingDp Boolean / Int Like the web plugin: once the isochrones are drawn the camera fits the largest area, and hideIsochrones() restores the previous camera. Default: true / 40.
minZoomForPois Double No POI request below this zoom. Default: 13.0.
onPoiSelected (YatmoPoi) -> Unit Marker tapped. The SDK draws no popup.
onError (Throwable) -> Unit Network or licence errors while loading POIs or isochrones.

Data: YatmoClient

Every method is a suspend function running on Dispatchers.IO:

lifecycleScope.launch {
    val client = yatmo.client

    val summary = client.summary(50.8520525, 4.3442926)
    summary.categories.forEach { category ->
        Log.d("Yatmo", category.label)
        category.subCategories.forEach { Log.d("Yatmo", "  ${it.label}: ${it.places.size}") }
    }

    val text = client.summaryText(50.8520525, 4.3442926)        // generated paragraphs
    val scores = client.scores(50.8520525, 4.3442926)           // 0 to 10 per category
    val enrichment = client.enrichment(50.8520525, 4.3442926)   // nearest place per category, 4 travel modes
    val places = client.geocode("Rue Neuve", 50.85, 4.35)       // address autocomplete

    val pois = client.points(southWest = LatLng(50.848, 4.338), northEast = LatLng(50.856, 4.350))
    val isochrones = client.isochrones(TravelMode.WALKING, 50.8520525, 4.3442926)
    val groups = client.simplifiedCategories()
}

Errors are thrown as YatmoException with the HTTP status and the server message. Response shapes: Summary, Enrichment, Points, Isochrone, Geolocation.

WebView fallback

val web = YatmoWebView(this)
web.load(yatmo, YatmoWebViewOptions(latitude = 50.8520525, longitude = 4.3442926, mode = "overlay", zoom = 15, accentColor = "#428BFF"))
container.addView(web)

It enables JavaScript and DOM storage and loads the iframe plugin page.

Troubleshooting

Related: iOS SDK · Bring your own map · Overview.