Installation

You can download the .aar file directly and install it into your project:

  1. Download the latest release from: http://download.powermap.live/#sdks
  2. Copy the downloaded powermap-sdk-release.aar into your app module's libs directory: app/libs/
  3. Add the dependency pointing to your local file in your build.gradle.kts (app level):
dependencies {
    implementation(files("libs/powermap-sdk-release.aar"))
    
    // Note: When loading a local .aar file manually, you may also need to 
    // include the SDK's transitive dependencies (MapLibre, Compose, etc.) 
    // inside this file as well.
}
Looking for a complete implementation? Check out the Android SDK Demo Repository on GitHub.

Architecture Overview

The PowerMap Android SDK is built with a Layered Facade Architecture, ensuring that high-performance native rendering is wrapped in an easy-to-use Kotlin API.

graph TD
    App[Android App] --> PowerMap[PowerMapView]
    PowerMap --> Controller[PowerMapController]
    
    subgraph "Facade Layer"
        Controller
    end

    subgraph "Manager Layer"
        Controller --> MarkerMgr[MarkerManager]
        Controller --> SearchMgr[SearchManager]
        Controller --> RouteMgr[RoutingManager]
        Controller --> GeomMgr[GeometryManager]
        Controller --> TrackingMgr[TrackingManager]
        Controller --> CameraMgr[CameraManager]
    end

    subgraph "Service Layer"
        MarkerMgr --> TileSvc[TileService]
        SearchMgr --> GeoSvc[GeocodingProvider]
        TileSvc --> Cache[3-Layer Cache]
    end

    subgraph "Native Engine"
        PowerMap --> NativeEngine[High-Performance OpenGL/Vulkan Rendering]
    end

    Cache -.-> Disk[(Local Storage)]
    Cache -.-> CDN((PowerMap CDN))
  • Unified Facade: PowerMapController provides a single entry point for all features, matching the Web and Flutter DX.
  • Native Rendering: Symbols, Lines, and Polygons are rendered via MapLibre/OpenGL for gorgeous 60FPS performance.
  • Coroutines Native: Fully asynchronous APIs (search, addRoute, etc.) built seamlessly with Kotlin Coroutines.

Quick Start

Get a map up and running in less than 2 minutes.

Step A: Initialize Dual-Auth

Setup the SDK with your API keys globally in your Application class or main Activity.

import com.powermap.sdk.PowerMapSDK

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        PowerMapSDK.initialize(
            context = this,
            apiKey = "YOUR_MAP_API_KEY",      // Public key for tiles/base layers
            clientId = "YOUR_CLIENT_ID",      // Core Service Credentials 
            clientSecret = "YOUR_CLIENT_SECRET"
        )
    }
}

Step B: The Map View

You can add PowerMapView directly via XML or instantiate it programmatically.

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.powermap.sdk.ui.PowerMapView
import com.powermap.sdk.PowerMapStyle
import org.maplibre.android.geometry.LatLng

class MapActivity : AppCompatActivity() {
    private lateinit var mapView: PowerMapView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        mapView = PowerMapView(this)
        setContentView(mapView)

        mapView.initialize(
            style = PowerMapStyle.DARK, // TH, EN, DARK, GRAY
            initialCenter = LatLng(13.7563, 100.5018), // Bangkok
            initialZoom = 14.0
        ) { controller ->
            // The map is fully loaded and safe to manipulate!
            
            controller.setOnMapClickListener { coords ->
                println("Tapped at: $coords")
                true
            }
        }
    }
}

Configuration Reference

PowerMapView.initialize Options

Parameter Type Default Description
style PowerMapStyle PowerMapStyle.TH Base tile style (TH, EN, DARK, GRAY).
initialCenter LatLng LatLng(13.7563, 100.5018) The initial center point of the map.
initialZoom Double 14.0 Initial viewport zoom level (0-22).
onMapReady (PowerMapController) -> Unit Required Triggered when the GL context is fully initialized.

Global Event Listeners (on PowerMapController)

  • setOnMapClickListener { latLng -> ... }
  • setOnPoiClickListener { jsonElement -> ... }
  • setOnMapLongClickListener { latLng -> ... }
  • setOnErrorListener { throwable -> ... }

Marker Management

The SDK renders native Symbol Layers for highest drawing performance.

Core Marker Methods

Method Arguments Description
addMarker(position, ...) LatLng, color?, iconImage?, size?, data? Adds a GL-rendered map symbol marker. Best for performance.
clearMarkers() - Removes all GL symbols and circles.
setOnMarkerClickListener (LatLng) -> Unit Intercepts click events on rendered markers.

