diff --git a/SVGChartBuilder.php b/SVGChartBuilder.php index ec8a007..de89a8d 100644 --- a/SVGChartBuilder.php +++ b/SVGChartBuilder.php @@ -8,20 +8,6 @@ class SVGChartBuilder { $yMin = $xMin = INF; $yMax = $xMax = -INF; - function median($arr) { - sort($arr); - $count = count($arr); //total numbers in array - $middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value - if($count % 2) { // odd number, middle is the median - $median = $arr[$middleval]; - } else { // even number, calculate avg of 2 medians - $low = $arr[$middleval]; - $high = $arr[$middleval+1]; - $median = (($low+$high)/2); - } - return $median; - } - foreach ($chartData as $thisX => $thisY) { if (!is_null($previousValue)) { $absoluteDeltas[] = abs($thisY - $previousValue); @@ -43,7 +29,7 @@ class SVGChartBuilder { $xMin = key($chartData); $xRange = $xMax - $xMin; $count = count($chartData); - $medianDelta = abs(median($absoluteDeltas)); + $averageDelta = abs(array_sum($absoluteDeltas)/$count); /* We want the height of the median y-delta to be the same as @@ -51,15 +37,11 @@ class SVGChartBuilder { 45 degrees. This improves comprehension. http://vis4.net/blog/posts/doing-the-line-charts-right/ */ - $aspectRatio = $yRange / $medianDelta / $count; + $aspectRatio = max(0.25, $yRange / $averageDelta / $count); $height = floor($aspectRatio * $width); - function labelFormat($float, $sigFigs = 4, $minPlaces = 1) { - return number_format($float, max( - $minPlaces, - $sigFigs + floor(-log($float, 10))) - /* this floor(log) thing is the first significant place value */ - ); + function labelFormat($float, $sigFigs, $minPlaces = 0) { + return number_format($float, max($minPlaces, $sigFigs)); } /* Transform data coords to chart coords */ @@ -75,26 +57,42 @@ class SVGChartBuilder { , 2); } + function getPrecision($value) { // thanks http://stackoverflow.com/a/21788335/5402566 + if (!is_numeric($value)) { return false; } + $decimal = $value - floor($value); //get the decimal portion of the number + if ($decimal == 0) { return 0; } //if it's a whole number + $precision = strlen(trim(number_format($decimal,10),"0")) - 1; //-2 to account for "0." + return $precision; + } + $chartPoints = "M"; foreach ($chartData as $x => $y) { $chartPoints .= transformX($x, $xMin, $xRange, $width) . ',' . transformY($y, $yMax, $yRange, $height) . ' '; } - $numLabels = floor($height / 25); - $labelModulation = 10 ** (1 + floor(-log($yRange / $numLabels, 10)));// / 5; - $labelInterval = ceil($yRange / $numLabels * $labelModulation) / $labelModulation; + $numLabels = min(4, ceil($height / 40)); + $labelInterval = $yRange / $numLabels; + $labelModulation = 10 ** (1 + floor(-log($yRange / $numLabels, 10))); +// if (fmod($labelInterval, $labelModulation / 5) < $labelInterval * 0.5) { + $labelModulation /= 2.5; +// } else if (fmod($labelInterval, $labelModulation / 2) < $labelInterval * 0.25) { +// $labelModulation /= 2; +// } +// var_dump($labelInterval, $labelModulation, $labelInterval * $labelModulation, ceil($labelInterval * $labelModulation) / $labelModulation); + $labelInterval = ceil($labelInterval * $labelModulation) / $labelModulation; + $labelPlaces = getPrecision($labelInterval); // Top and bottom grid lines $gridLines = - "M0,0 ".$width.",0 - M0,".$height.",".$width.",".$height." + "M10,0 ".$width.",0 + M10,".$height.",".$width.",".$height." "; // Top and bottom grid labels $gridText = - ''.labelFormat($yMax).'' . - ''.labelFormat($yMin).''; + ''.labelFormat($yMax, $labelPlaces + 1).'' . + ''.labelFormat($yMin, $labelPlaces + 1).''; // Start at the first "nice" Y value > min + 50% of the interval // Keep going until max - 50% of the interval @@ -106,12 +104,16 @@ class SVGChartBuilder { ) { $labelHeight = transformY($labelY, $yMax, $yRange, $height); if ( - $labelY < $yMax - 0.4 * $labelInterval && - $labelY > $yMin + 0.4 * $labelInterval + $labelY < $yMax - 0.05 * $labelInterval && + $labelY > $yMin + 0.05 * $labelInterval ) { - $gridLines .= "M0,".$labelHeight." ".$width.",".$labelHeight." - "; - $gridText .= ''.labelFormat($labelY).''; + $gridLines .= " M0,".$labelHeight." ".$width.",".$labelHeight; + } + if ( + $labelY < $yMax - 0.3 * $labelInterval && + $labelY > $yMin + 0.3 * $labelInterval + ) { + $gridText .= ''.labelFormat($labelY, $labelPlaces).''; } }