Get in between lat long from two lat long and direction - php

I am working on one application where I need in between lat long from two lat long.
So for that what I have is
1) Lat long of start point
2) Lat long of end point
3) Direction, like south(180 degree) or north(0 degree)
4) Distance between two points
Now what I will do is I will divide whole path in some number of chunks, like I will divide whole path by 10 KM.
So lets say total distance is 100KM then I will get 10 chunks.
if 200KM then there will be 20 chunks.
So now I want to find out those point's lat long from two lat long.
So all 20 points should be in one line and at equal distance.
How to achieve that ?

You can try taking the difference between the two Lat Lon points, dividing them by the number of chunks. This will give the "increment". This can then be added, or taken away depending on the direction, in a loop to create the points in between.
Consider the following I did as a test:
//Position 1
$lat1 = 53.2226754;
$lon1 = 0.124584;
//Position 2
$lat2 = 52.212445;
$lon2 = -0.12458;
//Differences in Lat / Lon
$latDif = round($lat1 - $lat2, 7);
$lonDif = round($lon1 - $lon2, 7);
//Calculate Step / Increment
//I used 10 as an example for the number of steps
$chunks = 10;
$latStep = round($latDif / $chunks, 7);
$lonStep = round($lonDif / $chunks, 7);
//New Lat Lon starts at start point
$newLat = $lat1;
$newLon = $lon1;
echo $lat1 . ", " . $lon1 . "<br>"; //Start Point
for($i = 1; $i < $chunks; $i++){
//Direction could be substituted here
if($lat1 < $lat2){
$newLat = $newLat + $latStep; //Going North
} else {
$newLat = $newLat - $latStep; //Going South
}
if($lon1 < $lon2){
$newLon = $newLon + $lonStep; //Going East
} else {
$newLon = $newLon - $lonStep; //Going West
}
echo $newLat . ", " . $newLon . "<br>"; //New Point
}
echo $lat2 . ", " . $lon2 . "<br>"; //End Point
This leads to the following Lat Lon output:
53.2226754, 0.124584;
53.1216524, 0.0996676;
53.0206294, 0.0747512;
52.9196064, 0.0498348;
52.8185834, 0.0249184;
52.7175604, 0.0000002;
52.6165374, -0.0249144;
52.5155144, -0.0498308;
52.4144914, -0.0747472;
52.3134684, -0.0996636;
52.212445, -0.12458;
I plotted these on an online geoplanner and got the following:
GPS Results Link

