Skip to main content

Place Content in Real-World Locations Using WPS

Prerequisites

This guide assumes that you’ve already gone through:

  • How to set up a basic AR session guide.
  • Getting started with WPS guide.

Tracking Geolocated Objects with WPS

Adding the ARWorldPositioningManager to the XROrigin component in the Hierarchy will automatically add an ARWorldPositioningCameraHelper to the Main Camera.

  1. In the ARWorldPositioningObjectHelper Component, set the Altitude Mode to Camera-relative with smart averaging.
  2. In the Assets folder, right-click and mouse over Create, then select C# Script. Name the new script AddWPSObjects.
  3. Back in the Hierarchy, right-click, then select Create Empty to add a new GameObject. Name it WPSObjects. Select WPSObjects, then click Add Component in the Inspector and add AddWPSObjects as a script component.

For the basic use of WPS in a script, initialize it as normal, then call AddOrUpdateObject from ARWorldPositioningObjectHelper to update objects with WPS data. The example below, AddWPSObjects.cs, creates a cube, then uses ARWorldPositioningObjectHelper to place it dynamically in the scene.

note

When trying this out, set the latitude and longitude in the script to a location close to you (for live testing) or the playback scan (for remote testing).

Click to reveal AddWPSObjects.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Niantic.Lightship.AR.WorldPositioning;

public class AddWPSObjects : MonoBehaviour
{
[SerializeField] ARWorldPositioningObjectHelper positioningHelper;

// Start is called before the first frame update
void Start()
{
// replace the coordinates here with your location
double latitude = 37.79534850764306;
double longitude = -122.39243231803636;
double altitude = 0.0;
// We're using camera-relative positioning,
// so make the cube appear at the same height as the camera

// instantiate a cube, scale it up for visibility, then update its location
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale *= 2.0f;
positioningHelper.AddOrUpdateObject(cube, latitude, longitude, altitude, Quaternion.identity);
}

// Update is called once per frame
void Update()
{

}
}

Add the AddWPSObjects component to your WPSObjects GameObject, then populate positioningHelper field with the ARWorldPositioningManager component from the XROrigin GameObject.

Setting a Location

Before you can use WPS in your project, you will need to know the latitude and longitude coordinates of the location where you created your playback scan. To get the coordinates of the location:

  1. Open Google Maps and find the location where you captured your playback scan.
  2. Click the map to add a marker.
  3. Right-click the marker, then select the coordinates at the top of the menu to copy them to your clipboard.
  4. Paste the latitude and longitude values in AddWPSObjects.cs.

Afterwards, build and run the application to test it out. The cube should appear where you placed it!

Querying live GPS location

Alternatively, query Unity's Input.location API to use the device's live GPS reading:

Attention!

If you're using Android, it might take a moment for tracking to start and the cube to appear

Click to reveal modified AddWPSObjects.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Niantic.Lightship.AR.WorldPositioning;

public class AddWPSObjects : MonoBehaviour
{
[SerializeField] ARWorldPositioningObjectHelper positioningHelper;

// Start is called before the first frame update
IEnumerator Start()
{
Input.location.Start();

// Waits until the location service initializes
int maxWait = 60;
while (Input.location.status != LocationServiceStatus.Running && maxWait > 0)
{
yield return new WaitForSeconds(1);
maxWait--;
}

// If the service didn't initialize, this cancels location service use.
if (maxWait < 1)
{
Debug.LogError("GPS timed out with status " + Input.location.status);
yield break;
}

double latitude = Input.location.lastData.latitude;
double longitude = Input.location.lastData.longitude;
double altitude = 0.0; // We're using camera-relative positioning, so make the cube appear at the same height as the camera
Debug.LogError("GPS started successfully with lat: " + latitude + ", long: " + longitude);

// Instantiate a cube and scale it up for visibility (make it even bigger if you need to).
// Initially, the cube is invisible.
// The cube will appear nearby once WorldPositioningStatus == Available.
// Its position will be continually refined and updated by WPS.
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale *= 2.0f;
positioningHelper.AddOrUpdateObject(cube, latitude, longitude, altitude, Quaternion.identity);
}

// Update is called once per frame
void Update()
{

}
}

Comparing WPS and GPS

To show the difference between WPS and GPS, we have also provided an example script that creates a second cube using GPS and displays both at once. Try it out and see the difference for yourself!

Click to reveal the comparison script
using UnityEngine;
using Niantic.Lightship.AR.WorldPositioning;
using System;

public class AddWPSObjects : MonoBehaviour
{
[SerializeField] ARWorldPositioningObjectHelper positioningHelper;
[SerializeField] Camera trackingCamera;

// TODO: replace the coordinates here with your location. This defaults to the San Francisco ferry building!
double latitude = 37.795328;
double longitude = -122.392394;
double altitude = 0.0; // We're using camera-relative positioning so make the cube appear at the same height as the camera

// Start is called before the first frame update
void Start()
{
// instantiate a cube, scale it up for visibility (make it even bigger if you need), then update its location
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale *= 2.0f;
positioningHelper.AddOrUpdateObject(cube, latitude, longitude, altitude, Quaternion.identity);
}

// Create a second cube and move it to the position predicted using the raw GPS + compass
private GameObject gpsCube = null;
void Update()
{
// Create a second cube if we don't already have one:
if(gpsCube == null)
{
gpsCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
gpsCube.GetComponent<Renderer>().material.color = Color.red;
}

if (Input.location.isEnabledByUser)
{
double deviceLatitude = Input.location.lastData.latitude;
double deviceLongitude = Input.location.lastData.longitude;

Vector2 eastNorthOffsetMetres = EastNorthOffset(latitude,longitude, deviceLatitude, deviceLongitude);
Vector3 trackingOffsetMetres = Quaternion.Euler(0, 0, Input.compass.trueHeading)*new Vector3(eastNorthOffsetMetres[0], (float)altitude, eastNorthOffsetMetres[1]);
Vector3 trackingMetres = trackingCamera.transform.localPosition + trackingOffsetMetres;
gpsCube.transform.localPosition = trackingMetres;
}
}

public Vector2 EastNorthOffset(double latitudeDegreesA, double longitudeDegreesA, double latitudeDegreesB, double longitudeDegreesB)
{
double DEGREES_TO_METRES = 111139.0;
float lonDifferenceMetres = (float)(Math.Cos((latitudeDegreesA+latitudeDegreesB)*0.5* Math.PI / 180.0) * (longitudeDegreesA - longitudeDegreesB) * DEGREES_TO_METRES);
float latDifferenceMetres = (float)((latitudeDegreesA - latitudeDegreesB) * DEGREES_TO_METRES);
return new Vector2(lonDifferenceMetres,latDifferenceMetres);
}
}

Update the latitude and longitude fields with the GPS coordinates that you got from the Setting a Location section to compare WPS to GPS.