Calculating Area of Irregular Polygon in C# - c#

I've managed to write a 'for dummies' how to calculate the area of irregular polygon in C#, but I need it to be dynamic for any amount of verticies.
Can someone please help?
Class:
public class Vertex
{
private int _vertexIdx;
private double _coordX;
private double _coordY;
private double _coordZ;
public Vertex()
{ }
public Vertex(int vertexIdx, double coordX, double coordY, double coordZ)
{
_vertexIdx = vertexIdx;
_coordX = coordX;
_coordY = coordY;
_coordZ = coordZ;
}
public int VertexIdx
{
get { return _vertexIdx; }
set { _vertexIdx = value; }
}
public double X
{
get { return _coordX; }
set { _coordX = value; }
}
public double Y
{
get { return _coordY; }
set { _coordY = value; }
}
public double Z
{
get { return _coordZ; }
set { _coordZ = value; }
}
}
Form_Load:
List<Vertex> verticies = new List<Vertex>();
verticies.Add(new Vertex(1, 930.9729, 802.8789, 0));
verticies.Add(new Vertex(2, 941.5341, 805.662, 0));
verticies.Add(new Vertex(3, 946.5828, 799.271, 0));
verticies.Add(new Vertex(4, 932.6215, 797.0548, 0));
dataGridView1.DataSource = verticies;
Code to calculate when button is pressed: (hard-coded for 4 points polygon - should be for any amount...)
// X-coords
double x1;
double x2;
double x3;
double x4;
double x5;
// Y-coords
double y1;
double y2;
double y3;
double y4;
double y5;
// Xn * Yn++
double x1y2;
double x2y3;
double x3y4;
double x4y5;
// Yn * Xn++
double y1x2;
double y2x3;
double y3x4;
double y4x5;
// XnYn++ - YnXn++
double x1y2my1x2;
double x2y3my2x3;
double x3y4my3x4;
double x4y5my4x5;
double result;
double area;
x1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString());
y1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString());
txtLog.Text += String.Format("X1 = {0}\tY1 = {1}\r\n", x1, y1);
x2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[1].Value.ToString());
y2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[2].Value.ToString());
txtLog.Text += String.Format("X2 = {0}\tY2 = {1}\r\n", x2, y2);
x3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[1].Value.ToString());
y3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[2].Value.ToString());
txtLog.Text += String.Format("X3 = {0}\tY3 = {1}\r\n", x3, y3);
x4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[1].Value.ToString());
y4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[2].Value.ToString());
txtLog.Text += String.Format("X4 = {0}\tY4 = {1}\r\n", x4, y4);
// add the start point again
x5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString());
y5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString());
txtLog.Text += String.Format("X5 = {0}\tY5 = {1}\r\n", x5, y5);
txtLog.Text += "\r\n";
// Multiply
x1y2 = x1 * y2;
x2y3 = x2 * y3;
x3y4 = x3 * y4;
x4y5 = x4 * y5;
y1x2 = y1 * x2;
y2x3 = y2 * x3;
y3x4 = y3 * x4;
y4x5 = y4 * x5;
// Subtract from each other
x1y2my1x2 = x1y2 - y1x2;
x2y3my2x3 = x2y3 - y2x3;
x3y4my3x4 = x3y4 - y3x4;
x4y5my4x5 = x4y5 - y4x5;
// Sum all results
result = x1y2my1x2 + x2y3my2x3 + x3y4my3x4 + x4y5my4x5;
area = Math.Abs(result / 2);
txtLog.Text += String.Format("Area = {0}\r\n", area);
Example output:
X1 = 930.9729
Y1 = 802.8789
X2 = 941.5341
Y2 = 805.662
X3 = 946.5828
Y3 = 799.271
X4 = 932.6215
Y4 = 797.0548
X5 = 930.9729
Y5 = 802.8789
Area = 83.2566504099523

Using lambda expressions this becomes trivial!
var points = GetSomePoints();
points.Add(points[0]);
var area = Math.Abs(points.Take(points.Count - 1)
.Select((p, i) => (points[i + 1].X - p.X) * (points[i + 1].Y + p.Y))
.Sum() / 2);
The algorithm is explained here:
[This method adds] the areas of the trapezoids defined by the polygon's edges dropped
to the X-axis. When the program considers a bottom edge of a polygon,
the calculation gives a negative area so the space between the polygon
and the axis is subtracted, leaving the polygon's area.
The total calculated area is negative if the polygon is oriented clockwise [so the] function simply returns the absolute value.
This method
gives strange results for non-simple polygons (where edges cross).

