Geolocation.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System;
  2. using System.Threading.Tasks;
  3. using System.Linq;
  4. using Xamarin.Forms;
  5. using Xamarin.Essentials;
  6. namespace InABox.Mobile
  7. {
  8. public class LocationServices
  9. {
  10. public delegate void LocationEvent(LocationServices sender);
  11. public event LocationEvent OnLocationFound;
  12. public delegate void LocationError(LocationServices sender, Exception error);
  13. public event LocationError OnLocationError;
  14. public TimeSpan ScanDelay { get; set; }
  15. public double Latitude { get; private set; }
  16. public double Longitude { get; private set; }
  17. public String Address { get; private set; }
  18. private bool bLocating = false;
  19. public DateTime TimeStamp { get; private set; }
  20. public LocationServices() : base()
  21. {
  22. TimeStamp = DateTime.MinValue;
  23. ScanDelay = new TimeSpan(0, 0, 0);
  24. Address = "Searching for GPS";
  25. }
  26. public bool RecentlyLocated
  27. {
  28. get
  29. {
  30. return (DateTime.Now.Subtract(TimeStamp).Ticks < ScanDelay.Ticks);
  31. }
  32. }
  33. private double DistanceBetween(double sLatitude, double sLongitude, double eLatitude,
  34. double eLongitude)
  35. {
  36. var radiansOverDegrees = (Math.PI / 180.0);
  37. var sLatitudeRadians = sLatitude * radiansOverDegrees;
  38. var sLongitudeRadians = sLongitude * radiansOverDegrees;
  39. var eLatitudeRadians = eLatitude * radiansOverDegrees;
  40. var eLongitudeRadians = eLongitude * radiansOverDegrees;
  41. var dLongitude = eLongitudeRadians - sLongitudeRadians;
  42. var dLatitude = eLatitudeRadians - sLatitudeRadians;
  43. var result1 = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
  44. Math.Cos(sLatitudeRadians) * Math.Cos(eLatitudeRadians) *
  45. Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);
  46. // Using 3956 as the number of miles around the earth
  47. var result2 = 3956.0 * 2.0 *
  48. Math.Atan2(Math.Sqrt(result1), Math.Sqrt(1.0 - result1));
  49. return result2;
  50. }
  51. public void GetLocation(bool skiprecentlylocated = false)
  52. {
  53. if (bLocating || RecentlyLocated)
  54. {
  55. if (!skiprecentlylocated)
  56. return;
  57. }
  58. bLocating = true;
  59. // Don't reset this on every refresh, otherwise the final UI will randomly get "Searching for GPS" as the address
  60. //Address = "Searching for GPS";
  61. Task.Run(async () =>
  62. {
  63. bool bOK = await MobileUtils.IsPermitted(PermissionType.Location);
  64. if (!bOK)
  65. {
  66. Latitude = 0.0F;
  67. Longitude = 0.0F;
  68. TimeStamp = DateTime.MinValue;
  69. Address = "GPS Services Disabled";
  70. Device.BeginInvokeOnMainThread(() =>
  71. {
  72. OnLocationError?.Invoke(this, new Exception("Please enable GPS Services to continue"));
  73. });
  74. bOK = false;
  75. bLocating = false;
  76. }
  77. else
  78. {
  79. try
  80. {
  81. var request = new GeolocationRequest(GeolocationAccuracy.Best, new TimeSpan(0, 0, 20));
  82. var location = await Geolocation.GetLocationAsync(request);
  83. if (location != null)
  84. {
  85. //if (location.IsFromMockProvider)
  86. //{
  87. // Device.BeginInvokeOnMainThread(() =>
  88. // {
  89. // OnLocationError?.Invoke(this, new Exception("Mock GPS Location Detected!\nPlease correct and restart TimeBench."));
  90. // });
  91. //}
  92. //else
  93. {
  94. Latitude = location.Latitude;
  95. Longitude = location.Longitude;
  96. TimeStamp = DateTime.Now;
  97. String sErr = "";
  98. Placemark address = null;
  99. try
  100. {
  101. var addresses = await Geocoding.GetPlacemarksAsync(Latitude, Longitude);
  102. double maxdist = double.MaxValue;
  103. foreach (var cur in addresses.Where(x => !String.IsNullOrEmpty(x.Thoroughfare)))
  104. {
  105. var delta = Location.CalculateDistance(location, cur.Location, DistanceUnits.Kilometers);
  106. if (delta < maxdist)
  107. {
  108. address = cur;
  109. maxdist = delta;
  110. }
  111. }
  112. }
  113. catch (Exception ee2)
  114. {
  115. sErr = ee2.Message;
  116. //address = null;
  117. }
  118. if (address != null)
  119. Address = String.Format("{0} {1} {2}", address.SubThoroughfare, address.Thoroughfare, address.Locality);
  120. else
  121. Address = String.Format("Lat: {0}, Lng: {1}", Latitude, Longitude);
  122. if (location.IsFromMockProvider)
  123. Address = "** " + Address;
  124. if (!String.IsNullOrEmpty(sErr))
  125. Address = String.Format("{0} (ERROR: {1})", Address, sErr);
  126. Device.BeginInvokeOnMainThread(() =>
  127. {
  128. OnLocationFound?.Invoke(this);
  129. });
  130. bLocating = false;
  131. }
  132. }
  133. else
  134. {
  135. Latitude = 0.00;
  136. Longitude = 0.00;
  137. TimeStamp = DateTime.MinValue;
  138. bLocating = false;
  139. Device.BeginInvokeOnMainThread(() =>
  140. {
  141. OnLocationError?.Invoke(this, new Exception("Unable to get GPS Location"));
  142. });
  143. }
  144. }
  145. catch (Exception e)
  146. {
  147. bLocating = false;
  148. Device.BeginInvokeOnMainThread(() =>
  149. {
  150. OnLocationError?.Invoke(this, e);
  151. });
  152. }
  153. }
  154. bLocating = false;
  155. });
  156. }
  157. }
  158. }