complex math with great circle formula - php

I have a from location (latitude, longitude) and to location (latitude, longitude). After calculating, it should show me what would be the nearest way to go from using a compass. The following is PHP code to do that, but its showing the wrong direction, I need little help on this.
function GreatCircleDirection ($OrigLat, $DestLat, $OrigLong, $DestLong, $Distance)
{
$Result = 0.0;
$L1 = deg2rad($OrigLat);
$L2 = deg2rad($DestLat);
$D = deg2rad($Distance / 60); # divide by 60 for nautical miles NM to degree
$I1 = deg2rad($OrigLong);
$I2 = deg2rad($DestLong);
$Dlong = $I1 - $I2;
$A = sin($L2) - cos($D + $L1 - pi() / 2);
$B = acos($A / (cos($L1) * sin($D)) + 1);
if ((abs($Dlong) < pi() and $Dlong < 0) or (abs($Dlong) > pi() and $Dlong > 0))
{
//$B = (2 * pi()) - $B;
}
$Result = $B;
return rad2deg($Result);
}
function GreatCircleDistance ($OrigLat , $DestLat, $OrigLong, $DestLong)
{
$L1 = deg2rad($OrigLat);
$L2 = deg2rad($DestLat);
$I1 = deg2rad($OrigLong);
$I2 = deg2rad($DestLong);
$D = acos(cos($L1 - $L2) - (1 - cos($I1 - $I2)) * cos($L1) * cos($L2));
# One degree of such an arc on the earth's surface is 60 international nautical miles NM
return rad2deg($D * 60);
}
Bug on if condition:
this is the values in the if condition of greatCircleDirection function, need to know what to change to fix it.
if (0.57700585070933 < 3.1415926535898 and 0.57700585070933 < 0) or (0.57700585070933 > 3.1415926535898 and 0.57700585070933 > 0)
example:
from lat: 33.71,
to lat: 21,
from long: 73.06,
to long: 40 ,
distance: 1908.842544944
direction 104.96527938779 (direction should be 255.87 or so)

