Skip to main content
Version: 1.x (beta)

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.Failed does 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 call session.deinit() from onCleared()) 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 a remember — it is network-loaded and cannot go into a Bundle, so a remember would drop it on rotation. Call session.deinit() from onCleared().
  • 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.
  • onLoaded is not replayed. Supply it from the first composition; a handler first passed after loading finished is not invoked retroactively. Read loadPhase — or collect loadPhases — 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}")
}
}
}
ArtifactEngineDevicesNative code, arm64-v8a
android-sdk-vulkan-opengl (default)Vulkan or OpenGL ES, per deviceall23.7 MB
android-sdk-openglOpenGL ESall10.8 MB
android-sdkVulkanwith Vulkan 1.0.3 only12.8 MB

android-sdk narrows 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.