public float Area(List<PointF> vertices)
{
vertices.Add(vertices[0]);
return Math.Abs(vertices.Take(vertices.Count - 1).Select((p, i) => (p.X * vertices[i + 1].Y) - (p.Y * vertices[i + 1].X)).Sum() / 2);
}

Something like that for a plane polygon (compiled with notepad):
static double GetDeterminant(double x1, double y1, double x2, double y2)
{
return x1 * y2 - x2 * y1;
}
static double GetArea(IList<Vertex> vertices)
{
if(vertices.Count < 3)
{
return 0;
}
double area = GetDeterminant(vertices[vertices.Count - 1].X, vertices[vertices.Count - 1].Y, vertices[0].X, vertices[0].Y);
for (int i = 1; i < vertices.Count; i++)
{
area += GetDeterminant(vertices[i - 1].X, vertices[i - 1].Y, vertices[i].X, vertices[i].Y);
}
return area / 2;
}
Although your approach doesn't pay attention to Z-axis. Therefore I'd advice to apply some transformation to get rid of it: you won't be able to get area if the polygon is not plane, whereas if it is plane you are able to get rid of the third dimension.

I've found that when calculating the area for approx 600,000 polygons, the basic formulae shown above worked for some polygons, but a lot were out by a huge degree. I was spot checking my results against https://geojson.io/ - which returned correct results for very complex polygons with holes in them (e.g. lakes in the middle). In order to calculate the correct area for a complex polygon, I ended up using the same system that geojson.io uses - a client side js library Turf.js see here https://turfjs.org/docs/#area
In this image, you can see my first try, then my second using Turf.js - there is a column there showing a ratio of how correct the first try was compared to the second where 1 is the same calculation. You can see that they are mostly close, but some are out by a factor of 10 or worse.
Here is some sample code to do the calculation. I had it load 200 onto the screen, then run the calculation in JS, and ajax the result back to the server side database.
$(document).ready(function () {
var items = $('.geojson');
for (var sc = 0; sc < items.length; sc++) {
var id = $(items[sc]).attr('data-id');
var polyData = JSON.parse($(items[sc]).find('.geojson-data').html());
//console.log('[' + polyData + ']');
var polygon = turf.polygon(polyData.coordinates);
var area = turf.area(polygon);
console.log('id[' + id + ']sqm[' + area + ']');
$(items[sc]).closest('tr').find('.data-id').html(id);
var answerDom = $(items[sc]).closest('tr').find('.answer');
if (true) {
$.ajax({
url: 'calc-poly-area-from-geojson-turf-scheduled.aspx',
type: "get",
data: {id:id, area: area },
invokedata: { answerDom: answerDom, area: area },
async: true,//run all at the same time
cache: false,
success: function (data) {
//console.log('test email done');
$(this.invokedata.answerDom).html('ok:' + this.invokedata.area);
$(this.invokedata.answerDom).removeClass('answer');
if ($('.answer').length == 0) {
window.location.reload();
}
},
error: function (msg) {
console.log("call failed: " + msg.responseText);
$(el).html('error in call');
//prompt('copy this',url+'?'+qs)
}
});
}
}
});
window.setTimeout(function () { window.location.reload(); }, 10000);
where an example polygon is here
{"type":"Polygon", "coordinates":[[[171.519147876006,-43.809111826162],[171.519264282931,-43.8094307100015],[171.519615782201,-43.8097268361192],[171.519874096036,-43.8097860548424],[171.525264107563,-43.8176887926426],[171.525356625489,-43.8179845471556],[171.525750029905,-43.8185636705947],[171.526002901974,-43.8187934292356],[171.526154917292,-43.8189686576417],[171.526249645477,-43.8191111884506],[171.526245660987,-43.819269203656],[171.526032299227,-43.8200263808647],[171.524134038501,-43.8268225827224],[171.523301803308,-43.8297987275054],[171.523129147529,-43.8301621243769],[171.522991616155,-43.8300725313285],[171.52248605771,-43.8302181414427],[171.522128893843,-43.8304084928376],[171.521558488905,-43.8304389785399],[171.521371202269,-43.830481916342],[171.521023295734,-43.8309120441211],[171.520774217465,-43.8310054055632],[171.520589483523,-43.8311387384524],[171.515210823266,-43.8294163992962],[171.514763136723,-43.8292736695248],[171.496256757791,-43.8233680542711],[171.494338310605,-43.8227558913632],[171.493450128779,-43.8224739752289],[171.493221517911,-43.8223838125259],[171.493001278557,-43.8222877021167],[171.492654147639,-43.821801588707],[171.491048512765,-43.8200169686591],[171.488157604579,-43.8168246695455],[171.488051808197,-43.8166695752984],[171.487648717141,-43.8162207994268],[171.486147094889,-43.8145461538075],[171.482241975825,-43.8101769774879],[171.481683765874,-43.8095751045999],[171.480858016595,-43.8085443728491],[171.481124633337,-43.8086557677844],[171.481334008334,-43.8085534985925],[171.481540735171,-43.8083379086683],[171.4815994175,-43.8077828104991],[171.481763314624,-43.8074471226617],[171.481812168914,-43.8064706917151],[171.48196041271,-43.8063093336607],[171.482260412185,-43.8062322290662],[171.482916004007,-43.8059780008537],[171.494844864468,-43.8013540958407],[171.501308718774,-43.7988446798756],[171.506019390319,-43.797017657826],[171.508275460952,-43.7961421972998],[171.508430707528,-43.7960805551645],[171.509117292333,-43.7963432108869],[171.510511038963,-43.7968679021071],[171.513299102675,-43.8007637699317],[171.513465917258,-43.8010892007185],[171.513696634335,-43.8013818859084],[171.513929550742,-43.8016136793445],[171.514114411714,-43.8018826827151],[171.514305634465,-43.8021912982997],[171.51440028511,-43.8024789426394],[171.514828618996,-43.8028429251794],[171.51494106207,-43.8031623582355],[171.515852739466,-43.8044825303059],[171.516111930457,-43.8047763591375],[171.517116748697,-43.8062534995253],[171.517374596163,-43.8065473602078],[171.517549793874,-43.8068229401963],[171.5176213721,-43.8070824951625],[171.517796573697,-43.8073580748019],[171.518070610117,-43.8076087983324],[171.518880109148,-43.8088563353488],[171.519147876006,-43.809111826162]]]}
you can paste that into geojson.io to check their area (click on poly, then 'properties' tab)

