How can I show real-time GPS coordinates on a terrain created at unity?

I get a terrain on terrain.map and created it in unity. I have a gps device and I want to show the real-time location information I received from this device on this terrain. Is this possible? How can I do if possible? Thank you from now.

Most GPS devices can be connected to, via TCP/IP, at which point the send data in NMEA format. You’d just need to decode the position sentences, convert global coordinates to your terrain in whatever way makes sense, and display accordingly.

If you have the GPS track file, this is what you are looking for. This can be converted to a real-time app to show exact coordinates/position of the device on a streaming 3D map with its built-in automatic objects placement on terrain tiles.

4 Likes

has terraland a GPS track file? because i will buy terraland

The video above shows a custom enterprise project we’ve been working on for a company but the whole project is based on TerraLand’s public streaming features and the custom part is that we’ve written a wrapper to convert GPS coords from a GPX file into Unity’s world position and stream terrains around the player. Let me know if you need the solution and we’ll negotiate on that.

Imagine world X axis representing longitude and Z latitude. Then figure out what lon/lat Rect is your terrain representing. The rest is just simple arithmetic.

İs there any tool like this? İ didn’t see this before.

No, it’s just my own project-specific tools. But here, take a look at those value types I use for this, you may find these helpful in some capacity:

using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;


[System.Serializable]
public struct Coordinate
{
    #region FIELDS


    public float latitude;
    public float longitude;
   

    #endregion
    #region OPERATORS


    public static Coordinate operator + ( Coordinate a , Coordinate b ) => new Coordinate{ latitude = a.latitude + b.latitude , longitude = a.longitude + b.longitude };
    public static Coordinate operator - ( Coordinate a , Coordinate b ) => new Coordinate{ latitude = a.latitude - b.latitude , longitude = a.longitude - b.longitude };
    public static Coordinate operator * ( Coordinate coord , float f ) => new Coordinate{ latitude = coord.latitude * f , longitude = coord.longitude * f };
    public static bool operator == ( Coordinate a , Coordinate b ) => a.latitude==b.latitude && a.longitude==b.longitude;
    public static bool operator != ( Coordinate a , Coordinate b ) => a.latitude!=b.latitude && a.longitude!=b.longitude;


    #endregion
    #region PUBLIC METHODS


    public override bool Equals ( object obj )
    {
        if( obj==null || (obj is Coordinate)==false ) { return false; }
        return this==(Coordinate)obj;
    }

    override public string ToString () => JsonUtility.ToJson( this );

    public override int GetHashCode ()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 31 + this.latitude.GetHashCode();
            hash = hash * 31 + this.longitude.GetHashCode();
            return hash;
        }
    }
   
    /// <summary> Calculates metric distance between two coordinates </summary>
    public static double HaversineDistance ( Coordinate A , Coordinate B )
    {
        double DegreesToRadians ( double degrees ) { return (math.PI / 180d) * degrees; }
        double R = 6371e3d;//metres
        double φ1 = DegreesToRadians( A.latitude );
        double φ2 = DegreesToRadians( B.latitude );
        double Δφ = DegreesToRadians( B.latitude - A.latitude );
        double Δλ = DegreesToRadians( B.longitude - A.longitude );
        double a = math.sin( Δφ / 2d ) * math.sin( Δφ / 2d ) +
                math.cos( φ1 ) * math.cos( φ2 ) *
                math.sin( Δλ / 2d ) * math.sin( Δλ / 2d );
        double c = 2d * math.atan2( math.sqrt( a ) , math.sqrt( 1d - a ) );
        double d = R * c;
        return d;
    }

    #endregion
}

[System.Serializable]
public struct CoordinateRect
{
    #region FIELDS
   
   
    public Coordinate a;
    public Coordinate b;

   
    #endregion
    #region PROPERTIES


    public Coordinate center { get{ return a + (b-a)*0.5f; } }

    public Coordinate cornerBottomLeft { get{ return new Coordinate{ latitude = math.min( a.latitude , b.latitude ) , longitude = math.min( a.longitude , b.longitude ) }; } }
    public Coordinate cornerTopLeft { get{ return new Coordinate{ latitude = math.max( a.latitude , b.latitude ) , longitude = math.min( a.longitude , b.longitude ) }; } }
    public Coordinate cornerTopRight { get{ return new Coordinate{ latitude = math.max( a.latitude , b.latitude ) , longitude = math.max( a.longitude , b.longitude ) }; } }
    public Coordinate cornerBottomRight { get{ return new Coordinate{ latitude = math.min( a.latitude , b.latitude ) , longitude = math.max( a.longitude , b.longitude ) }; } }
   
