I can normally catch these easy, but......
function linear_regression($x, $y) {
// calculate number points
$n = count($x);
// ensure both arrays of points are the same size
if ($n != count($y)) {
trigger_error("linear_regression(): Number of elements in coordinate arrays do not match.", E_USER_ERROR);
}
// calculate sums
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xx_sum = 0;
$xy_sum = 0;
for($i = 0; $i < $n; $i++) {
$xy_sum+=($x[$i]*$y[$i]);
$xx_sum+=($x[$i]*$x[$i]);
}
// calculate slope
//$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / (($n * $xx_sum) - ($x_sum * $x_sum));
$divisor = (($n * $xx_sum) – ($x_sum * $x_sum));
if ($divisor == 0){
$m = 0;
} else {
$m = (($n * $xy_sum) – ($x_sum * $y_sum)) / $divisor;
}
// calculate intercept
$b = ($y_sum - ($m * $x_sum)) / $n;
// return result
return array("m"=>$m, "b"=>$b);
}
var_dump( linear_regression(array(1, 2, 3, 4, 4), array(1.5, 1.6, 2.1, 3.0, 6)) );
The error is happening here $divisor = (($n * $xx_sum) – ($x_sum * $x_sum));
Any ideas why?
The minus sign is a fancy unicode dash (I think the em dash) instead of the regular ascii - character.
Don't ask me how I discovered that, but your "-" is not a real "-", is another character, but have the same apparence
Ok you didn't asked me, but I have discovered at http://writecodeonline.com/php/ , it doesn't recognize yours "-" character
Now working code with real "-", you can copy an paste, and see:
function linear_regression($x, $y) {
// calculate number points
$n = count($x);
// ensure both arrays of points are the same size
if ($n != count($y)) {
trigger_error("linear_regression(): Number of elements in coordinate arrays do not match.", E_USER_ERROR);
}
// calculate sums
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xx_sum = 0;
$xy_sum = 0;
for($i = 0; $i < $n; $i++) {
$xy_sum+=($x[$i]*$y[$i]);
$xx_sum+=($x[$i]*$x[$i]);
}
// calculate slope
//$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / (($n * $xx_sum) - ($x_sum * $x_sum));
$divisor = (($n * $xx_sum) - ($x_sum * $x_sum));
if ($divisor == 0){
$m = 0;
} else {
$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / $divisor;
}
// calculate intercept
$b = ($y_sum - ($m * $x_sum)) / $n;
// return result
return array("m"=>$m, "b"=>$b);
}
var_dump( linear_regression(array(1, 2, 3, 4, 4), array(1.5, 1.6, 2.1, 3.0, 6)) );
I retyped the offending line like this:
$divisor = (($n*$xx_sum)-($x_sum * $x_sum));
The error disappeared. After a bit of poking the error seems to be related to the minus sign in the middle. deleting it and retyping it seems to fix the problem.
See this fiddle:
Adam is right, but also, you didn’t close the for loop. Here try this.
Also, the minus sign was the wrong sign here:
$divisor = (($n * $xx_sum) - ($x_sum * $x_sum));
As well as here:
$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / $divisor;
My cleaned up version of your function with the closed for loop in place as well here.
function linear_regression($x, $y) {
// calculate number points
$n = count($x);
// ensure both arrays of points are the same size
if ($n != count($y)) {
trigger_error("linear_regression(): Number of elements in coordinate arrays do not match.", E_USER_ERROR);
}
// calculate sums
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xx_sum = 0;
$xy_sum = 0;
for($i = 0; $i < $n; $i++) {
$xy_sum+=($x[$i]*$y[$i]);
$xx_sum+=($x[$i]*$x[$i]);
// calculate slope
//$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / (($n * $xx_sum) - ($x_sum * $x_sum));
$divisor = (($n * $xx_sum) - ($x_sum * $x_sum));
if ($divisor == 0) {
$m = 0;
}
else {
$m = (($n * $xy_sum) - ($x_sum * $y_sum)) / $divisor;
}
// calculate intercept
$b = ($y_sum - ($m * $x_sum)) / $n;
}
// return result
return array("m"=>$m, "b"=>$b);
} // linear_regression
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
how to convert below panaroma image to cube iamges in php?
I used imagick, js code and ... but do not result.
main image
please help me
thanks
After much effort ...
I got the right result
my final code:
# get x,y,z coords from out image pixels coords
# i,j are pixel coords
# faceIdx is face number
# faceSize is edge length
function outImgToXYZ($i, $j, $faceIdx, $faceSize)
{
$a = 2.0 * (float) $i / $faceSize;
$b = 2.0 * (float) $j / $faceSize;
switch ($faceIdx)
{
case 0: return [-1.0, 1.0 - $a, 3.0 - $b]; break; # back
case 1: return [$a - 3.0, -1.0, 3.0 - $b]; break; # left
case 2: return [1.0, $a - 5.0, 3.0 - $b]; break; # front
case 3: return [7.0 - $a, 1.0, 3.0 - $b]; break; # right
case 4: return [$b - 1.0, $a - 5.0, 1.0]; break; # top
case 5: return [5.0 - $b, $a - 5.0, -1.0]; break; # bottom
}
return [0, 0, 0];
}
function clip($x, $min, $max)
{
if ($x > $max) return $max;
if ($x < $min) return $min;
return $x;
}
# convert using an inverse transformation
function convertFace($imgIn, $imgInWidth, $imgInHeight, $imgOut, $imgOutWidth, $imgOutHeight)
{
$pi = pi();
$edge = $imgInWidth / 4;
$face = 0;
$face2 = 0;
$rng = null;
for($xOut = 0; $xOut <= $imgOutWidth; $xOut++)
{
$face = floor($xOut / $edge); // 0 - back, 1 - left 2 - front, 3 - right
if ($face === 2) $rng = range(0, $edge * 3, 1);
else $rng = range($edge, $edge * 2, 1);
for($yOut =0; $yOut <= $imgOutHeight; $yOut++)
{
if ($yOut < $edge) $face2 = 4; // top
else if ($yOut >= 2 * $edge) $face2 = 5; // bottom
else $face2 = $face;
list($x, $y, $z) = outImgToXYZ($xOut, $yOut, $face2, $edge);
$theta = atan2($y, $x);
$r = hypot($x, $y);
$phi = atan2($z, $r);
# source img coords
$uf = 2.0 * $edge * ($theta + $pi) / $pi;
$vf = 2.0 * $edge * ($pi / 2 - $phi) / $pi;
# Use bilinear interpolation between the four surrounding pixels
$ui = floor($uf); # coord of pixel to bottom left
$vi = floor($vf);
$u2 = $ui + 1; # coords of pixel to top right
$v2 = $vi + 1;
$mu = $uf - $ui; # fraction of way across pixel
$nu = $vf - $vi;
# Pixel values of four corners
$a = imagecolorsforindex($imgIn, imagecolorat($imgIn, $ui % $imgInWidth, clip($vi, 0, $imgInHeight - 1)));
$b = imagecolorsforindex($imgIn, imagecolorat($imgIn, $u2 % $imgInWidth, clip($vi, 0, $imgInHeight - 1)));
$c = imagecolorsforindex($imgIn, imagecolorat($imgIn, $ui % $imgInWidth, clip($v2, 0, $imgInHeight - 1)));
$d = imagecolorsforindex($imgIn, imagecolorat($imgIn, $u2 % $imgInWidth, clip($v2, 0, $imgInHeight - 1)));
# interpolate
list($r, $g, $b) =
[
$a["red"] * (1 - $mu) * (1 - $nu) + $b["red"] * ($mu) * (1 - $nu) + $c["red"] * (1 - $mu) * $nu + $d["red"] * $mu * $nu,
$a["green"] * (1 - $mu) * (1 - $nu) + $b["green"] * ($mu) * (1 - $nu) + $c["green"] * (1 - $mu) * $nu + $d["green"] * $mu * $nu,
$a["blue"] * (1 - $mu) * (1 - $nu) + $b["blue"] * ($mu) * (1 - $nu) + $c["blue"] * (1 - $mu) * $nu + $d["blue"] * $mu * $nu
];
imagesetpixel($imgOut, $xOut, $yOut, imagecolorallocate($imgOut, (int) round($r), (int) round($g), (int) round($b)));
}
}
return $imgOut;
}
// start convert
$imgInPath = "./main.jpg";
$imgIn = imagecreatefromjpeg($imgInPath);
$imgInSize = getimagesize($imgInPath);
$imgInWidth = $imgInSize[0];
$imgInHeight = $imgInSize[1];
$imgOutWidthHeight = $imgInWidth / 4;
$imgOutFullWidth = $imgInWidth;
$imgOutFullHeight = $imgInWidth * 3 / 4;
$imgFormat = "jpg";
$imgOut = imagecreate($imgOutFullWidth, $imgOutFullHeight);
imagefill($imgOut, 0, 0, imagecolorallocate($imgOut, 0, 0, 0));
imagejpeg($imgOut, "./temp.jpg", 100);
$imgOut = imagecreatefromjpeg("./temp.jpg");
imagefill($imgOut, 0, 0, imagecolorallocate($imgOut, 0, 0, 0));
$imgOut = convertFace($imgIn, $imgInWidth, $imgInHeight, $imgOut, $imgOutFullWidth, $imgOutFullHeight);
imagejpeg($imgOut, "./temp.jpg", 100);
imagedestroy($imgOut);
$faceNames = [['', '', 'top', ''], ['left', 'front', 'right', 'back'], ['', '', 'bottom', '']];
for($i = 0; $i < 3; $i++)
{
for($j = 0; $j < 4; $j++)
{
$faceName = $faceNames[$i][$j];
if($faceName != "")
{
$im = imagecreatefromjpeg("temp.jpg");
$im2 = imagecrop($im, ["x" => ($j * $imgOutWidthHeight), "y" => ($i * $imgOutWidthHeight), "width" => $imgOutWidthHeight, "height" => $imgOutWidthHeight]);
if($im2 !== FALSE)
{
imagejpeg($im2, "main_$faceName.jpg", 100);
imagedestroy($im2);
}
imagedestroy($im);
}
}
}
unlink("./temp.jpg");
I'm trying to calculate EMI as per the attached screenshot. but there is a minor difference in my calculation. what I have done so far as below:
function interest($investment,$year,$rate=15,$n=1){
global $total_result, $total_interest, $totalamount;
$accumulated=0;
if ($year > 1){
$accumulated=interest($investment,$year-1,$rate,$n);
}
$accumulated += $investment;
$rateC = $rate / 100;
$result = $rateC / 12 * pow(1 + ($rateC) / 12, ($year * 12)) / (pow(1 + ($rateC) / 12, ($year * 12)) - 1) * $accumulated;
$result = 85.60;
for ($i=0; $i < 12; $i++) {
// echo round($accumulated,2).'<br>';
$accumulated = $accumulated * pow(1 + $rate/(100 * $n),$n);
$innntrest = ($accumulated - $investment) / 12;
$i_result = $result - $innntrest;
$accumulated = $investment - $i_result;
$investment = $accumulated;
echo '<br>***'.round($result,2).'***'.round($i_result,2).'***'.round($innntrest,2).'<br>';
$total_result = $total_result + round($result,2);
$total_interest = $total_interest + $i_result;
$totalamount = $totalamount + $innntrest;
}
return $accumulated;
}
Where $investment=1000(loan amount), $year=1 (loan term), $rate=5%(interest rate), $n=4 (compound)
So I want exact output as an attached screenshot, so can anyone help me with this code to find out the formula? Thanks in advance!
After a few days of research, I got the solution as below:
function interest($investment,$year,$rate=15,$n=1,$payment_frequency=4){
$accumulated=0;
$accumulated += $investment;
$rateC = $rate / 100;
$total_i = 0;
if($payment_frequency == 1) {
$accumulated = $accumulated * pow(1 + $rate/(100 * $n),$n);
} else {
$rate = pow(1 + ($rateC/$n),($n*($year/($year*$payment_frequency)))) - 1;
$result = ($rate*$accumulated)/(1 - pow((1 + $rate),-($year*$payment_frequency)));
for ($i=0; $i < ($year*$payment_frequency); $i++) {
$accumulated = $accumulated * pow(1 + $rate/(100 * $n),$n);
$innntrest = ($accumulated - $investment) * 100;
$i_result = $result - $innntrest;
$accumulated = $investment - $i_result;
$investment = $accumulated;
$total_i += $innntrest;
}
return $total_i;
}
}
I'm using the XIRR function from the php financial library (http://www.phpclasses.org/package/892-PHP-Financial-functions-with-the-Excel-function-names-.html) but I get strange results with these values (dates are d/m/y):
(01/01/2014, -400) , (01/10/2014, 18)
MS Excel correctly returns 0.98, while the XIRR function returns -1.5714653207915E+40. The code is as follow:
$f->XIRR(array(-400,18), array(
mktime(0,0,0,1,1,2014),
mktime(0,0,0,10,1,2014),
), 0.1);
Can anyone explain me what am I doing wrong? Thanks in advance for any help.
I have written one code from PHP Excel Functions.
I have calculated XIRR and XNPV.
Here is code sample with some dummy data.
Main advantage is that, There is no dependency on any library in this
code.
<?php
$rate = 0.12;
$values = array(-5000,-3000,-8000,25000,-4000);
$dates = array('01-02-2015','05-05-2016','02-03-2018','03-03-2019','05-03-2019');
/** FINANCIAL_MAX_ITERATIONS */
define('FINANCIAL_MAX_ITERATIONS', 128);
/** FINANCIAL_PRECISION */
define('FINANCIAL_PRECISION', 1.0e-08);
$result = XIRR($values,$dates,0.1);
print_r($result);
function XIRR($values, $dates, $guess = 0.1) {
$x1 = 0.0;
$x2 = $guess;
$f1 = XNPV($x1, $values, $dates);
$f2 = XNPV($x2, $values, $dates);
for ($i = 0; $i < 128; ++$i) {
if (($f1 * $f2) < 0.0) break;
if (abs($f1) < abs($f2)) {
$f1 = XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
} else {
$f2 = XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
}
}
$f = XNPV($x1, $values, $dates);
if ($f < 0.0) {
$rtb = $x1;
$dx = $x2 - $x1;
} else {
$rtb = $x2;
$dx = $x1 - $x2;
}
for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
$dx *= 0.5;
$x_mid = $rtb + $dx;
$f_mid = XNPV($x_mid, $values, $dates);
if ($f_mid <= 0.0) $rtb = $x_mid;
if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
}
}
function XNPV($rate, $values, $dates) {
$valCount = count($values);
$xnpv = 0.0;
for ($i = 0; $i < $valCount; ++$i)
{
$datediff = strtotime($dates[$i]) - strtotime($dates[0]);
$datediff = round($datediff / (60 * 60 * 24));
$xnpv += $values[$i] / pow(1 + $rate,$datediff / 365);
}
return $xnpv;
}
?>
The correct XIRR value is -98.417% as shown below
-400 + 18(1+i)^-(273/365) = 0
18(1+i)^-(273/365) = 400
(1+i)^-(273/365) = 400/18
(1+i)^(273/365) = 18/400
(1+i) = (18/400)^(365/273)
1+i = (0.045)^(1.336996337)
i = (0.045)^(1.336996337) - 1
i = -0.984174769
i = -98.417%
Use the American mm/dd/yyyy :
mktime(0,0,0,1,1,2014),
mktime(0,0,0,1,10,2014)
Does anyone know a PHP function (for >5.3) which can convert an HSL color to either RGB or Hex? I've tried a dozen Google searches and none of the functions I have found work as expected.
It doesn't matter whether the function converts to RGB or hex because converting between those two is trivial. The inputs are HSL values for CSS (Hue: 0–360, Saturation: 0–100, Lightness: 0–100).
Edit: Specifying the input and output format would be a bonus :)
Taking the code from one of the answers in the link of Jim's comment (PHP HSV to RGB formula comprehension), we can compute it as follows:
<?php
$hue = 209;
$sat = 75;
$lum = 60;
$hue /= 360;
$sat /= 100;
$lum /= 100;
$result = ColorHSLToRGB($hue, $sat, $lum);
var_dump($result); echo '<br>';
printf("rgb = %d,%d,%d<br>", $result['r'], $result['g'], $result['b']);
function ColorHSLToRGB($h, $s, $l){
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;
$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;
switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
return array('r' => $r * 255.0, 'g' => $g * 255.0, 'b' => $b * 255.0);
}
?>
Output:
array(3) { ["r"]=> float(76.5) ["g"]=> float(155.55) ["b"]=> float(229.5) }
rgb = 76,155,229
Putting this together (which helped me produce this chart)
/**
* convert a HSL colorscheme to either Hexadecimal (default) or RGB.
*
* We want a method where we can programmatically generate a series of colors
* between two values (eg. red to green) which is easy to do with HSL because
* you just change the hue. (0 = red, 120 = green). You can use this function
* to convert those hsl color values to either the rgb or hexadecimal color scheme.
* e.g. You have
* hsl(50, 100%, 50%)
* To convert,
* $hex = convertHSL(50,100,50); // returns #ffd500
* or
* $rgb = convertHSL(50,100,50, false); // returns rgb(255, 213, 0)
*
* see https://coderwall.com/p/dvsxwg/smoothly-transition-from-green-to-red
* #param int $h the hue
* #param int $s the saturation
* #param int $l the luminance
* #param bool $toHex whether you want hexadecimal equivalent or rgb equivalent
* #return string usable in HTML or CSS
*/
function convertHSL($h, $s, $l, $toHex=true){
$h /= 360;
$s /=100;
$l /=100;
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;
$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;
switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
$r = round($r * 255, 0);
$g = round($g * 255, 0);
$b = round($b * 255, 0);
if ($toHex) {
$r = ($r < 15)? '0' . dechex($r) : dechex($r);
$g = ($g < 15)? '0' . dechex($g) : dechex($g);
$b = ($b < 15)? '0' . dechex($b) : dechex($b);
return "#$r$g$b";
} else {
return "rgb($r, $g, $b)";
}
My tests of all other implementations showed only weird and possibly unplausible results. Here is a PHP implementation of #Mohsen's code from https://stackoverflow.com/a/9493060/1598477. Plus a test to show the full beauty...
Sorry to cross-post this. But I really haven't seen any other implementation that gives the quality I needed.
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param {number} h The hue
* #param {number} s The saturation
* #param {number} l The lightness
* #return {Array} The RGB representation
*/
function hue2rgb($p, $q, $t){
if($t < 0) $t += 1;
if($t > 1) $t -= 1;
if($t < 1/6) return $p + ($q - $p) * 6 * $t;
if($t < 1/2) return $q;
if($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
return $p;
}
function hslToRgb($h, $s, $l){
if($s == 0){
$r = $l;
$g = $l;
$b = $l; // achromatic
}else{
$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
$p = 2 * $l - $q;
$r = hue2rgb($p, $q, $h + 1/3);
$g = hue2rgb($p, $q, $h);
$b = hue2rgb($p, $q, $h - 1/3);
}
return array(round($r * 255), round($g * 255), round($b * 255));
}
/* Uncomment to test * /
for ($i=0;$i<360;$i++) {
$rgb=hslToRgb($i/360, 1, .9);
echo '<div style="background-color:rgb(' .$rgb[0] . ', ' . $rgb[1] . ', ' . $rgb[2] . ');padding:2px;"></div>';
}
/* End Test */
The PEAR package Image_Color2 has methods to transform between color models - see convertTo.
Here is my solution
HSV value restrictions: $H [0-359], $S [0-100], $V [0-100]
function hsv_to_rgb($iH, $iS, $iV) {
if($iH < 0) $iH = 0;
if($iH > 360) $iH = 360;
if($iS < 0) $iS = 0;
if($iS > 100) $iS = 100;
if($iV < 0) $iV = 0;
if($iV > 100) $iV = 100;
$dS = $iS/100.0;
$dV = $iV/100.0;
$dC = $dV*$dS;
$dH = $iH/60.0;
$dT = $dH;
while($dT >= 2.0) $dT -= 2.0; // php modulus does not work with float
$dX = $dC*(1-abs($dT-1)); // as used in the Wikipedia link
switch($dH) {
case($dH >= 0.0 && $dH < 1.0):
$dR = $dC; $dG = $dX; $dB = 0.0; break;
case($dH >= 1.0 && $dH < 2.0):
$dR = $dX; $dG = $dC; $dB = 0.0; break;
case($dH >= 2.0 && $dH < 3.0):
$dR = 0.0; $dG = $dC; $dB = $dX; break;
case($dH >= 3.0 && $dH < 4.0):
$dR = 0.0; $dG = $dX; $dB = $dC; break;
case($dH >= 4.0 && $dH < 5.0):
$dR = $dX; $dG = 0.0; $dB = $dC; break;
case($dH >= 5.0 && $dH < 6.0):
$dR = $dC; $dG = 0.0; $dB = $dX; break;
default:
$dR = 0.0; $dG = 0.0; $dB = 0.0; break;
}
$dM = $dV - $dC;
$dR += $dM; $dG += $dM; $dB += $dM;
$dR *= 255; $dG *= 255; $dB *= 255;
return array(round($dR), round($dG), round($dB));
}
Copied from this SO Answer by Cullub.
This code worked perfectly to convert a user email to user avatar colour. After long hours of searching, I got the correct hex value similar to the one generated using javascript on the front end.
TL;DR: The full code can be found here on Pastebin.
/**
* convert user email to hsl for user avatar
* #param string $string
* #return string HEX color code
*/
function stringToColor($string)
{
$hash = 0;
$l = 70;
$s = 60;
for ($i = 0; $i < strlen($string); $i++) {
$hash = ord($string[$i]) + (($hash << 5) - $hash);
}
$h = fmod($hash, 360);
return $this->hslToHex($h, $s, $l, true);
}
/**
* Converts HSL to Hex by converting it to
* RGB, then converting that to hex.
*
* string hslToHex($h, $s, $l[, $prependPound = true]
*
* $h is the Degrees value of the Hue
* $s is the Percentage value of the Saturation
* $l is the Percentage value of the Lightness
* $prependPound is a bool, whether you want a pound
* sign prepended. (optional - default=true)
*
* Calls:
* hslToRgb
*
* Output: Hex in the format: #00ff88 (with
* pound sign). Rounded to the nearest whole
* number.
*/
function hslToHex($h, $s, $l, $prependPound = true)
{
//convert hsl to rgb
$rgb = $this->hslToRgb($h, $s, $l);
//convert rgb to hex
$hexR = $rgb['r'];
$hexG = $rgb['g'];
$hexB = $rgb['b'];
//round to the nearest whole number
$hexR = round($hexR);
$hexG = round($hexG);
$hexB = round($hexB);
//convert to hex
$hexR = dechex($hexR);
$hexG = dechex($hexG);
$hexB = dechex($hexB);
//check for a non-two string length
//if it's 1, we can just prepend a
//0, but if it is anything else non-2,
//it must return false, as we don't
//know what format it is in.
if (strlen($hexR) != 2) {
if (strlen($hexR) == 1) {
//probably in format #0f4, etc.
$hexR = "0" . $hexR;
} else {
//unknown format
return false;
}
}
if (strlen($hexG) != 2) {
if (strlen($hexG) == 1) {
$hexG = "0" . $hexG;
} else {
return false;
}
}
if (strlen($hexB) != 2) {
if (strlen($hexB) == 1) {
$hexB = "0" . $hexB;
} else {
return false;
}
}
//if prependPound is set, will prepend a
//# sign to the beginning of the hex code.
//(default = true)
$hex = "";
if ($prependPound) {
$hex = "#";
}
$hex = $hex . $hexR . $hexG . $hexB;
return $hex;
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
* Assumes h, s, and l are in the format Degrees,
* Percent, Percent, and returns r, g, and b in
* the range [0 - 255].
*
* Called by hslToHex by default.
*
* Calls:
* degPercPercToHsl
* hueToRgb
*
* #param Number h The hue value
* #param Number s The saturation level
* #param Number l The luminence
* #return Array The RGB representation
*/
function hslToRgb($h, $s, $l)
{
//convert the hue's 360 degrees in a circle to 1
$h /= 360;
//convert the saturation and lightness to the 0-1
//range by multiplying by 100
$s /= 100;
$l /= 100;
//If there's no saturation, the color is a greyscale,
//so all three RGB values can be set to the lightness.
//(Hue doesn't matter, because it's grey, not color)
if ($s == 0) {
$r = $l * 255;
$g = $l * 255;
$b = $l * 255;
} else {
//calculate some temperary variables to make the
//calculation eaisier.
if ($l < 0.5) {
$temp2 = $l * (1 + $s);
} else {
$temp2 = ($l + $s) - ($s * $l);
}
$temp1 = 2 * $l - $temp2;
//run the calculated vars through hueToRgb to
//calculate the RGB value. Note that for the Red
//value, we add a third (120 degrees), to adjust
//the hue to the correct section of the circle for
//red. Simalarly, for blue, we subtract 1/3.
$r = 255 * $this->hueToRgb($temp1, $temp2, $h + (1 / 3));
$g = 255 * $this->hueToRgb($temp1, $temp2, $h);
$b = 255 * $this->hueToRgb($temp1, $temp2, $h - (1 / 3));
}
$rgb['r'] = $r;
$rgb['g'] = $g;
$rgb['b'] = $b;
return $rgb;
// return "rgb($r, $g, $b)";
}
/**
* Converts an HSL hue to it's RGB value.
*
* Input: $temp1 and $temp2 - temperary vars based on
* whether the lumanence is less than 0.5, and
* calculated using the saturation and luminence
* values.
* $hue - the hue (to be converted to an RGB
* value) For red, add 1/3 to the hue, green
* leave it alone, and blue you subtract 1/3
* from the hue.
*
* Output: One RGB value.
*
* Thanks to Easy RGB for this function (Hue_2_RGB).
* http://www.easyrgb.com/index.php?X=MATH&$h=19#text19
*
*/
function hueToRgb($temp1, $temp2, $hue)
{
if ($hue < 0) {
$hue += 1;
}
if ($hue > 1) {
$hue -= 1;
}
if ((6 * $hue) < 1) {
return ($temp1 + ($temp2 - $temp1) * 6 * $hue);
} elseif ((2 * $hue) < 1) {
return $temp2;
} elseif ((3 * $hue) < 2) {
return ($temp1 + ($temp2 - $temp1) * ((2 / 3) - $hue) * 6);
}
return $temp1;
}
If you have decimal RGB values (enhzflep showed how to get them), you can easily get the #ab01cd hex web string:
$rgb['r'] = ($t = round($rgb['r'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['g'] = ($t = round($rgb['g'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['b'] = ($t = round($rgb['b'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$hexweb = "#".$rgb['r'].$rgb['g'].$rgb['b'];
I have a database table of UK postcodes that has 4 fields:
postcode,
east,
north,
pqi
Example values: ST1 6BQ, 388605, 349057,10
The primary key is postcodes and east and north are bothint(11)` fields.
Does anybody have an efficient MySQL query that will return the nearest 5 postcodes to a given post?
I have seen many examples using long and lat, but not northing and easting.
Convert your northings/eastings to lat/long, remembering that The OS grid is based on OSGB36 rather than WGS84.
I use the following class:
<?php
namespace OSGB36;
use \Geodetic\Datum;
class Converter
{
private $_osRef;
private $_fromDatum;
private $_toDatum;
public function __construct()
{
$this->_osRef = new OSRef();
$this->_fromDatum = new Datum(Datum::OSGB36);
$this->_toDatum = new Datum(Datum::WGS84);
}
/**
* Converts easting/northing into lat/long
*
* #param integer $eastings
* #param integer $northings
* #return \Geodetic\LatLong
*/
public function calculateLatLong($eastings, $northings)
{
$this->_osRef->setNorthings($northings)
->setEastings($eastings);
$OSGB36LatLong = $this->_osRef->toLatLong(
$this->_fromDatum->getReferenceEllipsoid()
);
$ecef = $OSGB36LatLong->toECEF($this->_fromDatum);
$ecef->toWGS84($this->_fromDatum);
$WGS84LatLong = $ecef->toLatLong($this->_toDatum);
return $WGS84LatLong;
}
}
and
<?php
namespace OSGB36;
use \Geodetic\LatLong\CoordinateValues;
class OSRef
{
private $_northings;
private $_eastings;
public function __construct($northings = NULL, $eastings = NULL)
{
$this->_northings = $northings;
$this->_eastings = $eastings;
}
public function setNorthings($northings)
{
$this->_northings = $northings;
return $this;
}
public function setEastings($eastings)
{
$this->_eastings = $eastings;
return $this;
}
private function _sinSquared($x) {
return sin($x) * sin($x);
}
private function _tanSquared($x) {
return tan($x) * tan($x);
}
private function _secant($x) {
return 1.0 / cos($x);
}
private function _cosecant($x) {
return 1.0 / sin($x);
}
private function _cotangent($x) {
return 1.0 / tan($x);
}
public function toLatLong(\Geodetic\ReferenceEllipsoid $airy1830) {
$OSGB_F0 = 0.9996012717; // Central Meridan Scale factor
$N0 = -100000.0; // True origin Northing
$E0 = 400000.0; // True origin Easting
$phi0 = deg2rad(49.0); // True origin Latitude
$lambda0 = deg2rad(-2.0); // True origin Longitude
$semiMajorAxis = $airy1830->getSemiMajorAxis();
$semiMinorAxis = $airy1830->getSemiMinorAxis();
$eSquared = $airy1830->getFirstEccentricitySquared();
$easting = $this->_eastings - $E0;
$northing = $this->_northings - $N0;
$n = ($semiMajorAxis - $semiMinorAxis) / ($semiMajorAxis + $semiMinorAxis);
$M = 0.0;
$phiPrime = ($northing / ($semiMajorAxis * $OSGB_F0)) + $phi0;
do {
$M = ($semiMinorAxis * $OSGB_F0) *
(((1 + $n + ((5.0 / 4.0) * $n * $n) + ((5.0 / 4.0) * $n * $n * $n)) *
($phiPrime - $phi0)) -
(((3 * $n) + (3 * $n * $n) + ((21.0 / 8.0) * $n * $n * $n)) *
sin($phiPrime - $phi0) *
cos($phiPrime + $phi0)) +
((((15.0 / 8.0) * $n * $n) + ((15.0 / 8.0) * $n * $n * $n)) *
sin(2.0 * ($phiPrime - $phi0)) *
cos(2.0 * ($phiPrime + $phi0))) -
(((35.0 / 24.0) * $n * $n * $n) *
sin(3.0 * ($phiPrime - $phi0)) *
cos(3.0 * ($phiPrime + $phi0))));
$phiPrime += ($northing - $M) / ($semiMajorAxis * $OSGB_F0);
} while (($northing - $M) >= 0.001);
$v = $semiMajorAxis * $OSGB_F0 * pow(1.0 - $eSquared * $this->_sinSquared($phiPrime), -0.5);
$rho = $semiMajorAxis * $OSGB_F0 * (1.0 - $eSquared) *
pow(1.0 - $eSquared * $this->_sinSquared($phiPrime), -1.5);
$etaSquared = ($v / $rho) - 1.0;
$VII = tan($phiPrime) / (2 * $rho * $v);
$VIII = (tan($phiPrime) / (24.0 * $rho * pow($v, 3.0))) *
(5.0 + (3.0 * $this->_tanSquared($phiPrime)) + $etaSquared - (9.0 * $this->_tanSquared($phiPrime) * $etaSquared));
$IX = (tan($phiPrime) / (720.0 * $rho * pow($v, 5.0))) *
(61.0 + (90.0 * $this->_tanSquared($phiPrime)) + (45.0 * $this->_tanSquared($phiPrime) * $this->_tanSquared($phiPrime)));
$X = $this->_secant($phiPrime) / $v;
$XI = ($this->_secant($phiPrime) / (6.0 * $v * $v * $v)) * (($v / $rho) + (2 * $this->_tanSquared($phiPrime)));
$XII = ($this->_secant($phiPrime) / (120.0 * pow($v, 5.0))) *
(5.0 + (28.0 * $this->_tanSquared($phiPrime)) + (24.0 * $this->_tanSquared($phiPrime) * $this->_tanSquared($phiPrime)));
$XIIA = ($this->_secant($phiPrime) / (5040.0 * pow($v, 7.0))) *
(61.0 + (662.0 * $this->_tanSquared($phiPrime)) + (1320.0 * $this->_tanSquared($phiPrime) * $this->_tanSquared($phiPrime)) +
(720.0 * $this->_tanSquared($phiPrime) * $this->_tanSquared($phiPrime) * $this->_tanSquared($phiPrime)));
$phi = $phiPrime - ($VII * pow($easting, 2.0)) + ($VIII * pow($easting, 4.0)) - ($IX * pow($easting, 6.0));
$lambda = $lambda0 +
($X * $easting) - ($XI * pow($easting, 3.0)) + ($XII * pow($easting, 5.0)) - ($XIIA * pow($easting, 7.0));
$latLongCoordinates = new CoordinateValues(
$phi,
$lambda,
\Geodetic\Angle::RADIANS,
0.0,
\Geodetic\Distance::METRES
);
return new \Geodetic\LatLong($latLongCoordinates);
}
function toGridRef()
{
$hundredkmE = floor($this->_eastings / 100000);
$hundredkmN = floor($this->_northings / 100000);
$firstLetter = "";
if ($hundredkmN < 5) {
if ($hundredkmE < 5) {
$firstLetter = "S";
} else {
$firstLetter = "T";
}
} else if ($hundredkmN < 10) {
if ($hundredkmE < 5) {
$firstLetter = "N";
} else {
$firstLetter = "O";
}
} else {
$firstLetter = "H";
}
$secondLetter = "";
$index = 65 + ((4 - ($hundredkmN % 5)) * 5) + ($hundredkmE % 5);
$ti = $index;
if ($index >= 73)
{
$index++;
}
$secondLetter = chr($index);
$e = round(($this->_eastings - (100000 * $hundredkmE)) / 100);
$n = round(($this->_northings - (100000 * $hundredkmN)) / 100);
return sprintf("%s%s%03d%03d", $firstLetter, $secondLetter, $e, $n);
}
public static function createOSRefFromGridRef($gridRef)
{
$char1 = substr($gridRef, 0, 1);
$char2 = substr($gridRef, 1, 1);
$east = substr($gridRef, 2, 3) * 100;
$north = substr($gridRef, 5, 3) * 100;
if ($char1 == 'H') {
$north += 1000000;
} else if ($char1 == 'N') {
$north += 500000;
} else if ($char1 == 'O') {
$north += 500000;
$east += 500000;
} else if ($char1 == 'T') {
$east += 500000;
}
$char2ord = ord($char2);
if ($char2ord > 73)
{
$char2ord--; // Adjust for no I
}
$nx = (($char2ord - 65) % 5) * 100000;
$ny = (4 - floor(($char2ord - 65) / 5)) * 100000;
return new OSRef($north + $ny, $east + $nx);
}
}
combined with my Geodetic library.
It's pretty quick chundering through the entirety of CodePoint open to give me a database of postcode details including WGS84 lat/long coordinates for use with OpenStreetMap.