Computing the distance is unnecessary; it simply adds more operations and can introduces more numerical errors. Using your style of coding, something like this should work:
function GreatCircleDirection($OrigLat, $OrigLong, $DestLat, $DestLong)
{
$L1 = deg2rad($OrigLat);
$I1 = deg2rad($OrigLong);
$L2 = deg2rad($DestLat);
$I2 = deg2rad($DestLong);
return rad2deg(atan2((sin($I2-$I1),cos($L1)*tan($L2)-sin($L1)*cos($I2-$I1)));
}
The atan2 function takes care of identifying the correct quadrant for the direction, and gives you the angle between -180 to 180 measured from true North, e.g., GreaterCircleDirection(39,-77,21,40) evaluates to 56.76 degrees. Sign convention used: latitudes are positive when north, negative when south; longitudes are positive when east, negative when west.
The calculation is discussed in, among other places, http://patriot.net/~abdali/ftp/qibla.pdf.

Well, your distance calculation checks out. But I see that the answer you get for the initial bearing is (0+105)mod360 rather than (0-105)mod360 (approximately) so I suspect a wrong sign somewhere in the if statement in your GreatCircleDirection function.

Perhaps the worked examples, under "Using the Sine Rule", at http://www.krysstal.com/sphertrig.html will help.

Related

Calculate heading for kml in php/mysql

I have a table in mysql with 4 columns:
Latitude_1
Longitude_1
Latitude_2
Longitude_2
Now I want to calculate the heading for all rows to be used in a kml file.
I found this function:
// Takes two sets of geographic coordinates in decimal degrees and produces bearing (azimuth) from the first set of coordinates to the second set.//
public static function bearing($lat1, $lon1, $lat2, $lon2) {
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
$lonDelta = $lon2 - $lon1;
$y = sin($lonDelta) * cos($lat2);
$x = cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($lonDelta);
$brng = atan2($y, $x);
$brng = $brng * (180 / pi());
if ( $brng < 0 ) { $brng += 360; }
return $brng;
}
Now I hope that someone shows me a query that echoes all headings (bearings) of the table based on the above mentioned function
To combine an expression expr, take a look to the mysql math functions and find proper equivalents for php ones - https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html
Simplify your expr if possible
Use your result expression expr in following query SELECT expr FROM your_table
Profit

How to find intersection points between two circles?

We have two points (centers of two circles) and their radius in meters, those radius make the circle. We need to find intersection points. For example we have lat1 = 55.685025, lng1 = 21.118995, r1 = 150 and lat2 = 55.682393, lng2 = 21.121387, r2 = 250. Below you can find our current formula:
// Find a and h.
$a = ($circle_1_r * $circle_1_r - $circle_2_r * $circle_2_r + $distance * $distance) / (2 * $distance);
$h = sqrt($circle_1_r * $circle_1_r - $a * $a);
// Find P2.
$circle_3_x = $circle_1_x + $a * ($circle_2_x - $circle_1_x) / $distance;
$circle_3_y = $circle_1_y + $a * ($circle_2_y - $circle_1_y) / $distance;
// Get the points P3.
$intersection_1 = $this->newLatLngPoint(
($circle_3_x + $h * ($circle_2_y - $circle_1_y) / $distance),
($circle_3_y - $h * ($circle_2_x - $circle_1_x) / $distance)
);
$intersection_2 = $this->newLatLngPoint(
($circle_3_x - $h * ($circle_2_y - $circle_1_y) / $distance),
($circle_3_y + $h * ($circle_2_x - $circle_1_x) / $distance)
);
We find such intersection points (yellow markers), however those locations doesn't match in real world.
Someone, can help to find where the problem is and how to sort it ?
P.S.
Does the altitude (Height above mean sea level) affect the final result? I don't use it but may be should?

Calculate km difference from two coordinates

i've to calculate the distance between
(40.851774999999996,14.268123999999998)
and each coordinates into results of an sql query:
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $key => $value) {
echo "distance = ". calculateDistance("40.851774999999996","14.268123999999998",$value['lat'],$value['lng'])."<br>";
}
Where calculateDistance is
function calculateDistance($targetLat,$targetLng,$currentLat,$currentLng){
$r = 6371; // km
$dLat = $targetLat-$currentLat;
$dLng = $targetLng-$currentLng;
$a = sin($dLat/2)*sin($dLat/2) + sin($dLng/2)*sin($dLng/2);
$c = 2*atan2(sqrt($a), sqrt(1-$a));
return $r*$c;
}
it gives me strange result like:
distance = NAN //-> NAN???
distance = 3392.8405117312 // TOO MUCH!
distance = 3392.8405117312 // TOO MUCH!
...
Where is the problem? can someone help me to fix it? :)
According to this answer:
Calculate distance between two latitude-longitude points? (Haversine formula)
You don't convert from degrees to radians.
You formula is incorrect:
They say:
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
You wrote:
$a = sin($dLat/2)*sin($dLat/2) + sin($dLng/2)*sin($dLng/2);
The cosine is missing in your code.
You need to convert degrees to radians before using it in sin function.
$radians = $degrees * (M_PI/180);
Look at this function, too. It looks a little bit different.
You are referring to the Haversine formula:
http://en.wikipedia.org/wiki/Haversine_formula
There are plenty of examples and code snippets in these pages:
http://rosettacode.org/wiki/Haversine_formula
http://www.codecodex.com/wiki/Calculate_distance_between_two_points_on_a_globe
I've used this snippet in my code, which works very well:
http://www.codecodex.com/wiki/Calculate_distance_between_two_points_on_a_globe#PHP

get nearest lat long around 25 km from given array lat long

