.NET Equivalent of SQL Server STDistance - c#

I can use the following SQL to calculate the distance between a fixed location and the location against the venues in the database.
SELECT Location.STDistance(geography::Point(51, -2, 4326)) * 0.00062137119 FROM Venues
Please note the distance returned is in miles and the Location field is a geography type.
I was wondering what is the equivalent of this in .NET which would return the same values. This method would have the following signature:
public static double Distance(location1Latitude, location1Longitude, location2Latitude, location2Longitude) {
return ...;
}
I know I could call the database method in .NET but I don't wish to do this. I'm hoping there is a formula to calculate the distance. Thanks

I believe you can simply add Microsoft.SqlServer.Types.dll as a reference and then use the SqlGeometry type like any other .NET type, including calling the STDistance method.

You would need to compute the Geographical distance to compute the distance manually. There are many different techniques and formulas to do this, each with different underlying assumptions (ie: a spherical earth, ellipsoidal earth, etc).
A common option is the haversine formula, with a C# implementation available here.

this is very well explained here.
Shortly: with EF5 (to be more specific, with .net 4.5) Microsoft included the type DbGeography. Let say you already have a bunch of lat/long, you can then create a DbGeography object easily using an helper like:
public static DbGeography CreatePoint(double latitude, double longitude)
{
var text = string.Format(CultureInfo.InvariantCulture.NumberFormat,
"POINT({0} {1})", longitude, latitude);
// 4326 is most common coordinate system used by GPS/Maps
return DbGeography.PointFromText(text, 4326);
}
Once you got a two or more points (DbGeography) you got everything to calculate the Distance between them:
var distance = point1.Distance(point2)

Related

Entity Framework DbGeography not calculating the Area correctly?

If I define the polygon in WKT format as this (longitude/latitude):
string wkt = "POLYGON ((15.981800258159638 45.810693408924287, 15.983624160289764 45.810783148865241, 15.983688533306122 45.809844611497965, 15.981843173503876 45.8096838246252, 15.981800258159638 45.810693408924287))"
and I create the DbGeography object as this:
var polygon = System.Data.Entity.Spatial.DbGeography.FromText(wkt);
the value of the Area property is 510065621695499.69
If I do the same thing using Postgres and PostGis like this:
select ST_Area(ST_GeographyFromText(wkt))
the result is 15496.7483872527 which should be the correct value.
If I create the polygon by reversing coordinates (latitude/longitude), both DbGeography and ST_Area return the same value (21247.6269483712)
Can anybody explain what is going on here?
PS: the polygon is located in Zagreb, Croatia.
This is to do with polygon orientation. For these spatial data types, "The interior of the polygon in an ellipsoidal system is defined by the left-hand rule", to quote the documentation. See eg this question and no doubt many others for further reading and approaches to address this.
If you reverse the order of the points in your WKT, you will get an area 15496.7447813973 (using a SRID of 4326 corresponding to WGS84), which is very close to what your other GIS systems are giving you. The difference I would guess might be due to a different SRID, but I'm far from an expert.
Note that the erroneous result you are getting is very close to 510,072,000 km², the surface area of the entire Earth - since DbGeography uses the orientation protocol it does, it has constructed the outside of the polygon you intended.
To add to AakashM 's answer, if you always want the smaller area you can do this:
public double GetArea(string wellKnownText)
{
var sqlGeo = SqlGeography.Parse(wellKnownText);
var normalArea = sqlGeo.STArea().Value;
var reorientedArea = sqlGeo.ReorientObject().STArea().Value;
// just trying to not return almost the whole area of our beloved planet...
return Math.Min(normalArea, reorientedArea);
}

Distance and other stuff between two Location in C# / WinRT