Related

How to fit f(x) = sin(x)*sin(x) function to your data?

I'm trying to fit f(x) = sin(x)*sin(x) function to my data, but I can not accurately do that:
fitting result
My data could have random phase shift and that is the main problem with this fitting.
I use MathNet.Numerics library.
My code for fitting:
Func<double, double> f = Fit.LinearCombinationFunc(
xData,
yData,
x => 1.0,
x => Math.Pow(Math.Sin(x + 1.0), 2));
I extracted the (red) data for analysis. Here are my results, using the equation "y = amplitude * sin(pi * (x - center) / width) * sin(pi * (x - center) / width" with fitted C# code. I suggest making a test using this equation with these parameter values as initial parameter estimates.
using System;
class Trigonometric_SineSquared_Offset
{
double Trigonometric_SineSquared_Offset_model(double x_in)
{
double temp;
temp = 0.0;
// coefficients
double amplitude = 2.4582405471785171E+02;
double center = -7.3116553541287885E+02;
double width = 3.1152304928336734E+00;
double Offset = 1.3146489736138119E+02;
temp = amplitude * Math.Sin(3.14159265358979323846 * (x_in - center) / width) * Math.Sin(3.14159265358979323846 * (x_in - center) / width);
temp += Offset;
return temp;
}
}
I found a solution for non linear fitting.
You can use CenterSpace.NMath library and do the folowing(for i.e. f(x) = a + c*sin(x+b)*sin(x+b) ):
DoubleParameterizedFunction func = new Function();
var f = new DoubleParameterizedDelegate(
func.Evaluate);
var fitter = new OneVariableFunctionFitter<TrustRegionMinimizer>(f);
DoubleVector x = new DoubleVector(xData);
DoubleVector y = new DoubleVector(yData);
DoubleVector init = new DoubleVector("100.0 1.0 100.0");
DoubleVector solution = fitter.Fit(x, y, init);
And Function() looks like that:
public class Function : DoubleParameterizedFunction
{
public Function ()
{ }
public override double Evaluate (DoubleVector p, double x)
{
double a = p[0];
double b = p[1];
double c = p[2];
return a + c*Math.Sin(b + x) * Math.Sin(b + x);
}
public override void GradientWithRespectToParams (DoubleVector p,
double x, ref DoubleVector grad)
{
double a = p[0];
double b = p[1];
double c = p[2];
grad[0] = 1; //partial differential for a
grad[1] = 2 * c * Math.Sin(x + b) * Math.Cos(x + b); //partial differential for b
grad[2] = Math.Sin(x + b) * Math.Sin(x + b); //partial differential for c
}
}
https://www.centerspace.net/doc/NMath/user/nonlinear-least-squares-86564.htm

