Skip to main content
Version: 20 Mar 2024

Localization Map Examples

These scripts demonstrate various functionalities of the OpenXR Localization Map Feature, which is used for managing spaces in a mixed reality environment. The scripts are broken down into specific functionalities: requesting permissions, retrieving available spaces, localizing into a space, exporting and saving spaces, and importing previously saved spaces. Each script serves as an isolated example of a particular functionality, making it easier to understand and implement these functions in your own projects. They are designed to be used with the Unity game engine and the Magic Leap platform. Note that they should be used in a real device environment for proper functioning.

caution

This feature requires the Magic Leap 2 Localization Maps OpenXR Feature to be enabled in your project's OpenXR Settings (Window > XR Plugin Manager > OpenXR Settings).

Query Latest Localization Map Data

The GetLatestLocalizationMapData method can be used to query the current localization status at any point. The function will return true if the localization status changed the LocalizationResult was retrieved successfully.


using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class QueryLocalizationSample : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature = null;

private void Start()
{
// Obtain the instance of the localization Map Feature
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
// If it is not present return
if (localizationMapFeature == null || !localizationMapFeature.enabled)
{
Debug.LogError("Magic Leap Localization Map Feature does not exists or is disabled.");
return;
}

// Enable the localization Events
XrResult result = localizationMapFeature.EnableLocalizationEvents(true);
if (result != XrResult.Success)
{
Debug.LogError($"Failed to enable localization events with result: {result}");
}
}

private void Update()
{
CheckLocalizationStatus();
}

private void CheckLocalizationStatus()
{
MagicLeapLocalizationMapFeature.LocalizationEventData data;
if (localizationMapFeature.GetLatestLocalizationMapData(out data))
{
if (data.State == MagicLeapLocalizationMapFeature.LocalizationMapState.Localized)
{
Debug.Log($"Localized to space: {data.Map.Name}");
}
else
{
Debug.Log($"Localization State: {data.State}");
}
}
}
}

Localization Map Changed Event

The OnLocalizationChanged event is invoked when the localization status of the device changes. Subscribing to this event provides an easy way to receive updates about the localization status.

using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class QueryLocalizationSample : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature = null;

private void Start()
{
// Obtain the instance of the localization Map Feature
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
// If it is not present return
if (localizationMapFeature == null || !localizationMapFeature.enabled)
{
Debug.LogError("Magic Leap Localization Map Feature does not exists or is disabled.");
return;
}

// Enable the localization Events
XrResult result = localizationMapFeature.EnableLocalizationEvents(true);
if (result != XrResult.Success)
{
Debug.LogError($"Failed to enable localization events with result: {result}");
return;
}

MagicLeapLocalizationMapFeature.OnLocalizationChangedEvent += OnLocalizationChanged;
}

private void OnLocalizationChanged(MagicLeapLocalizationMapFeature.LocalizationEventData data)
{
// Log the localization status and the name of the localized space.
string status = localizationEventData.State.ToString();

string mapName = localizationEventData.State
== MagicLeapLocalizationMapFeature.LocalizationMapState.Localized ? localizationEventData.Map.Name : "None";

Debug.Log("Localization Status: " + status);
Debug.Log("Localized Map Name: " + mapName);
}
}

List Available Spaces

This script demonstrates how to fetch the list of available Spaces using the MLSpace API. Then the script prints the name of each Space to the debug log.


// This script fetches the list of available spaces.

using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class LocalizationMapTest : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature = null;

private void Start()
{
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
GetListOfAvailableSpaces();
}

public void GetListOfAvailableSpaces()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

XrResult result = localizationMapFeature.GetLocalizationMapsList(out MagicLeapLocalizationMapFeature.LocalizationMap[] maps);

if (result == XrResult.Success)
{
foreach (MagicLeapLocalizationMapFeature.LocalizationMap map in maps)
{
Debug.Log($"Map Name: {map.Name} \n"
+ $" Map UUID: {map.MapUUID} \n"
+ $" Map Type: {map.MapType}" );
}
}
}
}

Localizing into a Space

This script demonstrates how to localize a Magic Leap device into a specific space. The script automatically localizes to the first available space in the list, showcasing the basic process of using the localization feature.

// This script localizes into the first available space.

using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class LocalizationMapTest : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature = null;
private MagicLeapLocalizationMapFeature.LocalizationMap[] maps;

private void Start()
{
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
QueryMaps();
LocalizeIntoMap();
}

public void QueryMaps()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;
localizationMapFeature.GetLocalizationMapsList(out maps);
}

public void LocalizeIntoMap()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

if(maps.Length == 0)
{
Debug.LogError("Must query available maps first");
return;
}

XrResult result = localizationMapFeature.RequestMapLocalization(maps[0].MapUUID);
Debug.Log($"Localize request result: {result}");
}
}

Exporting and Saving Spaces

