Skip to main content

PositioningSDK - Getting started

Installation

Before you start developing your application with WemapSDKs, you need to configure your credentials and add the SDK as a dependency.

info

The code examples below demonstrate how to configure the WemapPositioningSDK to work as a standalone solution outside of the Wemap Map & AR ecosystem (i.e., with another Map SDK). However, it can also be used in combination with the WemapMapSDK. Instructions are available here.

Adding the Dependency

To add WemapSDKs to your app:

  1. In your module-level Gradle file (usually project/module/build.gradle.kts or project/module/build.gradle), add the dependency for the WemapMapSDK library for Android:

    dependencies {
    // Add the dependency for the WemapPositioningSDK library
    implementation "com.getwemap.sdk.positioning:wemap-vps-arcore:<version>" // Wemap VPS ARCore Location Source
    }
  2. Ensure that your project's minSdkVersion is set to API 21 or higher:

    android {
    ...
    defaultConfig {
    minSdkVersion 21
    }
    }
  3. Since you've modified your Gradle files, Android Studio will prompt you to sync the Gradle files. Proceed with syncing.

Location Sources

Wemap provides various location sources to track the user's location on the map.

To use any Location Source, you must have a mapID and token. For more details, please contact the Wemap team.

To set a specific location source, follow these steps:

  • Add the relevant WemapPositioningSDK library to your project dependencies.
  • Fetch MapData using your mapID and token.
  • Create a LocationSource using MapData.
  • Assign location source listeners to listen for location updates.

Wemap VPS ARCore location source

Fetching MapData

ServiceFactory
.getMapService()
.mapById(19158, "GUHTU6TYAWWQHUSR5Z5JZNMXX")
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
setupLocationSource(it)
}, {
println("Failed to get map data with error - ${it.message}")
})
.disposedBy(disposeBag)

Setting Up the VPS ARCore Location Source

Once you have the MapData, create a WemapVPSARCoreLocationSource instance, assign listeners, and start it:

fun setupLocationSource(mapData: MapData) {
vpsLocationSource = WemapVPSARCoreLocationSource(requireContext(), mapData)
vpsLocationSource.bind(requireContext(), surfaceView)
vpsLocationSource.vpsListeners.add(vpsListener)
vpsLocationSource.listener = locationListener

vpsLocationSource.start()
}

Handling Location Source State Changes

You must handle state changes in WemapVPSARCoreLocationSourceListener, as users may need to scan their environment.

private val vpsListener by lazy {
object : WemapVPSARCoreLocationSourceListener {

override fun onStateChanged(state: WemapVPSARCoreLocationSource.State) {
// Handle state changes
}

override fun onScanStatusChanged(status: WemapVPSARCoreLocationSource.ScanStatus) {
// Handle scan status changes
}
}
}

Retrieving User's Coordinate and Attitude

To get the user’s coordinate and attitude, implement LocationSourceListener:

private val locationListener by lazy {
object : LocationSourceListener {

override fun onCoordinateChanged(coordinate: Coordinate) {
// Pass the updated coordinate to your map implementation
}

override fun onAttitudeChanged(attitude: Attitude) {
// Pass the updated attitude to your map implementation
}

override fun onError(error: Throwable) {
// Handle errors
}
}
}

Scanning the Environment

To start tracking the user's location, allow them to scan their environment:

fun startScan() {
vpsLocationSource.startScan()
}

Once the system successfully recognizes the user's location, it will report the ACCURATE_POSITIONING state to WemapVPSARCoreLocationSourceListener. Shortly afterward, you will start receiving updated Coordinate and Attitude values in LocationSourceListener.

The VPS system will also report:

  • NOT_POSITIONING – indicates that the system hasn't yet recognized the environment and a VPS scan is required. At this point, you should present the camera view to the user to allow them to scan using vpsLocationSource.startScan().
  • DEGRADED_POSITIONING – indicates that user location tracking is limited. A scan is recommended, but not mandatory, to restore the ACCURATE_POSITIONING state. We suggest a subtle UI indication when this state occurs, such as a location icon warning or a small toast message prompting the user to rescan.

Assigning an Itinerary

Once you start receiving updated Coordinates, you can assign an itinerary to the WemapVPSARCoreLocationSource. Assigning an itinerary to WemapVPSARCoreLocationSource when a user is following an A→B itinerary enhances the overall navigation experience (e.g., itinerary projections, conveyor detection, etc.).

For example, when a conveyor is detected, the system will prompt you to rescan the environment to restore tracking.