Function that calculates diffrent positions from an array c#

I have a problem that I need help solving. Im supposed to create a function that calculates the distance between two positions. The positions are stored in two arrays, I can use as many parameters that I need to do this calculation
static double[] Latitudes = new double[] {
59.3261917, 57.7010496, 59.8939529, 65.5867395, 60.11021, 52.5069312, 48.859
};
static double[] Longitudes = new double[] {
17.7018773, 11.6136602, 10.6450348, 22.0422998, 24.7385057, 13.1445521, 2.2069765
};
I have been given an equation that will help me calculate the distance
distance = Math.sqrt( (x1 - x2)2 + (y1 - y2)2 )
My problem is that I can't get the elements from the arrays to the variables inside the function
Extract methods, split you problem into minor ones:
// Initial step:
// Distance between points
private static double Distance(double x1, double y1, double x2, double y2) {
return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
// Next step:
// Distance between points given as arrays' items indexes
private static double Distance(double[] xs, double[] ys, int indexFrom, indexTo) {
return Distance(xs[indexFrom], ys[indexFrom], xs[indexTo], ys[indexTo]);
}
Then use
// What is the distance between 0-th and 2-nd point?
double result = Distance(Latitudes, Longitudes, 0, 2);
Console.WriteLine(result);
// What is the distance between all the points?
for (int from = 0; from < Math.Min(Latitudes.Length, Longitudes.Length); ++from)
for (int to = from + 1; to < Math.Min(Latitudes.Length, Longitudes.Length); ++to) {
Console.WriteLine($"Distance from item #{from} to item #{to} is {Distance(Latitudes, Longitudes, from, to)}");
}
Well first you need to decide which positions you want to compare. This would be done by index. Lets says you want to compare positions at index 0 with positions at index 2. Then the code to get the correct variables would be:
double x1 = Latitudes[0];
double y1 = Longitudes[0];
double x2 = Latitudes[2];
double y2 = Longitudes[2];
You can then feed those values into your function. Your function code is wrong and won't compile. The correct call for the function would be:
double distance = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
In the interest of providing a more complete function for your class, and keeping in mind that your arrays are static, this will allow you to get the distance from any two given points based on index. Also, I expect this is a homework assignment so I am leaning towards your requirement to be creating a function similar to this:
double CalculateDistance(int index1, int index2)
{
double x1 = Latitudes[index1];
double y1 = Longitudes[index1];
double x2 = Latitudes[index2];
double y2 = Longitudes[index2];
return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
}
You can then call this function as follows:
double distance = CalculateDistance(0, 2);
Show distance for all pairs
if (Latitudes.Length == Longitudes.Length)
{
for (int i = 0; i < Latitudes.Length - 1; i = i + 2)
{
double x1 = Longitudes[i];
double x2 = Longitudes[i + 1];
double y1 = Latitudes[i];
double y2 = Latitudes[i + 1];
double distance = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
Console.WriteLine($"x1 = {x1}; x2 = {x2}; y1 = {y1}; y2 = {y2}; distance {distance}");
}
}

GPS Calculator conversion, calculate lat/lon value for new point

Here is problem which i have:
I load image in C#. On that image I have to insert 2 points: point A and point B by clicking mouse on random possitions.
Point A have it cords (Xa, Ya) which is read from program and I need to manually insert its GPS coords (LatitudeA and LongtudeA) for it.
Point B have it own cords (Xb, Yb) which is also read from program and I need to manually insert its GPS coords (LatitudeB and LongtudeB) for it.
So main problem is next: on every next click on screen I have to know GPS cords for that point. Also for that point C i have (Xc, Yc).
Here is my ComputeLatitudeAndLogitude method, but it seems it doesnt works perfectly. I need this on street level size.
Example:
A (487, 361, 45.252464, 19.850337)
B (1167, 397, 45.252026, 19.853990)
C (810, 513, ??? , ???); results should be C(810, 513, 45.251592 , 19.852075)
PLEASE feel free to contact me so we can fix problem, mymailis hladnopivo1990#gmail.com
public void ComputeLatitudeAndLogitud (Wpf_Maps.Point point)
{
int diffX = pointA.X - pointB.X;
int diffY = pointA.Y - pointB.Y;
double diffLatitude = pointA.Latitude - pointB.Latitude;
double diffLongitude = pointA.Longitude - pointB.Longitude;
double latitudePerPixel = Math.Abs(diffLatitude / diffX);
double longitudePerPixel = Math.Abs(diffLongitude / diffY);
int diffXforA = pointA.X - point.X;
int diffYforA = pointA.Y - point.Y;
int diffXforB = pointB.X - point.X;
int diffYforB = pointB.Y - point.Y;
double newLatitudeFromA = pointA.Latitude + (diffXforA * latitudePerPixel);
double newLatitudeFromB = pointB.Latitude + (diffXforB * latitudePerPixel);
double newLongitudeFromA = pointA.Longitude + (diffYforA * longitudePerPixel);
double newLongitudeFromB = pointB.Longitude + (diffYforB * longitudePerPixel);
point.Latitude = (newLatitudeFromA + newLatitudeFromB) / 2;
point.Longitude = (newLongitudeFromA + newLongitudeFromB) / 2;
}
Depending on the distance you need to cover, linear extrapolation will not work too good; the earth is not plain, and latitude distances vary with longitude.
One approximation would be a sphere on which you calculate the Great-circle distance.
(GPS) coordinates are usually recorded relative to a (non-sphere) model of the earth, the WGS-84 ellipsoid being the most common today. So for maximum accuracy, you'll have to calculate distances based on the corresponding reference model.
Additionally, if the reference model of the image is different from that of the GPS coordinates you may need more than two reference points to determine the exact mapping.
I presume pointA and pointB are at opposite corners of the map, A being bottom left (or top left?)... meaning every point C is up and right of the point A.
Try this simplification:
public void ComputeLatitudeAndLogitud (Wpf_Maps.Point point)
{
int diffX = pointA.X - pointB.X;
int diffY = pointA.Y - pointB.Y;
double diffLatitude = pointA.Latitude - pointB.Latitude;
double diffLongitude = pointA.Longitude - pointB.Longitude;
double latitudePerPixel = Math.Abs(diffLatitude / diffX);
double longitudePerPixel = Math.Abs(diffLongitude / diffY);
int diffXforC = point.X - pointA.X;
int diffYforC = point.Y - pointA.Y;
point.Latitude = pointA.Latitude + (diffXforC * latitudePerPixel);
point.Longitude = pointA.Longitude + (diffYforC * longitudePerPixel);
}
Here's my full code. I've got three tests cases there. The first is where pointC is somewhere random, in the second, pointC matches pointA, and in the third, pointC matches pointB.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ComputeLatitudeAndLongitude(new MyPoint(0, 0, 10, 10), new MyPoint(100, 100, 40, 40), new MyPoint(20, 40));
ComputeLatitudeAndLongitude(new MyPoint(0, 0, 10, 10), new MyPoint(100, 100, 40, 40), new MyPoint(0, 0));
ComputeLatitudeAndLongitude(new MyPoint(0, 0, 10, 10), new MyPoint(100, 100, 40, 40), new MyPoint(100, 100));
}
public void ComputeLatitudeAndLongitude(MyPoint pointA, MyPoint pointB, MyPoint pointC)
{
int diffX = pointA.X - pointB.X;
int diffY = pointA.Y - pointB.Y;
double diffLatitude = pointA.Latitude - pointB.Latitude;
double diffLongitude = pointA.Longitude - pointB.Longitude;
double latitudePerPixel = Math.Abs(diffLatitude / diffX);
double longitudePerPixel = Math.Abs(diffLongitude / diffY);
int diffXforC = pointC.X - pointA.X;
int diffYforC = pointC.Y - pointA.Y;
pointC.Latitude = pointA.Latitude + (diffXforC * latitudePerPixel);
pointC.Longitude = pointA.Longitude + (diffYforC * longitudePerPixel);
LogResults(String.Format("pointA X:{0} Y:{1} Lat:{2} Long:{3}", pointA.X, pointA.Y, pointA.Latitude, pointA.Longitude), true);
LogResults(String.Format("pointB X:{0} Y:{1} Lat:{2} Long:{3}", pointB.X, pointB.Y, pointB.Latitude, pointB.Longitude), true);
LogResults(String.Format("pointC X:{0} Y:{1} Lat:{2} Long:{3}", pointC.X, pointC.Y, pointC.Latitude, pointC.Longitude), true);
LogResults(String.Empty, true);
}
public void LogResults(string message, bool insertNewline)
{
txtResults.Text += message + (insertNewline ? Environment.NewLine : String.Empty);
}
}
public class MyPoint
{
public int X;
public int Y;
public double Latitude = 0;
public double Longitude = 0;
public MyPoint(int x, int y)
{
X = x;
Y = y;
}
public MyPoint(int x, int y, double latitude, double longitude) : this(x, y)
{
Latitude = latitude;
Longitude = longitude;
}
}
Results:
pointA X:0 Y:0 Lat:10 Long:10
pointB X:100 Y:100 Lat:40 Long:40
pointC X:20 Y:40 Lat:16 Long:22 // C.X is 20% of the way from A.X to B.X, so C.Lat is 20% of the way from A.Lat to B.Lat, Y/Long are 40%
pointA X:0 Y:0 Lat:10 Long:10
pointB X:100 Y:100 Lat:40 Long:40
pointC X:0 Y:0 Lat:10 Long:10 // C.X = A.X and C.Y = A.Y, therefore C.Lat and C.Long = A.Lat and A.Long
pointA X:0 Y:0 Lat:10 Long:10
pointB X:100 Y:100 Lat:40 Long:40
pointC X:100 Y:100 Lat:40 Long:40 // C.X = B.X and C.Y = B.Y, therefore C.Lat and C.Long = B.Lat and B.Long
It cant be done with code above i wrote. Constant EARTH_RADIUS MUST me implemented in function for calculation, if we want to get high precision (6 digits at least: n,mmmmmm). Next code will scale proper lat/lon per pixel, so calculated point C will match referent points A and B when we put that (X,Y) cords which points A and B have.
public void ComputeLatitudeAndLogitude(Wpf_Maps.Point point){
double diffLatAB = pointB.Latitude - pointA.Latitude;
double diffLonAB = pointB.Longitude - pointA.Longitude;
int diffXAB = pointB.X - pointA.X;
int diffYAB = pointB.Y - pointA.Y;
int diffXAC = point.X - pointA.X;
int diffYAC = point.Y - pointA.Y;
point.Latitude = diffLatAB / diffXAB * diffXAC + pointA.Latitude;
point.Longitude = diffLonAB / diffYAB * diffYAC + pointA.Longitude;
}

