I am creating a window form application, which calculate the distance between two locations by their Lat-Long using google API. This works fine, But i notice that some time api give different result (distance) for same locations.
Example :-
Location-A => Mumbai (19.075983 , 72.877655)
Location-B => Delhi (28.704060 , 77.102493)
When i calculate the distance from location-A To location-B
decimal Distance = EmployeeTaskWorkLogManager.getDistance(string.Concat(19.075983, ", ", 72.877655), string.Concat(28.704060, ", ", 77.102493));
This shows
Distance = 1423.297
And when i calculate the distance from location-B To location-A
decimal Distance = EmployeeTaskWorkLogManager.getDistance(string.Concat(28.704060, ", ", 77.102493), string.Concat(19.075983, ", ", 72.877655));
This shows
Distance = 1415.239
This the code that i am using to calculate distance between two locations,
public static decimal getDistance(string origin, string destination)
{
decimal num;
Thread.Sleep(1000);
decimal num1 = new decimal(0);
string[] strArrays = new string[] { "https://maps.googleapis.com/maps/api/distancematrix/json?origins=", origin, "&destinations=", destination, "&key=****" };
string str = string.Concat(strArrays);
JObject jObjects = JObject.Parse(EmployeeTaskWorkLogManager.fileGetContents(str));
try
{
num1 = (decimal)jObjects.SelectToken("rows[0].elements[0].distance.value");
num = num1 / new decimal(1000);
}
catch
{
num = num1;
}
return num;
}
I Checked each line of my code, but unable to find why this difference has occurs.
The Google Maps Distance Matrix API does not calculate a straight-line or big-circle distance between two points. Instead it returns the distance according to the mode of travel (which you haven't specified in your API call, so it defaults to driving).
Think about it: there is no way that you will travel the exact distance in both directions, and the difference will become more marked the longer the journey and the more route changes it requires. It will depend on freeway off ramps (not available at particular intersections in both directions), one way streets, etc etc.
Therefore, the result is as expected.
Source: Google Maps Distance Matrix API
Related
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.
I'm trying to use the .Net Chart object to interactively define a spline function that I use to map from one range of values to another. In other words, I have a 0-4095 range (x axis) that I want to convert to a 0-100 range (y axis) using a spline. I've successfully set up a chart that plots a spline through a group of points. The user can interactively move the points to get the desired function shape. Works great.
Now...once I have the spline like the user wants, how can I (using the spline function), find the corresponding y value for any x value?
I can't seem to find a way to do that. I know that the chart object is doing the calculation somewhere since it's plotting the spline...maybe they don't provide access to that.
The alternative is to make the spline calculations myself...I don't want to go there unless absolutely necessary.
Thanks.
Bryan
You need to find the value of "t" (tension parameter) that produces the desired value of x. if you are using the range of 0 to 1 the parameter "t" value will be somewhere near to 0.5. Once you know t you can calculate the corresponding value of y. Solve a cubic equation which will generate 3 values for "t" that will result in same value of x. Check the link below.
http://algorithmist.wordpress.com/2009/09/28/cardinal-splines-part-2/
Cardinal splines specify the tangents at interior points based on the vector from previous point to subsequent point. Each tangent is parallel to this vector and some multiple of its length. For example, the tangent direction at point P1 is parallel to the vector P2 – P0, or we could simply write something like T1 = s(P2 – P0) where s is a real number.
Check this part of code below where xtarget is the input value x.
Code:
for (Double t = 0; t<=1; t += 0.01)
{
s = (1 - t) / 2;
P(t)x = s(-t3 + 2t2 – t)P1X + s(-t3 + t2)P2X + (2t3 – 3t2 + 1)P2X + s(t3 – 2t2 + t)P3X + (-2t3 + 3t2)P3X + s(t3 – t2)P4X
P(t)y = s(-t3 + 2t2 – t)P1Y + s(-t3 + t2)P2Y + (2t3 – 3t2 + 1)P2Y + s(t3 – 2t2 + t)P3Y+ (-2t3 + 3t2)P3Y + s(t3 – t2)P4Y
if(P(t)x=>xtarget)
{
return P(t)y;
}
}
The above method will give the approximate point P(t)y on the curve.
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
I'm doing a bit of geocoding using Google Local Search and need to be able to set the viewport of a google maps api v3 object on the page.
Now this is fine if google local search has returned me a good viewport as it does for a postcode or street but if i put in the name of a bar for example. The viewport returned is very large presumable because local search has found many matches but has returned the best.
I'd like to calculate my own viewport in this case and am doing so in the following manner:
result.Centre = new LatLon(double.Parse(lat),double.Parse(lon));
result.Span = new LatLon(0.006295d, 0.008407d);
string viewport = "{{\"center\":{{\"lat\":\"{0}\",\"lng\":\"{1}\"}},\"span\":{{\"lat\":\"{2}\",\"lng\":\"{3}\"}},\"sw\":{{\"lat\":\"{4}\",\"lng\":\"{5}\"}},\"ne\":{{\"lat\":\"{6}\",\"lng\":\"{7}\"}}}}";
//Calculate the south west by taking half the span and adding it to the centre
double swLat = result.Centre.Latitude + (result.Span.Latitude/2);
double swLon = result.Centre.Longitude + (result.Span.Longitude/2);
//and northeast corners by taking half the span and subtracting from the centre
double neLat = result.Centre.Latitude - (result.Span.Latitude/2);
double neLon = result.Centre.Longitude - (result.Span.Longitude/2);
//Fingers crossed :)
result.ViewPortJSON = string.Format(viewport, result.Centre.Latitude, result.Centre.Longitude,result.Span.Latitude, result.Span.Longitude, swLat, swLon,neLat, neLon);
The problem is I get some valid json but google maps zooms right out so I can see the whole world. The same code is being used at the front end as if google has given me the viewport so I don't understand what is going on.
Any ideas? Have I got my calculations wrong? I have tried with the whole span (i.e. assuming it is already a radius) with the same effect.
Thanks in advance.
hey, shouldn't you be doing:
double swLat = result.Centre.Latitude - (result.Span.Latitude/2);
double swLon = result.Centre.Longitude - (result.Span.Longitude/2);
//and northeast corners by taking half the span and subtracting from the centre
double neLat = result.Centre.Latitude + (result.Span.Latitude/2);
double neLon = result.Centre.Longitude + (result.Span.Longitude/2);
I'm working in an application (C#) that applies some readability formulas to a text, like Gunning-Fog, Precise SMOG, Flesh-Kincaid.
Now, I need to implement the Fry-based Grade formula in my program, I understand the formula's logic, pretty much you take 3 100-words samples and calculate the average on sentences per 100-words and syllables per 100-words, and then, you use a graph to plot the values.
Here is a more detailed explanation on how this formula works.
I already have the averages, but I have no idea on how can I tell my program to "go check the graph and plot the values and give me a level." I don't have to show the graph to the user, I only have to show him the level.
I was thinking that maybe I can have all the values in memory, divided into levels, for example:
Level 1: values whose sentence average are between 10.0 and 25+, and whose syllables average are between 108 and 132.
Level 2: values whose sentence average are between 7.7 and 10.0, and .... so on
But the problem is that so far, the only place in which I have found the values that define a level, are in the graph itself, and they aren't too much accurate, so if I apply the approach commented above, trying to take the values from the graph, my level estimations would be too much imprecise, thus, the Fry-based Grade will not be accurate.
So, maybe any of you knows about some place where I can find exact values for the different levels of the Fry-based Grade, or maybe any of you can help me think in a way to workaround this.
Thanks
Well, I'm not sure about this being the most efficient solution, neither the best one, but at least it does the job.
I gave up to the idea of having like a math formula to get the levels, maybe there is such a formula, but I couldn't find it.
So I took the Fry's graph, with all the levels, and I painted each level of a different color, them I loaded the image on my program using:
Bitmap image = new Bitmap(#"C:\FryGraph.png");
image.GetPixel(int x, int y);
As you can see, after loading the image I use the GetPixel method to get the color at the specified coordinates. I had to do some conversion, to get the equivalent pixels for a given value on the graph, since the scale of the graph is not the equivalent to the pixels of the image.
In the end, I compare the color returned by GetPixel to see which was the Fry readability level of the text.
I hope this may be of any help for someone who faces the same problem.
Cheers.
You simply need to determine the formula for the graph. That is, a formula that accepts the number of sentences and number of syllables, and returns the level.
If you can't find the formula, you can determine it yourself. Estimate the linear equation for each of the lines on the graph. Also estimate the 'out-of-bounds' areas in the 'long words' and 'long sentences' areas.
Now for each point, just determine the region in which it resides; which lines it is above and which lines it is below. This is fairly simple algebra, unfortunately this is the best link I can find to describe how to do that.
I have made a first pass at solving this that I thought I would share in case someone else is looking sometime in the future. I built on the answer above and created a generic list of linear equations that one can use to determine an approximate grade level. First had to correct the values to make it more linear. This does not take into account the invalid areas, but I may revisit that.
The equation class:
public class GradeLineEquation
{
// using form y = mx+b
// or y=Slope(x)=yIntercept
public int GradeLevel { get; set; }
public float Slope { get; set; }
public float yIntercept { get; set; }
public float GetYGivenX(float x)
{
float result = 0;
result = (Slope * x) + yIntercept;
return result;
}
public GradeLineEquation(int gradelevel,float slope,float yintercept)
{
this.GradeLevel = gradelevel;
this.Slope = slope;
this.yIntercept = yintercept;
}
}
Here is the FryCalculator:
public class FryCalculator
{
//this class normalizes the plot on the Fry readability graph the same way a person would, by choosing points on the graph based on values even though
//the y-axis is non-linear and neither axis starts at 0. Just picking a relative point on each axis to plot the intercept of the zero and infinite scope lines
private List<GradeLineEquation> linedefs = new List<GradeLineEquation>();
public FryCalculator()
{
LoadLevelEquations();
}
private void LoadLevelEquations()
{
// load the estimated linear equations for each line with the
// grade level, Slope, and y-intercept
linedefs.Add(new NLPTest.GradeLineEquation(1, (float)0.5, (float)22.5));
linedefs.Add(new NLPTest.GradeLineEquation(2, (float)0.5, (float)20.5));
linedefs.Add(new NLPTest.GradeLineEquation(3, (float)0.6, (float)17.4));
linedefs.Add(new NLPTest.GradeLineEquation(4, (float)0.6, (float)15.4));
linedefs.Add(new NLPTest.GradeLineEquation(5, (float)0.625, (float)13.125));
linedefs.Add(new NLPTest.GradeLineEquation(6, (float)0.833, (float)7.333));
linedefs.Add(new NLPTest.GradeLineEquation(7, (float)1.05, (float)-1.15));
linedefs.Add(new NLPTest.GradeLineEquation(8, (float)1.25, (float)-8.75));
linedefs.Add(new NLPTest.GradeLineEquation(9, (float)1.75, (float)-24.25));
linedefs.Add(new NLPTest.GradeLineEquation(10, (float)2, (float)-35));
linedefs.Add(new NLPTest.GradeLineEquation(11, (float)2, (float)-40));
linedefs.Add(new NLPTest.GradeLineEquation(12, (float)2.5, (float)-58.5));
linedefs.Add(new NLPTest.GradeLineEquation(13, (float)3.5, (float)-93));
linedefs.Add(new NLPTest.GradeLineEquation(14, (float)5.5, (float)-163));
}
public int GetGradeLevel(float avgSylls,float avgSentences)
{
// first normalize the values given to cartesion positions on the graph
float x = NormalizeX(avgSylls);
float y = NormalizeY(avgSentences);
// given x find the first grade level equation that produces a lower y at that x
return linedefs.Find(a => a.GetYGivenX(x) < y).GradeLevel;
}
private float NormalizeY(float avgSentenceCount)
{
float result = 0;
int lower = -1;
int upper = -1;
// load the list of y axis line intervalse
List<double> intervals = new List<double> {2.0, 2.5, 3.0, 3.3, 3.5, 3.6, 3.7, 3.8, 4.0, 4.2, 4.3, 4.5, 4.8, 5.0, 5.2, 5.6, 5.9, 6.3, 6.7, 7.1, 7.7, 8.3, 9.1, 10.0, 11.1, 12.5, 14.3, 16.7, 20.0, 25.0 };
// find the first line lower or equal to the number we have
lower = intervals.FindLastIndex(a => ((double)avgSentenceCount) >= a);
// if we are not over the top or on the line grab the next higher line value
if(lower > -1 && lower < intervals.Count-1 && ((float) intervals[lower] != avgSentenceCount))
upper = lower + 1;
// set the integer portion of the respons
result = (float)lower;
// if we have an upper limit calculate the percentage above the lower line (to two decimal places) and add it to the result
if(upper != -1)
result += (float)Math.Round((((avgSentenceCount - intervals[lower])/(intervals[upper] - intervals[lower]))),2);
return result;
}
private float NormalizeX(float avgSyllableCount)
{
// the x axis is MUCH simpler. Subtract 108 and divide by 2 to get the x position relative to a 0 origin.
float result = (avgSyllableCount - 108) / 2;
return result;
}
}