Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Discussion How to get the geographic coordinates of a Vector3

Discussion in 'Scripting' started by Stader, Jan 6, 2023.

  1. Stader

    Stader

    Joined:
    Jun 8, 2018
    Posts:
    15
    I have as a reference 3 geographic points with latitude, longitude and height. I also know the XYZ positions of these points and I know the XYZ position of the point I have to find out the latitude and longitude.

    The question is, is it possible to calculate the latitude and longitude of this point with all these references?

    An image to represent what I'm trying to do:

    Sem título.png
     
  2. chemicalcrux

    chemicalcrux

    Joined:
    Mar 16, 2017
    Posts:
    726
    This sounds a lot like what you have to do in GIS (Geographic Information Systems).

    Are these points on a perfect sphere or ellipsoid? Or are they real-world positions?
     
  3. Stader

    Stader

    Joined:
    Jun 8, 2018
    Posts:
    15
    These are real world positions. This fourth point will be placed by the user anywhere around those 3 red dots.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,120
    Well, if the positions are relatively close (it seems your 3 reference points are just about 150m apart), there's no point in going through an ellipsoid as at such short distances you can simply interpolate linearly. If instead of a couple hundreds of meters you would have a couple hundred kilometers, it would be an issue. So if your distances are say around a kilometer, using linear interpolation would work just fine.

    The easiest approach would be to use barycentric coordinates. You can use my Barycentric helper struct. Simply use the constructor that takes Vector3 coordinates
    Code (CSharp):
    1. public Barycentric(Vector3 aV1, Vector3 aV2, Vector3 aV3, Vector3 aP)
    As you can see it takes your 3 reference points as well as your in-between point to calculate the barycentric coordinate. Once you have it, you can use the resulting barycentric coordinate to interpolate any other values across the 3 corners by using one of the Interpolate methods. Since you have 2d lat / lon coordinates, you can use the Vector2 version. Just make sure you pass the coriners in the same order as in the constructor.

    Just in case the pastebin link dies some day:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public struct Barycentric
    4. {
    5.     public float u;
    6.     public float v;
    7.     public float w;
    8.     public Barycentric(float aU, float aV, float aW)
    9.     {
    10.         u = aU;
    11.         v = aV;
    12.         w = aW;
    13.     }
    14.     public Barycentric(Vector2 aV1, Vector2 aV2, Vector2 aV3, Vector2 aP)
    15.     {
    16.         Vector2 a = aV2 - aV3, b = aV1 - aV3, c = aP - aV3;
    17.         float aLen = a.x * a.x + a.y * a.y;
    18.         float bLen = b.x * b.x + b.y * b.y;
    19.         float ab = a.x * b.x + a.y * b.y;
    20.         float ac = a.x * c.x + a.y * c.y;
    21.         float bc = b.x * c.x + b.y * c.y;
    22.         float d = aLen * bLen - ab * ab;
    23.         u = (aLen * bc - ab * ac) / d;
    24.         v = (bLen * ac - ab * bc) / d;
    25.         w = 1.0f - u - v;
    26.     }
    27.  
    28.     public Barycentric(Vector3 aV1, Vector3 aV2, Vector3 aV3, Vector3 aP)
    29.     {
    30.         Vector3 a = aV2 - aV3, b = aV1 - aV3, c = aP - aV3;
    31.         float aLen = a.x * a.x + a.y * a.y + a.z * a.z;
    32.         float bLen = b.x * b.x + b.y * b.y + b.z * b.z;
    33.         float ab = a.x * b.x + a.y * b.y + a.z * b.z;
    34.         float ac = a.x * c.x + a.y * c.y + a.z * c.z;
    35.         float bc = b.x * c.x + b.y * c.y + b.z * c.z;
    36.         float d = aLen * bLen - ab * ab;
    37.         u = (aLen * bc - ab * ac) / d;
    38.         v = (bLen * ac - ab * bc) / d;
    39.         w = 1.0f - u - v;
    40.     }
    41.     public Barycentric(Vector4 aV1, Vector4 aV2, Vector4 aV3, Vector4 aP)
    42.     {
    43.         Vector4 a = aV2 - aV3, b = aV1 - aV3, c = aP - aV3;
    44.         float aLen = a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w;
    45.         float bLen = b.x * b.x + b.y * b.y + b.z * b.z + b.w * b.w;
    46.         float ab = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
    47.         float ac = a.x * c.x + a.y * c.y + a.z * c.z + a.w * c.w;
    48.         float bc = b.x * c.x + b.y * c.y + b.z * c.z + b.w * c.w;
    49.         float d = aLen * bLen - ab * ab;
    50.         u = (aLen * bc - ab * ac) / d;
    51.         v = (bLen * ac - ab * bc) / d;
    52.         w = 1.0f - u - v;
    53.     }
    54.     public Barycentric(Color aV1, Color aV2, Color aV3, Color aP)
    55.     {
    56.         Color a = aV2 - aV3, b = aV1 - aV3, c = aP - aV3;
    57.         float aLen = a.r * a.r + a.g * a.g + a.b * a.b;
    58.         float bLen = b.r * b.r + b.g * b.g + b.b * b.b;
    59.         float ab = a.r * b.r + a.g * b.g + a.b * b.b;
    60.         float ac = a.r * c.r + a.g * c.g + a.b * c.b;
    61.         float bc = b.r * c.r + b.g * c.g + b.b * c.b;
    62.         float d = aLen * bLen - ab * ab;
    63.         u = (aLen * bc - ab * ac) / d;
    64.         v = (bLen * ac - ab * bc) / d;
    65.         w = 1.0f - u - v;
    66.     }
    67.  
    68.     public bool IsInside
    69.     {
    70.         get
    71.         {
    72.             return (u >= 0.0f) && (u <= 1.0f) && (v >= 0.0f) && (v <= 1.0f) && (w >= 0.0f); //(w <= 1.0f)
    73.         }
    74.     }
    75.     public Vector2 Interpolate(Vector2 v1, Vector2 v2, Vector2 v3)
    76.     {
    77.         return v1 * u + v2 * v + v3 * w;
    78.     }
    79.     public Vector3 Interpolate(Vector3 v1, Vector3 v2, Vector3 v3)
    80.     {
    81.         return v1 * u + v2 * v + v3 * w;
    82.     }
    83.     public Vector4 Interpolate(Vector4 v1, Vector4 v2, Vector4 v3)
    84.     {
    85.         return v1 * u + v2 * v + v3 * w;
    86.     }
    87.     public Color Interpolate(Color v1, Color v2, Color v3)
    88.     {
    89.         return v1 * u + v2 * v + v3 * w;
    90.     }
    91. }

    Note that the barycentric coordinate can also represent any position outside the 3 reference points. So any point in the plane that the 3 reference corners describe can be mapped that way.
     
    Stader and Kurt-Dekker like this.
  5. Stader

    Stader

    Joined:
    Jun 8, 2018
    Posts:
    15
    Just out of curiosity, in this case the best option is to interpolate linearly because of the short distances, but if they were distances like 4 - 5 kilometers I would have to use an ellipsoid, but how do I use it?

    PS: Even using the barycentric coordinates worked, thanks a lot!
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,120
    Well 5km are still way too small to worry about. 5km are about 2.7 nautical miles which equals "2.7/60" degrees, or 0.045°. The max difference in height between two points that far apart is only about 2m. The sideways distortion is even smaller.

    Of course the larger the distances, the more relevant the difference between a triangle and a spherical triangle gets. it's still a continuous smooth space, it just doesn't work well with longer distances as the distortion gets larger and larger the further you go.

    You can use trigonometry to calculate the position on an actual sphere. Though the question is what level of precision you actually need. Earth is not a perfect sphere but roughly a spheroid. Even then the actual topography of the location plays a major role as well. You can not "calculate" that out of thin air since you would need actual topographic data to get accurate results. Though the math would be orders of magnitude more complex if you want to factor all that information in. So depending on the usecase it's probably not worth it.
     
    Stader likes this.