Converting Lat / long to PointClass - c#

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

Related

Cross-platform solution to do geography calculations

I'm working on migrating a .Net framework application to .Net Core and I need to support running on Linux.
The application needs to calculate the intersection of polygons and very long lines on the Earths surface, and so it uses Geography objects as apposed to Geometry to take into account the Earth's elliptical shape.
For this we use Microsoft.SqlServer.Types, which lets us do the following:
// Line from New York to Paris
SqlGeography line = SqlGeography.STGeomFromText(new System.Data.SqlTypes.SqlChars("LINESTRING(40.730610 -73.935242, 48.864716 2.349014)"), 4326);
// Polygon in the Atlantic
SqlGeography polygon = SqlGeography.STGeomFromText(new System.Data.SqlTypes.SqlChars("POLYGON((60 -40, 60 -20, 30 -20, 30 -40, 60 -40))"), 4326);
// Contains the two locations where the line intersects with the polygon
SqlGeography intersection = line.STIntersection(polygon);
The problem is that Microsoft.SqlServer.Types only works on Windows. How can I get the same result in a way that will also compile and run on Linux?
I've looked into NetTopologySuite but it seems to only support geometry calculations
Not sure if you are using EFCore or the NetTopology Suite for handling geography data in your project - but all these are already supported in that package.
Microsoft
Nuget
As you are using the WellKnownText format you can use the Docs from here:
Github Docs
I can't post the source code of my solution but an example of how to use it would be:
public class GeographyHelper
{
private static GeometryFactory _geometryFactory
{
get
{
return NetTopologySuite.NtsGeometryServices.Instance.CreateGeometryFactory(4326);
}
}
public bool TestIntersectsAPolygon(double latitude, double longitude)
{
var wellKnownText = "YOUR POLYGON WKT";
var point = _geometryFactory.CreatePoint(new Coordinate(longitude, latitude));
var rdr = new NetTopologySuite.IO.WKTReader();
var geometry = rdr.Read(wellKnownText);
var polygon = _geometryFactory.CreatePolygon(geometry.Coordinates);
var intersects = polygon.Intersects(point);
return intersects ;
}
}

Zoom level by distance in Mapbox map for Xamarin.Forms

Does anyone know how to select the correct zoom level so that two dots hit the screen?
I am using Mapbox and Xamarin.Forms.
In fact, there are more points, but the two most extreme are taken. Using them, I can get the center where the camera will look. Also, of course, I can get the distance between them. But here's how to calculate the specific zoom level (from 0 to 22), I have no idea.
Here is the zoom level documentation.
I was helped by an article on Wikipedia, which some user left in the comments, but then for some reason deleted it. Here
The result is a method:
public const double EARTH_EQUATORIAL_CIRCUMFERENCE_METTERS = 40075016.686; //Equatorial circumference of the Earth
public const double C = EARTH_EQUATORIAL_CIRCUMFERENCE_METTERS;
public static double CalculateZoomLevel(double lat, double distanceBetweenPoints)
{
double logNum = C * Math.Cos(MathHelper.DegreesToRadians(lat)) / distanceBetweenPoints;
double zoomLvl = Math.Log(logNum, 2);
return zoomLvl;
}
It's not perfect, but a very good result. But there is a problem when the path between the points becomes horizontal. In order to avoid this problem, I make some modifications to the distance between the points:
var distance = Distance.BetweenPositions(new Xamarin.Forms.Maps.Position(minLat, minLon),
new Xamarin.Forms.Maps.Position(maxLat, maxLon)).Meters;
if (Math.Abs(minLat - maxLat) < Math.Abs(minLon - maxLon))
{
distance += distance * (Math.Abs(minLon - maxLon) / Math.Abs(minLat - maxLat) / 10);
}
map.ZoomLevel = MapboxHelper.CalculateZoomLevel(zoomPos.Latitude, distance);
zoomPos - Center between two points.
Maybe someone will come in handy.

Map projection with System.Drawing.Graphics