This script is concerned with exporting a space and saving it to a file. Exporting a space allows you to share it between devices. In this script, the first available space is exported and then saved to a binary file.

caution

This features requires the com.magicleap.permission.SPACE_IMPORT_EXPORT permission to be requested at runtime and enabled in your project's Manifest Settings (Edit > Project Settings > Magic Leap > Manifest Settings).

// This script exports the first available space and saves it to a file.

using System.IO;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class LocalizationMapTest : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature = null;
private MagicLeapLocalizationMapFeature.LocalizationMap[] maps;
private string spaceFileName = "exported_space.bin";
private string filePath => Path.Combine(Application.persistentDataPath, spaceFileName);

private void Start()
{
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
QueryMaps();
ExportMap();
}

public void QueryMaps()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;
localizationMapFeature.GetLocalizationMapsList(out maps);
}

public void ExportMap()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

if (maps.Length == 0)
{
Debug.LogError("Must query available maps first");
return;
}

XrResult result = localizationMapFeature.ExportLocalizatioMap(maps[0].MapUUID, out byte[] mapData);
Debug.Log($"Export map result: {result}");
if (result == XrResult.Success)
{
SaveBytesToFile(mapData);
}
}

private void SaveBytesToFile(byte[] binaryData)
{
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
fileStream.Write(binaryData, 0, binaryData.Length);
}

if (File.Exists(filePath))
{
Debug.Log("Binary data saved to file: " + filePath);
}
else
{
Debug.LogError("Failed to save binary data to file: " + filePath);
}
}
}

Importing Spaces

This script demonstrates importing a previously saved space. Importing a space involves loading a previously exported space, allowing the device to recognize and localize into it. In this case, the script attempts to import a space from a previously saved binary file.

caution

This features requires the com.magicleap.permission.SPACE_IMPORT_EXPORT permission to be requested at runtime and enabled in your project's Manifest Settings (Edit > Project Settings > Magic Leap > Manifest Settings).

// This script imports a previously saved space.

using System.IO;
using UnityEngine;
using UnityEngine.XR.MagicLeap;

public class ImportSpaceExample : MonoBehaviour
{
private string spaceFileName = "exported_space.bin";
private string filePath;

void Start()
{
filePath = Path.Combine(Application.persistentDataPath, spaceFileName);
ImportLastSpace();
}

public void ImportLastSpace()
{
if (!File.Exists(filePath))
{
Debug.LogWarning("No spaces have been exported, cannot import last exported space.");
return;
}

byte[] binaryData;

using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
{
binaryData = new byte[fileStream.Length];
fileStream.Read(binaryData, 0, binaryData.Length);
}

MLSpace.SpaceData data = new MLSpace.SpaceData();
data.Size = (uint)binaryData.Length;
data.Data = binaryData;

MLResult.Code result = MLSpace.ImportSpace(in data, out MLSpace.SpaceInfo info);

if (result == MLResult.Code.Ok)
{
Debug.Log("Space imported successfully");
}
else
{
Debug.LogError("Failed to import space");
}
}
}

Complete Example

This example script that script demonstrates how to use the Open Magic Leap Localization Map Features to manage and interact with virtual spaces. The script manages a list of available Maps, keeps track of the localization status, and supports the importing and exporting of spaces. The script logs relevant status updates and errors are logged to the Debug Log, providing visibility to the script's operation.

caution

This features requires the com.magicleap.permission.SPACE_IMPORT_EXPORT permission to be requested at runtime and enabled in your project's Manifest Settings (Edit > Project Settings > Magic Leap > Manifest Settings).

using System;
using System.IO;
using UnityEngine;
using UnityEngine.XR.MagicLeap;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
using UnityEngine.XR.OpenXR.NativeTypes;