Determine If Two Points Are Near

I have the following:
bool AreNear(Point Old, Point Current)
{
int x1 = Convert.ToInt32(Old.X);
int x2 = Convert.ToInt32(Current.X);
int y1 = Convert.ToInt32(Old.Y);
int y2 = Convert.ToInt32(Current.Y);
if (x1 == x2) {
if (y1 == y2) {
return true;
}
}
return false;
}
I want to return true in the function if the current point is in 25 pixels radius of the old point. Can anyone tell me how to do that?
You can use the Pythagorean formula to calculate the distance between two points. In C#:
var d = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2))
Why does this work? Have a look at the following diagram and remember that a^2 + b^2 = c^2 holds for right triangles:
Just calculate the square of the distance using Pythagoras' theorem, and compare to the square of the radius:
bool ComparePoints(Point Old, Point Current)
{
int x1 = Convert.ToInt32(Old.X);
int x2 = Convert.ToInt32(Current.X);
int y1 = Convert.ToInt32(Old.Y);
int y2 = Convert.ToInt32(Current.Y);
int dx = x1 - x2;
int dy = y1 - y2;
return (dx*dx + dy*dy) < 25*25;
}
You can use Math.Abs to get the distance:
public static bool InDistance(Point Old, Point Current, int distance)
{
int diffX = Math.Abs(Old.X - Current.X);
int diffY = Math.Abs(Old.Y - Current.Y);
return diffX <= distance && diffY <= distance;
}
use it:
bool arePointsInDistance = InDistance(new Point(100, 120), new Point(120, 99), 25);
Try using the distance formula http://www.purplemath.com/modules/distform.htm and compare the distance <=25