i have array of lat long like :
var locationList = new Array( '23.2531803, 72.4774396', '22.808782, 70.823863', '24.3310019, 72.8516531', '22.3073095, 73.1810976', '22.3038945, 70.8021599', '23.850809, 72.114838' );
i want get nearest around 25 km 's lat long from first given array which is 23.2531803, 72.4774396
are there any calculation for nearest 25 km 's lat long from given array.
NOTE: for some reason i can not use sql query, because i get lat long from given address
Step 1: Calculate the distance between your start coordinate and every subcoordinate
Step 2: Pick the smallest distance
Step 3: Is it < 25 km? Success!
How to calculate distance between two coordinates:
function distance($lat1, $lon1, $lat2, $lon2) {
$D = 6371; // Earth Radius
$dLat = $lat2-$lat1;
$dLon = $lon2-$lon1;
$a = sin($dLat/2) * sin($dLat/2) +
sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2);
$b = 2 * atan2(sqrt($a), sqrt(1-$a));
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
return $D * $c;
}
This function thinks of the Earth as a perfect ball, which it is not - slight variations do apply, but are neglible at the 25km diameter you want.
First of all your data array is awfull. You need to make your data more computer readable. Then you can use Pythagorean theorem to calculate the distance from each location. You can save the first distance and index in an variable then replace it with new distance and index if it's shorter.
var closest = {id:0,dist:-1};
var myloc = [23.2531303, 72.4774398]
for(var i = 0; i < locationList.length; i++)
{
var long_lat = locationList[i].match(/([0-9.]+)+/)
long_lat[0] = parseFloat(long_lat[0]);
long_lat[1] = parseFloat(long_lat[1]);
var dist = Math.sqrt(Math.pow(long_lat[0] - myloc[0], 2)+Math.pow(long_lat[1] - myloc[1], 2)));
if((closest.dist == -1) || (closest.dist > dist))
{
closest.dist = dist;
closest.id = i;
}
}
When you like to calculate in JS you may use google.maps.geometry.spherical.computeDistanceBetween() to calculate the distance between the single points.
Store the results in an array, sort the array and when the smallest entry is <25km you got what you want.

Algorithm to add Color in Bezier curves

