diff --git a/Algorithms.Tests/Other/GeofenceTests.cs b/Algorithms.Tests/Other/GeofenceTests.cs new file mode 100644 index 00000000..30dcfff4 --- /dev/null +++ b/Algorithms.Tests/Other/GeofenceTests.cs @@ -0,0 +1,66 @@ +using Algorithms.Other; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Algorithms.Tests.Other +{ + [TestFixture] + public class GeofenceTests + { + private Geofence? geofence; + + [SetUp] + public void Setup() + { + geofence = new Geofence(10.8231, 106.6297, 500); + } + + [Test] + public void IsInside_ShouldReturnTrue_WhenUserIsInsideGeofence() + { + double userLat = 10.8221; + double userLon = 106.6289; + + bool? result = geofence?.IsInside(userLat, userLon); + + Assert.That(result, Is.True); + } + + [Test] + public void IsInside_ShouldReturnFalse_WhenUserIsOutsideGeofence() + { + double userLat = 10.8300; + double userLon = 106.6400; + + bool? result = geofence?.IsInside(userLat, userLon); + + Assert.That(result, Is.False); + } + + [Test] + public void IsInside_ShouldReturnTrue_WhenUserIsExactlyOnGeofenceBoundary() + { + double userLat = 10.8231; + double userLon = 106.6297; + + bool? result = geofence?.IsInside(userLat, userLon); + + Assert.That(result, Is.True); + } + + [Test] + public void IsInside_ShouldReturnFalse_WhenUserIsFarFromGeofence() + { + double userLat = 20.0000; + double userLon = 100.0000; + + bool? result = geofence?.IsInside(userLat, userLon); + + Assert.That(result, Is.False); + } + } +} diff --git a/Algorithms.Tests/Other/TriangulatorTests.cs b/Algorithms.Tests/Other/TriangulatorTests.cs new file mode 100644 index 00000000..39014684 --- /dev/null +++ b/Algorithms.Tests/Other/TriangulatorTests.cs @@ -0,0 +1,62 @@ +using Algorithms.Other; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Algorithms.Tests.Other +{ + [TestFixture] + public class TriangulatorTests + { + [Test] + public void CalculatePosition_ValidCoordinatesAndDistances_ReturnsExpectedPosition() + { + var triangulator = new Triangulator(); + var baseLocations = new List<(double Latitude, double Longitude)> + { + (16.054407, 108.202167), + (16.049807, 108.218991), + (16.063597, 108.215553) + }; + + var distances = new List { 0.5, 0.7, 0.6 }; + + var expectedPosition = (Latitude: 16.054, Longitude: 108.210); + var result = triangulator.CalculatePosition(baseLocations, distances); + + Assert.That(result.Latitude, Is.EqualTo(expectedPosition.Latitude).Within(0.01)); + Assert.That(result.Longitude, Is.EqualTo(expectedPosition.Longitude).Within(0.01)); + } + + [Test] + public void CalculatePosition_InvalidBaseLocations_ThrowsArgumentException() + { + var triangulator = new Triangulator(); + var baseLocations = new List<(double Latitude, double Longitude)> + { + (10.762622, 106.660172) + }; + var distances = new List { 1.0 }; + + Assert.That(() => triangulator.CalculatePosition(baseLocations, distances), Throws.ArgumentException); + } + + [Test] + public void CalculatePosition_InvalidDistances_ThrowsArgumentException() + { + var triangulator = new Triangulator(); + var baseLocations = new List<(double Latitude, double Longitude)> + { + (10.762622, 106.660172), + (10.774981, 106.665504), + (10.771817, 106.681179) + }; + var distances = new List { 1.0 }; + + Assert.That(() => triangulator.CalculatePosition(baseLocations, distances), Throws.ArgumentException); + } + } +} diff --git a/Algorithms/Other/Geofence.cs b/Algorithms/Other/Geofence.cs new file mode 100644 index 00000000..90fb9626 --- /dev/null +++ b/Algorithms/Other/Geofence.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Algorithms.Other +{ + public class Geofence + { + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public double RadiusInMeters { get; set; } + + public Geofence(double latitude, double longitude, double radiusInMeters) + { + Latitude = latitude; + Longitude = longitude; + RadiusInMeters = radiusInMeters; + } + + /// + /// Checks whether the provided user location (latitude and longitude) is within the geofence boundary. + /// The geofence is defined by a center point (latitude, longitude) and a radius in meters. + /// + /// The latitude of the user's current location. + /// The longitude of the user's current location. + /// Returns true if the user is inside the geofence, otherwise returns false. + public bool IsInside(double userLatitude, double userLongitude) + { + double distance = GeoLocation.CalculateDistanceFromLatLng(Latitude, Longitude, userLatitude, userLongitude); + return distance <= RadiusInMeters; + } + } +} diff --git a/Algorithms/Other/Triangulator.cs b/Algorithms/Other/Triangulator.cs new file mode 100644 index 00000000..d1e63d9f --- /dev/null +++ b/Algorithms/Other/Triangulator.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Algorithms.Other +{ + public class Triangulator + { + public (double Latitude, double Longitude) CalculatePosition(List<(double Latitude, double Longitude)> baseLocations, List distances) + { + if (baseLocations.Count < 3 || distances.Count < 3) + { + throw new ArgumentException("At least three points and corresponding distances are required."); + } + + // Convert distances from kilometers to degrees (approximately) + // 1 degree is approximately 111.32 km at the equator + const double kmPerDegree = 111.32; + var distancesInDegrees = distances.Select(d => d / kmPerDegree).ToList(); + + // Get the coordinates of the three base stations + double lat1 = baseLocations[0].Latitude; + double lon1 = baseLocations[0].Longitude; + double lat2 = baseLocations[1].Latitude; + double lon2 = baseLocations[1].Longitude; + double lat3 = baseLocations[2].Latitude; + double lon3 = baseLocations[2].Longitude; + + // Convert coordinates to radians + lat1 = ToRadians(lat1); + lon1 = ToRadians(lon1); + lat2 = ToRadians(lat2); + lon2 = ToRadians(lon2); + lat3 = ToRadians(lat3); + lon3 = ToRadians(lon3); + + // Calculate the center point + double centerLat = (lat1 + lat2 + lat3) / 3; + double centerLon = (lon1 + lon2 + lon3) / 3; + + // Convert back to degrees + centerLat = ToDegrees(centerLat); + centerLon = ToDegrees(centerLon); + + return (centerLat, centerLon); + } + + private double ToRadians(double degrees) + { + return degrees * Math.PI / 180; + } + + private double ToDegrees(double radians) + { + return radians * 180 / Math.PI; + } + } +} diff --git a/README.md b/README.md index ddf2c1cc..8085ccab 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,8 @@ find more than one implementation for the same objective but using different alg * [Julian Easter](./Algorithms/Other/JulianEaster.cs) * [Pollard's Rho](./Algorithms/Other/PollardsRhoFactorizing.cs) * [GeoLocation Hash](./Algorithms/Other/Geohash.cs) + * [Geofencing](./Algorithms/Other/Geofence.cs) + * [Triangulation Algorithm](./Algorithms/Other/Triangulator.cs) * [Problems](./Algorithms/Problems) * [Stable Marriage](./Algorithms/Problems/StableMarriage) * [Gale-Shapley](./Algorithms/Problems/StableMarriage/GaleShapley.cs)