As per #Piskvor's comment, my previous method would not have worked over long distances due to the Mercator Projection of maps.
After some Internet digging, some head-scratching and serious mind boggling, I think I've created a PHP method for calculating the Great Circle path between two Lat Lon points.
For the following example, I'm using the start point as London Heathrow Airport and end point as JFK Intl Airport.
Firstly, we need to calculate the distance between Point A and Point B along the Great Circle that connects them, using the Haversine Formula:
//Must convert to Radians for use with Trig functions
$lat1 = deg2rad(51.4700223); $lon1 = deg2rad(-0.4542955); // LHR
$lat2 = deg2rad(40.6413111); $lon2 = deg2rad(-73.7781391); // JFK
$eRadius = 6367000; // Earth Radius in metres
//Difference between lat and lon
$difLat = $lat2 - $lat1;
$difLon = $lon2 - $lon1;
//Some mathematical magic
$a = sin($difLat / 2) * sin($difLat / 2) +
cos($lat1) * cos($lat2) *
sin($difLon / 2) * sin($difLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
//Distance from pA to pB along the great circle in metres
$t_distance = $eRadius * $c;
Now we have the total distance we can plan the points of the path:
//Step distance in metres
$sDist = 800000; //800km
//Lat Lon Storage;
$lat = []; //Short hand for array(); PHP > 5.4
$lon = [];
//Number of steps - rounded down!
$steps = floor($t_distance / $sDist);
//Percentage of distance for 1 step
$_f = 100 / $steps;
//CALCULATE POINTS
for($i = 0; $i <= $steps; $i++){
$f = ($_f * $i) / 100; // value between 0-1 corresponding with percentage of step with the step number
$_d = $t_distance / $eRadius; //Angular Distance
$a = sin((1- $f) * $_d) / sin($_d);
$b = sin($f * $_d) / sin($_d);
$x = ($a * cos($lat1) * cos($lon1)) + ($b * cos($lat2) * cos($lon2));
$y = ($a * cos($lat1) * sin($lon1)) + ($b * cos($lat2) * sin($lon2));
$z = ($a * sin($lat1)) + ($b * sin($lat2));
//New Lat Lon point
$nLat = round(atan2($z, sqrt(pow($x, 2) + pow($y, 2))),7);
$nLon = round(atan2($y, $x),7);
//Push to Lat Lon arrays, converting Radians back to Degrees
array_push($lat, rad2deg($nLat));
array_push($lon, rad2deg($nLon));
}
Using a simple foreach loop the Lat Long points are something like:
51.470024866283, -0.45429823575923
53.302510052872, -13.752895032598
53.561704700234, -27.704938735627
52.21669773532, -41.302699715615
49.42997998883, -53.701145438835
45.476181591127, -64.517161945995
40.6413111, -73.7781391
Plotting these points on the online geoplanner will give you this result:
I should also like to point out that these two websites really helped me when creating this method:
http://www.movable-type.co.uk/scripts/latlong.html
http://williams.best.vwh.net/avform.htm

Related

How to match a string in an array list and if not found then find the closest string to match that using PHP? [duplicate]

Edit:
With the answer given I made this function
function grabclosestcolor($r, $g, $b){
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
$differencearray = array();
foreach ($colors as $value) {
$difference = sqrt(pow($r-$value[0],2)+pow($g-$value[1],2)+pow($b-$value[2],2));
array_push($differencearray, $difference);
$smallest = min($differencearray);
$key = array_search($smallest, $differencearray);
return $colors[$key];
}
}
My goal is this. I grab a picture and loop through each pixel and grab its x,y, and rgb.
Instead of just grabbing the rgb, I have a predefined array and I'm looking for the closest match from the color I grabbed to the predefined array.
The goal here is to only use colors from the predefined array.
Here is my array of colors.
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
and here is my existing code that loops through it all.
$int = imagesx($im) - 1;
$int2 = imagesy($im) - 1;
$start2 = 0;
do{
$start = 0;
do{
$rgb = imagecolorat($im, $start, $start2);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$value = rgb2hex($r,$g,$b).":$start:$start2";
array_push($colorsofimage, $value);
} while($int > $start++);
} while($int2 > $start2++);
rgb2hex is a User Defined Function, but what I want to accomplish is to change that function with the function to grab the closest color.
$colorsofimage contains an array of each pixels info with hex:x:y
what i want it to be is rgb2hex(NEWFUNCTION($r,$g,$b));
So that the new hex is the 1 out of the predefined array.
I hope you understood, because I have no clue how to do it because I don't know the algorithm of a color.
You have to calculate the distance to each color, and pick the smallest.
There are a few ways to do this. A simple method would be to calculate the distance would be:
sqrt((r-r1)^2+(g-g1)^2+(b-b1)^2)
A better method might be to incorporate the weighted values to calculate a distance, for instance the values used when converting RGB->YUV:
Y = 0.299 * R + 0.587 * G + 0.114 * B
in that case you would use
sqrt(((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2)
Of course, since you don't need the exact distances, just a comparison, you can and probably should just skip the square root, making the last calculation:
((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2
The RGB colour-space is simply a cube. In 24-bit colour each side has a length of 256, allowing values from 0 to 255. In order to find the closest colour in within this cube, you need a distance function. The simplest and most intuitive is the Euclidean distance: if you have colour (r1, g1, b1) and another colour (r2, g2, b2) the distance would be sqrt((r2-r1)^2 + (g2-g1)^2 + (b2-b1)^2).
The challenge for you is then to find the best match across all the values in your predefined array. I suggest that you start simply by iterating over all your values and check the distance for each in turn. Note that for this purpose you do not need to perform the sqrt, simply comparing on the sum of the squares would be sufficient, and would have the benefit of being all based in integer maths. My PHP isn't great, but roughly you would do:
function dist($col1,$col2) {
$delta_r = $col1[0] - $col2[0];
$delta_g = $col1[1] - $col2[1];
$delta_b = $col1[2] - $col2[2];
return $delta_r * $delta_r + $delta_g * $delta_g + $delta_b * $delta_b;
}
$closest=$colors[0];
$mindist=dist($rgb,$colors[0]);
$ncolors=sizeof($colors);
for($i = 1; $i < $ncolors; ++$i)
{
$currdist = dist($rgb,$colors[$i]);
if($currdist<$mindist) {
$mindist=$currdist;
$closest=$colors[$i];
}
}
There are more complicated distance functions (for instance, taking better account of psychovisual interpretation of colour differences (look into Delta E) but I suspect this is more than you need.
Since this question is displayed in the top ten of goolge search results, here is a more complex function I wrote some years ago, which produced better results than the existing PHP functions.
/*
* Die Funktion gibt den Array-Schlüssel der Farbe ($palette),
* die am ehesten der Farbe $givenColor entspricht.
*
* Returns the index of the palette-color which is most similar
* to $givenColor.
*
* $givenColor und die Einträge in $palette können entweder
* Strings im Format (#)rrggbb
* (z. B. "ff0000", "4da4f3" oder auch "#b5d7f3")
* oder Arrays mit je einem Wert für Rot, Grün und Blau
* (z. B. $givenColor = array( 0xff, 0x00, 0x00 ) )
* sein.
*
* $givenColor and the colors in $palette should be either
* formatted as (#)rrggbb
* (e. g. "ff0000", "4da4f3" or "#b5d7f3")
* or arrays with values for red, green and blue
* (e. g. $givenColor = array( 0xff, 0x00, 0x00 ) )
*
* Referenzen/References:
* function rgb2lab
* - http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHilfe/farbraumJava.htm
* - http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
* - http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
*
* function deltaE
* - http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html
*/
function getNearestColor( $givenColor,
$palette = array('blue' => '0000ff','red' => 'ff0000','green' => '00ff00','yellow' => 'ffff00','black' => '000000','white' => 'ffffff','orange' => 'ff8800','purple' => 'ff00ff', 'teal' => '00ffff')
)
{
if(!function_exists('rgb2lab'))
{
function rgb2lab($rgb) {
$eps = 216/24389; $k = 24389/27;
// reference white D50
$xr = 0.964221; $yr = 1.0; $zr = 0.825211;
// reference white D65
#$xr = 0.95047; $yr = 1.0; $zr = 1.08883;
// RGB to XYZ
$rgb[0] = $rgb[0]/255; //R 0..1
$rgb[1] = $rgb[1]/255; //G 0..1
$rgb[2] = $rgb[2]/255; //B 0..1
// assuming sRGB (D65)
$rgb[0] = ($rgb[0] <= 0.04045)?($rgb[0]/12.92):pow(($rgb[0]+0.055)/1.055,2.4);
$rgb[1] = ($rgb[1] <= 0.04045)?($rgb[1]/12.92):pow(($rgb[1]+0.055)/1.055,2.4);
$rgb[2] = ($rgb[2] <= 0.04045)?($rgb[2]/12.92):pow(($rgb[2]+0.055)/1.055,2.4);
// sRGB D50
$x = 0.4360747*$rgb[0] + 0.3850649*$rgb[1] + 0.1430804*$rgb[2];
$y = 0.2225045*$rgb[0] + 0.7168786*$rgb[1] + 0.0606169*$rgb[2];
$z = 0.0139322*$rgb[0] + 0.0971045*$rgb[1] + 0.7141733*$rgb[2];
// sRGB D65
/*$x = 0.412453*$rgb[0] + 0.357580*$rgb[1] + 0.180423*$rgb[2];
$y = 0.212671*$rgb[0] + 0.715160*$rgb[1] + 0.072169*$rgb[2];
$z = 0.019334*$rgb[0] + 0.119193*$rgb[1] + 0.950227*$rgb[2];*/
// XYZ to Lab
$xr = $x/$xr; $yr = $y/$yr; $zr = $z/$zr;
$fx = ($xr > $eps)?pow($xr, 1/3):($fx = ($k * $xr + 16) / 116); $fy = ($yr > $eps)?pow($yr, 1/3):($fy = ($k * $yr + 16) / 116); $fz = ($zr > $eps)?pow($zr, 1/3):($fz = ($k * $zr + 16) / 116);
$lab = array();
$lab[] = round(( 116 * $fy ) - 16); $lab[] = round(500*($fx-$fy)); $lab[] = round(200*($fy-$fz));
return $lab;
} // function rgb2lab
}
if(!function_exists('deltaE'))
{
function deltaE($lab1, $lab2)
{
// CMC 1:1
$l = 1; $c = 1;
$c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]); $c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]);
$h1 = (((180000000/M_PI) * atan2($lab1[1],$lab1[2]) + 360000000) % 360000000)/1000000;
$t = (164 <= $h1 AND $h1 <= 345)?(0.56 + abs(0.2 * cos($h1+168))):(0.36 + abs(0.4 * cos($h1+35)));
$f = sqrt(pow($c1,4)/(pow($c1,4) + 1900));
$sl = ($lab1[0] < 16)?(0.511):((0.040975*$lab1[0])/(1 + 0.01765*$lab1[0]));
$sc = (0.0638 * $c1)/(1 + 0.0131 * $c1) + 0.638;
$sh = $sc * ($f * $t + 1 -$f);
return sqrt( pow(($lab1[0]-$lab2[0])/($l * $sl),2) + pow(($c1-$c2)/($c * $sc),2) + pow(sqrt(($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1]) + ($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]) + ($c1-$c2)*($c1-$c2))/$sh,2) );
} // function deltaE
}
if(!function_exists('colorDistance'))
{
function colorDistance($lab1,$lab2)
{
return sqrt(($lab1[0]-$lab2[0])*($lab1[0]-$lab2[0])+($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1])+($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]));
}
}
if(!function_exists('str2rgb'))
{
function str2rgb($str)
{
$str = preg_replace('~[^0-9a-f]~','',$str);
$rgb = str_split($str,2);
for($i=0;$i<3;$i++)
$rgb[$i] = intval($rgb[$i],16);
return $rgb;
} // function str2rgb
}
// split into RGB, if not already done
$givenColorRGB = is_array($givenColor)?$givenColor:str2rgb($givenColor);
$min = 0xffff;
$return = NULL;
foreach($palette as $key => $color)
{
// split into RGB
$color = is_array($color)?$color:str2rgb($color);
// deltaE
#if($min >= ($deltaE = deltaE(rgb2lab($color),rgb2lab($givenColorRGB))))
// euclidean distance
if($min >= ($deltaE = colorDistance(rgb2lab($color),rgb2lab($givenColorRGB))))
{
$min = $deltaE;
$return = $key;
}
}
return $return;
}
Calculate the distance from the input color to all possible candidates of your palette, and then pick the one with the smallest distance as the one to replace it with.
Distance can be defined in any way you like; Euclidean distance seems workable for RGB cubes, cylinders or HSL/HSV cones.
There is no point in taking the square root. Finding the shortest distance is the same as finding the shortest squared distance. sqrt is an expensive operation, so just skip it.
Whether it really matters, of course, depends of how often your program will make this calculation, but it's still pointless to have it.