Below are two examples demonstrating how to obtain an itinerary and assign it to WemapVPSARCoreLocationSource.

Itinerary from Wemap API
private fun calculateItinerary() {

val origin = Coordinate(48.88007462, 2.35591097, 0f)
val destination = Coordinate(48.88141308, 2.35747255, -2f)

ServiceFactory
.getItineraryProvider()
.itineraries(origin, destination, mapId = mapData.id)
.subscribe({ itineraries ->
vpsLocationSource.itinerary = itineraries.first()
}, { error ->
println("Failed to calculate itineraries with error: $error")
})
.disposedBy(disposeBag)
}
Manually created Itinerary

You can create an itinerary by providing the minimum necessary information, as shown below.

An Itinerary consists of an origin, a destination, and a list of segments, where:

  • origin is the starting point of the itinerary.
  • destination is the ending point of the itinerary.
  • segments is a collection of segment objects, where each:
    • segment is a class with two connected points (p1, p2) along the itinerary, and optionally a levelChange class that describes the type of level transition.
private fun hardcodedItinerary() {
val origin = Coordinate(48.88007462, 2.35591097, 0f)
val destination = Coordinate(48.88141308, 2.35747255, -2f)

val coordinatesLevel0 = listOf(
listOf(2.3559003, 48.88005135),
listOf(2.35613366, 48.88000508),
listOf(2.35623278, 48.87998696),
listOf(2.35636233, 48.87996258),
listOf(2.3564454, 48.88014336),
listOf(2.35657153, 48.88013655)
).map { Coordinate(it[1], it[0], 0f) }

val legSegmentsLevel0 = LegSegment.fromCoordinates(coordinatesLevel0)

val coordinatesFrom0ToMinus1 = listOf(
listOf(2.35657153, 48.88013655),
listOf(2.3567008, 48.8801748)
).map { Coordinate(it[1], it[0], listOf(-1f, 0f)) }

val levelChangeFrom0ToMinus1 = LevelChange(-1f, Incline.DOWN, LevelChangeType.Escalator)
val legSegmentsFrom0ToMinus1 = LegSegment.fromCoordinates(coordinatesFrom0ToMinus1, levelChangeFrom0ToMinus1)

val coordinatesLevelMinus1 = listOf(
listOf(2.3567008, 48.8801748),
listOf(2.35672744, 48.88017653),
listOf(2.35684126, 48.88020268),
listOf(2.35688225, 48.88028507),
listOf(2.35702803, 48.88032342),
listOf(2.35714357, 48.88055641),
listOf(2.35716058, 48.88058641),
listOf(2.35719467, 48.8805796),
listOf(2.35723088, 48.88057183),
listOf(2.357253, 48.88061996)
).map { Coordinate(it[1], it[0], -1f) }

val legSegmentsLevelMinus1 = LegSegment.fromCoordinates(coordinatesLevelMinus1)

val coordinatesFromMinus1ToMinus2 = listOf(
listOf(2.357253, 48.88061996),
listOf(2.35727559, 48.88066565)
).map { Coordinate(it[1], it[0], listOf(-2f, -1f)) }

val levelChangeFromMinus1ToMinus2 = LevelChange(-1f, Incline.DOWN, LevelChangeType.Escalator)
val legSegmentsFromMinus1ToMinus2 = LegSegment.fromCoordinates(coordinatesFromMinus1ToMinus2, levelChangeFromMinus1ToMinus2)

val coordinatesLevelMinus2 = listOf(
listOf(2.35727559, 48.88066565),
listOf(2.35731332, 48.88074658),
listOf(2.35728039, 48.88075276),
listOf(2.35723537, 48.88076096),
listOf(2.35731739, 48.88094625),
listOf(2.35738281, 48.88110437),
listOf(2.35745716, 48.88126764),
listOf(2.35749811, 48.88135969),
listOf(2.35752604, 48.88142664),
listOf(2.35748253, 48.88143515)
).map { Coordinate(it[1], it[0], -2f) }

val legSegmentsLevelMinus2 = LegSegment.fromCoordinates(coordinatesLevelMinus2)

val segments = legSegmentsLevel0 + legSegmentsFrom0ToMinus1 + legSegmentsLevelMinus1 +
legSegmentsFromMinus1ToMinus2 + legSegmentsLevelMinus2

vpsLocationSource.itinerary = Itinerary.fromSegments(origin, destination, segments)
}

You can also check out an example of how a sample GeoJSON is transformed into a Wemap Itinerary in our GitHub repository.

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.