I have a set of geo coordinates as latitude/longitude pairs that I wish to project onto a 2D surface. Some of the coordinates are connected by lines forming a shape/outline/polygon.
I understand how to project individual points using one of the many available map projections like Mercator and then drawing them with Graphics.DrawArc but how do I go about projecting the connecting lines between them? I can't just project the two defining coordinates of a line and draw it Graphics.DrawLine because every single point on that line has to be projected as well, right? I don't know much about these things, so I hope you understand what I mean.
Is it even possible to do what I'm trying to do using just the methods provided by the System.Drawing.Graphics class? Can I do this with a projection matrix? If anyone could explain a bit how I would go about doing this, I would greatly appreciate it. Thanks.
If your are only drawing lines it is probably easiest just to subdivide the lines into short segments, project each segment point and draw straight lines between them. This might not be the most performant or exact way to do it. But it should be fairly easy to implement.
For example:
public static void DrawSubdivided(this Graphics g, Pen pen, Vector2 p1, Vector2 p2, float subdivisionLength)
{
var d = p2 - p1;
var length = d.Length();
// Add check for zero-length lines
var dn = d / length;
var points = new List<Vector2>();
for (float l = 0; l < length; l += subdivisionLength)
{
points.Add( p1 + dn * l);
}
points.Add(p2);
// apply transformation to the points
g.DrawLines(pen, points.Select(p => new PointF(p.X, p.Y)).ToArray());
}
This uses System.Numerics.Vector2 since it has reasonable arithmetic operations defined, in contrast to PointF.

Call function when chart y-value exceeds line's y-value

I have a candlestick chart which automatically updates with real time prices from a cryptocurrency exchange in the .NET forms. The goal is to make the bot preform actions when the price on chart passes one of the lines drawn by the user. So far I've come to the point of enabling line-drawing for users thanks to this article.
Could anyone please point me towards a method of detecting collision between the chart candles and the drawn lines? I feel like there must be an easier way than what I'm thinking of currently, just can't seem to figure out the way to it.
Using the exact solution for the line drawing as in the article, also posted code for the line-drawing below:
int index1 = 1;
int index2 = 4;
DataPoint left = chart.Series[0].Points[index1];
DataPoint right = chart.Series[0].Points[index2];
//Init the annotation
LineAnnotation line = new LineAnnotation();
line.AxisX = chart.ChartAreas[0].AxisX;
line.AxisY = chart.ChartAreas[0].AxisY;
line.IsSizeAlwaysRelative = false;
//Each point in a candlestick series has several y values, 0=high, 1=low, 2=open, 3=close
line.Y = left.YValues[1]; //low
line.X = left.XValue;
//If your data is indexed (your x values are Strings or you've set Series.IsXValueIndexed to true), use the data point index(+1) as the line X coordinate.
//line.X = index1 + 1;
//Use the width and height properties to determine the end position of the annotation.
line.Height = right.YValues[1] - left.YValues[1];
line.Width = right.XValue - left.XValue;
//Again, use the index if necessary
//line.Width = index2 - index1;
chart.Annotations.Add(line);
Just looking for a point in the direction of an easier solution, not the solution itself :) Thanks in advance!
So it sounds like you are asking is if a Point (Geometry) is above or below a line.
Here are the assumption (which you can change later to fit your needs):
an external resource is giving you a specific value (Y) at a specific point in time (X), which will call the Integral point XY.
The user has drawn a line which gives you a starting point (x1, y1) and an end point (x2, y2).
The graphs X component is in minutes, with each tick horizontally is 1 minute.
The graphs Y component is in dollars, with each tick is $25.
The user has drawn a line from (1:00pm, $50) to (1:05pm, $75).
We get an Integral Point XY at 1:10pm of $125.
What is the value of the line at 1:10pm so you can compare it to the Integral Point XY.
Based on my comments of Trigonometry..
We know the adjacent length is: 1:05 - 1:00 = 5
We know the opposite length is: 75 - 25 = 50
Using the formula: atan(opposite / adjacent) = angle
We calculate that the angle is: atan(50 / 5) = 1.47112767rad (radians)
Now we simply reverse our math:
We know the adjacent length is: 1:10 - 1:00 = 10
We know our Angle in Radians: 1.47112767
Using the formula: adjacent * tan(angle) = opposite
We calculate that the opposite is: 10 * tan(1.47112767) = ~$99.999999 or $100
$125 is above $100, do what you want.

SqlGeography.STIntersection() returns even when I know there is no intersection

