Check if point is inside a circle - c#

I have a point expressed in lat/long
Position louvreMuseum = new Position( 48.861622, 2.337474 );
and I have a radius value expressed in meters. I need to check if another point, also expressed in lat/long, is inside the circle.
If I were on a flat surface I can simply use the formula
(x - center_x)^2 + (y - center_y)^2 <= radius^2
as deeply explained in these SO answer.
However as per the latitude/longitude usage I can not use that formula because of the spherical nature of the planet.
How can I calculate a distance from any given point to the center to be compared with the radius?

Function to calculate the distance between two coordinates (converted to C# from this answer):
double GetDistance(double lat1, double lon1, double lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = ToRadians(lat2-lat1);
var dLon = ToRadians(lon2-lon1);
var a =
Math.Sin(dLat/2) * Math.Sin(dLat/2) +
Math.Cos(ToRadians(lat1)) * Math.Cos(ToRadians(lat2)) *
Math.Sin(dLon/2) * Math.Sin(dLon/2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
double ToRadians(double deg)
{
return deg * (Math.PI/180);
}
If the distance between the two points is less than the radius, then it is within the circle.

Related

Fast Approximation if a point on earth is in range

For a game I need to calculate if items on the map are in range of the player.
The map is the earth.
I'm using the Haversine formula to calculate the distance between the player and each item.
However I did some profiling and found out that all those sin/cos calculations are too slow to allow for smoth gameplay.
Is there some other method to check of two points on earth could be withing a range of x metres?
The method does not need to be exact, but it must be fast and return true if distance <= x. It may also return true if distance > x (but should not always return true).
My test code (LinqPad)
void Main()
{
var lat = 53.553072;
var lng = 9.993023;
var lat0 = 53.553073;
var lng0 = 9.993178;
"Google Maps: 10.02m".Dump(); // 10.02m
$"Euclid: {DistanceEuclid(lat, lng, lat0, lng0)}m".Dump(); // 10,2396639400397m
$"Haversine: {DistanceHaversine(lat, lng, lat0, lng0)}m".Dump(); // 10,2396637520237m
}
const int R = 6371000;
const double PiBy180 = Math.PI / 180;
const double deglen = 111194.93;
double DistanceEuclid(double lat, double lng, double lat0, double lng0)
{
var x = lat - lat0;
var y = (lng - lng0)*Math.Cos(ToRadians(lat0));
return deglen*Math.Sqrt(x*x + y*y);
}
public double DistanceHaversine(double lat, double lng, double lat0, double lng0)
{
var lat1 = ToRadians(lat);
var lat2 = ToRadians(lat0);
var dLat = ToRadians(lat0 - lat);
var dLng = ToRadians(lng0 - lng);
var h = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(dLng / 2) * Math.Sin(dLng / 2);
var c = 2 * Math.Atan2(Math.Sqrt(h), Math.Sqrt(1 - h));
return R * c;
}
double ToRadians(double degrees) => degrees * PiBy180;
In the case of short distances where the curvature is negligible, you could use linear approximation and take the Euclidean distance between the points.
A quick and dirty approach for long distance measurements where curvature matters could involve pre-calculating arc lengths between points on the spheroid (i.e. earth) for distinct arc-lengths ahead of time. For instance, you would create an array(s)/lookup table to find approximate 𝘥 from quantized values of φ₀, φ₁ and Δλ (|λ₀-λ₁|) for a hemisphere about the longitudinal (vertical) axis, since the distances are the same for equal and opposite longitudinal differences. You can improve accuracy by increasing the size of the array(s). If your memory budget isn't tight, you can try making it very large.
Improving the accuracy of the approximation might be possible using specific data structures or correction formulas, but I'm not sure.
Then you can compare the range and the distance between the item and the player.

calculate Point by point, angle and distance on a map

I have a mission to calculate point on a Map. I have the start point, the angle and the distance from the point. How can I do it ? I search a lot I found something but it doesn't work good - I mean it it doesn't calculate the correct point. Thank's all.
My try :
public Point MesPoint(double x1, double x2, double y1, double y2, double distance, double x) // X is the angle
{
double xEndP, yEndP;
var angularDistance = distance / c_EarthRadiusInKilometers; // angular distance in radians
var lat = ToRadian(y2);
var lon = ToRadian(x2);
var angel = ToRadian(x);
double latRadians = Math.Asin((Math.Sin(lat) * Math.Cos(angularDistance)) + (Math.Cos(lat) * Math.Sin(angularDistance) * Math.Cos(angel)));
double lngRadians = Math.Atan2(
Math.Sin(angel) * Math.Sin(angularDistance) * Math.Cos(lat),
Math.Cos(angularDistance) - (Math.Sin(lat) * Math.Sin(latRadians)));
double lon1 = (lon + lngRadians + Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
yEndP = ToDegrees(latRadians);
xEndP = ToDegrees(lon1);
return (new Point(xEndP, yEndP));
}

Clockwise right hand turn angle in 2D?

If I have two points p1 and p2 where p1 is the pivot point and p2 is the original direction the user was headed and they have a number of possible directions to go p3...pn in random sequence. How do I get the angles between the choices and the segment formed by p1,p2 as clockwise(right hand) positive values between 0 and 360 so that I can sort them from least to greatest?
Also the points p1...pn will be in any quadrant, I can’t assume they will always be in the positive x,y direction. The grid is a standard Cartesian grid not screen coordinates so Y gets smaller as you go down not larger.
So in this example (sorry for the poor drawing but Paint was all I had on my laptop) I need to get the angles:
(p2-p1-p3)
( p2-p1-p4)
( p2-p1-p5)
( p2-p1-p6)
In this order(smallest right hand turn to largest right hand turn):
[( p2-p1-p4), ( p2-p1-p6), ( p2-p1-p5), (p2-p1-p3)]
The points in my case are a class called Vertex:
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
}
And the code for getting the angles and sorting looks like this right now but has a problem:
private static IEnumerable<Vertex> SortByAngle(Vertex original, Vertex pivot, List<Vertex> choices)
{
choices.Sort((v1, v2) => GetTurnAngle(original, pivot, v1).CompareTo(GetTurnAngle(original, pivot, v2)));
return choices;
}
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var rads = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
return (180 / Math.PI * rads);
}
The problem is the above is if I check it for:
original 66,-66
pivot 280,-191
choice 200,-180
I get an angle of 22.460643124 instead of 337.539356876 which means it went counter-clockwise from the original direction to get that angle. I need it to always go clockwise to get the angle.
What am I doing wrong and how do I fix it?
Update: OK so according to what you guys are saying I can probably use some cross product like math to determine CW vs CCW so the new method would look like this:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var angle = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
angle = (180 / Math.PI * angle);
var z = (choice.X - pivot.X) * (original.Y - pivot.Y) - (choice.Y - pivot.Y) * (original.X - pivot.X);
if (z < 0)
{
return 360 - angle;
}
return angle;
}
Update 2:
Using the accepted solution it now looks like so:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var angle1 = Math.Atan2(original.Y - pivot.Y, original.X - pivot.X);
var angle2 = Math.Atan2(choice.Y - pivot.Y, choice.X - pivot.X);
var angleDiff = (180 / Math.PI * (angle2 - angle1));
if (angleDiff > 0)//It went CCW so adjust
{
return 360 - angleDiff;
}
return -angleDiff;//I need the results to be always positive so flip sign
}
So far as I can tell that works great so far. Thank you guys for the help!
Take a look at atan2 function. It takes delta y and delta x, so can distinguish all angles.
angle1 = atan2(p1.y-p0.y, p1.x-p0.x);
angle2 = atan2(p2.y-p0.y, p2.x-p0.x);
angle = angle2 - angle1;
If angle is negative, then CW, if positive CCW (or other way around depending on your axis orientation). Note |angle| may be > 180, in which case you may want to do 360-|angle| and reverse the CW CCW conclusion if you're after the shortest route.
You find the Dn=direction from p1 to pn (x=pn.x-p1.x and y=pn.y-p1.y) by the formula:
Dn=f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)
-180/pi()*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
So the angles are Angle(p2-p1-pn)=Dn-D2.