    public float width { get{ return math.abs( a.longitude - b.longitude ); } }
    public float height { get{ return math.abs( a.latitude - b.latitude ); } }
    public float area { get{ return width * height; } }

    public float heightMeters { get{ return (float)Coordinate.HaversineDistance( this.cornerTopRight , this.cornerBottomRight ) ; } }
    public float widthMeters { get{ return (float)Coordinate.HaversineDistance( this.cornerBottomLeft , this.cornerBottomRight ) ; } }
    public float areaMeters { get{ return widthMeters * heightMeters; } }
   

    #endregion
    #region OPERATORS


    public static CoordinateRect operator - ( CoordinateRect A , CoordinateRect B ) { return new CoordinateRect { a = A.cornerBottomLeft - B.cornerBottomLeft , b = A.cornerTopRight - B.cornerTopRight }; }
    public static CoordinateRect operator - ( CoordinateRect rect , Coordinate point ) { return new CoordinateRect { a = rect.cornerBottomLeft - point , b = rect.cornerTopRight - point }; }
    public static CoordinateRect operator + ( CoordinateRect A , CoordinateRect B ) { return new CoordinateRect { a = A.cornerBottomLeft + B.cornerBottomLeft , b = A.cornerTopRight + B.cornerTopRight }; }
    public static CoordinateRect operator + ( CoordinateRect rect , Coordinate point ) { return new CoordinateRect { a = rect.cornerBottomLeft + point , b = rect.cornerTopRight + point }; }

    #endregion
    #region PUBLIC METHODS

   
    override public string ToString () { return JsonUtility.ToJson( this ); }

    public override int GetHashCode ()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 31 + this.a.GetHashCode();
            hash = hash * 31 + this.b.GetHashCode();
            return hash;
        }
    }

   
    public void Encapsulate ( CoordinateRect other )
    {
        var encapsulated = Encapsulate( this , other );
        this.a = encapsulated.a;
        this.b = encapsulated.b;
    }
    public static CoordinateRect Encapsulate ( CoordinateRect A , CoordinateRect B )
    {
        Coordinate bottomLeftA = A.cornerBottomLeft;
        Coordinate bottomLeftB = B.cornerBottomLeft;
        Coordinate topRightA = A.cornerTopRight;
        Coordinate topRightB = B.cornerTopRight;
        CoordinateRect result = new CoordinateRect {
            a = new Coordinate {
                latitude = math.min( bottomLeftA.latitude , bottomLeftB.latitude ) ,
                longitude = math.min( bottomLeftA.longitude , bottomLeftB.longitude )
            } ,
            b = new Coordinate {
                latitude = math.max( topRightA.latitude , topRightB.latitude ) ,
                longitude = math.max( topRightA.longitude , topRightB.longitude )
            }
        };
        return result;
    }

   
    public void Overlap ( CoordinateRect other )
    {
        var overlaped = Overlap( this , other );
        this.a = overlaped.a;
        this.b = overlaped.b;
    }
    public static CoordinateRect Overlap ( CoordinateRect A , CoordinateRect B )
    {
        Coordinate bottomLeftA = A.cornerBottomLeft;
        Coordinate bottomLeftB = B.cornerBottomLeft;
        Coordinate topRightA = A.cornerTopRight;
        Coordinate topRightB = B.cornerTopRight;
        CoordinateRect result = new CoordinateRect {
            a = new Coordinate {
                latitude = math.max( bottomLeftA.latitude , bottomLeftB.latitude ) ,
                longitude = math.max( bottomLeftA.longitude , bottomLeftB.longitude )
            } ,
            b = new Coordinate {
                latitude = math.min( topRightA.latitude , topRightB.latitude ) ,
                longitude = math.min( topRightA.longitude , topRightB.longitude )
            }
        };
        return result;
    }

   
    public bool Overlaps ( CoordinateRect other )
    {
        return Overlaps( this , other );
    }
    public static bool Overlaps ( CoordinateRect A , CoordinateRect B )
    {
        Coordinate Ac = A.center;
        Coordinate Bc = B.center;
        return ( math.abs(Ac.longitude - Bc.longitude) * 2f < (A.width + B.width)) && ( math.abs(Ac.latitude - Bc.latitude) * 2f < (A.height + B.height));
    }


    /// <summary> Calculates overlap relative to primary rect, results is normalized (according to primary width/height) </summary>
    public FloatRange2 RelativeOverlap ( CoordinateRect secondary )
    {
        return RelativeOverlap( this , secondary );
    }
    /// <summary> Calculates overlap relative to primary rect, results is normalized (according to primary width/height) </summary>
    public static FloatRange2 RelativeOverlap ( CoordinateRect primary , CoordinateRect secondary )
    {
        CoordinateRect overlap = Overlap( primary , secondary );
        CoordinateRect overlapRelativeToPrimary = overlap - primary.cornerBottomLeft;
        Coordinate overlapBottomLeft = overlapRelativeToPrimary.cornerBottomLeft;
        Coordinate overlapTopRight = overlapRelativeToPrimary.cornerTopRight;
        float primaryWidth = primary.width;
        float primaryHeight = primary.height;
        var result = new FloatRange2 {
            x = new FloatRange {
                min = overlapBottomLeft.longitude / primaryWidth ,
                max = overlapTopRight.longitude / primaryWidth
            } ,
            y = new FloatRange {
                min = overlapBottomLeft.latitude / primaryHeight ,
                max = overlapTopRight.latitude / primaryHeight
            }
        };
      return result;
    }


    #endregion
}

