Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Unity Reflect Develop with GPS issue

Discussion in 'Unity Reflect' started by MAATpe, Aug 9, 2023.

  1. MAATpe

    MAATpe

    Joined:
    Apr 12, 2023
    Posts:
    11
    I previously posted an article, and the URL link is here: https://forum.unity.com/threads/ars...pace-unityengine-xr-in-unity-reflect.1454125/. My goal is to place a revit model at a specific GPS coordinates in AR mode. I had previously tried using the AR + GPS Location plugin, but I gave up because I kept failing to write the assemblies definition. Additionally, AR + GPS Location requires capturing the actual model object and unity reflect doesn't seem to have an actual model object in AR mode, so I have now switched to writing code to place the model at a specific GPS location in AR mode myself.

    My solution is as follows: I can obtain the GPS coordinates of the mobile phone and the GPS coordinates of the project base point in the Revit model. Within Unity's AR coordinate system, I can also capture the AR coordinates of the MARS camera. I've read some articles that says the mobile phone's GPS location can be the same as the MARS camera's AR coordinates. Assuming this premise is correct, I can calculate the distance (in meters) and offset angle between the mobile phone's GPS coordinates and the model's GPS coordinates. I then add the calculated distance and angle to the MARS camera's AR coordinates to compute the model's coordinate position within the AR coordinate system. Once I have the model's coordinates in the AR system, I can update the value of m_RootSelector.GetValue().position to simulate placing the model at the real-world GPS coordinates.

    The current problem I'm encountering is that Unity's GPS latitude and longitude values are not accurate enough. I've already set updateDistanceInMeters to 1 meter with UnityEngine.Input.location.Start(1, 1);, but the values of horizontalAccuracy and verticalAccuracy remain at 2000, and the timestamp value is constantly 1691461476.448. Even when I move more than 1 meter, the latitude and longitude values don't update. I've also tried turning off the Wi-Fi and Bluetooth precision on my phone, but the latitude and longitude still don't update. I've also tried Geolocation API, but the latitude and longitude values are too inaccurate. I later found that this issue might be related to Unity 2020's GPS functionality not working well with Android 12 or later versions, as mentioned here: https://forum.unity.com/threads/gps-not-work-well-on-android-12.1212534/. I tried upgrading Unity Reflect to 2021 and 2022 versions, but a bunch of errors appeared, and the official GitHub mentions that Unity Reflect only support the latest LTS version of Unity 2019.

    I would like to ask the following:

    1. Is there anything wrong with calculating the distance and offset angle between the mobile phone's GPS and the model's project base point GPS, then adding them to the MARS camera to compute the model's AR coordinates?
    2. What solutions are there for the problem of Unity capturing GPS locations?
    3. Will Unity Reflect's GitHub provide support for the Unity 2022 version in the future?
    Below is my code:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using SharpFlux.Dispatching;
    5. using Unity.Reflect.Viewer.Core.Actions;
    6. using UnityEngine;
    7. using UnityEngine.Networking;
    8. using UnityEngine.UI;
    9. using UnityEngine.Reflect.Viewer.Core;
    10. using UnityEngine.Reflect.Viewer.Core.Actions;
    11. using UnityEngine.XR.ARFoundation;
    12.  
    13. namespace Unity.Reflect.Viewer.UI
    14. {
    15.     public class ARSideBarController : MonoBehaviour
    16.     {
    17. #pragma warning disable CS0649
    18.         [SerializeField]
    19.         ToolButton m_BackButton;
    20.  
    21.         [SerializeField]
    22.         ToolButton m_ScaleButton;
    23.  
    24.         [SerializeField]
    25.         ToolButton m_TargetButton;
    26.  
    27.         [SerializeField]
    28.         ToolButton m_HeightButton;
    29.  
    30.         [SerializeField]
    31.         GameObject m_ScaleRadial;
    32. #pragma warning restore CS0649
    33.  
    34.         bool m_ToolbarsEnabled;
    35.         List<IDisposable> m_DisposeOnDestroy = new List<IDisposable>();
    36.  
    37.         IUISelector<IARInstructionUI> m_CurrentARInstructionUIGetter;
    38.         LeftSideBarController m_LeftSideBarController;
    39.         IUISelector<Transform> m_RootSelector;
    40.         public GameObject scrollbarObject;
    41.         public Vector3 originalPosition;
    42.         public Transform targetObject;
    43.         public Text GPSText;
    44.         public double latitude;
    45.         public double longitude;
    46.  
    47.         MARS.MARSCamera m_MARSCamera;
    48.         public Vector2 phoneGPSLocation;
    49.         public Vector3 cameraPosition;
    50.         public Quaternion cameraRotation;
    51.         private const double ARModelLat = x.xxxxxxxf;
    52.         private const double ARModelLon = x.xxxxxxxf;
    53.  
    54.  
    55.         void Awake()
    56.         {
    57.             m_DisposeOnDestroy.Add(UISelectorFactory.createSelector<bool>(UIStateContext.current, nameof(IToolBarDataProvider.toolbarsEnabled),
    58.                 data =>
    59.                 {
    60.                     m_ToolbarsEnabled = data;
    61.                 }));
    62.  
    63.             m_LeftSideBarController = GameObject.FindObjectOfType<LeftSideBarController>();
    64.  
    65.             m_DisposeOnDestroy.Add(UISelectorFactory.createSelector<bool>(ARToolStateContext.current, nameof(IARToolStatePropertiesDataProvider.scaleEnabled),
    66.                 data =>
    67.                 {
    68.                     m_ScaleButton.transform.parent.gameObject.SetActive(m_ToolbarsEnabled && data);
    69.                     m_LeftSideBarController.UpdateLayout();
    70.                 }));
    71.  
    72.             m_DisposeOnDestroy.Add(UISelectorFactory.createSelector<bool>(ARToolStateContext.current, nameof(IARToolStatePropertiesDataProvider.previousStepEnabled),
    73.                 data =>
    74.                 {
    75.                     m_BackButton.button.interactable = m_ToolbarsEnabled && data;
    76.                 }));
    77.  
    78.  
    79.             m_DisposeOnDestroy.Add(m_CurrentARInstructionUIGetter = UISelectorFactory.createSelector<IARInstructionUI>(ARContext.current, nameof(IARModeDataProvider.currentARInstructionUI)));
    80.  
    81.             m_BackButton.buttonClicked += OnBackButtonClicked;
    82.             m_ScaleButton.buttonClicked += OnScaleButtonClicked;
    83.             m_TargetButton.buttonClicked += OnTargetButtonClicked;
    84.             m_HeightButton.buttonClicked += OnHeightButtonClicked;
    85.             m_RootSelector = UISelectorFactory.createSelector<Transform>(PipelineContext.current, nameof(IPipelineDataProvider.rootNode));
    86.         }
    87.  
    88.         void OnDestroy()
    89.         {
    90.             m_DisposeOnDestroy.ForEach(x => x.Dispose());
    91.             m_RootSelector?.Dispose();
    92.         }
    93.  
    94.         void OnTargetButtonClicked()
    95.         {
    96.         }
    97.  
    98.         void OnScaleButtonClicked()
    99.         {
    100.             // help mode
    101.             if (HelpDialogController.SetHelpID(SetHelpModeIDAction.HelpModeEntryID.Scale))
    102.                 return;
    103.             Dispatcher.Dispatch(SetActiveToolBarAction.From(SetActiveToolBarAction.ToolbarType.ARScaleDial));
    104.             ARScaleRadialUIController.m_previousToolbar = SetActiveToolBarAction.ToolbarType.ARSidebar;
    105.  
    106.             var radialPosition = m_ScaleRadial.transform.position;
    107.             radialPosition.y = m_ScaleButton.transform.position.y;
    108.             m_ScaleRadial.transform.position = radialPosition;
    109.         }
    110.  
    111.         void OnBackButtonClicked()
    112.         {
    113.             // help mode
    114.             if (HelpDialogController.SetHelpID(SetHelpModeIDAction.HelpModeEntryID.Back))
    115.                 return;
    116.             m_CurrentARInstructionUIGetter.GetValue().Back();
    117.         }
    118.  
    119.         void OnHeightButtonClicked()
    120.         {
    121.             Text m_GPSText = GameObject.Find("GPSText").GetComponent<Text>();
    122.             m_GPSText.text = "Height button clicked." + "\n";
    123.  
    124.             originalPosition = m_RootSelector.GetValue().localPosition;
    125.  
    126.             var scrollbar = scrollbarObject.GetComponent<Scrollbar>();
    127.  
    128.             scrollbarObject.SetActive(!scrollbarObject.activeSelf);
    129.  
    130.             if (scrollbarObject.activeSelf)
    131.             {
    132.                 // Register the value change event of the scrollbar
    133.                 scrollbar.onValueChanged.AddListener(OnScrollbarValueChanged);
    134.                 StartCoroutine(GetGPSLocation());
    135.             }
    136.             else
    137.             {
    138.                 // Unregister the value change event of the scrollbar
    139.                 scrollbar.onValueChanged.RemoveAllListeners();
    140.                 UnityEngine.Input.location.Stop();
    141.             }
    142.  
    143.         }
    144.  
    145.         void OnScrollbarValueChanged(float value)
    146.         {
    147.             var rootTransform = m_RootSelector.GetValue();
    148.             var currentPosition = rootTransform.localPosition;
    149.             Text m_GPSText = GameObject.Find("GPSText").GetComponent<Text>();
    150.             m_GPSText.text += "Scrollbar MAX: " + ScrollbarMinValue + "\n";
    151.             m_GPSText.text += "Scrollbar MIN: " + ScrollbarMaxValue + "\n";
    152.  
    153.             // Map the scrollbar value from 0 to 1 to the new value range
    154.             float mappedValue = Mathf.Lerp(ScrollbarMinValue, ScrollbarMaxValue, value);
    155.  
    156.             // Update the Y coordinate using the mapped value and the original position
    157.             currentPosition.y = originalPosition.y + mappedValue;
    158.             rootTransform.localPosition = currentPosition;
    159.         }
    160.  
    161.         IEnumerator GetGPSLocation()
    162.         {
    163.             Text m_GPSText = GameObject.Find("GPSText").GetComponent<Text>();
    164.  
    165.             // Check permissions
    166. #if UNITY_EDITOR
    167.             // No need to handle permissions in the editor
    168. #elif UNITY_ANDROID
    169.             if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.CoarseLocation)) {
    170.                 UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.CoarseLocation);
    171.             }
    172.  
    173.             // Check if the user has enabled location services
    174.             if (!UnityEngine.Input.location.isEnabledByUser) {
    175.                 m_GPSText.text = "Android and Location not enabled" + "\n";
    176.                 yield break;
    177.             }
    178. #elif UNITY_IOS
    179.             if (!UnityEngine.Input.location.isEnabledByUser) {
    180.                 m_GPSText.text = "IOS and Location not enabled" + "\n";
    181.                 yield break;
    182.             }
    183. #endif
    184.  
    185.             // Start GPS service
    186.             UnityEngine.Input.location.Start(1.0f, 1.0f);
    187.  
    188.             // Wait for GPS service to initialize
    189.             while (UnityEngine.Input.location.status == LocationServiceStatus.Initializing)
    190.             {
    191.                 yield return new WaitForSeconds(1);
    192.             }
    193.  
    194.             // Check if GPS service is available
    195.             if (UnityEngine.Input.location.status == LocationServiceStatus.Failed)
    196.             {
    197.                 // New error handling code
    198.                 m_GPSText.text = "GPS service cannot be started, please check your device settings and make sure GPS service is enabled." + "\n";
    199.                 yield break;
    200.             }
    201.             else
    202.             {
    203.                 m_GPSText.text += "GPS has started." + "\n";
    204.             }
    205.  
    206.             int counter = 0;
    207.  
    208.             // Get GPS coordinates
    209.             while (UnityEngine.Input.location.status == LocationServiceStatus.Running)
    210.             {
    211.                 // Increment counter by 1 for each iteration
    212.                 counter++;
    213.                 m_GPSText.text += "Counter value: " + counter + "\n";
    214.                 latitude = UnityEngine.Input.location.lastData.latitude;
    215.                 longitude = UnityEngine.Input.location.lastData.longitude;
    216.                 m_GPSText.text += "Mobile GPS Latitude: " + latitude + "\n";
    217.                 m_GPSText.text += "Mobile GPS Longitude: " + longitude + "\n";
    218.                 float horizontalAccuracy = UnityEngine.Input.location.lastData.horizontalAccuracy;
    219.                 float verticalAccuracy = UnityEngine.Input.location.lastData.verticalAccuracy;
    220.                 double timestamp = UnityEngine.Input.location.lastData.timestamp;
    221.                 m_GPSText.text += "Horizontal Accuracy: " + horizontalAccuracy + "\n";
    222.                 m_GPSText.text += "Vertical Accuracy: " + verticalAccuracy + "\n";
    223.                 m_GPSText.text += "Timestamp: " + timestamp + "\n";
    224.                 m_GPSText.text += "MARS Camera Position: " + cameraPosition + "\n";
    225.                 // Get MARS camera's coordinates
    226.                 m_MARSCamera = Camera.main.GetComponent<MARS.MARSCamera>();
    227.                 if (m_MARSCamera != null)
    228.                 {
    229.                     // Get MARS camera's position
    230.                     cameraPosition = m_MARSCamera.transform.position;
    231.                     cameraPosition.y = m_RootSelector.GetValue().position.y;
    232.                     m_GPSText.text += "MARS Camera Position: " + cameraPosition + "\n";
    233.                 }
    234.  
    235.                 // Use the defined latitude and longitude constants for the AR model
    236.                 double distance = CalculateDistance(latitude, longitude, ARModelLat, ARModelLon);
    237.                 double angle = CalculateAngle(latitude, longitude, ARModelLat, ARModelLon);
    238.                 m_GPSText.text += "Distance: " + distance + "\n";
    239.  
    240.                 // Convert the angle from radians to degrees
    241.                 float angleInDegrees = (float)(angle * 180.0 / Math.PI) + 180;
    242.                 m_GPSText.text += "Angle (degrees): " + angle + "\n";
    243.  
    244.                 // Calculate the new position of the AR model in the AR coordinate system
    245.                 Vector3 newPosition = cameraPosition + Quaternion.Euler(0, angleInDegrees, 0) * new Vector3((float)distance, 0, 0);
    246.                 m_GPSText.text += "MARS Camera Coordinates to add distance and angle: " + Quaternion.Euler(0, angleInDegrees, 0) * new Vector3((float)distance, 0, 0) + "\n";
    247.                 m_GPSText.text += "New Coordinates of AR model: " + newPosition + "\n";
    248.  
    249.                 // Update the AR model's position
    250.                 m_RootSelector.GetValue().position = newPosition;
    251.  
    252.                 yield return new WaitForSeconds(5); // Update every 5 seconds
    253.             }
    254.  
    255.         }
    256.  
    257.         // Convert degrees to radians
    258.         double DegreesToRadians(double degrees)
    259.         {
    260.             return degrees * Math.PI / 180.0;
    261.         }
    262.  
    263.         // Calculate the distance between two GPS coordinates using the Haversine formula
    264.         double CalculateDistance(double lat1, double lon1, double lat2, double lon2)
    265.         {
    266.             double R = 6371e3;
    267.             double phi1 = DegreesToRadians(lat1);
    268.             double phi2 = DegreesToRadians(lat2);
    269.             double deltaPhi = DegreesToRadians(lat2 - lat1);
    270.             double deltaLambda = DegreesToRadians(lon2 - lon1);
    271.  
    272.             double a = Math.Sin(deltaPhi / 2) * Math.Sin(deltaPhi / 2) +
    273.                        Math.Cos(phi1) * Math.Cos(phi2) *
    274.                        Math.Sin(deltaLambda / 2) * Math.Sin(deltaLambda / 2);
    275.             double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
    276.  
    277.             return R * c;
    278.         }
    279.  
    280.         // Calculate the angle between two GPS coordinates using the difference in longitude formula in spherical trigonometry
    281.         double CalculateAngle(double lat1, double lon1, double lat2, double lon2)
    282.         {
    283.             double deltaLon = DegreesToRadians(lon2 - lon1);
    284.             double y = Math.Sin(deltaLon) * Math.Cos(DegreesToRadians(lat2));
    285.             double x = Math.Cos(DegreesToRadians(lat1)) * Math.Sin(DegreesToRadians(lat2)) -
    286.                        Math.Sin(DegreesToRadians(lat1)) * Math.Cos(DegreesToRadians(lat2)) * Math.Cos(deltaLon);
    287.             return Math.Atan2(y, x);
    288.         }
    289.  
    290.     }
    291. }
    292.  
     
  2. steweye

    steweye

    Joined:
    Nov 29, 2022
    Posts:
    18
    Hi, MAATpe.

    1. Why would you rely solely on a mobile phone's GPS for AR? The accuracy of a mobile phone's GPS alone is somewhere around 10 meters. so displaying revit models in AR won't be acurate enough,plus how would you deal with(if it's neccecery in your case) going onto 1st floor or any kind of significant elevation? Today, there is no flawless(or even close to it) indoor positioning system available for navigation. You will have to depend on a lot of different types of sensors to get the accurate position(WiFi, Bluetooth, magnetic positioning...) and GPS alone is,in my opinion, quite ineffective for this.

    2. I'm not familiar with Unity's solution for this kind of problem.

    3. Nobody knows. This question, and variations of it, have been posed countless times before. The Unity team has yet to provide any responses; their engagement with Reflect products(and users of) is virtually non-existent (some might even say they have abandoned it and might soon discontinue support). But I don't know, though...

    Cheers.
     
  3. MAATpe

    MAATpe

    Joined:
    Apr 12, 2023
    Posts:
    11
    @steweye

    Thank you for your reply. Regarding the first point, let's assume that there is 100 Revit models in 100 different places, it is inconvenient to show the models in AR mode in all places using the method currently provided by Unity Reflect. So my idea is that the project base points of the Revit models in 100 places all provide GPS locations. After putting this information into the database, the specified GPS location is fetched according to the Unity Reflect project name, and placing the Revit models at the GPS locations is the simplest way I can think of right now.
     
  4. MAATpe

    MAATpe

    Joined:
    Apr 12, 2023
    Posts:
    11
    Hello everyone, due to the low accuracy of the GPS feature in Unity, I switched to Android Studio to develop a plugin for Unity Reflect. This plugin calls a function from the Android library to return the GPS location. The GPS feature I implemented in Android Studio is much more precise. Although the final result might still have some discrepancies in location due to GPS accuracy, using the AR mode in Table Top mode allows for adjustments in the model's position and rotation angle, making this approach acceptable. Thank you all for your help.
     
  5. PoisonToad

    PoisonToad

    Joined:
    Aug 24, 2023
    Posts:
    1
    FYI, The code I was using Started GPS and then Stopped GPS over and over.
    UnityEngine.Input.location.Start
    UnityEngine.Input.location.Stop
    The GPS almost never updated correctly and was accurate at best to around 300 ft or so...
    I commented out the "//UnityEngine.Input.location.Stop"
    GPS accuracy instantly went to accurate to 1 foot and ran like a charm.
    From my understanding GPS stopped working with newer versions of Android but my fix resolved.
    I clean toilets but my uneducated guess is turning it on and off every frame just breaks it at this point.
    Start it and leave it on??????