Trying to draw a square around a latitude and longitude point

I am working on a C# script to do some mapping. I have one function I am working on where I am given a latitude and longitude, a distance, and an angle of rotation.
I then want to create a rectangle, that has the width of the distance passed in (length is dependant on something else), this square would have the latitude and longitude point at it's centre, and would be rotated any angle from 0 - 360.
The distance double that is passed in can either be in feet, or in meters, and the units are determined using the isMetric boolean.
The problem I believe has to do with my formula's that I am using because when it draws the square from the 4 points, the size of the square is too large. As well the angle of rotation seems to be set at 45 degrees when you pass it a angle of 0.0.
Here is what I have so far:
Parameters: Latitude, Longitude (in decimal format), (Double) distance, (double) angle
double diagonal = Math.Sqrt(Math.Pow((distance / 2), 2) * 2); //a^2 + b^2 = d^2
if (isMetric) //Convert meters to km.
{
diagonal = diagonal / 1000;
}
else //Convert feet to km.
{
diagonal = diagonal * 0.0003048;
}
MessageBox.Show("Diagonal: " + diagonal, "DEBUG"); //DEBUG
double pt1_lat = latDistance(diagonal * Math.Sin(angle), latitude);
double pt1_long = longDistance(diagonal * Math.Cos(angle), latitude, longitude);
double pt2_lat = latDistance(diagonal * Math.Cos(angle), latitude);
double pt2_long = longDistance(-diagonal * Math.Sin(angle), latitude, longitude);
double pt3_lat = latDistance(-diagonal * Math.Sin(angle), latitude);
double pt3_long = longDistance(-diagonal * Math.Cos(angle), latitude, longitude);
double pt4_lat = latDistance(-diagonal * Math.Cos(angle), latitude);
double pt4_long = longDistance(diagonal * Math.Sin(angle), latitude, longitude);
The remaining methods are below:
private double latDistance(double distance, double latitude)
{
return latitude + degToRad(distance / EARTH_RADIUS);
}
private double longDistance(double distance, double latitude, double longitude)
{
return longitude + degToRad(distance / EARTH_RADIUS / Math.Cos(latitude));
}
private double degToRad(double degrees)
{
return (degrees * Math.PI) / 180;
}
public double radToDeg(double radians)
{
return (180.0 * radians) / Math.PI;
}
Any help is greatly appreciated :)
First of all I assume you have a "small" square with respect to EARTH radius. In this case if distance is the length of the side and angle is the rotation angle, you should compute dx,dy which are the cartesian coordinates of one vertex with respect to the center of the square:
dx = 0.5 * Math.Sqrt(2.0) * distance * Math.Cos(angle+0.25*Math.PI);
dy = 0.5 * Math.Sqrt(2.0) * distance * Math.Sin(angle+0.25*math.PI);
(here I assume that angle is in radians, otherwise you should convert it)
The cartesian coordinates of the other 3 vertices are, respectively: (-dy,dx), (-dx,-dy), (dy,-dx)
To convert the cartesian coordinates to (longitude,latitude) you can use your formulas:
pt_lat = latDistance(latitude,dy)
pt_long = longDistance(dx,latitude,longitude)

