1
2016
Latitude Longitude Position On 3D Sphere (V2)
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Related Posts
15 Comments + Add Comment
Leave a comment
Recent posts
- [GreaseMonkey] Unity Forum Fixer
- UnityHub: Make Hub application background Translucent
- Customize SpriteShapeRenderer quality (but has issues)
- Editor tool: Copy selected gameobject’s names into clipboard as rows (for Excel)
- Editor tool: Replace string in selected gameobject’s names
- UnityHub: Enable built-in Login Dialog (no more browser login/logout issues!)
- Use TikTok-TTS in Unity (with WebRequest)
- Create Scene Thumbnail Image using OnSceneSaved & OnPreviewGUI
- webgl+javascript TTS
- Using Moonsharp (LUA) + Unity Webgl
- Using 3D gameobject prefabs with Unity Tilemap + NavMesh Surface
- Custom Unity Hub Project Template Preview Image/Video (using HTML+CSS in package description)
Recent Comments
- Vector3 maths for dummies! on
- UnityHub: Make Hub application background Translucent on
- UnityHub: Make Hub application background Translucent on
- Install Android SDK+JDK+NDK for Unity (without AndroidStudio or Unity Hub) on
- Install Android SDK+JDK+NDK for Unity (without AndroidStudio or Unity Hub) on
- [Asset Store] Point Cloud Viewer & Tools on
- [Asset Store] Point Cloud Viewer & Tools on
- ffmpeg stream raw video into Unity Texture2D on
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, there is no new package, but new script (the on on the post) https://gist.github.com/unitycoder/8c632b39d0893a8d6c40#file-latlong-cs
Also need to use the sphere mode linked in the post,
https://www.opengl.org/discussion_boards/showthread.php/176762-looking-for-a-simple-sphere-obj-file?p=1233337&viewfull=1#post1233337
Hi there,
Thanks for providing this code.
I have added the new latlang code to the old version of your project but the lat long is a bit off. I was going to use the sphere you suggested to Bill Rigas but the dropbox link does not exist anymore.
Also, i need to upscale the sphere to around 100 times bigger but i can get the marker working relative to this size, its just sitting inside the sphere
Any help would be much appreciated!
Cheers,
Jamie
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?
uploaded sphere.obj to dropbox and updated the link in post.
https://dl.dropboxusercontent.com/u/227470828/UnityCoder/public/sphere.obj
*select with right click and Save link as..
Fantastic, thank you!
Rotate sphere to look at camera based on lat lon
https://forum.unity3d.com/threads/rotate-sphere-to-look-at-camera-based-on-lat-lon.474738/#post-3100679
[…] The following is my code for this conversion, this code is based on the code here […]