MapSDK Getting Started
The WemapMapSDK is a library for embedding customized interactive maps within mobile applications.
Requirements
Check common requirement.
Installation
Check common installation.
Add a map
At first add WemapMapView to your layout:
<com.getwemap.sdk.map.WemapMapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Then create a MapSession and pass it to the WemapMapView
via configure. The session and the (optional) rendering config are set once:
lifecycleScope.launch {
runCatching {
MapSession.create(requireContext(), 19158, "GUHTU6TYAWWQHUSR5Z5JZNMXX") // your mapId and token
}.onSuccess { session ->
mapView.configure(session) // pass a MapViewConfig() as the second argument to tune rendering
}.onFailure {
println("Failed to create session with error - $it")
}
}
Wait for the MapView to load
Only once the view has finished loading is it safe to access WemapMapView properties. You can await
loading with the suspending awaitLoaded():
lifecycleScope.launch {
runCatching {
mapView.awaitLoaded()
}.onSuccess {
// now it's safe to access MapView properties
}.onFailure { error ->
println("Failed to load MapView with error - $error")
}
}
Or observe loadPhases — a StateFlow<LoadPhase> that starts at LoadPhase.Loading and settles on
LoadPhase.Ready or LoadPhase.Failed. The current phase is replayed to every new collector, so a late
subscriber still sees the terminal state:
lifecycleScope.launch {
mapView.loadPhases.collect { phase ->
when (phase) {
LoadPhase.Loading -> showSpinner()
LoadPhase.Ready -> showMap()
is LoadPhase.Failed -> showError(phase.error)
}
}
}
A
LoadPhase.Faileddoes not always mean the managers are missing. A style that failed to load leaves them unbuilt, and accessing one throws; a points-of-interest download that failed or timed out leaves every manager built and usable, with no POI content. That is deliberate — a network timeout must never bring down the host app.
MapView lifecycle
WemapMapView manages the map's rendering lifecycle for you. It observes a Lifecycle and drives MapLibre's
lifecycle automatically — when the view is placed in a normal Activity/Fragment layout it discovers the
lifecycle owner from the view tree on attach, so you do not need to forward onStart/onResume/onDestroy
and friends to it.
Assign mapView.lifecycle yourself only when the view tree can't provide a lifecycle owner (a plain
Dialog/PopupWindow, a WindowManager overlay, Compose interop, or tests):
mapView.lifecycle = viewLifecycleOwner.lifecycle
When you're done with the screen, tear down the session that owns the shared services:
override fun onDestroy() {
super.onDestroy()
session.deinit()
}
Keep the session in a
ViewModel(and callsession.deinit()fromonCleared()) if you want it to survive configuration changes such as rotation.
Jetpack Compose
Compose support ships as a separate artifact, com.getwemap.sdk:map-compose, so that View-based apps are not
made to depend on the Compose runtime:
implementation("com.getwemap.sdk:map-compose:<version>")
WemapMap takes the session and hands you the loaded WemapMapView — everything the SDK offers is reached
through it, so there is no second API to learn:
@Composable
fun MapScreen(session: MapSession) {
var mapView by remember { mutableStateOf<WemapMapView?>(null) }
WemapMap(
session = session,
modifier = Modifier.fillMaxSize(),
onLoaded = { mapView = it },
onFailed = { error -> Log.e("Map", "Failed to load the map", error) }
)
// the SDK's own flows are collected directly — no wrapper parameters
val building by (mapView?.buildingManager?.focusedBuildings ?: emptyFlow())
.collectAsStateWithLifecycle(null)
building?.let { BuildingBanner(it) }
}
Three things to know:
- Hoist the session into a
ViewModel, not aremember— it is network-loaded and cannot go into aBundle, so arememberwould drop it on rotation. Callsession.deinit()fromonCleared(). - The map view is torn down when it leaves composition, and rebuilt if you pass a different
session(a view's session is set once). You do not forward any lifecycle callback. onLoadedis not replayed. Supply it from the first composition; a handler first passed after loading finished is not invoked retroactively. ReadloadPhase— or collectloadPhases— for the current state.
Full details, the parameter list and the pitfalls are in MapSDK with Jetpack Compose.
User Location
Wemap provides various location sources to track the user's location on the map. For more info check positioning docs
By default, WemapMapSDK uses Android GPS and Network Providers. But you can easily take any
WemapPositioningSDK location source — created with the same session — and connect it to the
WemapMapView as shown below:
fun setupLocationSource(session: MapSession) {
val locationSource = AndroidFusedAdaptiveLocationSource(requireContext(), session) // any LocationSource from WemapPositioningSDK
mapView.locationManager.locationSource = locationSource
}
The full example is available in our GitHub repository.
Choosing a renderer
WemapMapSDK draws the map with MapLibre, which has two rendering engines: Vulkan and
OpenGL ES. By default the SDK ships both and picks one per device, so most apps have nothing to do here. Read
on if you want a smaller app, or need to control which engine a device gets.
The default: both engines
com.getwemap.sdk:map depends on MapLibre's android-sdk-vulkan-opengl artifact. The first
MapLibre.getInstance(context) call picks the engine, which then stays for the lifetime of the process:
- Vulkan on Android 7.0 (API 24) or newer, when the device declares Vulkan 1.0.3 support
- OpenGL ES everywhere else
Your app still installs on every device the SDK supports: the artifact declares Vulkan as optional, so Google Play filters no device out.
Forcing an engine at runtime
The choice is based on what a device declares, not on whether Vulkan actually works on it. A device that
advertises Vulkan but cannot run MapLibre's Vulkan renderer fails with No Vulkan compatible GPU found. To keep
such a device — or any device you choose — on OpenGL ES, pass the engine to MapLibre.getInstance yourself,
before the first map view is created:
MapLibre.getInstance(context, null, WellKnownTileServer.MapLibre, RenderingEngine.Type.OPENGL)
The first call wins: a later call, forced or not, does not change the engine. RenderingEngine exists only in
the default artifact, so drop this call if you switch to a single-engine artifact below.
Shipping one engine only
Both engines together are 23.7 MB of native code on arm64-v8a, against 10.8 MB for OpenGL ES alone. To ship a
single engine, swap the MapLibre artifact the SDK depends on, in your app module's build.gradle.kts. The
public API is the same, so no code changes:
configurations.configureEach {
resolutionStrategy.eachDependency {
if (requested.group == "org.maplibre.gl" && requested.name == "android-sdk-vulkan-opengl") {
// keep the MapLibre version the Wemap SDK depends on
useTarget("org.maplibre.gl:android-sdk-opengl:${requested.version}")
}
}
}
| Artifact | Engine | Devices | Native code, arm64-v8a |
|---|---|---|---|
android-sdk-vulkan-opengl (default) | Vulkan or OpenGL ES, per device | all | 23.7 MB |
android-sdk-opengl | OpenGL ES | all | 10.8 MB |
android-sdk | Vulkan | with Vulkan 1.0.3 only | 12.8 MB |
android-sdknarrows who can install your app. Its manifest requires Vulkan, and that requirement is merged into your app's: Google Play then hides the app from every device without Vulkan — all Android 6.0 devices among them — and on such a device the map cannot draw. Choose it only if every device you target has Vulkan.
Don't remove one engine's native library with packaging { jniLibs { excludes } } instead. The default artifact
still picks the engine per device, and crashes wherever it picks the one you removed.
MapLibre plugins
Other MapLibre artifacts, such as org.maplibre.gl:android-plugin-annotation-v9, depend on the plain
android-sdk. Next to the SDK's MapLibre artifact that duplicates every org.maplibre.android class, and the
build fails with Duplicate class. Exclude it:
implementation("org.maplibre.gl:android-plugin-annotation-v9:<version>") {
exclude(group = "org.maplibre.gl", module = "android-sdk")
}
Examples
For additional examples and sample implementations of WemapSDKs, visit the official GitHub repository.
Clone the repository and follow the README instructions to run the sample application.