Algorithm for intersection of 2 lines?

I have 2 lines. Both lines containing their 2 points of X and Y. This means they both have length.
I see 2 formulas, one using determinants and one using normal algebra. Which would be the most efficient to calculate and what does the formula looks like?
I'm having a hard time using matrices in code.
This is what I have so far, can it be more efficient?
public static Vector3 Intersect(Vector3 line1V1, Vector3 line1V2, Vector3 line2V1, Vector3 line2V2)
{
//Line1
float A1 = line1V2.Y - line1V1.Y;
float B1 = line1V1.X - line1V2.X;
float C1 = A1*line1V1.X + B1*line1V1.Y;
//Line2
float A2 = line2V2.Y - line2V1.Y;
float B2 = line2V1.X - line2V2.X;
float C2 = A2 * line2V1.X + B2 * line2V1.Y;
float det = A1*B2 - A2*B1;
if (det == 0)
{
return null;//parallel lines
}
else
{
float x = (B2*C1 - B1*C2)/det;
float y = (A1 * C2 - A2 * C1) / det;
return new Vector3(x,y,0);
}
}
Assuming you have two lines of the form Ax + By = C, you can find it pretty easily:
float delta = A1 * B2 - A2 * B1;
if (delta == 0)
throw new ArgumentException("Lines are parallel");
float x = (B2 * C1 - B1 * C2) / delta;
float y = (A1 * C2 - A2 * C1) / delta;
Pulled from here
I recently went back on paper to find a solution to this problem using basic algebra. We just need to solve the equations formed by the two lines and if a valid solution exist then there is an intersection.
You can check my Github repository for extended implementation handling potential precision issue with double and tests.
public struct Line
{
public double x1 { get; set; }
public double y1 { get; set; }
public double x2 { get; set; }
public double y2 { get; set; }
}
public struct Point
{
public double x { get; set; }
public double y { get; set; }
}
public class LineIntersection
{
// Returns Point of intersection if do intersect otherwise default Point (null)
public static Point FindIntersection(Line lineA, Line lineB, double tolerance = 0.001)
{
double x1 = lineA.x1, y1 = lineA.y1;
double x2 = lineA.x2, y2 = lineA.y2;
double x3 = lineB.x1, y3 = lineB.y1;
double x4 = lineB.x2, y4 = lineB.y2;
// equations of the form x=c (two vertical lines) with overlapping
if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance && Math.Abs(x1 - x3) < tolerance)
{
throw new Exception("Both lines overlap vertically, ambiguous intersection points.");
}
//equations of the form y=c (two horizontal lines) with overlapping
if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance && Math.Abs(y1 - y3) < tolerance)
{
throw new Exception("Both lines overlap horizontally, ambiguous intersection points.");
}
//equations of the form x=c (two vertical parallel lines)
if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance)
{
//return default (no intersection)
return default(Point);
}
//equations of the form y=c (two horizontal parallel lines)
if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance)
{
//return default (no intersection)
return default(Point);
}
//general equation of line is y = mx + c where m is the slope
//assume equation of line 1 as y1 = m1x1 + c1
//=> -m1x1 + y1 = c1 ----(1)
//assume equation of line 2 as y2 = m2x2 + c2
//=> -m2x2 + y2 = c2 -----(2)
//if line 1 and 2 intersect then x1=x2=x & y1=y2=y where (x,y) is the intersection point
//so we will get below two equations
//-m1x + y = c1 --------(3)
//-m2x + y = c2 --------(4)
double x, y;
//lineA is vertical x1 = x2
//slope will be infinity
//so lets derive another solution
if (Math.Abs(x1 - x2) < tolerance)
{
//compute slope of line 2 (m2) and c2
double m2 = (y4 - y3) / (x4 - x3);
double c2 = -m2 * x3 + y3;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x1=c1=x
//subsitute x=x1 in (4) => -m2x1 + y = c2
// => y = c2 + m2x1
x = x1;
y = c2 + m2 * x1;
}
//lineB is vertical x3 = x4
//slope will be infinity
//so lets derive another solution
else if (Math.Abs(x3 - x4) < tolerance)
{
//compute slope of line 1 (m1) and c2
double m1 = (y2 - y1) / (x2 - x1);
double c1 = -m1 * x1 + y1;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x3=c3=x
//subsitute x=x3 in (3) => -m1x3 + y = c1
// => y = c1 + m1x3
x = x3;
y = c1 + m1 * x3;
}
//lineA & lineB are not vertical
//(could be horizontal we can handle it with slope = 0)
else
{
//compute slope of line 1 (m1) and c2
double m1 = (y2 - y1) / (x2 - x1);
double c1 = -m1 * x1 + y1;
//compute slope of line 2 (m2) and c2
double m2 = (y4 - y3) / (x4 - x3);
double c2 = -m2 * x3 + y3;
//solving equations (3) & (4) => x = (c1-c2)/(m2-m1)
//plugging x value in equation (4) => y = c2 + m2 * x
x = (c1 - c2) / (m2 - m1);
y = c2 + m2 * x;
//verify by plugging intersection point (x, y)
//in orginal equations (1) & (2) to see if they intersect
//otherwise x,y values will not be finite and will fail this check
if (!(Math.Abs(-m1 * x + y - c1) < tolerance
&& Math.Abs(-m2 * x + y - c2) < tolerance))
{
//return default (no intersection)
return default(Point);
}
}
//x,y can intersect outside the line segment since line is infinitely long
//so finally check if x, y is within both the line segments
if (IsInsideLine(lineA, x, y) &&
IsInsideLine(lineB, x, y))
{
return new Point { x = x, y = y };
}
//return default (no intersection)
return default(Point);
}
// Returns true if given point(x,y) is inside the given line segment
private static bool IsInsideLine(Line line, double x, double y)
{
return (x >= line.x1 && x <= line.x2
|| x >= line.x2 && x <= line.x1)
&& (y >= line.y1 && y <= line.y2
|| y >= line.y2 && y <= line.y1);
}
}
How to find intersection of two lines/segments/ray with rectangle
public class LineEquation{
public LineEquation(Point start, Point end){
Start = start;
End = end;
IsVertical = Math.Abs(End.X - start.X) < 0.00001f;
M = (End.Y - Start.Y)/(End.X - Start.X);
A = -M;
B = 1;
C = Start.Y - M*Start.X;
}
public bool IsVertical { get; private set; }
public double M { get; private set; }
public Point Start { get; private set; }
public Point End { get; private set; }
public double A { get; private set; }
public double B { get; private set; }
public double C { get; private set; }
public bool IntersectsWithLine(LineEquation otherLine, out Point intersectionPoint){
intersectionPoint = new Point(0, 0);
if (IsVertical && otherLine.IsVertical)
return false;
if (IsVertical || otherLine.IsVertical){
intersectionPoint = GetIntersectionPointIfOneIsVertical(otherLine, this);
return true;
}
double delta = A*otherLine.B - otherLine.A*B;
bool hasIntersection = Math.Abs(delta - 0) > 0.0001f;
if (hasIntersection){
double x = (otherLine.B*C - B*otherLine.C)/delta;
double y = (A*otherLine.C - otherLine.A*C)/delta;
intersectionPoint = new Point(x, y);
}
return hasIntersection;
}
private static Point GetIntersectionPointIfOneIsVertical(LineEquation line1, LineEquation line2){
LineEquation verticalLine = line2.IsVertical ? line2 : line1;
LineEquation nonVerticalLine = line2.IsVertical ? line1 : line2;
double y = (verticalLine.Start.X - nonVerticalLine.Start.X)*
(nonVerticalLine.End.Y - nonVerticalLine.Start.Y)/
((nonVerticalLine.End.X - nonVerticalLine.Start.X)) +
nonVerticalLine.Start.Y;
double x = line1.IsVertical ? line1.Start.X : line2.Start.X;
return new Point(x, y);
}
public bool IntersectWithSegementOfLine(LineEquation otherLine, out Point intersectionPoint){
bool hasIntersection = IntersectsWithLine(otherLine, out intersectionPoint);
if (hasIntersection)
return intersectionPoint.IsBetweenTwoPoints(otherLine.Start, otherLine.End);
return false;
}
public bool GetIntersectionLineForRay(Rect rectangle, out LineEquation intersectionLine){
if (Start == End){
intersectionLine = null;
return false;
}
IEnumerable<LineEquation> lines = rectangle.GetLinesForRectangle();
intersectionLine = new LineEquation(new Point(0, 0), new Point(0, 0));
var intersections = new Dictionary<LineEquation, Point>();
foreach (LineEquation equation in lines){
Point point;
if (IntersectWithSegementOfLine(equation, out point))
intersections[equation] = point;
}
if (!intersections.Any())
return false;
var intersectionPoints = new SortedDictionary<double, Point>();
foreach (var intersection in intersections){
if (End.IsBetweenTwoPoints(Start, intersection.Value) ||
intersection.Value.IsBetweenTwoPoints(Start, End)){
double distanceToPoint = Start.DistanceToPoint(intersection.Value);
intersectionPoints[distanceToPoint] = intersection.Value;
}
}
if (intersectionPoints.Count == 1){
Point endPoint = intersectionPoints.First().Value;
intersectionLine = new LineEquation(Start, endPoint);
return true;
}
if (intersectionPoints.Count == 2){
Point start = intersectionPoints.First().Value;
Point end = intersectionPoints.Last().Value;
intersectionLine = new LineEquation(start, end);
return true;
}
return false;
}
public override string ToString(){
return "[" + Start + "], [" + End + "]";
}
}
full sample is described [here][1]

Categories

Resources