From 6fc8543e22002a689f3255214419f32de9bcb302 Mon Sep 17 00:00:00 2001 From: Rodney Littles II <6969701+RLittlesII@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:10:18 -0600 Subject: [PATCH] feature: add GpsLocation (#241) * feature: add GpsLocation switch Location Events to use GpsLocation for underlying platform locations add GeoCoordinate remove beacon events and properties +semver: minor * feature: i can read error messages! --- src/Apple/LocationEventExtensions.cs | 51 +++-------- src/Apple/Locations/CoreLocationManager.cs | 21 ----- src/Core/Geofence/GeoRegion.cs | 19 +++- src/Core/Locations/Events/ErrorEvent.cs | 6 ++ .../Locations/Events/LocationUpdatedEvent.cs | 6 +- .../Locations/Events/LocationsUpdatedEvent.cs | 4 +- .../RegionBeaconsConstraintFailedEvent.cs | 9 -- .../RegionBeaconsConstraintRangedEvent.cs | 9 -- .../Events/RegionBeaconsFailedEvent.cs | 9 -- .../Locations/Events/RegionChangedEvent.cs | 2 +- src/Core/Locations/Events/VisitedEvent.cs | 6 +- src/Core/Locations/GeoCoordinate.cs | 58 ++++++++++++ src/Core/Locations/GpsLocation.cs | 91 +++++++++++++++++++ src/Core/Locations/ICoreLocationService.cs | 10 -- src/Core/Locations/IGpsLocation.cs | 50 ++++++++++ 15 files changed, 245 insertions(+), 106 deletions(-) delete mode 100644 src/Core/Locations/Events/RegionBeaconsConstraintFailedEvent.cs delete mode 100644 src/Core/Locations/Events/RegionBeaconsConstraintRangedEvent.cs delete mode 100644 src/Core/Locations/Events/RegionBeaconsFailedEvent.cs create mode 100644 src/Core/Locations/GeoCoordinate.cs create mode 100644 src/Core/Locations/GpsLocation.cs create mode 100644 src/Core/Locations/IGpsLocation.cs diff --git a/src/Apple/LocationEventExtensions.cs b/src/Apple/LocationEventExtensions.cs index c1075d0c9..a608fe5e0 100644 --- a/src/Apple/LocationEventExtensions.cs +++ b/src/Apple/LocationEventExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reactive; using CoreLocation; using Foundation; @@ -35,22 +34,6 @@ internal static class LocationEventExtensions public static AuthorizationChangedEvent ToNotification(this CLAuthorizationChangedEventArgs args) => new(AuthorizationStatuses[args.Status]); - /// - /// Converts the to an instance of . - /// - /// The arguments. - /// The changed notification. - public static RegionBeaconsConstraintFailedEvent ToNotification(this CLRegionBeaconsConstraintFailedEventArgs args) => - new(); - - /// - /// Converts the to an instance of . - /// - /// The arguments. - /// The changed notification. - public static RegionBeaconsConstraintRangedEvent ToNotification(this CLRegionBeaconsConstraintRangedEventArgs args) => - new(); - /// /// Converts the to an instance of . /// @@ -65,7 +48,7 @@ public static HeadingUpdatedEvent ToNotification(this CLHeadingUpdatedEventArgs /// The arguments. /// The notification. public static LocationsUpdatedEvent ToNotification(this CLLocationsUpdatedEventArgs args) => - new(args.Locations.Select(x => new GeoLocation(x.Coordinate.Latitude, x.Coordinate.Longitude))); + new(args.Locations.Select(location => location.ToGpsLocation())); /// /// Converts the to . @@ -74,8 +57,8 @@ public static LocationsUpdatedEvent ToNotification(this CLLocationsUpdatedEventA /// The notification. public static LocationUpdatedEvent ToNotification(this CLLocationUpdatedEventArgs args) => new( - args.OldLocation.Coordinate.ToLocation(), - args.NewLocation.Coordinate.ToLocation()); + args.OldLocation.ToGpsLocation(), + args.NewLocation.ToGpsLocation()); /// /// Converts the to . @@ -99,7 +82,7 @@ public static RegionChangedEvent ToNotification(this CLRegionStateDeterminedEven /// The arguments. /// The notification. public static ErrorEvent ToNotification(this NSErrorEventArgs args) => - new() { }; + new(new Exception(args.ToString())); /// /// Converts the to . @@ -117,34 +100,26 @@ public static VisitedEvent ToNotification(this CLVisitedEventArgs args) => public static RegionErrorEvent ToNotification(this CLRegionErrorEventArgs args) => new(/*args.Error*/new Exception(), ToGeoRegion(args.Region)); - /// - /// Converts the to . - /// - /// The object. - /// The notification. - public static Unit ToNotification(this object obj) => Unit.Default; - /// /// Converts a to a . /// /// The region. /// The converted value. - public static GeoRegion ToGeoRegion(this CLRegion region) => - new() - { - Identifier = region.Identifier, - Center = region.Center.ToLocation(), - Radius = region.Radius, - NotifyOnEntry = region.NotifyOnEntry, - NotifyOnExit = region.NotifyOnExit - }; + public static GeoRegion ToGeoRegion(this CLRegion region) => new( + region.Identifier, + region.Center.ToLocation(), + region.Radius, + region.NotifyOnEntry, + region.NotifyOnExit); /// /// Converts the to a . /// /// The location. /// The converted vale. - public static GeoLocation ToLocation(this CLLocationCoordinate2D location) => new(location.Latitude, location.Longitude); + public static GeoCoordinate ToLocation(this CLLocationCoordinate2D location) => new(location.Latitude, location.Longitude); + + public static IGpsLocation ToGpsLocation(this CLLocation location) => new GpsLocation(location.Coordinate.Latitude, location.Coordinate.Longitude, location.Altitude, location.Course, location.CourseAccuracy, location.Speed, location.SpeedAccuracy, 0, location.Timestamp.ToLocalTime()); public static DateTime ToLocalTime(this NSDate nsDate) { diff --git a/src/Apple/Locations/CoreLocationManager.cs b/src/Apple/Locations/CoreLocationManager.cs index 9b1fc20c0..5cee3863d 100644 --- a/src/Apple/Locations/CoreLocationManager.cs +++ b/src/Apple/Locations/CoreLocationManager.cs @@ -39,21 +39,6 @@ public CoreLocationManager() handler => _locationManager.Value.DidDetermineState -= handler) .Select(LocationEventExtensions.ToNotification); - FailedRangingBeacons = - Observable - .FromEvent, - CLRegionBeaconsConstraintFailedEventArgs>( - handler => _locationManager.Value.DidFailRangingBeacons += handler, - handler => _locationManager.Value.DidFailRangingBeacons -= handler) - .Select(LocationEventExtensions.ToNotification); - - RangedBeaconsSatisfyingConstraint = - Observable - .FromEvent, CLRegionBeaconsConstraintRangedEventArgs>( - handler => _locationManager.Value.DidRangeBeaconsSatisfyingConstraint += handler, - handler => _locationManager.Value.DidRangeBeaconsSatisfyingConstraint -= handler) - .Select(LocationEventExtensions.ToNotification); - StartedMonitoringForRegion = Observable .FromEvent, CLRegionEventArgs>( @@ -138,12 +123,6 @@ public CoreLocationManager() /// public IObservable DeterminedState { get; } - /// - public IObservable FailedRangingBeacons { get; } - - /// - public IObservable RangedBeaconsSatisfyingConstraint { get; } - /// public IObservable StartedMonitoringForRegion { get; } diff --git a/src/Core/Geofence/GeoRegion.cs b/src/Core/Geofence/GeoRegion.cs index 4fd0ffae5..d8a50b2d7 100644 --- a/src/Core/Geofence/GeoRegion.cs +++ b/src/Core/Geofence/GeoRegion.cs @@ -5,6 +5,23 @@ namespace Rocket.Surgery.Airframe /// public class GeoRegion { + /// + /// Initializes a new instance of the class. + /// + /// The unique identifier. + /// The center point of the region. + /// The radius. + /// A value indicating whether to notify on entry. + /// A value indicating whether to notify on exit. + public GeoRegion(string identifier, GeoCoordinate center, double radius, bool notifyOnEntry, bool notifyOnExit) + { + Identifier = identifier; + Center = center; + NotifyOnEntry = notifyOnEntry; + NotifyOnExit = notifyOnExit; + Radius = radius; + } + /// /// Gets or sets the region identifier. /// @@ -13,7 +30,7 @@ public class GeoRegion /// /// Gets or sets the center. /// - public GeoLocation Center { get; set; } + public GeoCoordinate Center { get; set; } /// /// Gets or sets a value indicating whether to notify on entry of the region. diff --git a/src/Core/Locations/Events/ErrorEvent.cs b/src/Core/Locations/Events/ErrorEvent.cs index 53fd66566..bcb261ebf 100644 --- a/src/Core/Locations/Events/ErrorEvent.cs +++ b/src/Core/Locations/Events/ErrorEvent.cs @@ -7,6 +7,12 @@ namespace Rocket.Surgery.Airframe /// public class ErrorEvent { + /// + /// Initializes a new instance of the class. + /// + /// The exception. + public ErrorEvent(Exception exception) => Exception = exception; + /// /// Gets an exception. /// diff --git a/src/Core/Locations/Events/LocationUpdatedEvent.cs b/src/Core/Locations/Events/LocationUpdatedEvent.cs index ea2172f98..e7157155a 100644 --- a/src/Core/Locations/Events/LocationUpdatedEvent.cs +++ b/src/Core/Locations/Events/LocationUpdatedEvent.cs @@ -10,7 +10,7 @@ public class LocationUpdatedEvent /// /// The previous. /// The current. - public LocationUpdatedEvent(GeoLocation previous, GeoLocation current) + public LocationUpdatedEvent(IGpsLocation? previous, IGpsLocation current) { Previous = previous; Current = current; @@ -19,11 +19,11 @@ public LocationUpdatedEvent(GeoLocation previous, GeoLocation current) /// /// Gets the previous location. /// - public GeoLocation Previous { get; } + public IGpsLocation? Previous { get; } /// /// Gets the current location. /// - public GeoLocation Current { get; } + public IGpsLocation Current { get; } } } \ No newline at end of file diff --git a/src/Core/Locations/Events/LocationsUpdatedEvent.cs b/src/Core/Locations/Events/LocationsUpdatedEvent.cs index f88a2637c..a741e7cf3 100644 --- a/src/Core/Locations/Events/LocationsUpdatedEvent.cs +++ b/src/Core/Locations/Events/LocationsUpdatedEvent.cs @@ -11,11 +11,11 @@ public class LocationsUpdatedEvent /// Initializes a new instance of the class. /// /// The locations. - public LocationsUpdatedEvent(IEnumerable locations) => Locations = locations; + public LocationsUpdatedEvent(IEnumerable locations) => Locations = locations; /// /// Gets the locations. /// - public IEnumerable Locations { get; } + public IEnumerable Locations { get; } } } \ No newline at end of file diff --git a/src/Core/Locations/Events/RegionBeaconsConstraintFailedEvent.cs b/src/Core/Locations/Events/RegionBeaconsConstraintFailedEvent.cs deleted file mode 100644 index c8c6ec20f..000000000 --- a/src/Core/Locations/Events/RegionBeaconsConstraintFailedEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Rocket.Surgery.Airframe -{ - /// - /// Represents a beacon constraint failure event. - /// - public class RegionBeaconsConstraintFailedEvent - { - } -} \ No newline at end of file diff --git a/src/Core/Locations/Events/RegionBeaconsConstraintRangedEvent.cs b/src/Core/Locations/Events/RegionBeaconsConstraintRangedEvent.cs deleted file mode 100644 index a28fb36ad..000000000 --- a/src/Core/Locations/Events/RegionBeaconsConstraintRangedEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Rocket.Surgery.Airframe -{ - /// - /// Represents a beacon constraint event. - /// - public class RegionBeaconsConstraintRangedEvent - { - } -} \ No newline at end of file diff --git a/src/Core/Locations/Events/RegionBeaconsFailedEvent.cs b/src/Core/Locations/Events/RegionBeaconsFailedEvent.cs deleted file mode 100644 index 792f960c3..000000000 --- a/src/Core/Locations/Events/RegionBeaconsFailedEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Rocket.Surgery.Airframe -{ - /// - /// Represents failed beacon. - /// - public class RegionBeaconsFailedEvent - { - } -} \ No newline at end of file diff --git a/src/Core/Locations/Events/RegionChangedEvent.cs b/src/Core/Locations/Events/RegionChangedEvent.cs index 7b54d6237..fffd0a6c1 100644 --- a/src/Core/Locations/Events/RegionChangedEvent.cs +++ b/src/Core/Locations/Events/RegionChangedEvent.cs @@ -10,7 +10,7 @@ public class RegionChangedEvent /// /// The region. /// The region state. - public RegionChangedEvent(GeoRegion region, RegionState? state = default) + public RegionChangedEvent(GeoRegion region, RegionState? state = RegionState.Unknown) { Region = region; State = state; diff --git a/src/Core/Locations/Events/VisitedEvent.cs b/src/Core/Locations/Events/VisitedEvent.cs index 4bf7cc9c4..405373d82 100644 --- a/src/Core/Locations/Events/VisitedEvent.cs +++ b/src/Core/Locations/Events/VisitedEvent.cs @@ -14,9 +14,9 @@ public class VisitedEvent /// The arrival date. /// The departure date. /// The accuracy. - public VisitedEvent(GeoLocation location, DateTimeOffset arrivalDate, DateTimeOffset departureDate, double horizontalAccuracy) + public VisitedEvent(GeoCoordinate location, DateTimeOffset arrivalDate, DateTimeOffset departureDate, double horizontalAccuracy) { - GeoLocation = location; + Location = location; ArrivalDate = arrivalDate; DepartureDate = departureDate; HorizontalAccuracy = horizontalAccuracy; @@ -30,7 +30,7 @@ public VisitedEvent(GeoLocation location, DateTimeOffset arrivalDate, DateTimeOf /// /// Gets the geo location. /// - public GeoLocation GeoLocation { get; } + public GeoCoordinate Location { get; } /// /// Gets the departure date. diff --git a/src/Core/Locations/GeoCoordinate.cs b/src/Core/Locations/GeoCoordinate.cs new file mode 100644 index 000000000..d83778f2d --- /dev/null +++ b/src/Core/Locations/GeoCoordinate.cs @@ -0,0 +1,58 @@ +using System; + +namespace Rocket.Surgery.Airframe +{ + /// + /// Represents a coordinate. + /// + /// any of a set of numbers used in specifying the location of a point on a line, on a surface, or in space. + public class GeoCoordinate : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The latitude value. + /// The longitude value. + public GeoCoordinate(double latitude, double longitude) + { + if (latitude is < -90 or > 90) + { + throw new ArgumentException($"Invalid latitude value - {latitude}"); + } + + if (longitude is < -180 or > 180) + { + throw new ArgumentException($"Invalid longitude value - {longitude}"); + } + + Latitude = latitude; + Longitude = longitude; + } + + /// + /// Gets the latitude of the coordinate. + /// + public double Latitude { get; } + + /// + /// Gets the longitude of the coordinate. + /// + public double Longitude { get; } + + public static bool operator ==(GeoCoordinate? left, GeoCoordinate? right) => Equals(left, right); + + public static bool operator !=(GeoCoordinate? left, GeoCoordinate? right) => !Equals(left, right); + + /// + public override string ToString() => $"Latitude: {Latitude} - Longitude: {Longitude}"; + + /// + public bool Equals(GeoCoordinate? other) => other != null && (Latitude, Longitude).Equals((other.Latitude, other.Longitude)); + + /// + public override bool Equals(object? obj) => obj is GeoCoordinate coordinate && Equals(coordinate); + + /// + public override int GetHashCode() => (Latitude, Longitude).GetHashCode(); + } +} \ No newline at end of file diff --git a/src/Core/Locations/GpsLocation.cs b/src/Core/Locations/GpsLocation.cs new file mode 100644 index 000000000..c37e4edb9 --- /dev/null +++ b/src/Core/Locations/GpsLocation.cs @@ -0,0 +1,91 @@ +using System; + +namespace Rocket.Surgery.Airframe +{ + /// + /// Represents a gps location. + /// + public class GpsLocation : IGpsLocation + { + /// + /// Initializes a new instance of the class. + /// + /// The latitude. + /// The longitude. + /// The altitude. + /// The heading. + /// The heading accuracy. + /// The speed. + /// The speed accuracy. + /// The position accuracy. + /// The date time of the reading. + public GpsLocation( + double latitude, + double longitude, + double altitude, + double heading, + double headingAccuracy, + double speed, + double speedAccuracy, + double positionAccuracy, + DateTimeOffset timestamp) + : this(new GeoCoordinate(latitude, longitude), altitude, heading, headingAccuracy, speed, speedAccuracy, positionAccuracy, timestamp) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The position. + /// The altitude. + /// The heading. + /// The heading accuracy. + /// The speed. + /// The speed accuracy. + /// The position accuracy. + /// The date time of the reading. + public GpsLocation( + GeoCoordinate position, + double altitude, + double heading, + double headingAccuracy, + double speed, + double speedAccuracy, + double positionAccuracy, + DateTimeOffset timestamp) + { + Altitude = altitude; + Heading = heading; + HeadingAccuracy = headingAccuracy; + Speed = speed; + SpeedAccuracy = speedAccuracy; + Position = position; + PositionAccuracy = positionAccuracy; + Timestamp = timestamp; + } + + /// + public double Altitude { get; } + + /// + public double Heading { get; } + + /// + public double HeadingAccuracy { get; } + + /// + public double Speed { get; } + + /// + public double SpeedAccuracy { get; } + + /// + public GeoCoordinate Position { get; } + + /// + public double PositionAccuracy { get; } + + /// + public DateTimeOffset Timestamp { get; } + } +} \ No newline at end of file diff --git a/src/Core/Locations/ICoreLocationService.cs b/src/Core/Locations/ICoreLocationService.cs index 0ec8ec59f..2b4bf57b4 100644 --- a/src/Core/Locations/ICoreLocationService.cs +++ b/src/Core/Locations/ICoreLocationService.cs @@ -23,16 +23,6 @@ public interface ICoreLocationService /// IObservable DeterminedState { get; } - /// - /// Gets an observable sequence of region beacon failure notifications. - /// - IObservable FailedRangingBeacons { get; } - - /// - /// Gets an observable sequence of beacon range constraint notifications. - /// - IObservable RangedBeaconsSatisfyingConstraint { get; } - /// /// Gets an observable sequence of regions when the are started being monitored. /// diff --git a/src/Core/Locations/IGpsLocation.cs b/src/Core/Locations/IGpsLocation.cs new file mode 100644 index 000000000..7d33879f0 --- /dev/null +++ b/src/Core/Locations/IGpsLocation.cs @@ -0,0 +1,50 @@ +using System; + +namespace Rocket.Surgery.Airframe +{ + /// + /// Interface representing a gps location. + /// + public interface IGpsLocation + { + /// + /// Gets the altitude of the reading. + /// + double Altitude { get; } + + /// + /// Gets the heading of the reading. + /// + double Heading { get; } + + /// + /// Gets the accuracy of the heading. + /// + double HeadingAccuracy { get; } + + /// + /// Gets the current speed. + /// + double Speed { get; } + + /// + /// Gets the accuracy in meters per second for the speed. + /// + double SpeedAccuracy { get; } + + /// + /// Gets the position of the reading. + /// + GeoCoordinate Position { get; } + + /// + /// Gets the position accuracy. + /// + double PositionAccuracy { get; } + + /// + /// Gets the timestamp. + /// + DateTimeOffset Timestamp { get; } + } +} \ No newline at end of file