0

I have list of places and there latitude and longitude and i have to find out nearest places to my current location by calculating distance and then it will print on console one by one

public class Location
   {
      
     public string Place { get; set; }
     public double Latitude { get; set; }
     public double Longitude { get; set; }

     public Location( string Place_, double Latitude_, double Longitude_)
     {

       this.Place = Place_;
       this.Latitude = Latitude_;
       this.Longitude = Longitude_;
     }
  }

public static void Main() {
    List<Location> locations = new List<Location>();
    locations.Add(new Location( "Narayana International",   28.6075582,77.04700729999999));
    locations.Add(new Location( "Digital zone", 28.8610328484855, 77.0951520020164));
    locations.Add(new Location( "Balaji computers", 28.841364952771706, 77.09076910484801));
    locations.Add(new Location( "Codac Infosys", 29.967759964202948, 76.88406425590289));
    locations.Add(new Location( "Vijay computers", 30.6920753011206, 76.80162611916126));
    locations.Add(new Location( "Aadi online exam solutions", 30.393033405609803, 76.79088732285474));
    locations.Add(new Location( "Shamal online exam solutions", 31.039831347298907, 77.12095380938695));
    locations.Add(new Location( "Solani online exam solutions", 30.906763881433086, 77.08748709160578));
    locations.Add(new Location( "S.R.P online exam solutions", 30.68625270405994, 76.82522332445183));
    locations.Add(new Location( "SCORE HIGH", 28.7022839, 77.16902390000001));

    double searchLat = 19;//my loaction
    double searchLong = 74;//my location
   
  
    Dictionary<double, List<Location>> distances = new Dictionary<double, List<Location>>();
    
        locations.ForEach(location => {
        double distance = Program.GetDistance(location.Latitude, location.Longitude, searchLat, searchLong, location.Latitude ,location.Longitude  );
        if(distances.ContainsKey(distance)){
            distances[distance].Add(location);
        }else{
            distances.Add(distance, new List<Location>() { location });
        }
    });
    Console.WriteLine("Locations Are:" + locations.Count );
    Console.WriteLine("This is the nearest location to me:");

    double closestDistanceFromSearch = distances.Keys.OrderBy(k => k).First();

    Console.WriteLine(distances[distances.Keys.OrderBy(k => k).First()].First().Place);
    Console.ReadLine();
    
}
public static double GetDistance(double latitude, double longitude, double Latitude1, double Longitude1, double Latitude2, double Longitude2) {
    var d1 = latitude * (Math.PI / 180.0);
    var num1 = longitude * (Math.PI / 180.0);
    var d2 = Latitude1 * (Math.PI / 180.0);
    var num2 = Longitude1 * (Math.PI / 180.0) - num1;
    var d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);
    //var d3 = Math.Sqrt((Math.Pow(d1  - d2 , 2) + Math.Pow(num1 - num2, 2)));
    Console.WriteLine(d3);

    return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3)));

    var d4 = Latitude2 * (Math.PI / 180.0);
    var num4 = Longitude2 * (Math.PI / 180.0) - num1;
    var d5 = Math.Pow(Math.Sin((d4 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d4) * Math.Pow(Math.Sin(num4 / 2.0), 2.0);
    //var d3 = Math.Sqrt((Math.Pow(d1  - d2 , 2) + Math.Pow(num1 - num2, 2)));
    Console.WriteLine(d5);

    return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d5), Math.Sqrt(1.0 - d5)));

and these is my code but it is print only one nearest place name i want all names from the list nearest to me and after that the calculated distance of that it has to ascending order but how....

  • I'd suggest to use the location as *key*, not as *value*. Apart from this it's pretty unlikly two locations have the exact same distance form your search-point, so you won't need a list of locations within that dictionary. – MakePeaceGreatAgain Jun 13 '22 at 12:34
  • Hint: You write `First()` a lot of times in your code. If you want *all* instead of just the first, *don't* call `First()`, but use a **loop** to iterate through all elements instead. – Heinzi Jun 13 '22 at 12:36
  • 1
    A few notes here. Do not use a `double` as a key to a dictionary as the nature of floating-point numbers and roundoff errors is going to lead to trouble. Second, move the `GetDistance()` function under the `Location` class, to get the distance from the current location instance. It will be less awkward. – John Alexiou Jun 13 '22 at 12:44
  • 3
    Why is your `GetDistance()` function have **two** return statements? Please [edit] the post to fix the code. Addiitonally, `Latitude2` and `Longitude2` is never used. – John Alexiou Jun 13 '22 at 12:54
  • Your `GetDistance` seems to be taken from here: https://stackoverflow.com/questions/60700865/find-distance-between-2-coordinates-in-net-core/60809937#60809937. However I cannot see why you provide three longitudes and latitudes to your method. Just provide two and you can use that proposed fuction as is without any further change. There's no such thing as a distance between three points. You seem to provide the locations coords twice: `Program.GetDistance(location.Latitude, location.Longitude, searchLat, searchLong, location.Latitude ,location.Longitude );`. Just omit the two last params. – MakePeaceGreatAgain Jun 14 '22 at 11:00

