Mar
1
2016

Latitude Longitude Position On 3D Sphere (V2)

latitude_longitude_unity_1

This is just a c# conversion and few updated notes for the old post : http://unitycoder.com/blog/2011/08/09/latitude-longitude-on-3d-sphere/

Instructions
– Attach the script below to your sphere (Unity sphere UV mapping is not suitable so I downloaded this sphere model and scaled transform to 5)
– Add earth texture map to the sphere (its included in the old version package)
– Add a marker sphere, scale it to 0.3 or so
– Assign marker sphere to “marker” field on the script
– Set lat long values in the inspector (default value is for London, get more values from here www.findlatitudeandlongitude.com )
– Note: Rotate your earth sphere by 180 degrees in Y axis (otherwise marker goes to wrong position) *Probably not needed
– The hit play to test it.

Resources:
– Map texture was taken from somewhere here : http://visibleearth.nasa.gov/view_cat.php?categoryID=1484
– Original math code is from: http://www.actionscript.org/forums/index.php#post722957 *link broken..

 

Script source


// https://forum.unity.com/threads/latitude-and-longitude-coordinates-in-vector-3-unity.1446328/#post-9066883
static readonly float QUAD = .5f * MathF.PI;
static readonly float TAU = 2f * MathF.PI;
 
static public Vector2 FromPolarAngle(float theta)
  => new Vector2(MathF.Cos(theta), MathF.Sin(theta));
 
static public float ToPolarAngle(Vector2 v)
  => Mod(MathF.Atan2(v.y, v.x), TAU);
 
/// <summary> Conversion from spherical to cartesian coordinates. </summary>
/// <param name="theta"> Polar angle 0..Tau (top-down). </param>
/// <param name="phi"> Azimuthal angle -Pi/2..+Pi/2 where 0 represents equator. </param>
/// <returns> A unit vector. </returns>
static public Vector3 FromSpherical(float theta, float phi) {
  var th = FromPolarAngle(theta); var ph = FromPolarAngle(QUAD – phi);
  return new Vector3(th.x * ph.y, ph.x, th.y * ph.y);
}
 
static public Vector3 FromSpherical(Vector2 coords)
  => FromSpherical(coords.x, coords.y);
 
/// <summary> Conversion from cartesian to spherical coordinates.
/// Returns <see langword="true"/> on success, <see langword="false"/>
/// otherwise. (Values are defined in any case.) </summary>
/// <param name="spherical"> The resulting spherical unit coordinates. </param>
/// <param name="magnitude"> Optional magnitude of the input vector.
/// Leave at 1 when input vector is unit to avoid normalization. </param>
static public bool ToSpherical(Vector3 v, out Vector2 spherical, float magnitude = 1f) {
  var theta = MathF.Atan2(v.z, v.x);
  theta = theta < 0f? theta + TAU : theta;
  var im = (magnitude == 1f)? 1f : 1f / SubstZero(MathF.Max(0f, magnitude), float.NaN);
  var phi = QUAD – MathF.Acos(v.y * im);
  var success = true;
  if(float.IsNaN(theta)) { theta = 0f; success = false; }
  if(float.IsNaN(phi)) { phi = 0f; success = false; }
  spherical = new Vector2(theta, phi);
  return success;
}
 
static public float SubstZero(float v, float subst, float epsilon = 1E-6f) => MathF.Abs(v) < epsilon? subst : v;
static public float Mod(float n, float m) => (m <= 0f)? 0f : (n %= m) < 0f? n + m : n;
// You can now easily transform some 3D coordinates to longitude / latitude (in degrees) if you do
static public Vector2 ToLongLat(Vector3 coords, Vector3 center = default) {
coords -= center;
ToSpherical(coords, out var spherical, coords.magnitude);
if(spherical.x < 0f) spherical.x += 2f * MathF.PI;
return spherical * (180f / MathF.PI);
}
// And back (from longitude / latitude in degrees, to some exact 3D point on the surface)
// https://forum.unity.com/threads/latitude-and-longitude-coordinates-in-vector-3-unity.1446328/#post-9066883
static public Vector3 FromLongLat(Vector2 longLat, Vector3 center = default, float radius = 1f)
  => center + radius * FromSpherical(longLat * (MathF.PI / 180f));

view raw

gistfile1.txt

hosted with ❤ by GitHub


// blog post: https://unitycoder.com/blog/2016/03/01/latitude-longitude-position-on-3d-sphere-v2/
using UnityEngine;
public class LatLong : MonoBehaviour
{
public Transform marker; // marker object
public float radius = 5; // globe ball radius (unity units)
public float latitude = 51.5072f; // lat
public float longitude = 0.1275f; // long
// Use this for initialization
void Start()
{
// calculation code taken from
// @miquael http://www.actionscript.org/forums/showthread.php3?p=722957#post722957
// convert lat/long to radians
latitude = Mathf.PI * latitude / 180;
longitude = Mathf.PI * longitude / 180;
// adjust position by radians
latitude -= 1.570795765134f; // subtract 90 degrees (in radians)
// and switch z and y (since z is forward)
float xPos = (radius) * Mathf.Sin(latitude) * Mathf.Cos(longitude);
float zPos = (radius) * Mathf.Sin(latitude) * Mathf.Sin(longitude);
float yPos = (radius) * Mathf.Cos(latitude);
// move marker to position
marker.position = new Vector3(xPos, yPos, zPos);
}
}