High Performance GL Markers

Best for large datasets. Rendered directly by the GPU.

// In your initialization callback:
val myMarker = controller.markers.addMarker(
    position = LatLng(13.76, 100.50),
    color = "#f97316", // Vibrant Orange natively parsed pinpoint
    size = 1.5f
)

// Add an interactive tap listener
controller.setOnMarkerClickListener { latLng ->
    println("Marker tapped! $latLng")
}

Camera & Viewport Control

Directly manipulate the map viewport matching the fluidity of the Flutter SDK.

Movement Methods

Method Arguments Description
moveCamera(target, ...) LatLng, zoom?, tilt?, bearing?, durationMs? Smooth fly-to animation with optional rotations.
flyTo(target, zoom) LatLng, Double Alias for moving the camera directly.
zoomIn() / zoomOut() - Modifies zoom level by 1.0 with smooth animation.
setTilt(deg) / setPitch Double Set tilt angle (0 = 2D, 60+ = 3D).
setBearing(deg) Double Set direction (0 = North).
tiltUp() - Shortcut to animate the camera to a 3D perspective view (60 degrees).
tiltDown() - Shortcut to animate back to a top-down view (0 degrees).

State Properties

Method Returns Description
getCenter() LatLng? Returns current view center.
getZoom() Double? Returns current map zoom level.
getBearing() Double? Returns current map rotation.
getTilt() Double? Returns current camera pitch.
controller.moveCamera(
    target = LatLng(13.7649, 100.5383), 
    zoom = 17.0,
    tilt = 60.0,
    bearing = 90.0
)

Routing & Optimization

Calculate logistics-grade paths containing step-by-step metadata. Will automatically draw the route line on the map.

addRoute() Options

Option Type Default Description
start LatLng Required Origin coordinate.
destination LatLng Required Final destination coordinate.
waypoints List<LatLng> emptyList() Stops between origin and destination (Max 25).
optimize Boolean false If true, applies TSP logic to reorder waypoints.
profile String "driving" Modes: "driving", "walking", "cycling".
language String "th" Output instruction language: "th", "en".
lifecycleScope.launch {
    val route = controller.addRoute(
        start = LatLng(13.7468, 100.5346),
        destination = LatLng(13.7649, 100.5383),
        optimize = true
    )
    println("Route calculated! Distance: ${route?.distanceStr}")
}

Turn-by-Turn Navigation & Simulation

The SDK includes a fully-featured, 60FPS native-feeling Navigation Engine. It supports real-time GPS driving, dynamic route simulation, smooth 3D camera tracking, and Voice Guidance via com.google.android.tts.

Getting Started

import com.powermap.sdk.navigation.PowerMapNavigator
import com.powermap.sdk.navigation.NavigationMode

// 1. Calculate the route
lifecycleScope.launch {
    val route = controller.addRoute(
        start = LatLng(13.7468, 100.5346),
        destination = LatLng(13.7649, 100.5383)
    )
    
    if (route != null) {
        // 2. Initialize Navigator
        val navigator = PowerMapNavigator(context)
        navigator.setController(controller)
        
        // 3. Start 60FPS Simulation
        navigator.startNavigation(route, mode = NavigationMode.SIMULATION)
        
        // OR Real-time tracking (Requires Foreground Service Permissions):
        // navigator.startNavigation(route, mode = NavigationMode.GPS)
    }
}

Binding to the UI

React to real-time navigation states by passing a listener:

navigator.setNavigationStateListener { state ->
    // state.instructionText -> "Turn left onto Sukhumvit Rd"
    // state.distanceToNextManeuver -> 500.0 (meters)
    // state.speedMps -> Current speed
    
    binding.instructionText.text = state.instructionText
    binding.distanceText.text = "${state.distanceToNextManeuver}m"
}

Search & Geocoding

The Mobile SDK features Coroutine-based geocoding mirroring the Web SDK while adding safety boundaries like Debouncing and LRU Caching (50 items).

Search Methods

Method Execution Description
search() suspend Standard forward search. Checks local LRU Cache first.
searchByCategory() suspend Searches for places nearby a location using a category ID (e.g. "2100" for Restaurant).
autocompleteDebounced() Callback Safe to bind to UI inputs. Custom delay prevents quota burn.
reverseGeocode() suspend Converts coordinates into a structured address.
clearSearchCache() sync Clears the internal LRU cache manually.

Language Detection (lang Parameter)

The lang parameter controls which search endpoint is used. The SDK automatically detects the language from the query text and passes the appropriate value:

lang value Endpoint When to use
"th" (default) /api/v2/map/address_th Query contains Thai characters only
"en" /api/v2/map/address_en Query contains Latin/English characters only
"mixed" /api/v2/map/address Query contains both Thai and English
The detection logic in the SDK uses Unicode ranges: Thai (U+0E00–U+0E7F) and Latin (U+0041–U+007A). If a query contains both, "mixed" is passed to use the unified endpoint.
// Language is detected automatically in powermap-android-sdk-demo:
fun detectLang(q: String): String {
    val hasLatin = q.any { it.code in 0x41..0x7A }
    val hasThai  = q.any { it.code in 0x0E00..0x0E7F }
    return when {
        hasLatin && !hasThai -> "en"
        hasLatin && hasThai  -> "mixed"
        else                 -> "th"
    }
}

// Then pass it to the search call:
val lang = detectLang(query)
controller.autocompleteDebounced(
    query = query,
    delayMs = 500,
    lang = lang
) { results ->
    println("Found: ${results.first().name}")
}

// Or use with suspend search():
lifecycleScope.launch {
    val results = controller.search("Central World", lang = "en")
    println("Found: ${results.first().name} at ${results.first().position}")
}

// Category Search (e.g. Restaurants near current location)
lifecycleScope.launch {
    val myLocation = controller.getCurrentLocation()
    if (myLocation != null) {
        val restaurants = controller.searchByCategory("2100", myLocation, dist = 5)
        println("Found ${restaurants.size} restaurants nearby!")
    }
}

// Reverse Geocode (Lat/Lng to Address)
lifecycleScope.launch {
    val response = controller.reverseGeocode(LatLng(14.2558, 100.9810))
    println("Address: ${response?.name}")
}

Geometry & Vector Data

The SDK features a robust GeometryManager that natively handles GeoJSON, making it incredibly easy to visualize Custom Geofences or 3D buildings.

Geometry API Reference

Method Execution Description
addGeoJson() sync Injects raw GeoJSON data into the map source.
importGeoJson() sync Parses GeoJSON and automatically renders layers based on Polygon, LineString, or Point types.
addPolygonLayer() sync Renders Fills and Outlines. Mix with fillOpacity to create interactive Geofences.
addPolylineLayer() sync Renders colored paths/lines with customizable thickness.
addIndoorExtrusionLayer() sync Automatically extrudes Polygons into 3D assuming data has a height property.
// 1. Prepare your Geometry Data (GeoJSON format: Lng, Lat)
val geofenceData = mapOf(
    "type" to "Feature",
    "geometry" to mapOf(
        "type" to "Polygon",
        "coordinates" to listOf(listOf(
            listOf(100.534, 13.746), listOf(100.538, 13.746), 
            listOf(100.538, 13.749), listOf(100.534, 13.749), 
            listOf(100.534, 13.746) // Must close the loop
        ))
    )
)

// 2. Inject Data into the Map Source
controller.geometry.addGeoJson("my-geofence", geofenceData)

// 3. Apply Visual Styles to that Source ID
controller.geometry.addPolygonLayer(
    sourceId = "my-geofence",
    fillColor = "#4264fb", 
    fillOpacity = 0.5f,
    outlineColor = "#000000"
)

Real-Time Tracking

Highly integrated with FusedLocationProviderClient for battery-efficient, high-accuracy GPS positioning.

Core Methods

Method Returns Description
enableFollowMode() suspend Unit Locks camera on device GPS location.
enableFollowModeWithCompass() suspend Unit Locks camera + rotates to match device heading with 3D tilt.
disableFollowMode() sync Unlocks camera, stops tracking.
getCurrentLocation() suspend LatLng? One-shot GPS request.
requestPermission() suspend Boolean Checks and requests location permission automatically.
showUserLocation(true) sync Enables visual blue dot indicator.
lifecycleScope.launch {
    // 1. One-shot: Get current coordinates
    val myLocation = controller.getCurrentLocation()
    
    // 2. Navigation mode with compass heading (3D view)
    controller.enableFollowModeWithCompass()
    
    // 3. Stop tracking later
    controller.disableFollowMode()
}

Error Handling System

Global tracking for SDK crashes or network failures is handled seamlessly.

controller.setOnErrorListener { throwable ->
    println("PowerMap Error Detected: ${throwable.message}")
    // Handle gracefully in UI
}

API Reference

Browse our generated Dokka guides to see the comprehensive API surface. Start with PowerMapView and PowerMapController as your primary integration points.


© 2026 PowerMap Development Team. All rights reserved.