[System.Serializable]
public struct FloatRange
{
    public float min, max;
    public float range => max - min;
    override public string ToString () { return $"{ '{' }{ UnityEngine.JsonUtility.ToJson( this ) }{ '}' }"; }
}

[System.Serializable]
public struct FloatRange2
{
    public FloatRange x, y;
   
    public float width => x.max - x.min;
    public float height => y.max - y.min;

    override public string ToString () { return UnityEngine.JsonUtility.ToJson( this ); }
}
5 Likes

thank you soo much, I will examine in detail.

[Worth noticing.]( TerraLand 3 - Streaming Huge Real-World & Custom Terrains For Open-World Environments page-5#post-4133215)

As @TerraUnity pointed out - yup, these are a naive calculations to get you off the ground and will only work for small terrain samples. These were totally enough in my case but may become unusable in cases where more precision is required.

Hey @esoob , you may want to check this solution grabbed directly from my project (requires struct types from posts above)
CoordinateToWorldPosition

public static Vector3 CoordinateToWorldPosition ( Terrain terrain , CoordinateRect terrainRect , Coordinate coordinate )
{
    var size = terrain.terrainData.bounds.size;
    var center = terrainRect.center;
    var localPosition = new Vector3
    {
        z = ( size.z / terrainRect.height ) * ( coordinate.latitude - center.latitude ) ,
        x = ( size.x / terrainRect.width ) * ( coordinate.longitude - center.longitude )
    };
    return terrain.transform.TransformPoint( localPosition );
}

WorldPositionToCoordinate

public static Coordinate WorldPositionToCoordinate ( Terrain terrain , CoordinateRect terrainRect , Vector3 worldPosition )
{
    var localPosition = terrain.transform.InverseTransformPoint( worldPosition );
    var extents = terrain.terrainData.bounds.extents;
    var center = terrainRect.center;
    return new Coordinate
    {
        latitude = center.latitude + (localPosition.z / extents.z) * (terrainRect.height * 0.5f) ,
        longitude = center.longitude + (localPosition.x / extents.x) * (terrainRect.width * 0.5f)
    };
}
2 Likes

FYI, there is a geo-referencing scene in TerraLand now which outputs very accurate world positions in Mercator projection even in areas above 1000 km.

Thank you for your help, I get my gps coordinate and ı create a terrain with @TerraUnity and now İ will show my position on the terrain. actually i hope i can…

In TerraLand package, find the scene “LatLon2UnityWorldSpace” in the Scenes directory in project and load the scene to see the implementation and source code of the Mercator version of geo-coding and its comparison with the less accurate one. x.com

yes, I see this before, but when i want to add a build on specific coordinate ı used “LatLon2UnityWorldSpace” te build come to right coordinate but under the terrain.

The geo-referencing script does not take into account the surface height when places objects and only sets x & z axis on the object transform but returning terrain/surface height under object is simple which you can add that feature like we did in the above video.

Hi @TerraUnity I bought your TeraWorld asset and wanted to confirm if the LatLon2UnityWorldSpace is available in TerraWorld or only in TerraLand?

It’s only available in TerraLand but if you own TerraLand, you can use that script in any other projects like TerraWorld to do geo-referencing as it’s a helper script which works in any scenes as long as you know the info it needs.