creating nice numbers - how to deal with numbers < 0 with log10? - php

I put together a function to create "nice numbers" for displaying labels. The function makes use of log10 to normalize odd numbers. Unfortunately, log10 doesn't deal with numbers < 0 but returns NAN.
Now, I have an "ugly" number like -2.36, how to get the nearest nice number, like -2.3or -2.0?
If I convert it to 2.36 in order to put it thru the function and multiply it by -1 afterwards, I'd get -2.4 ---> no go, because it is required that nice-number > ugly-number.
Any ideas?
Links to define nice-numbers etc:
http://wiki.tcl.tk/16640
Algorithm for "nice" grid line intervals on a graph
round to nearest nice number
Here's my code:
function niceNum($range, $round) {
// $exponent: exponent of range
// $fraction: fractional part of range
// $niceFraction: nice, rounded fraction
if ($range==0) return 0;
$exponent = floor(log10($range));
$fraction = $range / pow(10, $exponent);
if ($round) {
if ($fraction < 1.5)
$niceFraction = 1;
elseif ($fraction < 3)
$niceFraction = 2;
elseif ($fraction < 7)
$niceFraction = 5;
else
$niceFraction = 10;
} else {
if ($fraction <= 1)
$niceFraction = 1;
elseif ($fraction <= 2)
$niceFraction = 2;
elseif ($fraction <= 5)
$niceFraction = 5;
else
$niceFraction = 10;
}
return $niceFraction * pow(10, $exponent);
}

Related

How to validate decimal stepping from a starting number in PHP

I need to validate that an inputted number is a valid number based on my stepping rules and round up to the nearest valid number if not. These numbers will change but one example would be:
$min = 0.25;
$step = 0.1
$qty = 0.75 // user input
so these would be valid inputs:
0.75
0.85
0.95
But these should round:
0.76 (to 0.85)
0.80 (to 0.85)
I thought I could use modulus somehow but not getting the calculation correct.
if (($qty % min) / $step == 0)) {
echo "good";
}
I've tried some variations of math that are likely very wrong
$step = 0.1;
$min = 0.25;
$qty = .85;
$h = ($qty / $min) / $step;
echo $h;
$j = mround($qty, $min-$step);
echo $j;
function mround($num, $parts) {
if ($parts <= 0) { $parts = 1; }
$res = $num * (1/$parts);
$res = round($res);
return $res /(1/$parts);
}
I think you can use fmod to do this.
$new = $original + ($step - fmod($original - $minimum, $step));
Example on 3v4l.org

Calculating Longitude Center Over 180th Meridian

I am computing a simple average of latitude/longitude pairs to get the centerpoint (as projected on a flat plane) and I am struggling to develop an algorithm to correctly account for the 180th Meridian. I am developing this in PHP.
I'm computing a simple average, adding up all of the longitudes and dividing by the count. However, in a situation where there is, for example, a -175 and 175 point, it returns an average of 0, whereas I need a value of 180.
Once approach I tried was to add 360 to all negative values, take the average, and then subtract 360. This fixes the problem over the 180th Meridian, but it creates the same issue over the Prime Meridian (0 Meridian).
$neg_lng_flag = false;
//calculate centerpoint
$i = 0;
while($i < $vertice_count) {
$lat_sum += $vertice_obj[$i]->lat;
//if lng is negative, add 360
if($vertice_obj[$i]->lng < 0) {
$lng_sum += $vertice_obj[$i]->lng + 360;
$neg_lng_flag = true;
} else {
$lng_sum += $vertice_obj[$i]->lng;
}
$i++;
}
$avg_lat = round($lat_sum / $vertice_count,2);
$avg_lng = round($lng_sum / $vertice_count,2);
if($neg_lng_flag) {
$avg_lng = $avg_lng - 360;
if($avg_lng < -180) {
$avg_lng = $avg_lng + 180;
}
}
I am not sure what I can do to come up with a consistent algorithm to return a true center longitudal point for all scenarios. I have not been able to find a robust solution either here or through Google more generally. I would appreciate any outside consideration/ideas. Thank you.
I was able to come up with a robust solution for my purposes. In my case, I just needed to calculate the minimum and maximum Longitude coordinates, and if the difference exceeded 180, I knew to shift the negative values by +360, then calculate the average. If the average was greater than 180, I needed to subtract 360, and if it was less than -180 I needed to add 360.
//COMPUTE LONGITUDAL MIDPOINT
function getLngMidpoint($vertice_obj) {
//get min and max vals
$i = 0;
while($i < count($vertice_obj)) {
$min = $vertice_obj[$i]->lng;
$max = $vertice_obj[$i]->lng;
if($vertice_obj[i]->lng > $max) {
$max = $vertice_obj[$i]->lng;
}
if($vertice_obj[$i]->lng < $min) {
$min = $vertice_obj[$i]->lng;
}
$i += 1;
}
$shift = 0;
//check if distance between min and max > 180. If so, need to shift
if(($max - $min) > 180) {
//shift all lng by 180
$shift = 360;
}
$i = 0;
$sum_lng = 0;
while($i < count($vertice_obj)) {
if($vertice_obj[$i] < 0) {
$sum_lng += $vertice_obj[$i]->lng + $shift;
} else {
$sum_lng += $vertice_obj[$i]->lng;
}
$i += 1;
}
$avg_lng = $sum_lng / count($vertice_obj);
if($avg_lng > 180) {
$avg_lng = $avg_lng - 360;
}
if($avg_lng < -180) {
$avg_lng = $avg_lng + 360;
}
return $avg_lng;
}

