-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Location.shared.cs
295 lines (260 loc) · 11.6 KB
/
Location.shared.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
using System;
using Microsoft.Maui.Media;
namespace Microsoft.Maui.Devices.Sensors
{
/// <summary>Distance unit for use in conversion.</summary>
public enum DistanceUnits
{
/// <summary>Kilometers.</summary>
Kilometers,
/// <summary>Miles.</summary>
Miles
}
/// <summary>
/// Indicates the altitude reference system to be used in defining a location.
/// </summary>
public enum AltitudeReferenceSystem
{
/// <summary>The altitude reference system was not specified.</summary>
Unspecified = 0,
/// <summary>The altitude reference system is based on distance above terrain or ground level</summary>
Terrain = 1,
/// <summary>The altitude reference system is based on an ellipsoid (usually WGS84), which is a mathematical approximation of the shape of the Earth.</summary>
Ellipsoid = 2,
/// <summary>The altitude reference system is based on the distance above sea level (parametrized by a so-called Geoid).</summary>
Geoid = 3,
/// <summary>The altitude reference system is based on the distance above the tallest surface structures, such as buildings, trees, roads, etc., above terrain or ground level.</summary>
Surface = 4
}
/// <summary>
/// Represents a physical location with the latitude, longitude, altitude and time information reported by the device.
/// </summary>
public class Location
{
/// <summary>
/// Initializes a new instance of the <see cref="Location"/> class.
/// </summary>
public Location()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Location"/> class with the specified latitude and longitude.
/// </summary>
/// <param name="latitude">Latitude in degrees. Must be in the interval [-90, 90].</param>
/// <param name="longitude">Longitude in degrees. Will be projected to the interval (-180, 180].</param>
public Location(double latitude, double longitude) : this(latitude, longitude, DateTimeOffset.UtcNow)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Location"/> class with the specified latitude, longitude, and timestamp.
/// </summary>
/// <param name="latitude">Latitude in degrees. Must be in the interval [-90, 90].</param>
/// <param name="longitude">Longitude in degrees. Will be projected to the interval (-180, 180].</param>
/// <param name="timestamp">UTC timestamp for the location.</param>
public Location(double latitude, double longitude, DateTimeOffset timestamp)
{
// check if latitude is in [-90, 90]
if (Math.Abs(latitude) > 90)
throw new ArgumentOutOfRangeException(nameof(latitude));
else
Latitude = latitude;
// make sure that longitude is in (-180, 180]
Longitude = longitude;
while (Longitude > 180)
Longitude -= 360;
while (Longitude <= -180)
Longitude += 360;
Timestamp = timestamp;
}
/// <summary>
/// Initializes a new instance of the <see cref="Location"/> class with the specified latitude, longitude, and altitude.
/// </summary>
/// <param name="latitude">Latitude in degrees. Must be in the interval [-90, 90].</param>
/// <param name="longitude">Longitude in degrees. Will be projected to the interval (-180, 180].</param>
/// <param name="altitude">Altitude in meters.</param>
public Location(double latitude, double longitude, double altitude) : this(latitude, longitude)
{
Altitude = altitude;
}
/// <summary>
/// Initializes a new instance of the <see cref="Location"/> class from an existing instance.
/// </summary>
/// <param name="point">A <see cref="Location"/> instance that will be used to clone.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="point"/> is <see langword="null"/>.</exception>
public Location(Location point)
{
if (point == null)
throw new ArgumentNullException(nameof(point));
Latitude = point.Latitude;
Longitude = point.Longitude;
Timestamp = DateTime.UtcNow;
Altitude = point.Altitude;
Accuracy = point.Accuracy;
VerticalAccuracy = point.VerticalAccuracy;
ReducedAccuracy = point.ReducedAccuracy;
Speed = point.Speed;
Course = point.Course;
IsFromMockProvider = point.IsFromMockProvider;
}
/// <summary>
/// Gets or sets the timestamp of the location in UTC.
/// </summary>
public DateTimeOffset Timestamp { get; set; }
/// <summary>
/// Gets or sets the latitude coordinate of this location.
/// </summary>
public double Latitude { get; set; }
/// <summary>
/// Gets or sets the longitude coordinate of this location.
/// </summary>
public double Longitude { get; set; }
/// <summary>
/// Gets the altitude in meters (if available) in a reference system which is specified by <see cref="AltitudeReferenceSystem"/>.
/// </summary>
/// <remarks>Returns 0 or <see langword="null"/> if not available.</remarks>
public double? Altitude { get; set; }
/// <summary>
/// Gets or sets the horizontal accuracy (in meters) of the location.
/// </summary>
public double? Accuracy { get; set; }
/// <summary>
/// Gets or sets the vertical accuracy (in meters) of the location.
/// </summary>
public double? VerticalAccuracy { get; set; }
/// <summary>
/// Gets or sets whether this location has a reduced accuracy reading.
/// </summary>
/// <remarks>This functionality only applies to iOS. Other platforms will always report false.</remarks>
public bool ReducedAccuracy { get; set; }
/// <summary>
/// Gets or sets the current speed in meters per second at the time when this location was determined.
/// </summary>
/// <remarks>
/// <para>Returns 0 or <see langword="null"/> if not available. Otherwise the value will range between 0-360.</para>
/// <para>Requires <see cref="Accuracy"/> to be <see cref="GeolocationAccuracy.High"/> or better
/// and may not be returned when calling <see cref="Geolocation.GetLastKnownLocationAsync"/>.</para>
/// </remarks>
public double? Speed { get; set; }
/// <summary>
/// Gets or sets the current degrees relative to true north at the time when this location was determined.
/// </summary>
/// <remarks>Returns 0 or <see langword="null"/> if not available.</remarks>
public double? Course { get; set; }
/// <summary>
/// Gets or sets whether this location originates from a mocked sensor and thus might not be the real location of the device.
/// </summary>
public bool IsFromMockProvider { get; set; }
/// <summary>
/// Specifies the reference system in which the <see cref="Altitude"/> value is expressed.
/// </summary>
public AltitudeReferenceSystem AltitudeReferenceSystem { get; set; }
/// <summary>
/// Calculate distance between two locations.
/// </summary>
/// <param name="latitudeStart">Latitude coordinate of the starting location.</param>
/// <param name="longitudeStart">Longitude coordinate of the starting location.</param>
/// <param name="locationEnd">The end location.</param>
/// <param name="units">The unit in which the result distance is returned.</param>
/// <returns>Distance between two locations in the unit selected.</returns>
public static double CalculateDistance(double latitudeStart, double longitudeStart, Location locationEnd, DistanceUnits units) =>
CalculateDistance(latitudeStart, longitudeStart, locationEnd.Latitude, locationEnd.Longitude, units);
/// <summary>
/// Calculate distance between two locations.
/// </summary>
/// <param name="locationStart">The start location.</param>
/// <param name="latitudeEnd">Latitude coordinate of the end location.</param>
/// <param name="longitudeEnd">Longitude coordinate of the end location.</param>
/// <param name="units">The unit in which the result distance is returned.</param>
/// <returns>Distance between two locations in the unit selected.</returns>
public static double CalculateDistance(Location locationStart, double latitudeEnd, double longitudeEnd, DistanceUnits units) =>
CalculateDistance(locationStart.Latitude, locationStart.Longitude, latitudeEnd, longitudeEnd, units);
/// <summary>
/// Calculate distance between two locations.
/// </summary>
/// <param name="locationStart">The start location.</param>
/// <param name="locationEnd">The end location.</param>
/// <param name="units">The unit in which the result distance is returned.</param>
/// <returns>Distance between two locations in the unit selected.</returns>
public static double CalculateDistance(Location locationStart, Location locationEnd, DistanceUnits units) =>
CalculateDistance(locationStart.Latitude, locationStart.Longitude, locationEnd.Latitude, locationEnd.Longitude, units);
/// <summary>
/// Calculate distance between two <see cref="Location"/> instances.
/// </summary>
/// <param name="latitudeStart">Latitude coordinate of the starting location.</param>
/// <param name="longitudeStart">Longitude coordinate of the starting location.</param>
/// <param name="latitudeEnd">Latitude coordinate of the end location.</param>
/// <param name="longitudeEnd">Longitude coordinate of the end location.</param>
/// <param name="units">The unit in which the result distance is returned.</param>
/// <returns>Distance between two locations in the unit selected.</returns>
public static double CalculateDistance(
double latitudeStart,
double longitudeStart,
double latitudeEnd,
double longitudeEnd,
DistanceUnits units)
{
switch (units)
{
case DistanceUnits.Kilometers:
return UnitConverters.CoordinatesToKilometers(latitudeStart, longitudeStart, latitudeEnd, longitudeEnd);
case DistanceUnits.Miles:
return UnitConverters.CoordinatesToMiles(latitudeStart, longitudeStart, latitudeEnd, longitudeEnd);
default:
throw new ArgumentOutOfRangeException(nameof(units));
}
}
/// <summary>
/// Returns a string representation of the current values of <see cref="Location"/>.
/// </summary>
/// <returns>A string representation of this instance in the format of <c>Latitude: {value}, Longitude: {value}, Altitude: {value}, Accuracy: {value}, VerticalAccuracy: {value}, Speed: {value}, Course: {value}, Timestamp: {value}</c>.</returns>
public override string ToString() =>
$"{nameof(Latitude)}: {Latitude}, " +
$"{nameof(Longitude)}: {Longitude}, " +
$"{nameof(Altitude)}: {Altitude}, " +
$"{nameof(Accuracy)}: {Accuracy}, " +
$"{nameof(VerticalAccuracy)}: {VerticalAccuracy}, " +
$"{nameof(Speed)}: {Speed}, " +
$"{nameof(Course)}: {Course}, " +
$"{nameof(Timestamp)}: {Timestamp}";
/// <inheritdoc cref="object.Equals(object)"/>
public override bool Equals(object obj)
{
if (obj is null)
return false;
if (obj.GetType() != GetType())
return false;
var other = (Location)obj;
return Latitude == other.Latitude && Longitude == other.Longitude;
}
/// <inheritdoc cref="object.GetHashCode"/>
public override int GetHashCode()
{
unchecked
{
int hashCode = Latitude.GetHashCode();
hashCode = (hashCode * 397) ^ Longitude.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Equality operator for equals.
/// </summary>
/// <param name="left">Left to compare.</param>
/// <param name="right">Right to compare.</param>
/// <returns><see langword="true"/> if objects are equal, otherwise <see langword="false"/>.</returns>
public static bool operator ==(Location left, Location right)
{
return Equals(left, right);
}
/// <summary>
/// Inequality operator.
/// </summary>
/// <param name="left">Left to compare.</param>
/// <param name="right">Right to compare.</param>
/// <returns><see langword="true"/> if objects are not equal, otherwise <see langword="false"/>.</returns>
public static bool operator !=(Location left, Location right)
{
return !Equals(left, right);
}
}
}