What would be the best approach in a script to translate real-world coordinates (lat, long) to a scene in Unity? I am taking real-world GPS (lat, long) coordinates in a 60km area and want to translate/map it to a terrain with size 6000x6000, the center of the terrain (3000, 0, 3000) is where I want to be the point of origin. Currently, I am using Haversine Formula to get the distance and bearing between the lat/long of the center point and to the lat/long of the other object(s) and try to scale the distance down so I can calculate the position of the other object in the scene. But I have a feeling there might be a better way to do this.
I place 2 objects in the game world and assign them lat/long values. I then use those to convert any value between the two coordinate systems.
Here's the code I wrote for it. I can't claim it is the most efficient or anything, but it has been working for me. I haven't tested the antimeridian parts of the code in game yet. Code (csharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class LatLon { //class that converts latitude / longitude to Unity position and the reverse //Got the formula from here //https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio //convert a coordinate from one set of ranges to another set of ranges private static float convertCoordinate(float oldValue, float oldMin, float oldMax, float newMin, float newMax) { float oldRange = oldMax - oldMin; float newRange = newMax - newMin; float returnValue = (((oldValue - oldMin) * newRange) / oldRange) + newMin; return returnValue; } //A LatLon Vector2 includes Latitude as the x value and Longitude as the y value //A Unity world coordinate has x as the west/east (longitude) and z as the north/sounth (latitude) //This method takes a LatLon Vector2 and translates it into this zone's game world coordinates //It does this by taking two points, a Noth West point and South East point in both LatLon and Unity world space positions to do the translation public static Vector3 GetUnityPosition(Vector2 latLonPosition, Vector2 northWestLatLon, Vector2 southEastLatLon, Vector3 northWestUnity, Vector3 southEastUnity) { //check if this zone covers the antimeridian (where 180 and -180 degress longitude meet) if (southEastLatLon.y < northWestLatLon.y) { //Add 360 to any negative longitude positions so that longitude values are lower the further west southEastLatLon = new Vector2(southEastLatLon.x, southEastLatLon.y + 360f); if (latLonPosition.y < 0f) { latLonPosition = new Vector2(latLonPosition.x, latLonPosition.y + 360f); } } float newUnityLat = convertCoordinate(latLonPosition.x, southEastLatLon.x, northWestLatLon.x, southEastUnity.z, northWestUnity.z); float newUnityLon = convertCoordinate(latLonPosition.y, southEastLatLon.y, northWestLatLon.y, southEastUnity.x, northWestUnity.x); Vector3 unityWorldPosition = new Vector3(newUnityLon, 200f, newUnityLat); return unityWorldPosition; } public static Vector2 GetLatLonPosition(Vector3 unityPosition, Vector2 northWestLatLon, Vector2 southEastLatLon, Vector3 northWestUnity, Vector3 southEastUnity) { bool antimeridian = false; //check if this zone covers the antimeridian (where 180 and -180 degress longitude meet) if (southEastLatLon.y < northWestLatLon.y) { antimeridian = true; //Add 360 to any negative longitude positions so that longitude values are lower the further west southEastLatLon = new Vector2(southEastLatLon.x, southEastLatLon.y + 360f); } float newlat = convertCoordinate(unityPosition.z, southEastUnity.z, northWestUnity.z, southEastLatLon.x, northWestLatLon.x); float newlon = convertCoordinate(unityPosition.x, southEastUnity.x, northWestUnity.x, southEastLatLon.y, northWestLatLon.y); if (antimeridian) { if (newlon > 180f) { newlon = newlon - 360f; } } Vector2 latLonPosition = new Vector2(newlat, newlon); return latLonPosition; } } This is all I place on the two objects in the scene. One of them is in the north west, and the other is in the south east. The vector2 is Latitude X, Longitude Y. Code (csharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class LocationMarker : MonoBehaviour { public Vector2 LatLon; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } Example usage from my own game. Might not make sense out of context, but the scripts have too much unrelated stuff to post here in full. These are from two different scripts. I have no idea why I named the vector2 the same as the other class, I didn't notice until now. That was by mistake. Code (csharp): ShipObject.transform.position = LatLon.GetUnityPosition(currentLatLong, northWestLocationObject.GetComponent<LocationMarker>().LatLon, southEastLocationObject.GetComponent<LocationMarker>().LatLon, northWestLocationObject.transform.position, southEastLocationObject.transform.position); Code (csharp): //updates the current latitude and longitude public void UpdateLatLong() { currentLatLong = LatLon.GetLatLonPosition(gameObject.transform.position, northWestLocation.GetComponent<LocationMarker>().LatLon, southEastLocation.GetComponent<LocationMarker>().LatLon, northWestLocation.transform.position, southEastLocation.transform.position); }
Glad to hear that By the way, on line 38 where I include a Y value of 200f is something specific to my game. Its a sailboat game and the ocean surface is always at Y value of 200f. So you'll probably want to do something with that.
@Joe-Censored thanks a lot for your suggestion! I've been trying to draw a map in correct scale for 2 days, trying all kinds of formulas and your solution finally worked.