Location.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. using UnityEngine;
  2. using System;
  3. using UnityEngine.Serialization;
  4. namespace ARLocation
  5. {
  6. public enum AltitudeMode {
  7. GroundRelative,
  8. DeviceRelative,
  9. Absolute,
  10. Ignore
  11. };
  12. /// <summary>
  13. /// Represents a geographical location.
  14. /// </summary>
  15. [Serializable]
  16. public class Location
  17. {
  18. [FormerlySerializedAs("latitude")] [Tooltip("The latitude, in degrees.")]
  19. public double Latitude;
  20. [FormerlySerializedAs("longitude")] [Tooltip("The longitude, in degrees.")]
  21. public double Longitude;
  22. [FormerlySerializedAs("altitude")] [Tooltip("The altitude, in meters.")]
  23. public double Altitude;
  24. [FormerlySerializedAs("altitudeMode")]
  25. [Space(4)]
  26. [Tooltip("The altitude mode. 'Absolute' means absolute altitude, relative to the sea level. 'DeviceRelative' meas it is " +
  27. "relative to the device's initial position. 'GroundRelative' means relative to the nearest detected plane, and 'Ignore' means the " +
  28. "altitude is ignored (equivalent to setting it to zero).")]
  29. public AltitudeMode AltitudeMode = AltitudeMode.GroundRelative;
  30. [FormerlySerializedAs("label")] [Tooltip("An optional label for the location.")]
  31. public string Label = "";
  32. public bool IgnoreAltitude => AltitudeMode == AltitudeMode.Ignore;
  33. /// <summary>
  34. /// Gets the horizontal vector.
  35. /// </summary>
  36. /// <value>The horizontal vector.</value>
  37. public DVector2 HorizontalVector => new DVector2(Latitude, Longitude);
  38. public Location(double latitude = 0.0, double longitude = 0.0, double altitude = 0.0)
  39. {
  40. Latitude = latitude;
  41. Longitude = longitude;
  42. Altitude = altitude;
  43. }
  44. /// <summary>
  45. /// Clones this instance.
  46. /// </summary>
  47. /// <returns>The clone.</returns>
  48. public Location Clone()
  49. {
  50. return new Location()
  51. {
  52. Label = Label,
  53. Latitude = Latitude,
  54. Longitude = Longitude,
  55. Altitude = Altitude,
  56. AltitudeMode = AltitudeMode
  57. };
  58. }
  59. public override string ToString()
  60. {
  61. return "(" + Latitude + ", " + Longitude + ", " + Altitude + ")";
  62. }
  63. public DVector3 ToDVector3()
  64. {
  65. return new DVector3(Longitude, Altitude, Latitude);
  66. }
  67. public Vector3 ToVector3()
  68. {
  69. return ToDVector3().toVector3();
  70. }
  71. /// <summary>
  72. /// Given a Location returns the corresponding (x, y, z) ECEF coordinates.
  73. /// </summary>
  74. /// <param name="l"></param>
  75. /// <returns></returns>
  76. public static DVector3 LocationToEcef(Location l) {
  77. var rad = Math.PI / 180;
  78. var lat = l.Latitude * rad;
  79. var lon = l.Longitude * rad;
  80. var a = ARLocation.Config.EarthEquatorialRadiusInKM * 1000;
  81. var e2 = ARLocation.Config.EarthFirstEccentricitySquared;
  82. var N = a / Math.Sqrt(1 - e2 * Math.Pow(Math.Sin(lat), 2));
  83. var x = N * Math.Cos(lat) * Math.Cos(lon);
  84. var y = N * Math.Cos(lat) * Math.Sin(lon);
  85. var z = (1 - e2) * N * Math.Sin(lat);
  86. return new DVector3(x, y, z);
  87. }
  88. /// <summary>
  89. /// Given a pair of locations, returns the local-plane ENU coordinates, considering the first location, l1, to be the center of the local plane.
  90. /// </summary>
  91. /// <param name="l1"></param>
  92. /// <param name="l2"></param>
  93. /// <returns></returns>
  94. public static DVector2 VectorFromToEcefEnu(Location l1, Location l2) {
  95. var rad = Math.PI / 180;
  96. var lat = l1.Latitude * rad;
  97. var lon = l1.Longitude * rad;
  98. var p1 = LocationToEcef(l1);
  99. var p2 = LocationToEcef(l2);
  100. var delta = p2 - p1;
  101. var slat = Math.Sin(lat);
  102. var clat = Math.Cos(lat);
  103. var slon = Math.Sin(lon);
  104. var clon = Math.Cos(lon);
  105. var e = -slon * delta.x + clon * delta.y;
  106. var n = -clon * slat * delta.x -slat * slon * delta.y+ clat*delta.z;
  107. return new DVector2(n, e);
  108. }
  109. /// <summary>
  110. /// Given a center location, and the local-plane ENU coordinates of a second point, calculates the Location of the second point.
  111. /// </summary>
  112. /// <param name="center"></param>
  113. /// <param name="e"></param>
  114. /// <param name="n"></param>
  115. /// <param name="u"></param>
  116. /// <returns></returns>
  117. public static Location LocationFromEnu(Location center, double e, double n, double u)
  118. {
  119. var lat = DegToRad(center.Latitude);
  120. var lon = DegToRad(center.Longitude);
  121. var slat = Math.Sin(lat);
  122. var clat = Math.Cos(lat);
  123. var slon = Math.Sin(lon);
  124. var clon = Math.Cos(lon);
  125. var dx = -e * slon - n * clon * slat + u * clon * clat;
  126. var dy = e * clon - n * slon * slat + u * slon * clat;
  127. var dz = n * clat + u * slat;
  128. var centerEcef = LocationToEcef(center);
  129. var pointEcef = centerEcef + new DVector3(dx, dy, dz);
  130. var pointLocation = EcefToLocation(pointEcef);
  131. return pointLocation;
  132. }
  133. /// <summary>
  134. /// Converts from (x, y, z) ECEF coordinates to a wgs84 Location.
  135. /// </summary>
  136. /// <param name="ecef"></param>
  137. /// <returns></returns>
  138. public static Location EcefToLocation(DVector3 ecef)
  139. {
  140. var a = ARLocation.Config.EarthEquatorialRadiusInKM * 1000.0;
  141. var e2 = ARLocation.Config.EarthFirstEccentricitySquared;
  142. var b = a * Math.Sqrt(1 - e2);
  143. var x = ecef.x;
  144. var y = ecef.y;
  145. var z = ecef.z;
  146. var r = Math.Sqrt(x * x + y * y);
  147. var E2 = (a * a - b * b) / (b * b);
  148. var F = 54 * b * b * z * z;
  149. var G = r * r + (1 - e2) * z * z - e2 * (a * a - b * b);
  150. var c = (e2 * e2 * F * r * r) / (G * G * G);
  151. var s = Math.Pow(1 + c + Math.Sqrt(c*c + 2*c), 1.0 / 3.0);
  152. var P = F / (3 * Math.Pow(s + (1.0/s) +1, 2.0) * G * G);
  153. var Q = Math.Sqrt(1 + 2 * e2 * e2 * P);
  154. var r0 = -(P * e2 * r) / (1 + Q) + Math.Sqrt(((a * a * 0.5) * (1.0 + (1.0 / Q))) - ((P * (1 - e2) * z * z) / (Q * (1.0 + Q))) - (P*r*r*0.5));
  155. var U = Math.Sqrt(Math.Pow(r - e2 * r0, 2) + z*z);
  156. var V = Math.Sqrt(Math.Pow(r - e2 * r0, 2) + (1 - e2) * z * z);
  157. var z0 = (b * b * z) / (a * V);
  158. var h = U * (1 - ((b * b) / (a * V)));
  159. var phi = Math.Atan((z + E2 * z0) / r);
  160. var lambda = Math.Atan2(y, x);
  161. var rad2deg = 180.0 / Math.PI;
  162. return new Location()
  163. {
  164. Latitude = rad2deg * phi,
  165. Longitude = rad2deg * lambda,
  166. Altitude = h,
  167. AltitudeMode = AltitudeMode.GroundRelative
  168. };
  169. }
  170. /// <summary>
  171. /// Calculates the horizontal distance according to the current function
  172. /// set in the configuration.
  173. /// </summary>
  174. /// <returns>The distance, in meters.</returns>
  175. /// <param name="l1">L1.</param>
  176. /// <param name="l2">L2.</param>
  177. public static double HorizontalDistance(Location l1, Location l2)
  178. {
  179. #if ARGPS_CUSTOM_GEO_CALC
  180. return ArGpsCustomGeoCalc.HorizontalVectorFromTo(l1, l2).magnitude;
  181. #else
  182. return VectorFromToEcefEnu(l1, l2).magnitude;
  183. #endif
  184. }
  185. /// <summary>
  186. /// Calculates the full distance between locations, taking altitude into account.
  187. /// </summary>
  188. /// <returns>The with altitude.</returns>
  189. /// <param name="l1">L1.</param>
  190. /// <param name="l2">L2.</param>
  191. public static double DistanceWithAltitude(Location l1, Location l2)
  192. {
  193. var d = HorizontalDistance(l1, l2);
  194. var h = Math.Abs(l1.Altitude - l2.Altitude);
  195. return Math.Sqrt(d * d + h * h);
  196. }
  197. /// <summary>
  198. /// Calculates the horizontal vector pointing from l1 to l2, in meters.
  199. /// </summary>
  200. /// <returns>The vector from to.</returns>
  201. /// <param name="l1">L1.</param>
  202. /// <param name="l2">L2.</param>
  203. public static DVector2 HorizontalVectorFromTo(Location l1, Location l2)
  204. {
  205. #if ARGPS_USE_CUSTOM_GEO_CALC
  206. return ArGpsCustomGeoCalc.HorizontalVectorFromTo(l1, l2);
  207. #else
  208. return VectorFromToEcefEnu(l1, l2);
  209. #endif
  210. }
  211. /// <summary>
  212. /// Calculates the vector from l1 to l2, in meters, taking altitude into account.
  213. /// </summary>
  214. /// <returns>The from to.</returns>
  215. /// <param name="l1">L1.</param>
  216. /// <param name="l2">L2.</param>
  217. /// <param name="ignoreHeight">If true, y = 0 in the output vector.</param>
  218. public static DVector3 VectorFromTo(Location l1, Location l2, bool ignoreHeight = false)
  219. {
  220. var horizontal = HorizontalVectorFromTo(l1, l2);
  221. var height = l2.Altitude - l1.Altitude;
  222. return new DVector3(horizontal.y, ignoreHeight ? 0 : height, horizontal.x);
  223. }
  224. /// <summary>
  225. /// Gets the game object world-position for location.
  226. /// </summary>
  227. /// <param name="arLocationRoot"></param>
  228. /// <param name="userPosition"></param>
  229. /// <param name="userLocation"></param>
  230. /// <param name="objectLocation"></param>
  231. /// <param name="heightIsRelative"></param>
  232. /// <returns></returns>
  233. public static Vector3 GetGameObjectPositionForLocation(Transform arLocationRoot, Vector3 userPosition, Location userLocation, Location objectLocation, bool heightIsRelative)
  234. {
  235. var displacementVector = VectorFromTo(userLocation, objectLocation, objectLocation.IgnoreAltitude || heightIsRelative)
  236. .toVector3();
  237. var displacementPosition = arLocationRoot ? arLocationRoot.TransformVector(displacementVector) : displacementVector;
  238. return userPosition + displacementPosition + new Vector3(0, (heightIsRelative && !objectLocation.IgnoreAltitude) ? ((float)objectLocation.Altitude - userPosition.y) : 0, 0);
  239. }
  240. /// <summary>
  241. /// Gets the game object world-position for location.
  242. /// </summary>
  243. /// <returns>The game object position for location.</returns>
  244. /// <param name="arLocationRoot"></param>
  245. /// <param name="user">User.</param>
  246. /// <param name="userLocation">User location.</param>
  247. /// <param name="objectLocation">Object location.</param>
  248. /// <param name="heightIsRelative">If set to <c>true</c> height is relative.</param>
  249. ///
  250. public static Vector3 GetGameObjectPositionForLocation(Transform arLocationRoot, Transform user, Location userLocation, Location objectLocation, bool heightIsRelative)
  251. {
  252. return GetGameObjectPositionForLocation(arLocationRoot, user.position, userLocation, objectLocation,
  253. heightIsRelative);
  254. }
  255. /// <summary>
  256. /// Places the game object at location.
  257. /// </summary>
  258. /// <param name="arLocationRoot"></param>
  259. /// <param name="transform">The GameObject's transform.</param>
  260. /// <param name="user">The user's point of view Transform, e.g., camera.</param>
  261. /// <param name="userLocation">User Location.</param>
  262. /// <param name="objectLocation">Object Location.</param>
  263. /// <param name="heightIsRelative"></param>
  264. public static void PlaceGameObjectAtLocation(Transform arLocationRoot, Transform transform, Transform user, Location userLocation, Location objectLocation, bool heightIsRelative)
  265. {
  266. transform.position = GetGameObjectPositionForLocation(arLocationRoot, user, userLocation, objectLocation, heightIsRelative);
  267. }
  268. /// <summary>
  269. /// Calculates the wgs84 Location for a given world position vector.
  270. /// </summary>
  271. /// <param name="arLocationRoot">The ARLocationRoot game object transform.</param>
  272. /// <param name="center">The position of the center (i.e., the device).</param>
  273. /// <param name="userLocation">The wgs84 Location of the center/device.</param>
  274. /// <param name="worldPosition">The world position.</param>
  275. /// <returns></returns>
  276. public static Location GetLocationForWorldPosition(Transform arLocationRoot, Vector3 center, Location userLocation, Vector3 worldPosition)
  277. {
  278. center = arLocationRoot.InverseTransformVector(center);
  279. Vector3 position = arLocationRoot.InverseTransformVector(worldPosition);
  280. var n = position.z - center.z;
  281. var e = position.x - center.x;
  282. var u = 0;
  283. var loc = LocationFromEnu(userLocation, e, n, u);
  284. return loc;
  285. }
  286. public static bool Equal(Location a, Location b, double eps = 0.0000001)
  287. {
  288. return (Math.Abs(a.Latitude - b.Latitude) <= eps) &&
  289. (Math.Abs(a.Longitude - b.Longitude) <= eps) &&
  290. (Math.Abs(a.Altitude - b.Altitude) <= eps);
  291. }
  292. public static double RadToDeg(double rad)
  293. {
  294. return (180.0 * Math.PI) * rad;
  295. }
  296. public static double DegToRad(double deg)
  297. {
  298. return (Math.PI/ 180.0) * deg;
  299. }
  300. }
  301. }