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,
ARSessionmay be stopped automatically when anARViewis hidden/dismissed. If you useARView, see the workaround in the sample application.
Examples
For sample implementations, see the official sample-apps repository.