I am not looking for any code, just advice on a particular aspect of a project.
I have an XML file that contains coordinates and the building name and I want to be able to capture the coordinates of where the person is (I'm going to be creating a mobile application so this is ok).
Is the senario above possible using a LINQ statement in C#? If so, is it possible to get a close match? i.e. if the person isn't exactly in the coordinates, show him/her the nearest match.
I'm NOT specifically looking for any code just any hints, tips, or advanced tutorials on LINQ would be helpful.
Thanks
You can use System.Device.Location.GeoCoordinate class for this
List<GeoCoordinate> listTakenFromXml = ......
double lat = ......
double lon = ........
var nearest = new GeoCoordinate(lat, lon).NearestPoint(listTakenFromXml);
public static class SoExtensions
{
public static GeoCoordinate NearestPoint(this GeoCoordinate loc, IEnumerable<GeoCoordinate> coords)
{
GeoCoordinate minLoc = null;
double minDist = double.MaxValue;
foreach (var c in coords)
{
var dist = c.GetDistanceTo(loc);
if ( dist < minDist)
{
minDist = dist;
minLoc = c;
}
}
return minLoc;
}
}
See Calculate distance, bearing and more between Latitude/Longitude points and use the method which bests suits your situation. Once you have the calculation if you have a set of points in a list, then Linq to Object's extensions can help you with determining your logic as needed.
Related
I have created my own class 'Geolocation' which just holds double variables 'longitude' and 'latitude'.
I have a List and another static Geolocation object. I need to loop through my List and analyse which of these locations is closest to the static location. By 'closest' I am referring to real life distance, so I think it would be called a geospatial analysis.
My own pseudo idea is along the lines of this.
Geolocation[] closestPositions = new Geolocation[]
for (int i = 0; i < TrainStations.AllStations.Count; i++)
{
Geolocation A = TrainStations.AllStations[i];
Geolocation B = DataHandler.UserCurrentPosition;
if (A and B are closer than what is stored in closestPosition)
{
closestPosition[0] = A;
closestPosition[1] = B;
}
}
By the end of this loop I would be left with the two closest points (more specifically which train station in the city is closest to the user's current position).
However, I'm really not sure if the way my pesudo code would function is most efficient, and I don't know how to do the actual analysis (get the distances from A to B and measure which is shortest).
Rather than creating my own class 'Geolocation', I should have used the inbuilt System.Device.Location.GeoCoordinate class.
https://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate(v=vs.110).aspx
This class has a function 'GetDistanceTo(Geocoordinate)' which "Returns the distance between the latitude and longitude coordinates that are specified by this GeoCoordinate and another specified GeoCoordinate."
I can use this in my loop, using GeoCordinate objects, to analyse which points are closest.
Given an array of geo coordinates and another geo coordinate, I would like to find the nearest coordinate(s) to it.
For example, given the array:
lat long
52.525782 13.316927
52.526409 13.319083
52.525678 13.320317
And the point: 52.525730, 13.314556, then the first point 52.525782, 13.316927 will be returned, as it is the closest one.
Is the only way of acheiving it is looping through all the array and finding the distance between the points? What happens if the array contains too much coordinates?
You can try it using LINQ, but the inner workings of LINQ would still loop over your collection. For example:
//Your list with coordinates
List<GeoCoordinate> coords = new List<GeoCoordinate>();
//The coord you want to compare
GeoCoordinate crd = new GeoCoordinate();
//Sorts the list based on the proximity of the coords to crd
var sortedCoords = coords.OrderBy(x => x.GetDistanceTo(crd)).ToList();
I know it doesn't use an array, but I find using lists is easier.
I think that should work, let me know if it does!
struct coord
{
public double lat;
public double lon;
}
public void Main(coord coord)
{
var coords = new[]{ new coord(){lat=1, lon=1} };
var closest = coords.Min(p => Math.Abs(p.lat - coord.lat) + Math.Abs(p.lon - coord.lon));
}
I am trying to use the Net Topology Suite (v 1.13.2) in a simple console app to find points in a shape file that contains road information.
I have loaded the shape file and stored the data in a typed list, and I have checked that the data looks as expected..
When I iterate around the list I want to find if a given point is within any of the shapes in the shape file.
Sounds straightforward!
my code looks like;
private static Feature FindPoint(double lat, double lon)
{
Coordinate c = new Coordinate(lat, lon);
IGeometry g = factory.CreateGeometry(Geometry.DefaultFactory.CreatePoint(c));
foreach(Feature f in Features)
{
if (f.Geometry.Overlaps(g))
return f;
if (f.Geometry.EnvelopeInternal.Contains(c))
return f;
if (f.Geometry.Boundary.Contains(g))
return f;
if (f.Geometry.Contains(g))
return f;
}
return null;
}
None of these statements, or any of the others I have tried return any indication that the point is within any of the shapes in.
I am trying with a Lat and Long I picked from one of the shapes in the file!
SO it should be there.
Any ideas as to where I am going wrong?
#Habib, I have checked and the geometry type is LineString, but the Envelope is defined as a polygon.
I have looked at the link you sent, new code below;
DistanceOp dop = new DistanceOp(f.Geometry,g);
var np = dop.NearestPoints();
var d = dop.Distance();
Tried to do as indicated there, but the answer I get for the distance between the point I have chosen and the line is 71.236662957979718.
71.236662957979718 what? cm, metres, degrees???
Strange anyway, as I have picked a point on one of the lines, so I would expect an answer of 0.
Putting the longitude before the latitude while creating the coordinate got it working for me.
Corrected code:
private static Feature FindPoint(double lat, double lon)
{
Coordinate c = new Coordinate(lon, lat);
Geometry g = Geometry.DefaultFactory.CreatePoint(c);
foreach(Feature f in Features)
{
if (f.Geometry.Contains(g))
return f;
}
return null;
}
I'm using NetTopologySuite.IO.Esri found here: https://github.com/NetTopologySuite/NetTopologySuite.IO.Esri.
I want to be able to display a Bing map in a Windows 8/Store app with an array of pushpins/waypoints at a zoom setting that will show every location, but no more than that - IOW, I want as much detail as possible while still showing all of the locations/coordinates.
I have this pseudocode:
public static int GetMapZoomSettingForCoordinates(List<String> coordinatesList)
{
string furthestNorth = GetFurthestNorth(coordinatesList);
string furthestSouth = GetFurthestSouth(coordinatesList);
string furthestEast = GetFurthestEast(coordinatesList);
string furthestWest = GetFurthestWest(coordinatesList);
int milesBetweenNorthAndSouthExtremes = GetMilesBetween(furthestNorth, furthestSouth);
int milesBetweenEastAndWestExtremes = GetMilesBetween(furthestEast, furthestWest);
int greaterCardinalDistance = Math.Max(milesBetweenNorthAndSouthExtremes, milesBetweenEastAndWestExtremes);
return GetZoomSettingForDistance(greaterCardinalDistance);
}
...but the "sticking point" (the hard part) are the "milesBetween" functions. Is there an existing algorithm for computing the miles between two coordinates?
I do realize this is a U.S.-centric bunch of code for now (miles vs. kilometers); that is, for now, as designed.
UPDATE
This is my new pseudocode (actual compiling code, but untested):
public static int GetMapZoomSettingForCoordinates(List<string> coordinatePairsList)
{
List<double> LatsList = new List<double>();
List<double> LongsList = new List<double>();
List<string> tempList = new List<string>();
foreach (string s in coordinatePairsList)
{
tempList.AddRange(s.Split(';'));
double dLat;
double.TryParse(tempList[0], out dLat);
double dLong;
double.TryParse(tempList[0], out dLong);
LatsList.Add(dLat);
LongsList.Add(dLong);
tempList.Clear();
}
double furthestNorth = GetFurthestNorth(LatsList);
double furthestSouth = GetFurthestSouth(LatsList);
double furthestEast = GetFurthestEast(LongsList);
double furthestWest = GetFurthestWest(LongsList);
int milesToDisplay =
HaversineInMiles(furthestWest, furthestNorth, furthestEast, furthestSouth);
return GetZoomSettingForDistance(milesToDisplay);
}
private static double GetFurthestNorth(List<double> longitudesList)
{
double northernmostVal = 0.0;
foreach (double d in longitudesList)
{
if (d > northernmostVal)
{
northernmostVal = d;
}
}
return northernmostVal;
}
...I still don't know what GetZoomSettingForDistance() should be/do, though...
UPDATE 2
This is "more better":
public static int GetMapZoomSettingForCoordinates(List<Tuple<double, double>> coordinatePairsList)
{
var LatsList = new List<double>();
var LongsList = new List<double>();
foreach (Tuple<double,double> tupDub in coordinatePairsList)
{
LatsList.Add(tupDub.Item1);
LongsList.Add(tupDub.Item2);
}
double furthestNorth = GetFurthestNorth(LongsList);
double furthestSouth = GetFurthestSouth(LongsList);
double furthestEast = GetFurthestEast(LatsList);
double furthestWest = GetFurthestWest(LatsList);
int milesToDisplay =
HaversineInMiles(furthestWest, furthestNorth, furthestEast, furthestSouth);
return GetZoomSettingForDistance(milesToDisplay);
}
UPDATE 3
I realized that my logic was backwards, or wrong, at any rate, regarding meridians of longitude and parallels of latitude. While it's true that meridians of longitude are the vertical lines ("drawn" North-to-South or vice versa) and that parallels of latitude are the horizontal lines ("drawn" East-to-West), points along those line represent the North-South location based on parallels of latitude, and represent East-West locations based on meridians of longitude. This seemed backwards in my mind until I visualized the lines spinning across (longitude) and up and over (latitude) the earth, rather than simply circling the earth like the rings of Saturn do; what also helped get my perception right was reminding myself that it is the values of the meridians of longitude that determine in which time zone one finds themselves. SO, the code above should change to pass latitudes to determine furthest North and furthest South, and conversely pass longitudes to determine furthest East and furthest West.
You can use the Haversine formula to compute the distance along the surface of a sphere.
Here's a C++ function to compute the distance using the Earth as the size of the sphere. It would easily be convertible to C#.
Note that the formula can be simplified if you want to just find the distance either latitudinally or longitudinally (which it sounds like you are trying to do).
To get the straight line distance you use the Pythagorean Theorem to find the hypotenuse.
d = ((delta x)^2 + (delta y)^2)^.5
Basically square both the changes in the x direction and the y direction, add them, then take the square root.
in your pseudo code it looks like you could have many points and you want to find a maximum distance that should encompass all of them, which makes sense if you are trying to figure out a scale for the zoom of the map. The same formula should work, just use milesBetweenEastAndWestExtremes for delta x, and milesBetweenNorthAndSouthExtremes for delta y. You may opt to add a fixed amount to this just to make sure you don't have points right on the very edge of the map.
Given the code below, how do I compare a List of objects's values with a test value?
I'm building a geolocation application. I'll be passing in longitude and latitude and would like to have the service answer back with the location closest to those values.
I started down the path of converting to a string, and formatting the values down to two decimal places, but that seemed a bit too ghetto, and I'm looking for a more elegant solution.
public class Location : IEnumerable
{
public string label { get; set; }
public double lat { get; set; }
public double lon { get; set; }
//Implement IEnumerable
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
}
[HandleError]
public class HomeController : Controller
{
private List<Location> myList = new List<Location>
{
new Location {
label="Atlanta Midtown",
lon=33.657674,
lat=-84.423130},
new Location {
label="Atlanta Airport",
lon=33.794151,
lat=-84.387228},
new Location {
label="Stamford, CT",
lon=41.053758,
lat=-73.530979}, ...
}
public static int Main(String[] args)
{
string inLat = "-80.987654";
double dblInLat = double.Parse(inLat);
// here's where I would like to find the closest location to the inLat
// once I figure out this, I'll implement the Longitude, and I'll be set
}
You're going to want to use the correct distance formula for this if you don't want to end up with weird results:
double CalculateDistance(double lat1, double lon1, double lat2, double lon2)
{
const double R = 6371;
return Math.Acos(
Math.Sin(lat1) * Math.Sin(lat2) +
Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1)) * R;
}
I hope that's the right formula, my math might be a little rusty here. All of the parameters need to be in rads, so if you're taking inputs in degrees, write a utility method as well:
double DegToRad(double deg)
{
return deg * Math.PI / 180.0;
}
Anyway, after that, you can figure out the shortest distance as:
Location GetClosestLocation(Location origin)
{
double olatr = DegToRad(origin.Lat);
double olonr = DegToRad(origin.Lon);
return
(from l in locations
let latr = DegToRad(l.Lat)
let lonr = DegToRad(l.Lon)
orderby CalculateDistance(latr, lonr, olatr, olonr))
.FirstOrDefault();
}
This isn't technically the most performant solution, since it has to do a sort, but there's no nice-looking Linq extension method to do min with a projection. If you want that, you'll have to write your own foreach loop:
Location GetClosestLocation(Location origin)
{
double olatr = DegToRad(origin.Lat);
double olonr = DegToRad(origin.Lon);
Location closest = null;
double minDistance = double.MaxValue;
foreach (Location l in locations)
{
double latr = DegToRad(l.Lat);
double lonr = DegToRad(l.Lon);
double dist = CalculateDistance(latr, lonr, olatr, olonr));
if (dist < minDistance)
{
minDistance = dist;
closest = l;
}
}
return closest;
}
How accurate do you need to be? It's called the Great Circle Distance.
See for example http://www.movable-type.co.uk/scripts/gis-faq-5.1.html
I found this which someone created that calculates distances between two distances across the globe using one of several different methods. I had to convert the .NET project to the updated VS2008, but that seemed to work fine. Then I just added this project to my solution and made a reference to it.
My code then became:
string inLat = "-80.987654";
string inLon = "33.521478";
var miles = GetNearestLocation(inLat, inLon);
public double GetNearestLocation(string lat, string lon)
{
double dblInLat = double.Parse(lat);
double dblInLon = double.Parse(lon);
// instantiate the calculator
GeodeticCalculator geoCalc = new GeodeticCalculator();
// select a reference elllipsoid
Ellipsoid reference = Ellipsoid.WGS84;
// set user's current coordinates
GlobalCoordinates userLocation;
userLocation = new GlobalCoordinates(
new Angle(dblInLon), new Angle(dblInLat)
);
// set example coordinates- when fully fleshed out,
// this would be passed into this method
GlobalCoordinates testLocation;
testLocation= new GlobalCoordinates(
new Angle(41.88253), new Angle(-87.624207) // lon, then lat
);
// calculate the geodetic curve
GeodeticCurve geoCurve = geoCalc.CalculateGeodeticCurve(reference, userLocation, testLocation);
double ellipseKilometers = geoCurve.EllipsoidalDistance / 1000.0;
double ellipseMiles = ellipseKilometers * 0.621371192;
/*
Console.WriteLine("2-D path from input location to test location using WGS84");
Console.WriteLine(" Ellipsoidal Distance: {0:0.00} kilometers ({1:0.00} miles)", ellipseKilometers, ellipseMiles);
Console.WriteLine(" Azimuth: {0:0.00} degrees", geoCurve.Azimuth.Degrees);
Console.WriteLine(" Reverse Azimuth: {0:0.00} degrees", geoCurve.ReverseAzimuth.Degrees);
*/
return ellipseMiles;
}
I think the easiest would be to do the following. But not most performant :)
Iterate through the list and calculate the distance between each location and your reference location. At each step, check to see if this is the shortest distance you have seen so far and store that. Once you get the the end of the list, you will have the closest location in your stored variable.
If you are talking about a very large number of locations and you plan to do many spacial queries of this nature you might consider setting up a quadtree index on the data.
Here is a link I found after doing a quick 'Bing', it should help with the distance calculation, I hope. Please refer this Link:
http://www.delphiforfun.org/Programs/Math_Topics/Lat-Long%20Distance.htm