Determine if coordinate is inside region (MKMapView, solve in PHP)

I'm using MKMapView and I send my php program the visible region (center lat, center lon, span lat, span lon). I need to determine if a coordinate is inside that region using php. I'm hoping there's a standard formula somewhere, but I haven't found one. I'll keep trying to come up with a formula, but it's surprisingly complicated (hopefully not as much as the haversine, which I don't believe I could have figured out myself).
lets try this logic
$topRightLongitude = $centerLongitude + $spanLongitude/2;
if($topRightLongitude > 180 and ($pointLongitude < 0))
$topRightLongitude = $topRightLongitude - 360; // (180*2) - positive becomes negative
$bottomLeftLongitude = $centerLongitude - $spanLongitude/2;
if($bottomLeftLongitude< -180 and ($pointLongitude > 0))
$bottomLeftLongitude= 360 + $bottomLeftLongitude; // now is negative and will become positive
$topRightLatitude = $centerLatitude + $spanLatitude/2;
if($topRightLatitude > 90 and ($pointLatitude < 0))
$topRightLatitude = $topRightLatitude - 180; // (90*2) - positive becomes negative
$bottomLeftLatitude = $centerLatitude - $spanLatitude/2;
if($bottomLeftLatitude< -90 and ($pointLatitude > 0))
$bottomLeftLatitude= 180 + $bottomLeftLongitude; // now is negative and will become positive
if you have
$centerLongitude = 179;
$spanLongitude = 20;
$pointLongitude = -179;
results
$topRightLongitude = -171;
$bottomLeftLongitude = 169;
so your point is in if you test like this:
if($pointLongitude < $topRightLongitude &&
$pointLongitude > $bottomLeftLongitude &&
$pointLatitude < $topRightLatitude &&
$pointLatitude > $bottomLeftLatitude){
echo 'in';
}else{
echo 'out';
}
My Solution
$top = $c_lat + ($d_lat / 2.0);
$bottom = $c_lat - ($d_lat / 2.0);
$left = $c_lon - ($d_lon / 2.0);
$right = $c_lon + ($d_lon / 2.0);
if($left < -180)
{
$second_left = $left + 360.0;
$second_right = 180.0;
$left = -180;
}
elseif($right > 180)
{
$second_right = $right - 360.0;
$second_left = -180.0;
$right = 180.0;
}
$inside = false;
if($t_lat > $bottom && $t_lat < $top && $t_lon > $left && $t_lon < $right)
$inside = true;
else if($second_right && $second_left)
{
if($t_lat > $bottom && $t_lat < $top && $t_lon > $second_left && $t_lon < $second_right)
$inside = true;
}
if($inside)
{
}
This seems to work with MKMapView since the region latitudes are always between -90 and 90.
This logic should work:
if ( ($X > $center_lat - $span_lat/2) &&
($X < $center_lat + $span_lat/2) &&
($Y > $center_lon - $span_lon/2) &&
($Y < $center_lon + $span_lon/2) ) {
echo "It's inside!";
} else {
echo "It's outside ...";
}
I had worked a solution for my own problem before, but for decimal values of coordinates and it works. May be if you can convert deg to decimal it might work.
I have renamed the variable according to your problem.
Here's the logic.
if
(
(
($lat - $spanLat) < $centerLat &&
$centerLat < ($lat+ $spanLat)
) &&
(
($long - $spanLong) < $centerLong &&
$centerLong < ($long + $spanLong)
)
)

PHP - Number of units between two numbers

I'm trying to find the number of units between 2 numbers that are under zero between 0 and a limit and over that limit. Here is my function. It works fine until I have to work with some huge numbers which takes a lot of time to process. I am trying to find a way to execute this code without using a loop.
public function getBetween($num1, $num2) {
$limit = 500000;
$array = array(0,0,0);
if ($num1 >= $num2) {
$low = $num2;
$high = $num1;
} else {
$low = $num1;
$high = $num2;
}
for($i=$low; $i < $high; $i++) {
if ($i < 0) {
$array[0]++;
} elseif ($i >= 0 && $i < $limit) {
$array[1]++;
} else {
$array[2]++;
}
}
return $array;
}
I have started to split my loop into elseif statements but this is getting messy really quick and I will also have to eventually be able to set more than one limit which will become impossible to use.
if ($low < 0 && $high < 0) {
} elseif ($low < 0 && $high >= 0 && $high < $limit) {
} elseif ($low < 0 && $high >= $limit) {
} elseif ($low >= 0 && $low < $limit && $high < 0) {
} elseif ($low >= 0 && $low < $limit && $high >= 0 && $high < $limit) {
} elseif ($low >= 0 && $low < $limit && $high >= $limit) {
} elseif ($low >= $limit && $high < 0) {
} elseif ($low >= $limit && $high >= 0 && $high < $limit) {
} elseif ($low >= $limit && $high >= $limit) {
}
I am trying to find a clean way to do it. Any ideas?
EDIT
Here is an example of the array I'm trying to get.
If my limit was 500, $num1 = -100 and $num2 = 700 i would get the array
$array[0] = 100
$array[1] = 500
$array[2] = 200
I didn't test it (didn't run a PHP script but I tried it "manually" with a few examples).
You still have loops, but only one iteration per limit (instead of one per unit).
// Example datas
$limits = array(0, 500, 800);
$low = -100;
$high = 1000;
$splittedResults = array();
// Get total of units
$totalUnits = abs($high - $low);
$totalCounted = 0;
foreach($limits as $limit) {
if ($low > $limit) {
// Nothing under the limit
$nbUnderLimit = 0;
} elseif($high < $limit) {
// Both values under the limit
$nbUnderLimit = $totalUnits;
} else {
// $low under the limit and $high over it
$nbUnderLimit = abs($limit - $low);
}
// Here we know how much units are under current limit in total.
// We want to know how much are between previous limit and current limit.
// Assuming that limits are sorted ascending, we have to remove already counted units.
$nbBetweenLimits = $nbUnderLimit - $totalCounted;
$splittedResults[] = $nbBetweenLimits;
$totalCounted += $nbBetweenLimits;
}
// Finally, number of units that are over the last limit (the rest)
$splittedResults[] = $totalUnits - $totalCounted;
You could create an array of the numbers with range() and use array_filter
$count = sizeof(array_filter (range(0,800), function($value){ return ($value > 500); }));
And one for < as well etc.
You only need to define range array once, separately.

