Convert GPS position to Vector3 (X, Y, Z)

Hello, I’m looking for a long time for a way to convert a GPS location (lat, lon) to a unity proportional location, I found a script by MichaelTaylor3D that I honestly don’t know if it works, but I can’t even tell when I try.

Try using your script in another Script I made, but I don’t know if I’m doing things right or what I should change.

This is the script I found on the internet:

``````//Copyright 2013 MichaelTaylor3D
//www.michaeltaylor3d.com

using UnityEngine;

public sealed class GPSEncoder {

/////////////////////////////////////////////////
//////-------------Public API--------------//////
/////////////////////////////////////////////////

/// <summary>
/// Convert UCS (X,Y,Z) coordinates to GPS (Lat, Lon) coordinates
/// </summary>
/// <returns>
/// Returns Vector2 containing Latitude and Longitude
/// </returns>
/// <param name='position'>
/// (X,Y,Z) Position Parameter
/// </param>
public static Vector2 USCToGPS(Vector3 position)
{
return GetInstance().ConvertUCStoGPS(position);
}

/// <summary>
/// Convert GPS (Lat, Lon) coordinates to UCS (X,Y,Z) coordinates
/// </summary>
/// <returns>
/// Returns a Vector3 containing (X, Y, Z)
/// </returns>
/// <param name='gps'>
/// (Lat, Lon) as Vector2
/// </param>
public static Vector3 GPSToUCS(Vector2 gps)
{
return GetInstance().ConvertGPStoUCS(gps);
}

/// <summary>
/// Convert GPS (Lat, Lon) coordinates to UCS (X,Y,Z) coordinates
/// </summary>
/// <returns>
/// Returns a Vector3 containing (X, Y, Z)
/// </returns>
public static Vector3 GPSToUCS(float latitude, float longitude)
{
return GetInstance().ConvertGPStoUCS(new Vector2(latitude,longitude));
}

/// <summary>
/// Change the relative GPS offset (Lat, Lon), Default (0,0),
/// used to bring a local area to (0,0,0) in UCS coordinate system
/// </summary>
/// <param name='localOrigin'>
/// Referance point.
/// </param>
public static void SetLocalOrigin(Vector2 localOrigin)
{
GetInstance()._localOrigin = localOrigin;
}

/////////////////////////////////////////////////
//////---------Instance Members------------//////
/////////////////////////////////////////////////

#region Singleton
private static GPSEncoder _singleton;

private GPSEncoder()
{

}

private static GPSEncoder GetInstance()
{
if(_singleton == null)
{
_singleton = new GPSEncoder();
}
return _singleton;
}
#endregion

#region Instance Variables
private Vector2 _localOrigin = Vector2.zero;
private float _LatOrigin { get{ return _localOrigin.x; }}
private float _LonOrigin { get{ return _localOrigin.y; }}

private float metersPerLat;
private float metersPerLon;
#endregion

#region Instance Functions
private void FindMetersPerLat(float lat) // Compute lengths of degrees
{
// Set up "Constants"
float m1 = 111132.92f;    // latitude calculation term 1
float m2 = -559.82f;        // latitude calculation term 2
float m3 = 1.175f;      // latitude calculation term 3
float m4 = -0.0023f;        // latitude calculation term 4
float p1 = 111412.84f;    // longitude calculation term 1
float p2 = -93.5f;      // longitude calculation term 2
float p3 = 0.118f;      // longitude calculation term 3

// Calculate the length of a degree of latitude and longitude in meters
metersPerLat = m1 + (m2 * Mathf.Cos(2 * (float)lat)) + (m3 * Mathf.Cos(4 * (float)lat)) + (m4 * Mathf.Cos(6 * (float)lat));
metersPerLon = (p1 * Mathf.Cos((float)lat)) + (p2 * Mathf.Cos(3 * (float)lat)) + (p3 * Mathf.Cos(5 * (float)lat));
}

private Vector3 ConvertGPStoUCS(Vector2 gps)
{
FindMetersPerLat(_LatOrigin);
float zPosition  = metersPerLat * (gps.x - _LatOrigin); //Calc current lat
float xPosition  = metersPerLon * (gps.y - _LonOrigin); //Calc current lat
return new Vector3((float)xPosition, 0, (float)zPosition);
}

private Vector2 ConvertUCStoGPS(Vector3 position)
{
FindMetersPerLat(_LatOrigin);
Vector2 geoLocation = new Vector2(0,0);
geoLocation.x = (_LatOrigin + (position.z)/metersPerLat); //Calc current lat
geoLocation.y = (_LonOrigin + (position.x)/metersPerLon); //Calc current lon
return geoLocation;
}
#endregion
}
``````