public class LocalizationMapTest : MonoBehaviour
{
private MagicLeapLocalizationMapFeature localizationMapFeature;
private MagicLeapLocalizationMapFeature.LocalizationMap[] maps;
private string saveFilePath => Path.Combine(Application.persistentDataPath, spaceFileName);
private string spaceFileName = "exported_space.bin";

private readonly MLPermissions.Callbacks permissionCallbacks = new MLPermissions.Callbacks();

private void Start()
{
// Setup event listeners for permissions and localization events.
permissionCallbacks.OnPermissionGranted += OnPermissionGranted;
permissionCallbacks.OnPermissionDenied += OnPermissionDenied;
permissionCallbacks.OnPermissionDeniedAndDontAskAgain += OnPermissionDenied;

// Request permissions for space import/export and define the path for the exported space file.
MLPermissions.RequestPermission(MLPermission.SpaceImportExport, permissionCallbacks);
}

void OnPermissionDenied(string permission)
{
Debug.LogError($"{permission} Denied, Test Won't Function. Disabling");
enabled = false;
}

void OnPermissionGranted(string permission)
{
// When the requested permission is granted, test the API features.
InitializeFeatureTest();
}

private void InitializeFeatureTest()
{
// Get the Magic Leap Localization Map Feature and make sure it is accessible
localizationMapFeature = OpenXRSettings.Instance.GetFeature<MagicLeapLocalizationMapFeature>();
if (localizationMapFeature == null || !localizationMapFeature.enabled)
{
Debug.LogError("Magic Leap Localization Map Feature is unavailable or disabled. Disabling script.");
return;
}
localizationMapFeature.EnableLocalizationEvents(true);

MagicLeapLocalizationMapFeature.OnLocalizationChangedEvent += OnLocalizationChangedEvent;
ExecuteFeatureTests();
}

private void ExecuteFeatureTests()
{
CheckLocalizationStatus();
QueryMaps();
ExportMap();
ImportMap();
LocalizeIntoMap();
}

void OnDestroy()
{
// Unsubscribe event listeners when the object is destroyed.
permissionCallbacks.OnPermissionGranted -= OnPermissionGranted;
permissionCallbacks.OnPermissionDenied -= OnPermissionDenied;
permissionCallbacks.OnPermissionDeniedAndDontAskAgain -= OnPermissionDenied;

if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

MagicLeapLocalizationMapFeature.OnLocalizationChangedEvent -= OnLocalizationChangedEvent;
}

// Gets an array of all the available maps
public void QueryMaps()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

XrResult result = localizationMapFeature.GetLocalizationMapsList(out maps);

if (result == XrResult.Success)
{
foreach (MagicLeapLocalizationMapFeature.LocalizationMap map in maps)
{
Debug.Log($"Map Name: {map.Name} \n" + $" Map UUID: {map.MapUUID} \n" + $" Map Type: {map.MapType}" );
}
}
}

// Poll the localization status manually.
void CheckLocalizationStatus()
{
if (localizationMapFeature.GetLatestLocalizationMapData(out MagicLeapLocalizationMapFeature.LocalizationEventData data))
{
OnLocalizationChangedEvent(data);
}
}

// Called when the localization status changes via the MagicLeapLocalizationMapFeature.OnLocalizationChangedEvent callback.
private void OnLocalizationChangedEvent(MagicLeapLocalizationMapFeature.LocalizationEventData localizationEventData)
{
// Update and log the localization status and the name of the localized space.
string status = localizationEventData.State.ToString();

string mapName = localizationEventData.State
== MagicLeapLocalizationMapFeature.LocalizationMapState.Localized ? localizationEventData.Map.Name : "None";

Debug.Log("Localization Status: " + status);
Debug.Log("Localized Map Name: " + mapName);
}

// Export the first Map from the map array to a file on the device using the SaveMapToFile() function
public void ExportMap()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

if (maps.Length == 0)
{
Debug.LogError("Must query available maps first");
return;
}

var result = localizationMapFeature.ExportLocalizatioMap(maps[0].MapUUID, out byte[] mapData);
Debug.Log($"Export map result: {result}");
SaveBytesToFile(mapData, saveFilePath);
}

// Import a Map from a saved file path
public void ImportMap()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

byte[] binaryData = LoadFileAsBytes(saveFilePath);
if (binaryData.Length > 0)
{
// Import the byte array as a map
XrResult result = localizationMapFeature.ImportLocalizationMap(binaryData, out string importedMapID);
Debug.Log($"Import map result: {result}. Imported Map UUID: {importedMapID}");
}
}

// Localize into the first map queried from the device
public void LocalizeIntoMap()
{
if (localizationMapFeature == null || !localizationMapFeature.enabled)
return;

if (maps.Length == 0)
{
Debug.LogError("Must query available maps first");
return;
}

XrResult result = localizationMapFeature.RequestMapLocalization(maps[0].MapUUID);
Debug.Log($"Localize request result: {result}");
}


// Saves binary data into a file on the device
private void SaveBytesToFile(byte[] binaryData, string filePath)
{
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
fileStream.Write(binaryData, 0, binaryData.Length);
}

if (File.Exists(filePath))
{
Debug.Log("Binary data saved to file: " + filePath);
}
else
{
Debug.LogError("Failed to save binary data to file: " + filePath);
}
}

// Loads a file as bytes if successful. Otherwise returns an empty byte array.
private byte[] LoadFileAsBytes(string filePath)
{
//Read file from bytes
if (!File.Exists(filePath))
{
Debug.LogWarning("No maps have been exported, cannot import last exported map.");
return Array.Empty<byte>();
}

using FileStream fileStream = new FileStream(filePath, FileMode.Open);
byte[] binaryData = new byte[fileStream.Length];
int bytesRead = fileStream.Read(binaryData, 0, binaryData.Length);
if (bytesRead < binaryData.Length)
{
Debug.LogError($"Could not read all bytes from: {filePath} . Import failed!");
return Array.Empty<byte>();
}

return binaryData;
}
}