Calculating the n-th root of an integer using PHP/GMP

How can I calculate the n-th root of an integer using PHP/GMP?
Although I found a function called gmp_root(a, nth) in the PHP source, it seems that this function has not been published in any release yet*: http://3v4l.org/8FjU7
*) 5.6.0alpha2 being the most recent one at the time of writing
Original source: Calculating Nth root with bcmath in PHP – thanks and credits to HamZa!
I've rewritten the code to use GMP instead of BCMath:
function gmp_nth_root($num, $n) {
if ($n < 1) return 0; // we want positive exponents
if ($num <= 0) return 0; // we want positive numbers
if ($num < 2) return 1; // n-th root of 1 or 2 give 1
// g is our guess number
$g = 2;
// while (g^n < num) g=g*2
while (gmp_cmp(gmp_pow($g, $n), $num) < 0) {
$g = gmp_mul($g, 2);
}
// if (g^n==num) num is a power of 2, we're lucky, end of job
if (gmp_cmp(gmp_pow($g, $n), $num) == 0) {
return $g;
}
// if we're here num wasn't a power of 2 :(
$og = $g; // og means original guess and here is our upper bound
$g = gmp_div($g, 2); // g is set to be our lower bound
$step = gmp_div(gmp_sub($og, $g), 2); // step is the half of upper bound - lower bound
$g = gmp_add($g, $step); // we start at lower bound + step , basically in the middle of our interval
// while step != 1
while (gmp_cmp($step, 1) > 0) {
$guess = gmp_pow($g, $n);
$step = gmp_div($step, 2);
$comp = gmp_cmp($guess, $num); // compare our guess with real number
if ($comp < 0) { // if guess is lower we add the new step
$g = gmp_add($g, $step);
} else if ($comp == 1) { // if guess is higher we sub the new step
$g = gmp_sub($g, $step);
} else { // if guess is exactly the num we're done, we return the value
return $g;
}
}
// whatever happened, g is the closest guess we can make so return it
return $g;
}

Categories