How can I get the distance between two Location in C#?
It also would be very nice to get the direction between two Locations if I have the compass value of the device.
is there any api for this?
It depends.
What do you mean by Location?
Case 1: When location is geographic point (Latitude, Longitude)
You need to go through spherical geometry. In fact you should make some calculation on a sphere (or Ellipsoid or other Spheroids). But the best way to avoid doing such complicated calculation is to use Microsoft.SqlServer.Types.dll (v.10 or v.11). If you have SQL Server 2012 (or 2008) installed on your system, you can find it somewhere like this (otherwise you may download it from the web):
"C:\Program Files\Microsoft SQL Server\110\Shared\Microsoft.SqlServer.Types.dll"
Then using this dll you can declare two SqlGeography type and call the STDistance() method and everything will be done correctly. (Remember there are lost of issue here, I'm not going to complicate it more than this). Here is the code:
SqlGeography p1 =
SqlGeography.STPointFromText(new System.Data.SqlTypes.SqlChars("POINT( lat1, long1)"), 4326); // SRID of WGS84: 4326
SqlGeography p2 =
SqlGeography.STPointFromText(new System.Data.SqlTypes.SqlChars("POINT( lat2, long2)"), 4326); // SRID of WGS84: 4326
double distance = p1.STDistance(p2).Value;
Case 2: When location is a simple local 2D point (x, y) on a plane
In this case you can use the famous formula to calculate the distance:
double distance = Math.Sqrt(dx*dx + dy*dy); //dx and dy are the difference of the x and y of the two points

operations with Spatial data from Google Maps in SQL Server 2008 r2

How can i store a rectangle - consisting of 2 points NorthEast and SouthWest each point is a coordinate of lattitude and longitude
And add a circle consisting of a center ( lat-lng ) and a radius (int/float value)
what is the best way to store and later on query if a lat-lng is within the bounds of a any circle or rectangle ?
also , can i store an array of those ? say 10 rectangles and 5 circles in a single record ?
Can i use Nhibernate to ease the pain?
Sorry if this seems noobish , i have never done anything with spatial data and i don't even have clue from where to start.
Any samples and pointers are helpful !
Thanks in advance.
Here's how I would approach this problem using TSQL.
For a rectangle, the simplest method is to extrapolate the extra 2 points by using the relevant coordinates from the original points. e.g.
NorthEast (lat1, lon1) NorthWest* (lat1, lon2)
SouthEast* (lat2, lon1) SouthWest (lat2, lon2)
*New point
That doesn't give you a true rectangle (in a mathematical sense) but it's a common method in GIS (it's how geohashes are formed) what you get is a rough rectangle with varying size based on the distance from the equator. If you need an extact rectangle of a certain height/width you should look into using the Haversine formula to calculate the remaining 2 points, that will take into account bearing, and great circle distance.
http://www.movable-type.co.uk/scripts/latlong.html
To store the rectangle, I'd create a SQL table with a GEOGRAPHY type column, this will allow you assign additional attributes (e.g. name) along with a spatial index that will make future queries much faster.
CREATE TABLE dbo.geographies
(
NAME VARCHAR(50)
,GEOG GEOGRAPHY
)
INSERT INTO dbo.geographies (NAME, GEOG)
VALUES ('Rectangle', geography::STPolyFromText('POLYGON((lon1 lat1, lon2 lat1, lon2 lat2, lon1 lat2, lon1 lat1))', 4326))
Note that both the first point and the last point are the same, this is required to 'close' the polygon, and the final number denotes the SRID, or coordinate system, in this case WGS84. You can reference this page: http://msdn.microsoft.com/en-us/library/bb933971
As to the circle, it's simple to store a point and then use the radius to apply a buffer around the point:
INSERT INTO dbo.geographies (NAME, GEOG)
VALUES ('Circle with Radius', geography::STPointFromText('POINT(lon lat)', 4326).STBuffer([radius]))
Note that the buffer takes its input in meters so you may need to apply a conversion, more notes on this page: http://msdn.microsoft.com/en-us/library/bb933979
Now the fun part, it's quite easy to check for intersection on a point using the STIntersects
method.
http://msdn.microsoft.com/en-us/library/bb933962.aspx
DECLARE #point GEOGRAPHY = geography::STPointFromText('POINT(lon lat)', 4326)
SELECT * FROM dbo.geographies
WHERE #point.STIntersects(GEOG) = 1
The code sample takes a point and returns a list of all the geographies that the point is found within. It's important the the SRIDs of the new point and the geographies in the table match, otherwise you'll get zero matches (and probably pound you head against a wall for a while until you realize your mistake, at least, that's what I do).
As to integrating this with C#, I'm not sure how much help I can be, but it shouldn't be too much of a challenge to return the SQLGeography type
http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeography.aspx
Hopefully this at least points you in the right direction.

How to call user defined function in LINQ in my visual C# web service for my android application?

I am currently working on an application that will retrieve other users' locations based on distance.
I have a database that store all the user location information in latitude and longitude.
Since the calculation of distance between these two pairs of latitude and longitude is quite complicated, I need a function to handle it.
from a in db.Location.Where(a => (calDistance(lat, longi, Double.Parse(a.latitude), Double.Parse(a.longitude)))<Math.Abs(distance) )) {...}
However, I got the following error: LINQ to Entities does not recognize the method and this method cannot be translated into a store expression.
I don't know how to translated it into a store expression and also, the calculation also need the math library.
Is there any method that i can do to let the LINQ expression call my own function?
Maybe there are other ways to achieve my goal, can anyone help?
LinqToEntities won't allow you to call a function, it doesn't even allow ToString()
this is not a Linq thing its a LinqToEntities restriction
you could put your code in to the database as a Stored Proc or Function and call it using ExecuteStoreQuery
see here Does Entity Framework Code First support stored procedures?
I don't really know LINQ, but assuming that you can only send simple constraints in the query, I would construct a method that basically does the inverse of calDistance - take a coordinate and a distance and convert it into a bounding box with a minimum longitude, maximum longitude, minimum latitude, and maximum latitude.
You should be able to construct a simple query that will serve your purposes with those constraints.
something like (using Java here):
public double[] getCoordinateBounds(double distance, double longitude, double latitude) {
double[] bounds = new double[4];
bounds[0] = longitude - distanceToLongitudePoints * (distance);
bounds[1] = longitude + distanceToLongitudePoints * (distance);
bounds[2] = latitude - distanceToLatitudePoints * (distance);
bounds[3] = latitude + distanceToLatitudePoints * (distance);
return bounds;
}
Then you could construct a query.
double[] bounds = getCoordinateBounds(distance, longi, lat);
var nearbyUserLocations = from a in db.Location
where longitude > bounds[0] and longitude < bounds[1]
and latitude > bounds[2] and latitude < bounds[3]
This would give you a box of points rather than a radius of points, but it would be few enough points that you could then process them and throw out the ones outside your radius. Or you might decide that a box is good enough for your purposes.
The problem you see is that the LINQ to SQL engine is trying to inject T-SQL from your user-defined function and it cannot. One (albeit nasty) option is to retrieve all of your locations and then calculate from that result set.
var locations = db.Location.ToList();
locations = locations.Where(a => (calDistance(lat, longi, Double.Parse(a.latitude), Double.Parse(a.longitude))).ToList();

Nearest GPS coordinate based on distance from a given point

I have a list of GPS locations in a MySQL server database. The user will be entering a GPS coordinate in the application and he should get the nearest GPS coordinate.
I don't mind the distance calculation is based on "crow's flight" or anything else. It should be fast enough to search thousands of GPS locations.
I prefer solution in C#, else I will try to get the logic and apply myself.
There's one question on MySQL lat/long distance search in Need help optimizing a lat/Lon geo search for mysql
For C# distance calculation, most sites use the Haversine formula - here's a C# implementation - http://www.storm-consultancy.com/blog/development/code-snippets/the-haversine-formula-in-c-and-sql/ - this also has a SQL (MS SQL) implementation too.
/// <summary>
/// Returns the distance in miles or kilometers of any two
/// latitude / longitude points.
/// </summary>
/// <param name="pos1">Location 1</param>
/// <param name="pos2">Location 2</param>
/// <param name="unit">Miles or Kilometers</param>
/// <returns>Distance in the requested unit</returns>
public double HaversineDistance(LatLng pos1, LatLng pos2, DistanceUnit unit)
{
double R = (unit == DistanceUnit.Miles) ? 3960 : 6371;
var lat = (pos2.Latitude - pos1.Latitude).ToRadians();
var lng = (pos2.Longitude - pos1.Longitude).ToRadians();
var h1 = Math.Sin(lat / 2) * Math.Sin(lat / 2) +
Math.Cos(pos1.Latitude.ToRadians()) * Math.Cos(pos2.Latitude.ToRadians()) *
Math.Sin(lng / 2) * Math.Sin(lng / 2);
var h2 = 2 * Math.Asin(Math.Min(1, Math.Sqrt(h1)));
return R * h2;
}
public enum DistanceUnit { Miles, Kilometers };
For most queries... you are probably OK splitting the work between C# and SQL
use MySQL to select "close" lat/lng points, e.g. say where lat and lng are within 1.0 of your target
then use C# to calculate a more accurate distance and to select "the best".
If you were using MS SQL 2008 then I'd recommend using the MS SQL geography types as these have built-in optimised indexing and calculation features - I see that MySQL also has some extensions - http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html - but I've no experience with these.
What you're trying to do is called a nearest-neighbor search and there are many good data structures which can speed up these sorts of searches. You may want to look into kd-trees, for example, as they can give expected sublinear time (O(√ n) in two dimensions) queries for the point in a data set nearest to some arbitrary test point. They're also surprisingly easy to implement if you're comfortable writing a modified binary search tree.
Note that when dealing with spherical geometry our euclidean geometry isn't quite precise (a^2+b^2=c^2) but for small subparts of the earth it might be approximate enough.
Otherwise: http://en.wikipedia.org/wiki/Great-circle_distance
If you have coordinate data stored in a database, you might want to query the database directly, especially if there is a large amount of the data. However, you need specific database support for that (normal indexes do not help). I know MSSQL supports geography data, I did not test MySQL, but online documentation seems to suggest there is similar support, too. As soon as you have built a spatial-aware database, you get your results with a simple query.

Categories

Resources