Skip to main content

VPS Location Source

The VPS (Visual Positioning System) Location Source provides highly accurate indoor positioning using camera input. It's especially useful in environments where GPS signals are weak or unavailable.

info

Wemap VPS must be configured by the Wemap team. Please contact us if you are interested in using it.

We recommend reading the VPS Understanding and Best Practices guide first (product and UX); then use this page for technical implementation.

Setup Steps​

Step 1: Initialize Core SDK​

The Core SDK must be initialized before creating the VPS location source.

import { core } from '@wemap/core';

await core.init({
emmid: 'your-map-id',
token: 'your-token',
});

Step 2: Initialize VPSLocationSource and Start It​

import { VPSLocationSource } from '@wemap/positioning';

const vpsLocationSource = new VPSLocationSource({
// Configuration options
});

await vpsLocationSource.start();

Check out the API reference for VPSLocationSource →

Step 3: Listen to Position Updates​

vpsLocationSource.onUpdate((pose) => {
console.log('Position:', pose.position);
console.log('Orientation:', pose.attitude.headingDegrees);
});

Step 4: Setup the Camera and start scan​

info

You can trigger a scan at any time to improve localization accuracy. It is recommended to re-scan the environment after a significant period of time has passed or when the user has moved for a significant distance. We recommend to trigger a scan every 200 meters.

When using the VPS background feature (backgroundScan), the camera must remain started and its element must stay in the DOM. You can hide it visually (for example with opacity: 0), but you should not call camera.stop() / camera.release() while background scanning is needed.

import { Camera, requestCameraPermissions } from '@wemap/camera';
import { requestSensorPermissions } from '@wemap/positioning';

const hasCameraPermission = await requestCameraPermissions();
if (!hasCameraPermission) {
throw new Error('Camera permission denied');
}

const cameraContainer = document.getElementById('camera-container');
const camera = new Camera(cameraContainer, {
width: 640,
height: 480,
resizeOnWindowChange: true,
});

await camera.start();

// Request permissions (required on iOS)
const hasPermission = await requestSensorPermissions();
if (!hasPermission) {
throw new Error('Permission denied');
}

const scanSuccess = await vpsLocationSource.startScan();

if (scanSuccess) {
console.log('VPS scan successful! Position found.');
// Important: when background scan is enabled, keep the camera started and in the DOM.
// If you want it hidden, hide the container visually instead of stopping/releasing:
// cameraContainer.style.opacity = '0';
// cameraContainer.style.pointerEvents = 'none';
}

Link to useful API Reference:

VPS Scan Background (backgroundScan)​

Background scan is active by default and the system will try to scan automatically in the background if user hold their phone vertically and the camera is accessible. You can listen to scan lifecycle updates to react in your UI (loading state, hints, retry messages, etc.).

import { VPSLocationSource } from '@wemap/positioning';

const vpsLocationSource = new VPSLocationSource();

// Listen to background scan status changes
const unsubscribeBackgroundStatus = vpsLocationSource.onBackgroundScanStatusChange(
(status) => {
console.log('Background scan status:', status);
// Example:
// - show message asking user to hold phone vertically if background scan is started
}
);

// You can also read the current status at any time
console.log('Current background status:', vpsLocationSource.backgroundScanStatus);

// Later, cleanup listener when not needed
unsubscribeBackgroundStatus();

Handling Location State​

As the user walks away from the last scan point, VPS positioning drifts. The location source tracks the distance travelled since the last successful scan and exposes it as a location state so you can guide the user to re-scan before the position becomes unreliable.

LocationState has three values:

StateMeaningSuggested UX
'accurate'Position is reliable (just scanned, or moved less than the degraded threshold).Hide any re-scan hints.
'degraded'The user has moved far enough that accuracy is dropping.Invite the user to raise their phone so the background scan can re-localize automatically.
'no_positioning'The user has moved too far; the position can no longer be trusted.Explicitly ask the user to re-scan manually: open the camera and trigger a scan (see Setup the Camera and start scan).

The thresholds are distance-since-last-scan in meters and are configurable via degradedLocationStateThreshold (default 50) and noPositioningLocationStateThreshold (default 100). A successful scan resets the state to 'accurate'.

// React to state changes
const onStateChange = (state) => {
switch (state) {
case 'accurate':
// hide re-scan hints
break;
case 'degraded':
// invite the user to raise their phone so background scan can re-localize
break;
case 'no_positioning':
// explicitly ask the user to re-scan manually (open the camera and call startScan())
break;
}
};

vpsLocationSource.onLocationStateChange(onStateChange);

// Read the current state at any time
console.log('Current location state:', vpsLocationSource.locationState);

// Cleanup when no longer needed
vpsLocationSource.offLocationStateChange(onStateChange);

Link to useful API Reference:

Permissions​

VPS requires camera and device orientation permissions.

Secure context required

Camera access (getUserMedia) only works in a secure context — serve your page over HTTPS (or localhost in development). On an insecure origin the browser blocks the camera and VPS cannot start.

Camera Permissions​

import { requestCameraPermissions } from '@wemap/camera';

const hasPermission = await requestCameraPermissions();
if (!hasPermission) {
throw new Error('Camera permission denied');
}

Device Orientation Permissions (iOS)​

On iOS, you need to request device orientation permission. This request needs to be triggered after a user interaction (click, tap, etc.).

import { requestSensorPermissions } from '@wemap/positioning';

const hasPermission = await requestSensorPermissions();
if (!hasPermission) {
throw new Error('Sensor permission denied');
}

Map Matching with VPS​

VPS location source supports map matching just like other location sources. See the main positioning guide for details on how to set up map matching.