view raw

LatLong.cs

hosted with ❤ by GitHub


// https://forum.unity.com/threads/calculate-latitude-and-longitude-using-uv-coordinates-of-a-sphere.1579935/#post-9775563
// Convert UV coordinate to World Coordinate
    public Vector3 UVTo3D(Vector2 uv, Mesh mesh, Transform transform)
    {
        int[] tris = mesh.triangles;
        Vector2[] uvs = mesh.uv;
        Vector3[] verts = mesh.vertices;
       
        for (int i = 0; i < tris.Length; i += 3)
        {
            Vector2 u1 = uvs[tris[i]];      // get the triangle UVs*
            Vector2 u2 = uvs[tris[i + 1]];
            Vector2 u3 = uvs[tris[i + 2]];
 
            // Calculate triangle area – if zero, skip it
            float a = Area(u1, u2, u3);
            if (a == 0)
                continue;
 
            // Calculate barycentric coordinates of u1, u2 and u3
            // If any is negative then point is outside the triangle, skip it
 
            float a1 = Area(u2, u3, uv)/a;
            if (a1 < 0)
                continue;
 
            float a2 = Area(u3, u1, uv)/a;
            if (a2 < 0)
                continue;
 
            float a3 = Area(u1, u2, uv)/a;
            if (a3 < 0)
                continue;
 
            // Point inside the triangle – find mesh position by interpolation…
            Vector3 p3D = a1 * verts[tris[i]] + a2 * verts[tris[i + 1]] + a3 * verts[tris[i + 2]];
 
            // return it in world coordinates:
            return transform.TransformPoint(p3D);
        }
        return Vector3.zero;
    }
 
    /// Calculate signed triangle area using a kind of “2D cross product”:
    private float Area(Vector2 p1, Vector2 p2, Vector2 p3)
    {
        Vector2 v1 = p1 – p3;
        Vector2 v2 = p2 – p3;
        return (v1.x * v2.y – v1.y * v2.x) / 2;
    }

view raw

UVTo3D.cs

hosted with ❤ by GitHub


15 Comments + Add Comment

  • Hi again, I would love to see your final project
    So I downloaded the white sphere.7z
    I copied it in, dragged the old texture on to it
    sized it to 5,5,5
    added a marker sphere, attached it to script

    Mine seems to be in the right place if I do not rotate the sphere by 180?

    • oh i see, actually i did remove the empty parent from the downloaded sphere, that was probably causing the rotation.

      • Actually, regarding the rotation of the sphere along the Y-axis, I found that I had to rotate mine by the amount that the ‘seam’ in the wrapped texture was off from Y == 0.In my case, that was -84.4.
        I was unable to get access to the linked dropbox sphere so I made my own in Blender, UV-mapped an Earth texture to it and exported it to .obj. In Unity, I added the same Earth texture and it used the Blender UV to seamlessly map to the sphere. I added a single pixel column to the edge of the texture in Photoshop so I could see the seam on the sphere. Then I rotated the sphere on the Y-axis until the seam lined up with where Y == 0 using the gridlines.
        Close enough for what I need it for…
        https://dl.dropboxusercontent.com/u/306957072/rotated_texture_seam.png
        https://dl.dropboxusercontent.com/u/306957072/rotated_texture_seam_running.png

  • Hi mate. Have you tested the script with Unity 5? I’ve download the older version package (I couldn’t find the V2 one) and just run the scene_lat_long. The marker appeared by far away London. I’ve then tested the scripts of the current version with no luck either. The model and scene is the one you provided by your V1 package; hence there is no chance to have an incompatible earth model. I need to be able to find correctly a 3d point on earth sphere for my project but so far I had no luck with the maths. What else could be the problem? Any help will be much appreciated.

  • HI,

    latitude -= 1.570795765134f

    From where did you get the above value?

    • I think it was in the original code already.. (but the link is broken)

      • Yes it was in the original code. May I please know on what basis the value is set?

  • Haversine formula: giving great-circle distances between two points on a sphere from their longitudes and latitudes
    https://rosettacode.org/wiki/Haversine_formula#C.23

  • Hi,

    The sphere model links no longer work. They take you to the forum with the post, but the links in the forum are dead… Any chance you can upload them to GitHub or somewhere else?

  • […] The following is my code for this conversion, this code is based on the code here […]

Leave a comment

Connect

Twitter View LinkedIn profile Youtube Github Join Discord Twitch Instagram BlueSky

UnityLauncherPro

Get UnityLauncherPRO and work faster with Unity Projects!
*free unity hub alternative

@unitycoder_com

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.