I'm playing with GD library for a while and more particuraly with Bezier curves atm.
I used some existant class which I modified a little (seriously eval()...). I found out it was a generic algorithm used in and convert for GD.
Now I want to take it to another level: I want some colors.
No problem for line color but with fill color it's harder.
My question is:
Is there any existant algorithm for that? I mean mathematical algorithm or any language doing it already so that I could transfer it to PHP + GD?
EDIT2
So, I tried #MizardX solution with a harder curve :
1st position : 50 - 50
final position : 50 - 200
1st control point : 300 - 225
2nd control point : 300 - 25
Which should show this :
And gives this :
EDIT
I already read about #MizardX solution. Using imagefilledpolygon to make it works.
But it doesn't work as expected. See the image below to see the problem.
Top graph is what I expect (w/o the blackline for now, only the red part).
Coordinates used:
first point is 100 - 100
final point is 300 - 100
first control point is 100 - 0
final control point is 300 - 200
Bottom part is what I get with that kind of algorithm...
Convert the Bezier curve to a polyline/polygon, and fill that. If you evaluate the Bezier polynomial at close enough intervals (~1 pixel) it will be identical to an ideal Bezier curve.
I don't know how familiar you are with Bezier curves, but here is a crash course:
<?php
// Calculate the coordinate of the Bezier curve at $t = 0..1
function Bezier_eval($p1,$p2,$p3,$p4,$t) {
// lines between successive pairs of points (degree 1)
$q1 = array((1-$t) * $p1[0] + $t * $p2[0],(1-$t) * $p1[1] + $t * $p2[1]);
$q2 = array((1-$t) * $p2[0] + $t * $p3[0],(1-$t) * $p2[1] + $t * $p3[1]);
$q3 = array((1-$t) * $p3[0] + $t * $p4[0],(1-$t) * $p3[1] + $t * $p4[1]);
// curves between successive pairs of lines. (degree 2)
$r1 = array((1-$t) * $q1[0] + $t * $q2[0],(1-$t) * $q1[1] + $t * $q2[1]);
$r2 = array((1-$t) * $q2[0] + $t * $q3[0],(1-$t) * $q2[1] + $t * $q3[1]);
// final curve between the two 2-degree curves. (degree 3)
return array((1-$t) * $r1[0] + $t * $r2[0],(1-$t) * $r1[1] + $t * $r2[1]);
}
// Calculate the squared distance between two points
function Point_distance2($p1,$p2) {
$dx = $p2[0] - $p1[0];
$dy = $p2[1] - $p1[1];
return $dx * $dx + $dy * $dy;
}
// Convert the curve to a polyline
function Bezier_convert($p1,$p2,$p3,$p4,$tolerance) {
$t1 = 0.0;
$prev = $p1;
$t2 = 0.1;
$tol2 = $tolerance * $tolerance;
$result []= $prev[0];
$result []= $prev[1];
while ($t1 < 1.0) {
if ($t2 > 1.0) {
$t2 = 1.0;
}
$next = Bezier_eval($p1,$p2,$p3,$p4,$t2);
$dist = Point_distance2($prev,$next);
while ($dist > $tol2) {
// Halve the distance until small enough
$t2 = $t1 + ($t2 - $t1) * 0.5;
$next = Bezier_eval($p1,$p2,$p3,$p4,$t2);
$dist = Point_distance2($prev,$next);
}
// the image*polygon functions expect a flattened array of coordiantes
$result []= $next[0];
$result []= $next[1];
$t1 = $t2;
$prev = $next;
$t2 = $t1 + 0.1;
}
return $result;
}
// Draw a Bezier curve on an image
function Bezier_drawfilled($image,$p1,$p2,$p3,$p4,$color) {
$polygon = Bezier_convert($p1,$p2,$p3,$p4,1.0);
imagefilledpolygon($image,$polygon,count($polygon)/2,$color);
}
?>
Edit:
I forgot to test the routine. It is indeed as you said; It doesn't give a correct result. Now I have fixed two bugs:
I unintentionally re-used the variable names $p1 and $p2. I renamed them $prev and $next.
Wrong sign in the while-loop. Now it loops until the distance is small enough, instead of big enough.
I checked the algorithm for generating a Polygon ensuring a bounded distance between successive parameter-generated points, and seems to work well for all the curves I tested.
Code in Mathematica:
pts={{50,50},{300,225},{300,25},{50,200}};
f=BezierFunction[pts];
step=.1; (*initial step*)
While[ (*get the final step - Points no more than .01 appart*)
Max[
EuclideanDistance ###
Partition[Table[f[t],{t,0,1,step}],2,1]] > .01,
step=step/2]
(*plot it*)
Graphics#Polygon#Table[f[t],{t,0,1,step}]
.
.
The algorithm could be optimized (ie. generate less points) if you don't require the same parameter increment between points, meaning you can chose a parameter increment at each point that ensures a bounded distance to the next.
Random examples:
Generate a list of successive points which lie along the curve (p_list)).
You create a line between the two end points of the curve (l1).
Then you are going to find the normal of the line (n1). Using this normal find the distance between the two furthest points (p_max1, and p_max2) along this normal (d1). Divide this distance into n discrete units (delta).
Now shift l1 along n1 by delta, and solve for the points of intersection (start with brute force and check for a solution between all the line segments in p_list). You should be able to get two points of intersection for each shift of l1, excepting boundaries and self intersection where you may have only have a single point. Hopefully the quad routine can have two points of the quad be at the same location (a triangle) and fill without complaint otherwise you'll need triangles in this case.
Sorry I didn't provide pseudo code but the idea is pretty simple. It's just like taking the two end points and joining them with a ruler and then keeping that ruler parallel to the original line start at one end and with successive very close pencil marks fill in the whole figure. You'll see that when you create your little pencil mark (a fine rectangle) that the rectangle it highly unlikely to use the points on the curve. Even if you force it to use a point on one side of the curve it would be quite the coincidence for it to exactly match a point on the other side, for this reason it is better to just calculate new points. At the time of calculating new points it would probably be a good idea to regenerate the curves p_list in terms of these points so you can fill it more quickly (if the curve is to stay static of course otherwise it wouldn't make any sense).
This answer is very similar to #MizardX's, but uses a different method to find suitable points along the Bezier for a polygonal approximation.
function split_cubic($p, $t)
{
$a_x = $p[0] + ($t * ($p[2] - $p[0]));
$a_y = $p[1] + ($t * ($p[3] - $p[1]));
$b_x = $p[2] + ($t * ($p[4] - $p[2]));
$b_y = $p[3] + ($t * ($p[5] - $p[3]));
$c_x = $p[4] + ($t * ($p[6] - $p[4]));
$c_y = $p[5] + ($t * ($p[7] - $p[5]));
$d_x = $a_x + ($t * ($b_x - $a_x));
$d_y = $a_y + ($t * ($b_y - $a_y));
$e_x = $b_x + ($t * ($c_x - $b_x));
$e_y = $b_y + ($t * ($c_y - $b_y));
$f_x = $d_x + ($t * ($e_x - $d_x));
$f_y = $d_y + ($t * ($e_y - $d_y));
return array(
array($p[0], $p[1], $a_x, $a_y, $d_x, $d_y, $f_x, $f_y),
array($f_x, $f_y, $e_x, $e_y, $c_x, $c_y, $p[6], $p[7]));
}
$flatness_sq = 0.25; /* flatness = 0.5 */
function cubic_ok($p)
{
global $flatness_sq;
/* test is essentially:
* perpendicular distance of control points from line < flatness */
$a_x = $p[6] - $p[0]; $a_y = $p[7] - $p[1];
$b_x = $p[2] - $p[0]; $b_y = $p[3] - $p[1];
$c_x = $p[4] - $p[6]; $c_y = $p[5] - $p[7];
$a_cross_b = ($a_x * $b_y) - ($a_y * $b_x);
$a_cross_c = ($a_x * $c_y) - ($a_y * $c_x);
$d_sq = ($a_x * $a_x) + ($a_y * $a_y);
return max($a_cross_b * $a_cross_b, $a_cross_c * $a_cross_c) < ($flatness_sq * $d_sq);
}
$max_level = 8;
function subdivide_cubic($p, $level)
{
global $max_level;
if (($level == $max_level) || cubic_ok($p)) {
return array();
}
list($q, $r) = split_cubic($p, 0.5);
$v = subdivide_cubic($q, $level + 1);
$v[] = $r[0]; /* add a point where we split the cubic */
$v[] = $r[1];
$v = array_merge($v, subdivide_cubic($r, $level + 1));
return $v;
}
function get_cubic_points($p)
{
$v[] = $p[0];
$v[] = $p[1];
$v = array_merge($v, subdivide_cubic($p, 0));
$v[] = $p[6];
$v[] = $p[7];
return $v;
}
function imagefilledcubic($img, $p, $color)
{
$v = get_cubic_points($p);
imagefilledpolygon($img, $v, count($v) / 2, $color);
}
The basic idea is to recursively split the cubic in half until the bits we're left with are almost flat. Everywhere we split the cubic, we stick a polygon point.
split_cubic splits the cubic in two at parameter $t. cubic_ok is the "are we flat enough?" test. subdivide_cubic is the recursive function. Note that we stick a limit on the recursion depth to avoid nasty cases really screwing us up.
Your self-intersecting test case:
$img = imagecreatetruecolor(256, 256);
imagefilledcubic($img, array(
50.0, 50.0, /* first point */
300.0, 225.0, /* first control point */
300.0, 25.0, /* second control point */
50.0, 200.0), /* last point */
imagecolorallocate($img, 255, 255, 255));
imagepng($img, 'out.png');
imagedestroy($img);
Gives this output:
I can't figure out how to make PHP nicely anti-alias this; imageantialias($img, TRUE); didn't seem to work.

Categories