Implementing Vincenty's Formula in PHP

I've been attempting to implement Vincenty's formulae with the following:
/* Implemented using Vincenty's formulae from http://en.wikipedia.org/wiki/Vincenty%27s_formulae,
* answers "Direct Problem".
* $latlng is a ('lat'=>x1, 'lng'=>y1) array
* $distance is in miles
* $angle is in degrees
*/
function addDistance($latlng, $distance, $bearing) {
//variables
$bearing = deg2rad($bearing);
$iterations = 20; //avoid too-early termination while avoiding the non-convergant case
//knowns
$f = EARTH_SPHEROID_FLATTENING; //1/298.257223563
$a = EARTH_RADIUS_EQUATOR_MILES; //3963.185 mi
$phi1 = deg2rad($latlng['lat']);
$l1 = deg2rad($latlng['lng']);
$b = (1 - $f) * $a;
//first block
$tanU1 = (1-$f)*tan($phi1);
$U1 = atan($tanU1);
$sigma1 = atan($tanU1 / cos($bearing));
$sinalpha = cos($U1)*sin($bearing);
$cos2alpha = (1 - $sinalpha) * (1 + $sinalpha);
$usquared = $cos2alpha * (($a*$a - $b*$b) / 2);
$A = 1 + ($usquared)/16384 * (4096+$usquared*(-768+$usquared*(320 - 175*$usquared)));
$B = ($usquared / 1024)*(256*$usquared*(-128 + $usquared * (74 - 47*$usquared)));
//the loop - determining our value
$sigma = $distance / ($b * $A);
for($i = 0; $i < $iterations; ++$i) {
$twosigmam = 2*$sigma1 + $sigma;
$delta_sigma = $B * sin($sigma) * (cos($twosigmam)+(1/4)*$B*(cos(-1 + 2*cos(cos($twosigmam))) - (1/6)*$B*cos($twosigmam)*(-3+4*sin(sin($sigma)))*(-3+4*cos(cos($twosigmam)))));
$sigma = $distance / ($b * $A) + $delta_sigma;
}
//second block
$phi2 = atan((sin($U1)*cos($sigma)+cos($U1)*sin($sigma)*cos($bearing)) / ((1-$f) * sqrt(sin($sinalpha) + pow(sin($U1)*sin($sigma) - cos($U1)*cos($sigma)*cos($bearing), 2))));
$lambda = atan((sin($sigma) * sin($bearing)) / (cos($U1)*cos($sigma) - sin($U1)*sin($sigma)*cos($bearing)));
$C = ($f / 16)* $cos2alpha * (4+$f*(4-3*$cos2alpha));
$L = $lambda - (1 - $C) * $f * $sinalpha * ($sigma + $C*sin($sigma)*(cos($twosigmam)+$C*cos($sigma)*(-1+2*cos(cos($twosigmam)))));
$alpha2 = atan($sinalpha / (-sin($U1)*sin($sigma) + cos($U1)*cos($sigma)*cos($bearing)));
//and return our results
return array('lat' => rad2deg($phi2), 'lng' => rad2deg($lambda));
}
var_dump(addDistance(array('lat' => 93.129, 'lng' => -43.221), 20, 135);
The issue is that the results are not reasonable - I'm getting variances of up to 20 latitude and longitude keeping the distance at 20. Is it not in units of elliptical distance on the sphere? Am I misunderstanding something, or is my implementation flawed?
There are a number of errors in transcription from the wikipedia page Direct Problem section:
Your u2 expression has 2 in the denominator where it should have b2;
Your A and B expressions are inconsistent about whether the initial fraction factor needs to be parenthesised to correctly express a / b * c as (a/b) * c - what happens without parentheses is a php syntax issue which I don't know the answer to, but you should favour clarity;
You should be iterating "until there is no significant change in sigma", which may or may not happen in your fixed number of iterations;
There are errors in your DELTA_sigma formula:
on the wikipedia page, the first term inside the square bracket [ is cos sigma (-1 etc, whereas you have cos (-1 etc, which is very different;
in the same formula and also later, note that cos2 x means (cos x)(cos x), not cos cos x!
Your phi_2 formula has a sin($sinalpha) where it should have a sin($sinalpha)*sin($sinalpha);
I think that's all.
Have you tried this:
https://github.com/treffynnon/Geographic-Calculations-in-PHP

PHP and GPS Coordinates, getting the total distance along paths made up of several points

I just can't seem to figure out how to make efficient and clean looking code for figuring this out.
I have a string of coordinates, each point is made up of Longitude, Latitude and Altitude naturally (I am not worried about altitude at all right now and I know the function I have for figuring out the footage does not support altitude):
$coordinates = "-82.36554110283872,26.15200821551467,0 -82.420692,26.097404,0 -82.52855700000001,26.186567,0 -82.41250599999999,25.996422,0 -82.50644510379755,26.05431354409091,0"
I need to find out the distance in feet between each coordinate and add up the total.
I have the following function(which works beautifully) to figure out the distance between two points:
function coordDistance($lat1, $lon1, $lat2, $lon2) {
$delta_lat = $lat2 - $lat1;
$delta_lon = $lon2 - $lon1;
$earth_radius = 20908800.00; //Distance around the earth in feet
$alpha = $delta_lat/2;
$beta = $delta_lon/2;
$a = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin(deg2rad($beta)) * sin(deg2rad($beta)) ;
$c = asin(min(1, sqrt($a)));
$distance = 2*$earth_radius * $c;
$distance = round($distance, 4);
return $distance;
}
The way I am currently breaking down my coordinates string and attempting to pass it to the function is completely ludicrous. Any suggestions on how to split the coordinates and break them down to get the total footage for the path in a nice way?
Essentially a variable to accumulate into. Splitting the string on space to get individual point. Looping for n-1. Calculating the distance between i and i+1.
$total = 0;
$points = explode(' ', $coordinates);
$count = count($points);
for ($i = 0; $count - 1 > $i; ++$i) {
list($lon1, $lat1, $alt1) = explode(',', $points[$i]);
list($lon2, $lat2, $alt2) = explode(',', $points[$i + 1]);
$total += coordDistance($lat1, $lon1, $lat2, $lon2);
}
I didn't quite test the code. Seems to me like your coordinate string has an extra space in it after a minus sign.
Make sure lon and lat are read in the right order too.
If I'm reading your input string correctly, first break up all the coordinates into an array using the explode function:
$coordinatesArray = explode(" ", $coordinates);
Now you can pass each coordinate as an array but you would need to update your function signature. For instance,
coordDistance($coordinatesArray[i], $coordinatesArray[i+1],); // in a loop
Or, you could further breakdown the coordinates to work with your existing function signature.
$coordinateA = explode(",", $coordinatesArray[i]); // again, in a loop
$coordinateB = explode(",", $coordinatesArray[i+1]);
coordDistance($coordinateA[0], $coordinateB[0], $coordinateA[1], $coordinateB[1])
More here: http://php.net/manual/en/function.explode.php

Why is NaN returned from the euclidean method

I have a problem with this function:
function encludean_method($lat1,$lon1,$lat2,$lon2)
{
$x1 = $lat1;
$y1 = $lon1;
$x2 = $lat2;
$y2 = $lon2;
$x = ( pow($x2,2) - pow($x1,2));
$y = ( pow($y2,2) - pow($y1,2));
$distance = (sqrt($x + $y));
return $distance;
}
The problem when I call the above function with some values such as
(1.57454123333,103.6200516333,1.57483825,103.619484475)
it returns NaN. Can anyone tell me why that NaN is returned and how to solve it?
$x and $y can assume negative values (which in fact does happen in your example), yielding in total a negative expression for $x + $y. I'm not sure whether what you're doing is right at all, wouldn't the correct version be more like:
$x1 = $lat1;
$y1 = $lon1;
$x2 = $lat2;
$y2 = $lon2;
$x = $x2 - $x1;
$y = $y2 - $y1;
$distance = (sqrt(pow($x, 2) + pow($y, 2)));
return $distance;
By squaring before calculating the square root, you're guaranteed to operate on positive values only.
Try this
function euclidean_method($lat1,$lon1,$lat2,$lon2) {
$x = $lat1-$lat2;
$y = $lon1-$lon2;
return sqrt($x*$x+$y*$y)
}
The problem is that your x value is smaller than the negativity of your y and thus it will become a negative value you're trying to take the root of.
Because of a wrong algorithm.
function encludean_method($lat1,$lon1,$lat2,$lon2)
{
$x1 = doubleval($lat1);
$y1 = doubleval($lon1);
$x2 = doubleval($lat2);
$y2 = doubleval($lon2);
$x = doubleval( pow($x2 - $x1, 2.0));
$y = doubleval( pow($y2 - $y1, 2.0));
$distance = doubleval(sqrt($x + $y));
return ($distance);
}
Note: (x12 - x22) < (x1 - x2)2.
That's why your code gave x = float(0.00093541820670495) and y = float(-0.11753762299304). So, sqrt(x + y) is too close to zero.
But if you are trying to get the distance between that point over the Earth surface, you should use another formula - the one you are using is applicable for Cartesian coordinate system only.
Notice, that latitude and longitude are measured in degrees. So the number you get is a bit misterious =)
So, after googling a bit, i've noticed some interesting services and formulas. Here's the code you are possibly trying to invoke:
function encludean_method($lat1, $lon1, $lat2, $lon2)
{
$R = 6372.8; // km
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);
$lon1 = deg2rad($lon1);
$lon2 = deg2rad($lon2);
$a = (cos($lat2) * cos($lon2)) - (cos($lat1) * cos($lon1));
$b = (cos($lat2) * sin($lon2)) - (cos($lat1) * sin($lon1));
$c = sin($lat2) - sin($lat1);
$ch = sqrt(pow($a, 2.0) + pow($b, 2.0) + pow($c, 2.0));
$phi = 2.0 * asin($ch / 2.0);
return $R * $phi;
}
Now, encludean_method(1.5745, 103.6200, 1.5748, 103.6195) returns 0.0648375378577 value, which is possibly the distance you are searching for; measured in kilometers.
Here's the example and one more (based on this answer). And here is the service i used to verify that code.
Hope this will help you! =)

RGB to closest predefined color

Edit:
With the answer given I made this function
function grabclosestcolor($r, $g, $b){
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
$differencearray = array();
foreach ($colors as $value) {
$difference = sqrt(pow($r-$value[0],2)+pow($g-$value[1],2)+pow($b-$value[2],2));
array_push($differencearray, $difference);
$smallest = min($differencearray);
$key = array_search($smallest, $differencearray);
return $colors[$key];
}
}
My goal is this. I grab a picture and loop through each pixel and grab its x,y, and rgb.
Instead of just grabbing the rgb, I have a predefined array and I'm looking for the closest match from the color I grabbed to the predefined array.
The goal here is to only use colors from the predefined array.
Here is my array of colors.
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
and here is my existing code that loops through it all.
$int = imagesx($im) - 1;
$int2 = imagesy($im) - 1;
$start2 = 0;
do{
$start = 0;
do{
$rgb = imagecolorat($im, $start, $start2);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$value = rgb2hex($r,$g,$b).":$start:$start2";
array_push($colorsofimage, $value);
} while($int > $start++);
} while($int2 > $start2++);
rgb2hex is a User Defined Function, but what I want to accomplish is to change that function with the function to grab the closest color.
$colorsofimage contains an array of each pixels info with hex:x:y
what i want it to be is rgb2hex(NEWFUNCTION($r,$g,$b));
So that the new hex is the 1 out of the predefined array.
I hope you understood, because I have no clue how to do it because I don't know the algorithm of a color.
You have to calculate the distance to each color, and pick the smallest.
There are a few ways to do this. A simple method would be to calculate the distance would be:
sqrt((r-r1)^2+(g-g1)^2+(b-b1)^2)
A better method might be to incorporate the weighted values to calculate a distance, for instance the values used when converting RGB->YUV:
Y = 0.299 * R + 0.587 * G + 0.114 * B
in that case you would use
sqrt(((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2)
Of course, since you don't need the exact distances, just a comparison, you can and probably should just skip the square root, making the last calculation:
((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2
The RGB colour-space is simply a cube. In 24-bit colour each side has a length of 256, allowing values from 0 to 255. In order to find the closest colour in within this cube, you need a distance function. The simplest and most intuitive is the Euclidean distance: if you have colour (r1, g1, b1) and another colour (r2, g2, b2) the distance would be sqrt((r2-r1)^2 + (g2-g1)^2 + (b2-b1)^2).
The challenge for you is then to find the best match across all the values in your predefined array. I suggest that you start simply by iterating over all your values and check the distance for each in turn. Note that for this purpose you do not need to perform the sqrt, simply comparing on the sum of the squares would be sufficient, and would have the benefit of being all based in integer maths. My PHP isn't great, but roughly you would do:
function dist($col1,$col2) {
$delta_r = $col1[0] - $col2[0];
$delta_g = $col1[1] - $col2[1];
$delta_b = $col1[2] - $col2[2];
return $delta_r * $delta_r + $delta_g * $delta_g + $delta_b * $delta_b;
}
$closest=$colors[0];
$mindist=dist($rgb,$colors[0]);
$ncolors=sizeof($colors);
for($i = 1; $i < $ncolors; ++$i)
{
$currdist = dist($rgb,$colors[$i]);
if($currdist<$mindist) {
$mindist=$currdist;
$closest=$colors[$i];
}
}
There are more complicated distance functions (for instance, taking better account of psychovisual interpretation of colour differences (look into Delta E) but I suspect this is more than you need.
Since this question is displayed in the top ten of goolge search results, here is a more complex function I wrote some years ago, which produced better results than the existing PHP functions.
/*
* Die Funktion gibt den Array-Schlüssel der Farbe ($palette),
* die am ehesten der Farbe $givenColor entspricht.
*
* Returns the index of the palette-color which is most similar
* to $givenColor.
*
* $givenColor und die Einträge in $palette können entweder
* Strings im Format (#)rrggbb
* (z. B. "ff0000", "4da4f3" oder auch "#b5d7f3")
* oder Arrays mit je einem Wert für Rot, Grün und Blau
* (z. B. $givenColor = array( 0xff, 0x00, 0x00 ) )
* sein.
*
* $givenColor and the colors in $palette should be either
* formatted as (#)rrggbb
* (e. g. "ff0000", "4da4f3" or "#b5d7f3")
* or arrays with values for red, green and blue
* (e. g. $givenColor = array( 0xff, 0x00, 0x00 ) )
*
* Referenzen/References:
* function rgb2lab
* - http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHilfe/farbraumJava.htm
* - http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
* - http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
*
* function deltaE
* - http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html
*/
function getNearestColor( $givenColor,
$palette = array('blue' => '0000ff','red' => 'ff0000','green' => '00ff00','yellow' => 'ffff00','black' => '000000','white' => 'ffffff','orange' => 'ff8800','purple' => 'ff00ff', 'teal' => '00ffff')
)
{
if(!function_exists('rgb2lab'))
{
function rgb2lab($rgb) {
$eps = 216/24389; $k = 24389/27;
// reference white D50
$xr = 0.964221; $yr = 1.0; $zr = 0.825211;
// reference white D65
#$xr = 0.95047; $yr = 1.0; $zr = 1.08883;
// RGB to XYZ
$rgb[0] = $rgb[0]/255; //R 0..1
$rgb[1] = $rgb[1]/255; //G 0..1
$rgb[2] = $rgb[2]/255; //B 0..1
// assuming sRGB (D65)
$rgb[0] = ($rgb[0] <= 0.04045)?($rgb[0]/12.92):pow(($rgb[0]+0.055)/1.055,2.4);
$rgb[1] = ($rgb[1] <= 0.04045)?($rgb[1]/12.92):pow(($rgb[1]+0.055)/1.055,2.4);
$rgb[2] = ($rgb[2] <= 0.04045)?($rgb[2]/12.92):pow(($rgb[2]+0.055)/1.055,2.4);
// sRGB D50
$x = 0.4360747*$rgb[0] + 0.3850649*$rgb[1] + 0.1430804*$rgb[2];
$y = 0.2225045*$rgb[0] + 0.7168786*$rgb[1] + 0.0606169*$rgb[2];
$z = 0.0139322*$rgb[0] + 0.0971045*$rgb[1] + 0.7141733*$rgb[2];
// sRGB D65
/*$x = 0.412453*$rgb[0] + 0.357580*$rgb[1] + 0.180423*$rgb[2];
$y = 0.212671*$rgb[0] + 0.715160*$rgb[1] + 0.072169*$rgb[2];
$z = 0.019334*$rgb[0] + 0.119193*$rgb[1] + 0.950227*$rgb[2];*/
// XYZ to Lab
$xr = $x/$xr; $yr = $y/$yr; $zr = $z/$zr;
$fx = ($xr > $eps)?pow($xr, 1/3):($fx = ($k * $xr + 16) / 116); $fy = ($yr > $eps)?pow($yr, 1/3):($fy = ($k * $yr + 16) / 116); $fz = ($zr > $eps)?pow($zr, 1/3):($fz = ($k * $zr + 16) / 116);
$lab = array();
$lab[] = round(( 116 * $fy ) - 16); $lab[] = round(500*($fx-$fy)); $lab[] = round(200*($fy-$fz));
return $lab;
} // function rgb2lab
}
if(!function_exists('deltaE'))
{
function deltaE($lab1, $lab2)
{
// CMC 1:1
$l = 1; $c = 1;
$c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]); $c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]);
$h1 = (((180000000/M_PI) * atan2($lab1[1],$lab1[2]) + 360000000) % 360000000)/1000000;
$t = (164 <= $h1 AND $h1 <= 345)?(0.56 + abs(0.2 * cos($h1+168))):(0.36 + abs(0.4 * cos($h1+35)));
$f = sqrt(pow($c1,4)/(pow($c1,4) + 1900));
$sl = ($lab1[0] < 16)?(0.511):((0.040975*$lab1[0])/(1 + 0.01765*$lab1[0]));
$sc = (0.0638 * $c1)/(1 + 0.0131 * $c1) + 0.638;
$sh = $sc * ($f * $t + 1 -$f);
return sqrt( pow(($lab1[0]-$lab2[0])/($l * $sl),2) + pow(($c1-$c2)/($c * $sc),2) + pow(sqrt(($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1]) + ($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]) + ($c1-$c2)*($c1-$c2))/$sh,2) );
} // function deltaE
}
if(!function_exists('colorDistance'))
{
function colorDistance($lab1,$lab2)
{
return sqrt(($lab1[0]-$lab2[0])*($lab1[0]-$lab2[0])+($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1])+($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]));
}
}
if(!function_exists('str2rgb'))
{
function str2rgb($str)
{
$str = preg_replace('~[^0-9a-f]~','',$str);
$rgb = str_split($str,2);
for($i=0;$i<3;$i++)
$rgb[$i] = intval($rgb[$i],16);
return $rgb;
} // function str2rgb
}
// split into RGB, if not already done
$givenColorRGB = is_array($givenColor)?$givenColor:str2rgb($givenColor);
$min = 0xffff;
$return = NULL;
foreach($palette as $key => $color)
{
// split into RGB
$color = is_array($color)?$color:str2rgb($color);
// deltaE
#if($min >= ($deltaE = deltaE(rgb2lab($color),rgb2lab($givenColorRGB))))
// euclidean distance
if($min >= ($deltaE = colorDistance(rgb2lab($color),rgb2lab($givenColorRGB))))
{
$min = $deltaE;
$return = $key;
}
}
return $return;
}
Calculate the distance from the input color to all possible candidates of your palette, and then pick the one with the smallest distance as the one to replace it with.
Distance can be defined in any way you like; Euclidean distance seems workable for RGB cubes, cylinders or HSL/HSV cones.
There is no point in taking the square root. Finding the shortest distance is the same as finding the shortest squared distance. sqrt is an expensive operation, so just skip it.
Whether it really matters, of course, depends of how often your program will make this calculation, but it's still pointless to have it.

Categories