and this would be the other Script I made, which uses this previous one:

``````using System.Collections;
using UnityEngine;

public class GPS: MonoBehaviour
{

public static GPS Instance { set; get; }

public float latitude;
public float longitude;

private void Start()
{
Instance = this;
Vector3 localOrigin = GPSEncoder.GPSToUCS(12.31565f, -12.53465f);
Vector2 vector2 = new Vector2(localOrigin.x, localOrigin.z);
GPSEncoder.SetLocalOrigin(vector2);
StartCoroutine(StartLocationService());
}

private void Update()
{
Vector3 coord = GPSEncoder.GPSToUCS(latitude, longitude);
transform.position = coord;
}

private IEnumerator StartLocationService()
{
if(!Input.location.isEnabledByUser)
{
Debug.Log("Usuario no activo el GPS");
yield break;
}

Input.location.Start();
int maxWait = 20;
while(Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
{
yield return new WaitForSeconds(1);
maxWait--;
}

if(maxWait <= 0)
{
yield break;
}

if (Input.location.status == LocationServiceStatus.Failed)
{
Debug.Log("Imposible determinar la ubicación del dispositivo");
yield break;
}

latitude = Input.location.lastData.latitude;
longitude = Input.location.lastData.longitude;
yield break;
}
}
``````

To make it clear what I want:

I want to navigate a real life 2D map, so I want X coordinate to be the 0, 0, 0 point in unity, so that when I’m standing there, it’s on the map and I can see it on the mobile device. The problem is that with what I did, the camera to which I apply the script goes to I don’t know where (to a place all black, I can’t even see anything when I go there in the scene, it seems like an unknown dimension), without However, in its position X and Z it is not very far from the point 0, 0, I don’t understand what happens there.

I hope you can help me and if I have an error, you can explain me what it is

PS: I’m sorry if the explanation is not very well understood.

First check the math. Mock up known GPS coordinate inputs and make sure they come through as expected.

In other words if you expect your kitchen to be at (-1,-5) and your bedroom to be at (+3,+1), put those two GPS coordinates in and make sure that’s what you get.

Beyond that, keep debugging!

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

• the code you think is executing is not actually executing at all
• the code is executing far EARLIER or LATER than you think
• the code is executing far LESS OFTEN than you think
• the code is executing far MORE OFTEN than you think
• the code is executing on another GameObject than you think it is
• you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling `Debug.Log()` statements through your code to display information in realtime.

• is this code even running? which parts are running? how often does it run? what order does it run in?
• what are the values of the variables involved? Are they initialized? Are the values reasonable?
• are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as `Debug.Log("Problem!",this);`

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: How To - Capturing Device Logs on iOS or this answer for Android: How To - Capturing Device Logs on Android

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

When in doubt, print it out!™

Note: the `print()` function is an alias for Debug.Log() provided by the MonoBehaviour class.

Thank you very much, it’s something super simple but I didn’t even think about it.

Now I understand why the camera goes to an unknown dimension:

Should I think that the script I got from the internet doesn’t work? I don’t know if it’s me doing something wrong or that’s it.

I’m naturally skeptical so that’s the first place my brain goes.

But there’s also the possibility there’s nothing wrong with the script and it is just intended to be used a different way. That would be up to the creator to correctly express the expectations of your use of the script, as well as to characterize the limitations of that script.

It’s kinda like if I give you a car to use but then it turns out there are no wheels on it. “I expect you to bring your OWN wheels, I’m not letting you wear out my wheels!” It’s all in the “what are the assumptions here.”

But there’s also the problem that latitude / longitude coordinates are on a sphere (the world), yet all paper maps are nominally flat. This is achieved by a process called projection and there are many ways: conformal, mercator, cylindrical, etc., and each of those ways has input parameters (reference datum, distortion corrections, etc.)