finding height on a heightmap stretched over a sphere C#

I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.
What I know:
The array that holds the heightmap
The angle in radians to my object
how many points there are on the heightmap
My problem look somewhat like so:
image
more images
The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.
Here's my current code to do it, but it doesn't work to well.
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double offset = MathHelper.TwoPi / (heightmap.Length - 1);
//total brainfart attempt
double lowerAngle = offset * angle;
double upperAngle = offset * angle + offset;
//find heights
double height1 = heightmap[(int)lowerAngle];
double height2 = heightmap[(int)upperAngle];
//find offset angle
double u = angle - lowerAngle / (upperAngle - lowerAngle);
//return the height
return height1 + (height1 - height2) * u;
}
from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.
double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];
EDIT: example at end based on additional question info
Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:
(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap
and one point somewhere between (x1,x2) at an unknown height:
pu = (xu, yu)
A generic formula for LERP is:
pu = p0 + (p1 - p0) * u
where:
p0 = first value
p1 = second value
u = % where your unknown point lies between (p0,p1)
Here, we'll say p0 == y2 and p1 == y1. Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:
u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))
Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.
So, let's try a contrived example:
p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees
note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.
pu = object location = py #angle 100 degrees ~ 1.74 radians
px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%
py = y0 + (y1 - y0) * u
= 2 + (4 - 2) * 0.74
= 2.96
Hopefully I didn't drop or misplace a sign there somewhere... :)
Ok, your example code - I've tweaked it a bit, here's what I've come up with:
First, let's define some helpers of my own:
public static class MathHelper
{
public const double TwoPi = Math.PI * 2.0;
public static double DegToRad(double deg)
{
return (TwoPi / 360.0) * deg;
}
public static double RadToDeg(double rad)
{
return (360.0 / TwoPi) * rad;
}
// given an upper/lower bounds, "clamp" the value into that
// range, wrapping over to lower if higher than upper, and
// vice versa
public static int WrapClamp(int value, int lower, int upper)
{
return value > upper ? value - upper - 1
: value < lower ? upper - value - 1
: value;
}
}
Our Test setup:
void Main()
{
var random = new Random();
// "sea level"
var baseDiameter = 10;
// very chaotic heightmap
heightmap = Enumerable
.Range(0, 360)
.Select(_ => random.NextDouble() * baseDiameter)
.ToArray();
// let's walk by half degrees, since that's roughly how many points we have
for(double i=0;i<360;i+=0.5)
{
var angleInDegrees = i;
var angleInRads = MathHelper.DegToRad(i);
Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
angleInDegrees,
angleInRads,
heightmap[(int)angleInDegrees],
getheight(angleInRads));
}
}
double[] heightmap;
And our "getheight" method:
// assume: input angle is in radians
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double dTheta = MathHelper.TwoPi / (heightmap.Length);
// our "offset" will be how many dThetas we are
double offset = angle / dTheta;
// Figure out two reference points in heightmap
// THESE MAY BE THE SAME POINT, if angle ends up
// landing on a heightmap index!
int lowerAngle = (int)offset;
int upperAngle = (int)Math.Round(
offset,
0,
MidpointRounding.AwayFromZero);
// find closest heightmap points to angle, wrapping
// around if we go under 0 or over max
int closestPointIndex = MathHelper.WrapClamp(
lowerAngle,
0,
heightmap.Length-1);
int nextPointIndex = MathHelper.WrapClamp(
upperAngle,
0,
heightmap.Length-1);
//find heights
double height1 = heightmap[closestPointIndex];
double height2 = heightmap[nextPointIndex];
// percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
double percent = (angle - (closestPointIndex * dTheta)) / dTheta;
// find lerp height = firstvalue + (diff between values) * percent
double lerp = Math.Abs(height1 + (height2 - height1) * percent);
// Show what we're doing
Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]",
dTheta,
offset,
closestPointIndex,
nextPointIndex);
Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}",
percent,
height1,
height2,
lerp);
return lerp;
}

Categories