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

VPS Location Source

Provide accurate indoor positioning from the device camera using ARKit.

Overview​

The VPS (Visual Positioning System) Location Source recognizes the user's surroundings from the camera and reports an accurate indoor position through the Wemap LocationSource interface. It can drive WemapMapSDK / WemapGeoARSDK, or run standalone with a third-party map/AR system.

  • Important: Wemap VPS must be configured for your map by the Wemap team. Contact us if you are interested.

Requirements​

In addition to the common requirements (see Getting started), the device must support ARKit. Check at runtime:

guard VPSARKitLocationSource.isAvailable else {
// explain to the user that their device is not supported
return
}

You can also require ARKit at install time via UIRequiredDeviceCapabilities in your Info.plist.

Permissions​

VPS requires camera access. Declare NSCameraUsageDescription in your Info.plist and request permission before creating the source:

<key>NSCameraUsageDescription</key>
<string>Camera is used to understand your surroundings and provide accurate indoor navigation</string>

Known limitations​

ARKit relative positioning can drift or jump. Quality falls with device temperature and in visually sparse surroundings — a corridor of plain white walls is the usual example — and the SDK's overlays reduce that without removing it.

  • Note: Relative positioning may be unreliable on iPhone SE (3rd generation).

Setting up the source​

Create a WemapPositioningSDKVPSARKit/VPSARKitLocationSource from your session, then assign it to the map and/or AR view:

func setUpLocationSource(session: CoreSession) throws {
let vps = try VPSARKitLocationSource(session: session, config: VPSConfig())

// WemapMapSDK
mapView.userLocationManager.locationSource = vps

// and/or WemapGeoARSDK
arView.locationManager.locationSource = vps
}

To use VPS with a third-party map/AR, call WemapPositioningSDKVPSARKit/VPSARKitLocationSource/start() and observe the inherited LocationSource coordinate/attitude streams directly.

Handling state and scanning​

Observe WemapPositioningSDKVPSARKit/VPSARKitLocationSource/states and WemapPositioningSDKVPSARKit/VPSARKitLocationSource/scanStatuses to guide the user, and call WemapPositioningSDKVPSARKit/VPSARKitLocationSource/startScan() to let them scan their environment:

let states = vps.states
Task {
for await state in states {
// .notPositioning → prompt the user to scan with startScan()
// .accuratePositioning → hide hints; location updates begin
// .degradedPositioning → suggest (but don't force) a re-scan
}
}

vps.startScan()

The system may also start a background scan on its own (based on distance travelled, time since the last scan, or changing conditions). Observe WemapPositioningSDKVPSARKit/VPSARKitLocationSource/backgroundScanStatuses to surface a subtle hint when appropriate.

Map matching (assigning an itinerary)​

Assigning the followed itinerary to WemapPositioningSDKVPSARKit/VPSARKitLocationSource/itinerary improves the experience (projections, conveyor detection, re-scan prompts). With WemapMapSDK/WemapGeoARSDK this is handled for you; standalone, assign it yourself:

let origin = Coordinate(coordinate2D: .init(latitude: 48.88007462, longitude: 2.35591097), levels: .single(0))
let destination = Coordinate(coordinate2D: .init(latitude: 48.88141308, longitude: 2.35747255), levels: .single(-2))

let itineraries = try await session.itineraryProvider.itineraries(origin: origin, destination: destination)
vps.itinerary = itineraries.first
  • Note: Since iOS 18, ARSession may be stopped automatically when an ARView is hidden/dismissed. If you use ARView, see the workaround in the sample application.

Examples​

For sample implementations, see the official sample-apps repository.