Microsoft.SqlServer.Types.SqlGeography.ToString has unexpected behavior.
Take a look at my unit test:
latitude = 40.584474F;
longitude = -111.633491F;
var location = SqlGeography.Point( latitude, longitude, 4326 );
var point = location.ToString();
At this point, the variable point has a value of:
POINT (-111.63349151611328 40.58447265625)
As you can see, latitude and longitude have been swapped from the norm.
This is from a nuget package downloaded today. (v11.0.2)
Clarification: Object values are correct but ToString() is producing a format which is not appropriate to SQL Server itself, even though this is a SQLServer specific class in my opinion.
Update 2: This is not a duplicate because it is different from the question with a similar title, since this is about a .NET class (Microsoft.SqlServer.Types.SqlGeography.ToString), rather than SQL Server itself.
The latitude and longitude have not been swapped. It's just that the Point() constructor accepts the longitude after latitude, but in ToString() longitude is printed before latitude.
Print the values location.Lat and location.Long to know for sure.
SQL Server 12 will accept: geography::Point(47.65100, -122.3490, 4326). To produce the string that would be acceptable to SQL Server:
var point = ColValue as SqlGeography;
if (point != null)
return "geography::Point(" + point.Lat + ", " + point.Long + ", 4326)";
Related
I was wondering if it was possible to get the individual Lat and Long number values that make up a BingMapsRESTToolkit.Location, and store them as their own number variables. As in something like this:
double Latitude = Location.getLatitude();
The Location class doesn't let you access these values, but I'm not sure if there's another way to do it, as it's something that seems like it should be doable.
The Location class is a fairly simple class. Use something like this:
var loc = Location.Point.GetCoordinate().
var latitude = loc.latitude;
var longitude = loc.longitude;
Here is my simplified code:
var P1 = Common.Geography.CreatePoint((decimal)-73.980000, (decimal)40.7155773);
var P2 = Common.Geography.CreatePoint((decimal)-73.984434, (decimal)40.7155773);
var Distance = P1.Distance(P2);
Here is "CreatePoint" function:
public static DbGeography CreatePoint(decimal Longitude, decimal Latitude)
{
return DbGeography.FromText(String.Format(CultureInfo.InvariantCulture, "POINT({0} {1})", Longitude, Latitude));
}
I saw in a several sources that results should be in meters so I expected to see 493:
http://boulter.com/gps/distance/?from=-73.98%2C40.7155773&to=-73.984434%2C40.7155773&units=k
Google Maps measure tool shows same result (but it doesn't allow to share link).
However result I got from "Distance" function is 374.65. I checked CoordinateSystemId for both objecs and it has default value 4326.
So what I'm doing wrong? Or in which units is that result?
Check if the latitude / longitude order is swapped somewhere.
I get 0.37k if I swap the order in your boulter link:
http://boulter.com/gps/distance/?from=40.7155773%2C-73.98&to=40.7155773%2C-73.984434&units=k which is probably what you get in C# code.
Note that for boulter and google maps order should be latitude, longitude;
WKT uses the opposite order longitude, latitude.
I have two GPS Coordinates i wanted to calculate distance between those but the result on SQL server is entirely different from result in c# I googled and found that the both approaches return distance in meters but this difference is driving me crazy.
SQL SERVER Query
select geography::Point(25.3132666, 55.2994054 ,4326)
.STDistance(geography::Point(25.25434, 55.32820,4326)) as Distance;
Web API
String format = "POINT(25.25434 55.32820)";
DbGeography myLocation = DbGeography.PointFromText(format, 4326);
var users = context.Users.Select(u => new
{
fullName = u.name,
lat = u.location.Latitude,
lng = u.location.Longitude,
distance = myLocation.Distance(u.location)
}).ToList();
Response
,{"fullName":"jons smith","lat":25.3132666,"lng":55.2994054,"distance":4133581.8647264037}
Thanks in advance.
Check latitude and longitude order in the WKT representation of the point in SQL when defining the point, they are as follow GEOGRAPHY::POINT(Latitude, Longitude, srid):
SELECT GEOGRAPHY::Point(25.3132666,55.2994054 ,4326)
.STDistance(GEOGRAPHY::Point(25.25434, 55.32820,4326)) as distance;
//distance: 7142.94965953253
But when defining DBGeography in C# code, the order is different:"POINT(Longitude Latitude)"
String format = "POINT(55.32820 25.25434)";
DbGeography myLocation = DbGeography.PointFromText(format, 4326);
var users = context.Users.Select(u => new
{
fullName = u.name,
lat = u.location.Latitude,
lng = u.location.Longitude,
distance = myLocation.Distance(u.location)
}).ToList();
//distance: 7142.949659532529
Also you should check those locations inserted in the Users table. Make sure when inserting they have been correctly inserted. Otherwise they will be somewhere else not the location of your Users
More:
SELECT GEOGRAPHY::Point(25, 55 ,4326).Lat //25
DbGeography.PointFromText("POINT(25 55)", 4326).Latitude.Value //55
Microsoft.SqlServer.Types.SqlGeography.STPointFromText(
new System.Data.SqlTypes.SqlChars("POINT(25 55)"), 4326).Lat.Value;
//55
What is WKT and where does it come from?
It's a Well-Known Text representation of different geometry types that is introduce by OGC in the Simple Feature Access specification and all software vendors are advised to follow it in the sake of the compatibility. This specification shows us how to define point, line (linestring), polygon and some other geometry types as text (WKT) and binary (WKB).
SQL Server do not completely follow this specification and we see the result of not following standards, causes such problems in different components of even the same company.
Switch the Lat/Lng in in API Version. In the API, it should go Lng Lat
select geography::Point(25.3132666, 55.2994054 ,4326).STDistance(geography::Point(25.25434, 55.32820,4326)) as Distance
Returns
7142.94965953253
This is where I flipped one Lat/Lng usinq my UDF
Select [dbo].[udf-Geo-Meters](55.2994054,25.3132666 ,25.25434,55.32820)
Returns
4135883.9028193
The issue is very minor but tricky
SQL SERVER Query
geography::Point(25.3132666, 55.2994054 ,4326)
SQL Server defines a point such that first value is latitude and second value is longitude.
Web API
String format = "POINT(25.25434 55.32820)";
DbGeography myLocation = DbGeography.FromText(format);
C# defines a point from above format in such a way that first value is longitude and second value is latitude
I have series of doubles in the format of decimal latitude/longitude values. I want to store these values and when it comes to adding a new value to the list see if there is already a value in the list that is ±0.0001 to the latitude and then the longitude. If the value is withing ±0.0001 of either the latitude or the longitude I do not want to store it.
What I would like to recreate is a version of the MongoDB Geospatial $near command.
Can anyone please offer any advice on how to go about this? Are there any free c# geospatial libraries that would help me achieve this goal?
Many Thanks for the advice.
I don't know any libraries, but you could use LINQ. Here's one for a List<Tuple<double, double>>:
var y = new Tuple<double, double>(15.25, 18.700001);
if(!coordinates.Any(x => Math.Abs(x.Item1 - y.Item1) <= 0.0001 || Math.Abs(x.Item2 - y.Item2) <= 0.0001)) {
// No coordinate in the list is within ±0.0001 of either the latitude or the longitude
coordinates.Add(y);
}
If I'm understanding correctly, something like this should work for you, assuming the coordinates are wrapped in some kind of container.
public class Coordinates
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public bool IsNear(List<Coordinates> coords, double lat, double lon, double tolerance)
{
return coords.Any(p => Math.Abs(p.Latitude - lat) < tolerance || Math.Abs(p.Longitude - lon) < tolerance);
}
You need to calculate distance between two points:
if (Math.Abs(Math.Sqrt((longitude - longitude_before) * (longitude - longitude_before) + (latitude - latitude_before) * (latitude - latitude_before))) > 0.0001)
{
// Record new point
}
where latitude_before and longitude_before are last entries in recorded path - it seems to me that it is not needed to check previous points. If it turns out that it is, and performance becames an issue, you will have to take a look at range search.
IPoint pPoint = new ESRI.ArcGIS.Geometry.PointClass();
pPoint.PutCoords(-92.96000, 44.9227); //This should be near Minneapolis
mapControl.CenterAt(pPoint); //mapControl is a AxMapControl
When I run this code the point always ends up near Kansas. Can anyone help me convert lat / longs to an PointClass that will work properly?
I'm using VS2010 ArcEngine 10 C#
There is a lot more to this than you have currently given. Both a lat/long point and your map have a specific spatial reference. If they do not match, it is likely your point will plot in an unexpected way.
The point you are showing is a standard Latitude/Longitude point. Which is likely Nad83 (North American), or WGS84 (World). These are Spatial References with a Geographical Coordinate System. You are likely trying to plot the point on a Projected Coordinate System.
You need to make your MapControl's Spatial Reference match the types of points you are trying to plot.
Since I do not know the Spatial Reference of your Map, I can only give you an example of translating a Lat/Lon into what the MapControl's current spatial reference is.
ISpatialReferenceFactory srFactory = new SpatialReferenceEnvironmentClass();
IGeographicCoordinateSystem gcs = srFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984);
ISpatialReference sr1 = gcs;
IPoint point = new PointClass() as IPoint;
point.PutCoords(-92.96000, 44.9227);
IGeometry geometryShape;
geometryShape = point;
geometryShape.SpatialReference = sr1;
geometryShape.Project(mapControl.SpatialReference);
mapControl.DrawShape(geometryShape);
This takes your point and projects it to the MapControls current spatial reference, then plots the point.
Good Luck.
Here is the code to zoom and center on a lat / long, the above poster was helpful but his solution did not work for me.
mapControl.MapScale = mapControl.MapScale / 2; //for zooming
ISpatialReferenceFactory srFactory = new SpatialReferenceEnvironmentClass(); //move up top later
IGeographicCoordinateSystem gcs = srFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984); //World lat / long format
ISpatialReference sr1 = gcs;
IPoint point = new PointClass();
point.SpatialReference = gcs;
point.PutCoords(-92.96000, 44.9227);
point.Project(mapControl.SpatialReference);
mapControl.CenterAt(point);