A little about the application;
The application allows the user to draw and save polygons onto bing maps WPF api.
The piece of code we are interested in is finding weather a point is whithin the polygon or not. The following function simply loops through the LocationCollection of the polygon on the bing map, and creates a SqlGeography object (OpenGisGeographyType.Polygon) which is an instance of a polygon.
We then convert the mouse click into SqlGeography object (OpenGisGeographyType.Point) by latitude and longitude and use the SqlGeography.STIntersection to find if our point lies within the polygon.
As seen in the picture, even when the point is outside the polygon, SqlGeography.STIntersection still returns a point of intersection. (You can tell this in the picture as I set a label to "Within Delivery Area" or "Customer our of Area" depending on what polygonSearch() function returned.
The right example in the picture has the expected results when a location is tested within the polygon.
The left example in the picture contains the unexpected results - Which indicates that a point is within a polygon , when it clearly is not!
NOTES:
I Use SqlGeography (myShape) to put the shapes on the map, so I know
the shape is being constructed with proper verticies.
I use SqlGeography (myPoint) to put the pin on the map, so I know the
pin is being tested at the correct verticies.
THIS ONLY FAILS ON LARGE POLYGONS
Below I give the peice of code which creates the polygon in memory, as well as converts the mouse click event to lat, longitude. ( I have included the polygon verticies in comments so that this can be looked at without the need of the bing api, just replace the for loop with the comments above it ) Although, you will need to reference Microsoft.SqlServer.Types.dll to create the SqlGeography objects. Its free with SQL Express 2008 , and can be found in C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies
public bool polygonSearch2(LocationCollection points, double lat, double lon)
{
SqlGeography myShape = new SqlGeography();
SqlGeographyBuilder shapeBuilder = new SqlGeographyBuilder();
// here are the verticies for the location collection if you want to hard code and try
//shapeBuilder.BeginFigure(47.4275329011347, -86.8136038458706);
//shapeBuilder.AddLine(36.5102408627967, -86.9680936860962);
//shapeBuilder.AddLine(37.4928909385966, -80.2884061860962);
//shapeBuilder.AddLine(38.7375329179818, -75.7180936860962);
//shapeBuilder.AddLine(48.0932596736361, -83.7161405610962);
//shapeBuilder.AddLine(47.4275329011347, -86.8136038458706);
//shapeBuilder.EndFigure();
//shapeBuilder.EndGeography();
// Here I just loop through my points collection backwards to create the polygon in the SqlGeography object
for (int i = points.Count - 1; i >= 0; i--)
{
if (i == 0)
{
shapeBuilder.AddLine(points[i].Latitude, points[i].Longitude);
shapeBuilder.EndFigure();
shapeBuilder.EndGeography();
continue;
}
if (i == points.Count - 1)
{
shapeBuilder.SetSrid(4326);
shapeBuilder.BeginGeography(OpenGisGeographyType.Polygon);
shapeBuilder.BeginFigure(points[i].Latitude, points[i].Longitude);
continue;
}
else
{
shapeBuilder.AddLine(points[i].Latitude, points[i].Longitude);
}
}
myShape = shapeBuilder.ConstructedGeography;
// Here I am creating a SqlGeography object as a point (user mouse click)
SqlGeography myPoint = new SqlGeography();
SqlGeographyBuilder pointBuilder = new SqlGeographyBuilder();
pointBuilder.SetSrid(4326);
pointBuilder.BeginGeography(OpenGisGeographyType.Point);
// Should pass, which it does
// Lat: lat = 43.682110574649791 , Lon: -79.79005605528323
// Should fail, but it intersects??
// Lat: 43.682108149690094 , Lon: -79.790037277494889
pointBuilder.BeginFigure(lat, lon);
pointBuilder.EndFigure();
pointBuilder.EndGeography();
myPoint = pointBuilder.ConstructedGeography;
SqlGeography result = myShape.STIntersection(myPoint);
if (result.Lat.IsNull)
return false;
else
return true;
}
Any help at all is much appreciated, I am starting to drive my boss nuts with this problem >.<
Could this have anything to do with the SRID?
I fixed this by converting all my polygon lat / long to a Point object on the screen using LocationToViewPortpoint function, as well as the point I'm testing for intersection, and use the X and Y values instead of lat / long in my STIntersects.

Categories

Resources