Below is a formula (Great Circle algorithm) that I want to be translated into PHP. I would really appreciate if someone would help me with that?
The input is to different sets of coordinates, e.g:
Place 1 Lat: 59.389057
Place 1 Long: 17.937422
Place 2 Lat: 59.388914
Place 2 Long: 17.920441
The wanted output is the distance between place 1 and place 2.
distance = ((115.1666667 * (lat2-lat1)) ^ 2 + (115.1666667 * (lng2 - lng1) * cos(lat2 / 57.3)) ^ 2) ^ .5
My try:
$distance = pow(((115.1666667 * ($lat2 - $lat1)), 2) + (115.1666667 * pow(($lng2 - $lng1) * cos($lat2 / 57.3)), 2)), 0.5)
As DCoder writes in the comments, temporary variables make things easier to read:
$latD = (691 / 6) * ($lat2 - $lat1);
$lngD = (691 / 6) * ($lng2 - $lng1) * cos($lat2 / 57.3);
$distance = sqrt( $latD * $latD + $lngD * $lngD );
(demo on codepad.org)
Here's the corrected code:
$distance = pow((pow((115.1666667 * ($lat2 - $lat1)), 2) + pow((115.1666667 * ($lng2 - $lng1) * cos($lat2 / 57.3)), 2)), 0.5);
Related
Im trying to take this function from php to mysql:
function distancia($p1LA, $p1LO, $p2LA, $p2LO) {
$r = 6371.0;
$p1LA = $p1LA * pi() / 180.0;
$p1LO = $p1LO * pi() / 180.0;
$p2LA = $p2LA * pi() / 180.0;
$p2LO = $p2LO * pi() / 180.0;
$dLat = $p2LA - $p1LA;
$dLong = $p2LO - $p1LO;
$a = sin($dLat / 2) * sin($dLat / 2) + cos($p1LA) * cos($p2LA) * sin($dLong / 2) * sin($dLong / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return round($r * $c * 1000);
}
And on mysql, I'm doing this:
DELIMITER |
CREATE FUNCTION Distancia (la1 DECIMAL, lo1 DECIMAL, la2 DECIMAL, lo2 DECIMAL)
RETURNS DECIMAL
DETERMINISTIC
BEGIN
DECLARE r DECIMAL;
DECLARE la1aux DECIMAL;
DECLARE lo1aux DECIMAL;
DECLARE la2aux DECIMAL;
DECLARE lo2aux DECIMAL;
DECLARE retorno DECIMAL;
DECLARE dLat DECIMAL;
DECLARE dLong DECIMAL;
DECLARE a DECIMAL;
DECLARE c DECIMAL;
SET r = 6371.0;
SET la1aux = la1 * pi()/180.0;
SET lo1aux = lo1 * pi()/180.0;
SET la2aux = la2 * pi()/180.0;
SET lo2aux = lo2 * pi()/180.0;
SET dLat = la2aux - la1aux;
SET dLong = lo2aux - lo1aux;
SET a = sin(dLat / 2) * sin(dLat / 2) + cos(la1aux) * cos(la2aux) * sin(dLong / 2) * sin(dLong / 2);
SET c = 2 * atan2(sqrt(a), sqrt(1 - a));
SET retorno = round(r * c * 1000);
RETURN retorno;
END|
Im calling the function on mysql like this:
select Distancia(-46.61579100, -23.54247250, -46.61354560, -23.54093070)/1000;
The result should be 0.276. But Im getting 0. I never worked with a procedure or function on mysql before, so I dont know what I could be missing. I tried to make the decimal variables decimal(11,8), and it didnt work too.
Anyone with a little help?
When you do calculations with floating numbers where calculations are not simple ones (adding, multiplication) and when the subtotals exceed the bounds of the datatype (especially when you use decimal without scale and precision), you better use float-datatype. You can at the end choose how many decimals you want the result to be.
I am using
$lat=rand(0-pi()*1000000,pi()*1000000)/1000000;
$lon=rand(0-pi()*1000000,pi()*1000000)/1000000;
to pick a random point on earth(in radians) however this method results in points being closer together near polls then near equator since 1 deg of $lon is a smaller distance near the polls then at the equator.
Any ideas on how to get a better distribution?
Based on this article on Wolfram you need something like this:
$long = pi() - 2 * pi() * rand(0, 1000000) / 1000000;
$lat = pi() / 2 - acos(2 * rand(0, 1000000) / 1000000 - 1);
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?
How can I calculate every Lat/Long coordinates between two Lat/Long coordinates in PHP?
Lets say I have coordinates A:
(39.126331, -84.113288)
and coordinates B:
(39.526331, -84.213288)
How would I calculate every possible coordinates between those two Lat/Long coordinates (in a direct line) up to five decimal places (e.g. 39.12633, -84.11328) and get list of coordinates between the two?
In addition, I have another set of coordinates (Coordinates C) that are slightly off and not on the track of coordinates between A and B.
How could I calculate the distance between coordinates C and the closest coordinates between A and B?
You can compute a voronoi diagram from all the lat lon pairs and then look for adjacent cell. Also note that lat lon are angles and not world coordinate or cartesian coordinates. You can download my PHP class voronoi diagram # phpclasses.org.
Here is what solved this for me,
function point_to_line_segment_distance($startX,$startY, $endX,$endY, $pointX,$pointY)
{
$r_numerator = ($pointX - $startX) * ($endX - $startX) + ($pointY - $startY) * ($endY - $startY);
$r_denominator = ($endX - $startX) * ($endX - $startX) + ($endY - $startY) * ($endY - $startY);
$r = $r_numerator / $r_denominator;
$px = $startX + $r * ($endX - $startX);
$py = $startY + $r * ($endY - $startY);
$closest_point_on_segment_X = $px;
$closest_point_on_segment_Y = $py;
$distance = user_bomb_distance_calc($closest_point_on_segment_X, $closest_point_on_segment_Y, $pointX, $pointY);
return array($distance, $closest_point_on_segment_X, $closest_point_on_segment_Y);
}
function user_bomb_distance_calc($uLat , $uLong , $bLat , $bLong)
{
$earthRadius = 6371; #km
$dLat = deg2rad((double)$bLat - (double) $uLat);
$dlong = deg2rad((double)$bLong - (double) $uLong);
$a = sin($dLat / 2 ) * sin($dLat / 2 ) + cos(deg2rad((double)$uLat)) * cos(deg2rad((double)$bLat)) * sin($dlong / 2) * sin($dlong / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$distance = $earthRadius * $c;
$meter = 1000; //convert to meter 1KM = 1000M
return intval( $distance * $meter ) ;
}
I need to draw something like a subway map (multiple routes along the same path) using PHP's image library. Here's an example:
*********
******** *
******* * *
* * *
* * *
* * *
* * *
* * *
* * *
* * *
* * *
* * *
* * *
* * ********************
* *********************
**********************
It's easy enough to draw one line along this path. I don't know how to draw multiple lines that follow the path but have an equal amount of space between them.
For a given point A, and more lines through it, for the first points you'll have to decide whether points go 'inside'(B) the track, or 'outside'(C):
********C
D******A *
Q*****B * *
* * *
* E *
Now, you can calculate the offset of your point B to point A as a path from with length=offset (5px for instance) along the angle the which is half the clockwise angle between AE & AD for the 'inside' B (or the clockwise angle from AD to AE for the 'outside' C, or just use a negative offset later on). You'll want point B on a distance of 5px from A along the line through A with an angle angle AE + ((angle AD - angle AE) / 2)
I'm by no means a Math wiz, and the only time I needed to calculate angles like those were in javascript, I'll give it as an example, rewrite to PHP as you please (anybody who does know math, feel free to laugh & correct when needed):
var dx = b.x - a.x;
var dy = b.y - a.y;
if(dx == 0 && dy == 0){
answer = 0;
} else if(dx > 0 && dy >= 0 ){
answer = Math.atan(dy/dx);
} else if(dx <= 0 && dy > 0){
answer = Math.atan(dx/dy) + (Math.PI * 0.5);
} else if(dx <= 0 && dy <= 0){
answer = Math.atan(dy/dx) + Math.PI;
} else if(dx >= 0 && dy <= 0){
answer = Math.atan(dy/dx) + (Math.PI * 1.5);
}
So, in a grid where D=(0,10),A=(10,10), E=(20,20):
The angle through AE = 45° (PI/4 rad),through AD = 180° (PI rad)
The angle through AB is then (45 + ((180-45)/2))=> 112.5° (5/8 PI rad)
5px offset from A=(10,10) through angle 112.5° gives you this location for B:
Bx = Ax + (cos(angle) * 5) = +/- 8.1
By = Ay + (sin(angle) * 5) = +/- 14.6
At the 'sibling' point Q next to starting point D you have no previous path to reference / calculate an angle from, so I'd take the perpendicular: angle DQ = angle DA + 90° (PI/2 rad) (in the example you could just do Dy+5, but maybe you don't always start parallel to one of the 2 axis)
Rinse and repeat for all other points, draw lines between the calculated coordinates.
To complement Wrikken's answer, here's an actual code sample using Objective-C and the cocos2d-iphone engine reconstructed from this thread and others. The atan is not needed, instead the cross product is used, see the C function at the end of the code sample and this link.
I also simply switched the sign of the offset vector from A to B in order to get the vector from A to C. This avoids calling cosf/sinf twice.
PS: This code runs in a for loop from i = 0 to i < numVertices.
CGPoint splinePoint = splinePoints[i];
CGPoint prevPoint = (i == 0) ? splinePoint : splinePoints[i - 1];
CGPoint railPoint = splinePoint;
CGPoint nextPoint = (i == (numVertices-1)) ? splinePoint : splinePoints[i + 1];
CGPoint toPrevPoint = ccpSub(railPoint, prevPoint);
CGPoint toNextPoint = ccpSub(railPoint, nextPoint);
float angleToPrevPoint = ccpAngleSigned(kAngleOriginVector, toPrevPoint);
float angleToNextPoint = ccpAngleSigned(kAngleOriginVector, toNextPoint);
float offsetAngle = 0.0f;
if (i > 0 && i < (numVertices - 1))
{
offsetAngle = angleToNextPoint + ((angleToPrevPoint-angleToNextPoint) / 2);
}
else if (i == 0)
{
offsetAngle = angleToNextPoint + M_PI_2;
}
else
{
offsetAngle = angleToPrevPoint + M_PI_2;
}
CGPoint offsetLeftRail, offsetRightRail, offsetRail;
offsetRail.x = cosf(offsetAngle) * railOffsetFromCenter;
offsetRail.y = sinf(offsetAngle) * railOffsetFromCenter;
offsetLeftRail = ccpAdd(railPoint, offsetRail);
offsetRightRail = ccpAdd(railPoint, ccpMult(offsetRail, -1.0f));
if (isPointToTheLeftOfLine(prevPoint, railPoint, offsetLeftRail))
{
leftRailSplinePoints[i] = offsetLeftRail;
rightRailSplinePoints[i] = offsetRightRail;
}
else
{
leftRailSplinePoints[i] = offsetRightRail;
rightRailSplinePoints[i] = offsetLeftRail;
}
BOOL isPointToTheLeftOfLine(CGPoint start, CGPoint end, CGPoint test)
{
return ((end.x - start.x) * (test.y - start.y) -
(end.y - start.y) * (test.x - start.x)) > 0;
}
This helped me to draw the rails on the railtrack: