SW 중심대학 OSS GIT 서버 박건태, 이승준, 고기완, 이준호 새로운 배포
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
11 KiB

4 years ago
  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. /// Calculates the horizontal distance according to the current function
  73. /// set in the configuration.
  74. /// </summary>
  75. /// <returns>The distance, in meters.</returns>
  76. /// <param name="l1">L1.</param>
  77. /// <param name="l2">L2.</param>
  78. public static double HorizontalDistance(Location l1, Location l2)
  79. {
  80. var type = ARLocation.Config.DistanceFunction;
  81. switch (type)
  82. {
  83. case ARLocationConfig.ARLocationDistanceFunc.Haversine:
  84. return HaversineDistance(l1, l2);
  85. case ARLocationConfig.ARLocationDistanceFunc.PlaneSpherical:
  86. return PlaneSphericalDistance(l1, l2);
  87. case ARLocationConfig.ARLocationDistanceFunc.PlaneEllipsoidalFcc:
  88. return PlaneEllipsoidalFccDistance(l1, l2);
  89. default:
  90. return HaversineDistance(l1, l2);
  91. }
  92. }
  93. /// <summary>
  94. /// Horizontal distance using spherical projection on a plane.
  95. /// https://en.wikipedia.org/wiki/Geographical_distance
  96. /// </summary>
  97. /// <returns>The distance, in meters.</returns>
  98. /// <param name="l1"></param>
  99. /// <param name="l2"></param>
  100. /// <returns></returns>
  101. public static double PlaneSphericalDistance(Location l1, Location l2)
  102. {
  103. var r = ARLocation.Config.EarthRadiusInKM;
  104. var rad = Math.PI / 180;
  105. var dLat = (l2.Latitude - l1.Latitude) * rad;
  106. var dLon = (l2.Longitude - l1.Longitude) * rad;
  107. var lat1 = l1.Latitude * rad;
  108. var lat2 = l2.Latitude * rad;
  109. var mLat = (lat1 + lat2) / 2.0;
  110. var mLatC = Math.Cos(mLat);
  111. var a = dLat * dLat;
  112. var b = mLatC * mLatC * dLon * dLon;
  113. return r * Math.Sqrt(a + b) * 1000.0;
  114. }
  115. /// <summary>
  116. /// Horizontal distance using ellipsoidal projection on a plane.
  117. /// https://en.wikipedia.org/wiki/Geographical_distance
  118. /// </summary>
  119. /// <returns>The distance, in meters.</returns>
  120. /// <param name="l1"></param>
  121. /// <param name="l2"></param>
  122. /// <returns></returns>
  123. public static double PlaneEllipsoidalFccDistance(Location l1, Location l2)
  124. {
  125. var rad = Math.PI / 180;
  126. var lat1 = l1.Latitude * rad;
  127. var lat2 = l2.Latitude * rad;
  128. var mLat = (lat1 + lat2) / 2.0;
  129. var k1 = 111.13209 - 0.56605 * Math.Cos(2 * mLat) + 0.00120 * Math.Cos(4 * mLat);
  130. var k2 = 111.41513 * Math.Cos(mLat) - 0.09455 * Math.Cos(3 * mLat) + 0.00012 * Math.Cos(5 * mLat);
  131. var a = k1 * (l2.Latitude - l1.Latitude);
  132. var b = k2 * (l2.Longitude - l1.Longitude);
  133. return 1000.0 * Math.Sqrt(a * a + b * b);
  134. }
  135. /// <summary>
  136. /// Horizontal distance, using the Haversine formula.
  137. /// https://stackoverflow.com/questions/41621957/a-more-efficient-haversine-function
  138. /// </summary>
  139. /// <returns>The distance, in meters.</returns>
  140. /// <param name="l1">L1.</param>
  141. /// <param name="l2">L2.</param>
  142. public static double HaversineDistance(Location l1, Location l2)
  143. {
  144. var r = ARLocation.Config.EarthRadiusInKM;
  145. var rad = Math.PI / 180;
  146. var dLat = (l2.Latitude - l1.Latitude) * rad;
  147. var dLon = (l2.Longitude - l1.Longitude) * rad;
  148. var lat1 = l1.Latitude * rad;
  149. var lat2 = l2.Latitude * rad;
  150. var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
  151. return r * 2 * Math.Asin(Math.Sqrt(a)) * 1000;
  152. }
  153. /// <summary>
  154. /// Calculates the full distance between locations, taking altitude into account.
  155. /// </summary>
  156. /// <returns>The with altitude.</returns>
  157. /// <param name="l1">L1.</param>
  158. /// <param name="l2">L2.</param>
  159. public static double DistanceWithAltitude(Location l1, Location l2)
  160. {
  161. var d = HorizontalDistance(l1, l2);
  162. var h = Math.Abs(l1.Altitude - l2.Altitude);
  163. return Math.Sqrt(d * d + h * h);
  164. }
  165. /// <summary>
  166. /// Calculates the horizontal vector pointing from l1 to l2, in meters.
  167. /// </summary>
  168. /// <returns>The vector from to.</returns>
  169. /// <param name="l1">L1.</param>
  170. /// <param name="l2">L2.</param>
  171. public static DVector2 HorizontalVectorFromTo(Location l1, Location l2)
  172. {
  173. var d = HorizontalDistance(l1, l2);
  174. var direction = (l2.HorizontalVector - l1.HorizontalVector).normalized;
  175. return direction * d;
  176. }
  177. /// <summary>
  178. /// Calculates the vector from l1 to l2, in meters, taking altitude into account.
  179. /// </summary>
  180. /// <returns>The from to.</returns>
  181. /// <param name="l1">L1.</param>
  182. /// <param name="l2">L2.</param>
  183. /// <param name="ignoreHeight">If true, y = 0 in the output vector.</param>
  184. public static DVector3 VectorFromTo(Location l1, Location l2, bool ignoreHeight = false)
  185. {
  186. var horizontal = HorizontalVectorFromTo(l1, l2);
  187. var height = l2.Altitude - l1.Altitude;
  188. return new DVector3(horizontal.y, ignoreHeight ? 0 : height, horizontal.x);
  189. }
  190. /// <summary>
  191. /// Gets the game object world-position for location.
  192. /// </summary>
  193. /// <param name="arLocationRoot"></param>
  194. /// <param name="userPosition"></param>
  195. /// <param name="userLocation"></param>
  196. /// <param name="objectLocation"></param>
  197. /// <param name="heightIsRelative"></param>
  198. /// <returns></returns>
  199. public static Vector3 GetGameObjectPositionForLocation(Transform arLocationRoot, Vector3 userPosition, Location userLocation, Location objectLocation, bool heightIsRelative)
  200. {
  201. var displacementVector = VectorFromTo(userLocation, objectLocation, objectLocation.IgnoreAltitude || heightIsRelative)
  202. .toVector3();
  203. var displacementPosition = arLocationRoot ? arLocationRoot.TransformVector(displacementVector) : displacementVector;
  204. return userPosition + displacementPosition + new Vector3(0, (heightIsRelative && !objectLocation.IgnoreAltitude) ? ((float)objectLocation.Altitude - userPosition.y) : 0, 0);
  205. }
  206. /// <summary>
  207. /// Gets the game object world-position for location.
  208. /// </summary>
  209. /// <returns>The game object position for location.</returns>
  210. /// <param name="arLocationRoot"></param>
  211. /// <param name="user">User.</param>
  212. /// <param name="userLocation">User location.</param>
  213. /// <param name="objectLocation">Object location.</param>
  214. /// <param name="heightIsRelative">If set to <c>true</c> height is relative.</param>
  215. ///
  216. public static Vector3 GetGameObjectPositionForLocation(Transform arLocationRoot, Transform user, Location userLocation, Location objectLocation, bool heightIsRelative)
  217. {
  218. return GetGameObjectPositionForLocation(arLocationRoot, user.position, userLocation, objectLocation,
  219. heightIsRelative);
  220. }
  221. /// <summary>
  222. /// Places the game object at location.
  223. /// </summary>
  224. /// <param name="arLocationRoot"></param>
  225. /// <param name="transform">The GameObject's transform.</param>
  226. /// <param name="user">The user's point of view Transform, e.g., camera.</param>
  227. /// <param name="userLocation">User Location.</param>
  228. /// <param name="objectLocation">Object Location.</param>
  229. /// <param name="heightIsRelative"></param>
  230. public static void PlaceGameObjectAtLocation(Transform arLocationRoot, Transform transform, Transform user, Location userLocation, Location objectLocation, bool heightIsRelative)
  231. {
  232. transform.position = GetGameObjectPositionForLocation(arLocationRoot, user, userLocation, objectLocation, heightIsRelative);
  233. }
  234. public static bool Equal(Location a, Location b, double eps = 0.0000001)
  235. {
  236. return (Math.Abs(a.Latitude - b.Latitude) <= eps) &&
  237. (Math.Abs(a.Longitude - b.Longitude) <= eps) &&
  238. (Math.Abs(a.Altitude - b.Altitude) <= eps);
  239. }
  240. }
  241. }