3 Answers3

2

You shoot yourself in the shoe by reverting your data-structure for no reason. Instead of having a dictionary that maps distances to a list of locations, you should make it a map from locations to distances.

Furthermore it's pretty unlikely that two different locations have the exact same distance to your search-point, so you don't even need a List<Location> within your dictionary. In particular as you're just using the closest one in your output anyway:

Console.WriteLine(distances[distances.Keys.OrderBy(k => k).First()].First().Place);

This just prints the very first location of the closest element.

So afterall you should do this instead:

Dictionary<Location, double> distances = new Dictionary<Location, double>();

locations.ForEach(location => 
{
    double distance = Program.GetDistance(location.Latitude, location.Longitude, searchLat, searchLong, location.Latitude ,location.Longitude  );
    distances[location] = distance;
}

Console.WriteLine("Locations Are:" + locations.Count );
Console.WriteLine("This is the nearest location to me:");

var kv = closestDistanceFromSearch = distances.OrderBy(x => x.Value).First();
Console.WriteLine($"Place: { kv.Key.Place }");
Console.WriteLine($"Distance: { kv.Value }");

Console.ReadLine();
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
0

You're just selecting the first location when you do First().Place. You need to loop through all of them e.g:

var nearestLocations = distances[closestDistanceFromSearch];

foreach (var location in nearestLocations)
{
   Console.WriteLine(location.Place);
}
YungDeiza
  • 3,128
  • 1
  • 7
  • 32
0

If you want to sort the location by the nearest then you need just one OrderBy() operation. In your case you call .First() after the sorting and so you disregard all other values other than the first one. Here is base of the issue you have. But there are more to address.

As far as logic you want to add functionality to the Location class and handle as much as possible and leave the outside code only for using the logic. See example usage below:

class Program
{
    static void Main(string[] args)
    {
        List<PlaceLocation> locations = new List<PlaceLocation>
        {
            new PlaceLocation("Narayana International", 28.6075582, 77.04700729999999),
            new PlaceLocation("Digital zone", 28.8610328484855, 77.0951520020164),
            new PlaceLocation("Balaji computers", 28.841364952771706, 77.09076910484801),
            new PlaceLocation("Codac Infosys", 29.967759964202948, 76.88406425590289),
            new PlaceLocation("Vijay computers", 30.6920753011206, 76.80162611916126),
            new PlaceLocation("Aadi online exam solutions", 30.393033405609803, 76.79088732285474),
            new PlaceLocation("Shamal online exam solutions", 31.039831347298907, 77.12095380938695),
            new PlaceLocation("Solani online exam solutions", 30.906763881433086, 77.08748709160578),
            new PlaceLocation("S.R.P online exam solutions", 30.68625270405994, 76.82522332445183),
            new PlaceLocation("SCORE HIGH", 28.7022839, 77.16902390000001)
        };

        var myLocation = new Location(19.0, 74.0);

        Console.WriteLine($"I am at {myLocation}");
        Console.WriteLine();

        var nearest = locations.OrderBy((loc) => myLocation.GetDistance(loc)).ToArray();
        Console.WriteLine("Nearby places are:");

        foreach (var place in nearest)
        {
            Console.WriteLine($"{myLocation.GetDistance(place)/1000,9:f1} km to {place}");
        }
    }
}

with the output:

I am at (Lat:19, Long:74)

Nearby places are:
   1113.2 km to Narayana International
   1126.7 km to SCORE HIGH
   1139.3 km to Balaji computers
   1141.5 km to Digital zone
   1254.9 km to Codac Infosys
   1298.8 km to Aadi online exam solutions
   1331.3 km to S.R.P online exam solutions
   1331.5 km to Vijay computers
   1361.0 km to Solani online exam solutions
   1376.2 km to Shamal online exam solutions

and most of the logic inside the Location class. In fact, I propose a class hierarchy of Location and PlaceLocation : Location which defines a named place. This way everything has access the to the base method GetDistance() which measures the distance to a target.

public class Location
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public Location(double latitude, double longitude)
    {
        this.Latitude = latitude;
        this.Longitude = longitude;
    }

    public double GetDistance(Location target)
    {
        var d1 = Latitude * (Math.PI / 180.0);
        var num1 = Longitude * (Math.PI / 180.0);
        var d2 = target.Latitude * (Math.PI / 180.0);
        var num2 = target.Longitude * (Math.PI / 180.0) - num1;
        var d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);

        return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3)));
    }
    public override string ToString() => $"(Lat:{Latitude}, Long:{Longitude})";
}

public class PlaceLocation : Location
{
    public PlaceLocation(string place, double latitude, double longitude)
        : base(latitude, longitude)
    {
        Place = place;
    }

    public string Place { get; set; }

    public override string ToString() => $"{Place}";
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133