Entity Framework DbGeography not calculating the Area correctly? - c#

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);
}

Related

Polygon to line string coverage path

I hope that this is not the wrong place for my question, however, when I don't have anywhere else to go, SO never lets me down.
I am looking for a way to convert any given polygon on a map, to a serpentine line string. I would like to pass the polygon as a geography data type (it could be a poly string) which then takes the polygon, and generates a line string covering the entire area of the polygon.
The below images illustrates perfectly what I want to achieve in the sense that I want to provide the blue polygon and I want the green path to be returned either as a collection of points or a geography data type:
Taken from https://robotics.stackexchange.com/questions/14539/generate-a-coverage-path-given-a-polygon-region
I have scoured the internet and cannot seem to find any code examples around how to get this done. I would like to do this in C# preferable but I am not too fussy about the language. Second to C# I can look at using SQL too or even Python as a last resort.
I have read countless articles on Path planning but they all seem to be overkill in terms of what I want to achieve.
Could anybody point me in any direction as to how I can achieve this? Any information or samples will be highly appreciated.
I have thought about breaking the polygon down to its boundary lines and draw a serpentine line string across it by manually checking if a given point is within the bounds of the polygon. Surely there has to be a more efficient way to achieve this? Maybe a ready made API of some sort?

How add a radius / proximity around a city for estimating traffic (Google Adwords)?

For a project I'm busy with the Google Adwords api (client library = C#). I show the visitor an estimate of traffic based on keywords and locality/city currently. It's using following method
https://developers.google.com/adwords/api/docs/guides/traffic-estimator-service
I want to add an extra requirement: the estimate must contain a proximity (radius) of 15 kilometers around locality/city s. I tried to add the proximity to the campaignestimator object, but then an error occured based on the response.
Part of the programcode
var proximity = new Proximity
{
radiusDistanceUnits = ProximityDistanceUnits.KILOMETERS,
radiusInUnits = seaConfigurationModel.Proximity,
geoPoint = new GeoPoint()
{
latitudeInMicroDegrees = 43633941,
longitudeInMicroDegrees = -79398718
}
};
campaignEstimateRequest.criteria = new Criterion[] { languageCriterion, proximity };
Exception:
Unmarshalling Error: cvc-elt.4.2: Cannot resolve 'q2:Proximity' to a type definition for element 'criteria'.
Does anyone know how to solve this? Do I need another method?
Thanks a lot.
Jordy
The problem is that a CampaignEstimateRequest criteria can only be on of two types, Language and Location, and you're trying to pass a Proximity type.
Also, Proximity only works (apparently) on the AdX API which I suspect you might not be using.
What you might need to do is calculate your results based on a Location, set to your target city and then manually remove anything that isn't in the radius.
I've found one such example of how to calculate the radius inclusion here:
How to check if a certain coordinates fall to another coordinates radius using PHP only
But you'll need to convert that into C#.
I think the proximity needs to be used. The code to create a proximity target is similar to how you add a location target, except that you have to create a Proximity object instead of a Location object.
Not sure but I think if you set the Proximity object is available in the TrafficEstimatorService. However if so you can set a radius. Check out the following URL:
https://developers.google.com/adwords/api/docs/guides/location-targeting
Good luck!

.NET Equivalent of SQL Server STDistance

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)

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 search through a list of coordinates and display, depending on the distance between it and current location? (Windows Phone 7)

I have gathered about 60 or so geo coordinates of cinema locations around Ireland and I want to use them in my WP7 app to show nearest cinemas to your current location.
I thought I would save them in an XML file but I don't think that will work for what I'm trying to achieve, which is to be able to search the entire list of coordinates and display whichever cinemas appear within (for example) 25kms of their location.
I have found a few links to helping find the distance between two points so that part shouldn't be too bad.
Any ideas on how I might go about achieving this?
Thanks in advance
I created a similar service.
First of all, as Shedal also pointed out, consider hosting the locations in an external service. This will make your life a lot easier when you need to add more locations. (The update application service in App Hub can take up to 10 days which is a long time if you need to quickly fix a wrong location)
Secondly, once you have loaded the list of locations, it is simple to loop through them and find the nearest cinemas. You can use the Haversine formula to calculate the distance between two geo locations (this formula takes into account that the earth is round, and not flat - unlike the Pythagorean theorem). Luckily for your the WP GeoLocation class comes with a built in function called GetDistanceTo that you can use.
The problem is now nothing but a simple Linq query:
List<GeoCoordinate> cinemaLocations = new List<GeoCoordinate>();
GeoCoordinate myLocation = new GeoCoordinate();
var closestCinemas = cinemaLocations.OrderBy(s => s.GetDistanceTo(myLocation));
var closestCinema = closestCinemas.FirstOrDefault();
Shouldn't the list of cinemas be updated from time to time? If so, I would host a web service somewhere and call it from your app to obtain the up-to-date list of cinemas.
In regards to calculation of the distance, it's simply the Pythagorean theorem.
D = sqrt(abs(x2 - x1) + abs(y2 - y1))
I would store them in a SQL server (SQL lite can run inside a WP7 app). Then I would query with lat and lon using a +/- of some value based on your current location. Now you will need to be sure to compensate for being near the 0 degrees axis, but this will give you a square area to reduce your results down to. Once you have this you can iterate over the result set looking for the distance to each point and just drop the results that sit in the corners of this square creating a result set that looks in all directions with a defined maximum tolerance. Of course I haven't had to solve this exact problem (I tend to rely on projects like Neo4J Spacial which handles stuff like this automatically (and really well)

Categories

Resources