diff options
Diffstat (limited to 'plugins/ImageGraph/StaticGraph/GridGraph.php')
-rw-r--r-- | plugins/ImageGraph/StaticGraph/GridGraph.php | 851 |
1 files changed, 405 insertions, 446 deletions
diff --git a/plugins/ImageGraph/StaticGraph/GridGraph.php b/plugins/ImageGraph/StaticGraph/GridGraph.php index 8e150054c5..dd70094106 100644 --- a/plugins/ImageGraph/StaticGraph/GridGraph.php +++ b/plugins/ImageGraph/StaticGraph/GridGraph.php @@ -16,452 +16,411 @@ */ abstract class Piwik_ImageGraph_StaticGraph_GridGraph extends Piwik_ImageGraph_StaticGraph { - const GRAPHIC_COLOR_KEY = 'GRAPHIC_COLOR'; - const VALUE_COLOR_KEY = 'VALUE_COLOR'; - const GRID_COLOR_KEY = 'GRID_COLOR'; - - const TRUNCATION_TEXT = '...'; - - const DEFAULT_TICK_ALPHA = 20; - const DEFAULT_SERIE_WEIGHT = 0.5; - const LEFT_GRID_MARGIN = 4; - const BOTTOM_GRID_MARGIN = 10; - const TOP_GRID_MARGIN_HORIZONTAL_GRAPH = 1; - const RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH = 4; - const OUTER_TICK_WIDTH = 5; - const INNER_TICK_WIDTH = 0; - const LABEL_SPACE_VERTICAL_GRAPH = 7; - - const HORIZONTAL_LEGEND_TOP_MARGIN = 5; - const HORIZONTAL_LEGEND_LEFT_MARGIN = 10; - const HORIZONTAL_LEGEND_BOTTOM_MARGIN = 10; - const VERTICAL_LEGEND_TOP_MARGIN = 8; - const VERTICAL_LEGEND_LEFT_MARGIN = 6; - const VERTICAL_LEGEND_MAX_WIDTH_PCT = 0.70; - const LEGEND_LINE_BULLET_WIDTH = 14; - const LEGEND_BOX_BULLET_WIDTH = 5; - const LEGEND_BULLET_RIGHT_PADDING = 5; - const LEGEND_ITEM_HORIZONTAL_INTERSTICE = 6; - const LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET = 4; - const LEGEND_SHADOW_OPACITY = 25; - const LEGEND_VERTICAL_SHADOW_PADDING = 3; - const LEGEND_HORIZONTAL_SHADOW_PADDING = 2; - const PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE = 5; - - protected function getDefaultColors() - { - return array( - self::VALUE_COLOR_KEY => '444444', - self::GRID_COLOR_KEY => 'CCCCCC', - self::GRAPHIC_COLOR_KEY . '1' => '5170AE', - self::GRAPHIC_COLOR_KEY . '2' => 'F29007', - self::GRAPHIC_COLOR_KEY . '3' => 'CC3399', - self::GRAPHIC_COLOR_KEY . '4' => '9933CC', - self::GRAPHIC_COLOR_KEY . '5' => '80A033', - self::GRAPHIC_COLOR_KEY . '6' => '246AD2' - ); - } - - protected function initGridChart( - $displayVerticalGridLines, - $bulletType, - $horizontalGraph, - $showTicks, - $verticalLegend - ) - { - $this->initpData(); - - $colorIndex = 1; - foreach($this->ordinateSeries as $column => $data) - { - $this->pData->setSerieWeight($column, self::DEFAULT_SERIE_WEIGHT); - $graphicColor = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; - $this->pData->setPalette($column, $graphicColor); - } - - $this->initpImage(); - - // graph area coordinates - $topLeftXValue = $this->getGridLeftMargin($horizontalGraph, $withLabel = true); - $topLeftYValue = $this->getGridTopMargin($horizontalGraph, $verticalLegend); - $bottomRightXValue = $this->width - $this->getGridRightMargin($horizontalGraph); - $bottomRightYValue = $this->getGraphBottom($horizontalGraph); - - $this->pImage->setGraphArea( - $topLeftXValue, - $topLeftYValue, - $bottomRightXValue, - $bottomRightYValue - ); - - // determine how many labels need to be skipped - $skippedLabels = 0; - if(!$horizontalGraph) - { - list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); - $graphWidth = $bottomRightXValue - $topLeftXValue; - $maxNumOfLabels = floor($graphWidth / ($abscissaMaxWidth + self::LABEL_SPACE_VERTICAL_GRAPH)); - - $abscissaSeriesCount = count($this->abscissaSeries); - if($maxNumOfLabels < $abscissaSeriesCount) - { - for($candidateSkippedLabels = 1 ; $candidateSkippedLabels < $abscissaSeriesCount; $candidateSkippedLabels++) - { - $numberOfSegments = $abscissaSeriesCount / ($candidateSkippedLabels + 1); - $numberOfCompleteSegments = floor($numberOfSegments); - - $numberOfLabels = $numberOfCompleteSegments; - if($numberOfSegments > $numberOfCompleteSegments) - { - $numberOfLabels++; - } - - if($numberOfLabels <= $maxNumOfLabels ) - { - $skippedLabels = $candidateSkippedLabels; - break; - } - } - } - - if($this->forceSkippedLabels - && $skippedLabels - && $skippedLabels < $this->forceSkippedLabels - && $abscissaSeriesCount > $this->forceSkippedLabels + 1 - ) - { - $skippedLabels = $this->forceSkippedLabels; - } - } - - $ordinateAxisLength = - $horizontalGraph ? $bottomRightXValue - $topLeftXValue : $this->getGraphHeight($horizontalGraph, $verticalLegend); - - $maxOrdinateValue = 0; - foreach($this->ordinateSeries as $column => $data) - { - $currentMax = $this->pData->getMax($column); - - if($currentMax > $maxOrdinateValue) - { - $maxOrdinateValue = $currentMax; - } - } - - // rounding top scale value to the next multiple of 10 - if($maxOrdinateValue > 10) - { - $modTen = $maxOrdinateValue % 10; - if($modTen) $maxOrdinateValue += 10 - $modTen; - } - - $gridColor = $this->colors[self::GRID_COLOR_KEY]; - $this->pImage->drawScale( - array( - 'Mode' => SCALE_MODE_MANUAL, - 'GridTicks' => 0, - 'LabelSkip' => $skippedLabels, - 'DrawXLines' => $displayVerticalGridLines, - 'Factors' => array(ceil($maxOrdinateValue / 2)), - 'MinDivHeight' => $ordinateAxisLength / 2, - 'AxisAlpha' => 0, - 'SkippedAxisAlpha' => 0, - 'TickAlpha' => $showTicks ? self::DEFAULT_TICK_ALPHA : 0, - 'InnerTickWidth' => self::INNER_TICK_WIDTH, - 'OuterTickWidth' => self::OUTER_TICK_WIDTH, - 'GridR' => $gridColor['R'], - 'GridG' => $gridColor['G'], - 'GridB' => $gridColor['B'], - 'GridAlpha' => 100, - 'ManualScale' => array( - 0 => array( - 'Min' => 0, - 'Max' => $maxOrdinateValue - ) - ), - 'Pos' => $horizontalGraph ? SCALE_POS_TOPBOTTOM : SCALE_POS_LEFTRIGHT, - ) - ); - - if($this->showLegend) - { - switch($bulletType) - { - case LEGEND_FAMILY_LINE: - $bulletWidth = self::LEGEND_LINE_BULLET_WIDTH; - - // measured using a picture editing software - $iconOffsetAboveLabelSymmetryAxis = -2; - break; - - case LEGEND_FAMILY_BOX: - $bulletWidth = self::LEGEND_BOX_BULLET_WIDTH; - - // measured using a picture editing software - $iconOffsetAboveLabelSymmetryAxis = 3; - break; - } - - // pChart requires two coordinates to draw the legend $legendTopLeftXValue & $legendTopLeftYValue - // $legendTopLeftXValue = legend's left padding - $legendTopLeftXValue = $topLeftXValue + ($verticalLegend ? self::VERTICAL_LEGEND_LEFT_MARGIN : self::HORIZONTAL_LEGEND_LEFT_MARGIN); - - // $legendTopLeftYValue = y coordinate of the top edge of the legend's icons - // Caution : - // - pChart will silently add some value (see $paddingAddedByPChart) to $legendTopLeftYValue depending on multiple criterias - // - pChart will not take into account the size of the text. Setting $legendTopLeftYValue = 0 will crop the legend's labels - // The following section of code determines the value of $legendTopLeftYValue while taking into account the following paremeters : - // - whether legend items have icons - // - whether icons are bigger than the legend's labels - // - how much colored shadow padding is required - list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize(array_values($this->ordinateLogos)); - if($maxLogoHeight >= $this->legendFontSize) - { - $heightOfTextAboveBulletTop = 0; - $paddingCreatedByLogo = $maxLogoHeight - $this->legendFontSize; - $effectiveShadowPadding = $paddingCreatedByLogo < self::LEGEND_VERTICAL_SHADOW_PADDING * 2 ? self::LEGEND_VERTICAL_SHADOW_PADDING - ($paddingCreatedByLogo / 2) : 0; - } - else - { - if($maxLogoHeight) - { - // measured using a picture editing software - $iconOffsetAboveLabelSymmetryAxis = 5; - } - $heightOfTextAboveBulletTop = $this->legendFontSize / 2 - $iconOffsetAboveLabelSymmetryAxis; - $effectiveShadowPadding = self::LEGEND_VERTICAL_SHADOW_PADDING; - } - - $effectiveLegendItemVerticalInterstice = $this->legendFontSize + self::LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET; - $effectiveLegendItemHorizontalInterstice = self::LEGEND_ITEM_HORIZONTAL_INTERSTICE + self::LEGEND_HORIZONTAL_SHADOW_PADDING; - - $legendTopMargin = $verticalLegend ? self::VERTICAL_LEGEND_TOP_MARGIN : self::HORIZONTAL_LEGEND_TOP_MARGIN; - $requiredPaddingAboveItemBullet = $legendTopMargin + $heightOfTextAboveBulletTop + $effectiveShadowPadding; - - $paddingAddedByPChart = 0; - if($verticalLegend) - { - if($maxLogoHeight) - { - // see line 1691 of pDraw.class.php - if($maxLogoHeight < $effectiveLegendItemVerticalInterstice) - { - $paddingAddedByPChart = ($effectiveLegendItemVerticalInterstice / 2) - ($maxLogoHeight / 2); - } - } - else - { - // see line 1711 of pDraw.class.php ($Y+$IconAreaHeight/2) - $paddingAddedByPChart = $effectiveLegendItemVerticalInterstice / 2; - } - } - - $legendTopLeftYValue = $paddingAddedByPChart < $requiredPaddingAboveItemBullet ? $requiredPaddingAboveItemBullet - $paddingAddedByPChart : 0; - - // add colored background to each legend item - if(count($this->ordinateLabels) > 1) - { - $currentPosition = $verticalLegend ? $legendTopMargin : $legendTopLeftXValue; - $colorIndex = 1; - foreach($this->ordinateLabels as $metricCode => &$label) - { - $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; - - $paddedBulletWidth = $bulletWidth; - if(isset($this->ordinateLogos[$metricCode])) - { - $paddedBulletWidth = $maxLogoWidth; - } - $paddedBulletWidth += self::LEGEND_BULLET_RIGHT_PADDING; - - // truncate labels if required - if($verticalLegend) - { - $label = $this->truncateLabel($label, ($this->width * self::VERTICAL_LEGEND_MAX_WIDTH_PCT) - $legendTopLeftXValue - $paddedBulletWidth, $this->legendFontSize); - $this->pData->setSerieDescription($metricCode, $label); - } - - $rectangleTopLeftXValue = ($verticalLegend ? $legendTopLeftXValue : $currentPosition) + $paddedBulletWidth - self::LEGEND_HORIZONTAL_SHADOW_PADDING; - $rectangleTopLeftYValue = $verticalLegend ? $currentPosition : $legendTopMargin; - - list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $this->legendFontSize); - $legendItemWidth = $paddedBulletWidth + $labelWidth + $effectiveLegendItemHorizontalInterstice; - $rectangleBottomRightXValue = $rectangleTopLeftXValue + $labelWidth + (self::LEGEND_HORIZONTAL_SHADOW_PADDING * 2); - - $legendItemHeight = max($maxLogoHeight, $this->legendFontSize) + ($effectiveShadowPadding * 2); - $rectangleBottomRightYValue = $rectangleTopLeftYValue + $legendItemHeight; - - $this->pImage->drawFilledRectangle( - $rectangleTopLeftXValue, - $rectangleTopLeftYValue, - $rectangleBottomRightXValue, - $rectangleBottomRightYValue, - array( - 'Alpha' => self::LEGEND_SHADOW_OPACITY, - 'R' => $color['R'], - 'G' => $color['G'], - 'B' => $color['B'], - ) - ); - - if($verticalLegend) - { - $currentPositionIncrement = max($maxLogoHeight, $effectiveLegendItemVerticalInterstice, $this->legendFontSize) + self::PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE; - } - else - { - $currentPositionIncrement = $legendItemWidth; - } - - $currentPosition += $currentPositionIncrement; - } - } - - // draw legend - $legendColor = $this->colors[self::VALUE_COLOR_KEY]; - $this->pImage->drawLegend( - $legendTopLeftXValue, - $legendTopLeftYValue, - array( - 'Style' => LEGEND_NOBORDER, - 'FontSize' => $this->legendFontSize, - 'BoxWidth' => $bulletWidth, - 'XSpacing' => $effectiveLegendItemHorizontalInterstice, // not effective when vertical - 'Mode' => $verticalLegend ? LEGEND_VERTICAL : LEGEND_HORIZONTAL, - 'BoxHeight' => $verticalLegend ? $effectiveLegendItemVerticalInterstice : null, - 'Family' => $bulletType, - 'FontR' => $legendColor['R'], - 'FontG' => $legendColor['G'], - 'FontB' => $legendColor['B'], - ) - ); - } - } - - protected static function getMaxLogoSize($logoPaths) - { - $maxLogoWidth = 0; - $maxLogoHeight = 0; - foreach($logoPaths as $logoPath) - { - list($logoWidth, $logoHeight) = self::getLogoSize($logoPath); - - if($logoWidth > $maxLogoWidth) - { - $maxLogoWidth = $logoWidth; - } - if($logoHeight > $maxLogoHeight) - { - $maxLogoHeight = $logoHeight; - } - } - - return array($maxLogoWidth, $maxLogoHeight); - } - - protected static function getLogoSize($logoPath) - { - $pathInfo = getimagesize($logoPath); - return array($pathInfo[0], $pathInfo[1]); - } - - protected function getGridLeftMargin($horizontalGraph, $withLabel) - { - $gridLeftMargin = self::LEFT_GRID_MARGIN + self::OUTER_TICK_WIDTH; - - if($withLabel) - { - list($maxTextWidth, $maxTextHeight) = $this->getMaximumTextWidthHeight($horizontalGraph ? $this->abscissaSeries : $this->ordinateSeries); - $gridLeftMargin += $maxTextWidth; - } - - return $gridLeftMargin; - } - - protected function getGridTopMargin($horizontalGraph, $verticalLegend) - { - list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); - - if($horizontalGraph) - { - $topMargin = $ordinateMaxHeight + self::TOP_GRID_MARGIN_HORIZONTAL_GRAPH + self::OUTER_TICK_WIDTH; - } - else - { - $topMargin = $ordinateMaxHeight / 2; - } - - if($this->showLegend && !$verticalLegend) - { - $topMargin += $this->getHorizontalLegendHeight(); - } - - return $topMargin; - } - - private function getHorizontalLegendHeight() - { - list($maxMetricLegendWidth, $maxMetricLegendHeight) = - $this->getMaximumTextWidthHeight(array_values($this->ordinateLabels), $this->legendFontSize); - - return $maxMetricLegendHeight + self::HORIZONTAL_LEGEND_BOTTOM_MARGIN + self::HORIZONTAL_LEGEND_TOP_MARGIN; - } - - protected function getGraphHeight($horizontalGraph, $verticalLegend) - { - return $this->getGraphBottom($horizontalGraph) - $this->getGridTopMargin($horizontalGraph, $verticalLegend); - } - - private function getGridBottomMargin($horizontalGraph) - { - $gridBottomMargin = self::BOTTOM_GRID_MARGIN; - if(!$horizontalGraph) - { - list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); - $gridBottomMargin += $abscissaMaxHeight; - } - return $gridBottomMargin; - } - - protected function getGridRightMargin($horizontalGraph) - { - if($horizontalGraph) - { - // in horizontal graphs, metric values are displayed on the far right of the bar - list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); - return self::RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH + $ordinateMaxWidth; - } - else - { - return 0; - } - } - - protected function getGraphBottom($horizontalGraph) - { - return $this->height - $this->getGridBottomMargin($horizontalGraph); - } - - protected function truncateLabel($label, $labelWidthLimit, $fontSize = false) - { - list($truncationTextWidth, $truncationTextHeight) = $this->getTextWidthHeight(self::TRUNCATION_TEXT, $fontSize); - list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $fontSize); - - if($labelWidth > $labelWidthLimit) - { - $averageCharWidth = $labelWidth / strlen($label); - $charsToKeep = floor(($labelWidthLimit - $truncationTextWidth) / $averageCharWidth); - $label = substr($label, 0, $charsToKeep) . self::TRUNCATION_TEXT; - } - return $label; - } - - // display min & max values - // can not currently be used because pChart's label design is not flexible enough - // e.g: it is not possible to remove the box border & the square icon - // it would require modifying pChart code base which we try to avoid - // see http://dev.piwik.org/trac/ticket/3396 + const GRAPHIC_COLOR_KEY = 'GRAPHIC_COLOR'; + const VALUE_COLOR_KEY = 'VALUE_COLOR'; + const GRID_COLOR_KEY = 'GRID_COLOR'; + + const TRUNCATION_TEXT = '...'; + + const DEFAULT_TICK_ALPHA = 20; + const DEFAULT_SERIE_WEIGHT = 0.5; + const LEFT_GRID_MARGIN = 4; + const BOTTOM_GRID_MARGIN = 10; + const TOP_GRID_MARGIN_HORIZONTAL_GRAPH = 1; + const RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH = 4; + const OUTER_TICK_WIDTH = 5; + const INNER_TICK_WIDTH = 0; + const LABEL_SPACE_VERTICAL_GRAPH = 7; + + const HORIZONTAL_LEGEND_TOP_MARGIN = 5; + const HORIZONTAL_LEGEND_LEFT_MARGIN = 10; + const HORIZONTAL_LEGEND_BOTTOM_MARGIN = 10; + const VERTICAL_LEGEND_TOP_MARGIN = 8; + const VERTICAL_LEGEND_LEFT_MARGIN = 6; + const VERTICAL_LEGEND_MAX_WIDTH_PCT = 0.70; + const LEGEND_LINE_BULLET_WIDTH = 14; + const LEGEND_BOX_BULLET_WIDTH = 5; + const LEGEND_BULLET_RIGHT_PADDING = 5; + const LEGEND_ITEM_HORIZONTAL_INTERSTICE = 6; + const LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET = 4; + const LEGEND_SHADOW_OPACITY = 25; + const LEGEND_VERTICAL_SHADOW_PADDING = 3; + const LEGEND_HORIZONTAL_SHADOW_PADDING = 2; + const PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE = 5; + + protected function getDefaultColors() + { + return array( + self::VALUE_COLOR_KEY => '444444', + self::GRID_COLOR_KEY => 'CCCCCC', + self::GRAPHIC_COLOR_KEY . '1' => '5170AE', + self::GRAPHIC_COLOR_KEY . '2' => 'F29007', + self::GRAPHIC_COLOR_KEY . '3' => 'CC3399', + self::GRAPHIC_COLOR_KEY . '4' => '9933CC', + self::GRAPHIC_COLOR_KEY . '5' => '80A033', + self::GRAPHIC_COLOR_KEY . '6' => '246AD2' + ); + } + + protected function initGridChart( + $displayVerticalGridLines, + $bulletType, + $horizontalGraph, + $showTicks, + $verticalLegend + ) + { + $this->initpData(); + + $colorIndex = 1; + foreach ($this->ordinateSeries as $column => $data) { + $this->pData->setSerieWeight($column, self::DEFAULT_SERIE_WEIGHT); + $graphicColor = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; + $this->pData->setPalette($column, $graphicColor); + } + + $this->initpImage(); + + // graph area coordinates + $topLeftXValue = $this->getGridLeftMargin($horizontalGraph, $withLabel = true); + $topLeftYValue = $this->getGridTopMargin($horizontalGraph, $verticalLegend); + $bottomRightXValue = $this->width - $this->getGridRightMargin($horizontalGraph); + $bottomRightYValue = $this->getGraphBottom($horizontalGraph); + + $this->pImage->setGraphArea( + $topLeftXValue, + $topLeftYValue, + $bottomRightXValue, + $bottomRightYValue + ); + + // determine how many labels need to be skipped + $skippedLabels = 0; + if (!$horizontalGraph) { + list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); + $graphWidth = $bottomRightXValue - $topLeftXValue; + $maxNumOfLabels = floor($graphWidth / ($abscissaMaxWidth + self::LABEL_SPACE_VERTICAL_GRAPH)); + + $abscissaSeriesCount = count($this->abscissaSeries); + if ($maxNumOfLabels < $abscissaSeriesCount) { + for ($candidateSkippedLabels = 1; $candidateSkippedLabels < $abscissaSeriesCount; $candidateSkippedLabels++) { + $numberOfSegments = $abscissaSeriesCount / ($candidateSkippedLabels + 1); + $numberOfCompleteSegments = floor($numberOfSegments); + + $numberOfLabels = $numberOfCompleteSegments; + if ($numberOfSegments > $numberOfCompleteSegments) { + $numberOfLabels++; + } + + if ($numberOfLabels <= $maxNumOfLabels) { + $skippedLabels = $candidateSkippedLabels; + break; + } + } + } + + if ($this->forceSkippedLabels + && $skippedLabels + && $skippedLabels < $this->forceSkippedLabels + && $abscissaSeriesCount > $this->forceSkippedLabels + 1 + ) { + $skippedLabels = $this->forceSkippedLabels; + } + } + + $ordinateAxisLength = + $horizontalGraph ? $bottomRightXValue - $topLeftXValue : $this->getGraphHeight($horizontalGraph, $verticalLegend); + + $maxOrdinateValue = 0; + foreach ($this->ordinateSeries as $column => $data) { + $currentMax = $this->pData->getMax($column); + + if ($currentMax > $maxOrdinateValue) { + $maxOrdinateValue = $currentMax; + } + } + + // rounding top scale value to the next multiple of 10 + if ($maxOrdinateValue > 10) { + $modTen = $maxOrdinateValue % 10; + if ($modTen) $maxOrdinateValue += 10 - $modTen; + } + + $gridColor = $this->colors[self::GRID_COLOR_KEY]; + $this->pImage->drawScale( + array( + 'Mode' => SCALE_MODE_MANUAL, + 'GridTicks' => 0, + 'LabelSkip' => $skippedLabels, + 'DrawXLines' => $displayVerticalGridLines, + 'Factors' => array(ceil($maxOrdinateValue / 2)), + 'MinDivHeight' => $ordinateAxisLength / 2, + 'AxisAlpha' => 0, + 'SkippedAxisAlpha' => 0, + 'TickAlpha' => $showTicks ? self::DEFAULT_TICK_ALPHA : 0, + 'InnerTickWidth' => self::INNER_TICK_WIDTH, + 'OuterTickWidth' => self::OUTER_TICK_WIDTH, + 'GridR' => $gridColor['R'], + 'GridG' => $gridColor['G'], + 'GridB' => $gridColor['B'], + 'GridAlpha' => 100, + 'ManualScale' => array( + 0 => array( + 'Min' => 0, + 'Max' => $maxOrdinateValue + ) + ), + 'Pos' => $horizontalGraph ? SCALE_POS_TOPBOTTOM : SCALE_POS_LEFTRIGHT, + ) + ); + + if ($this->showLegend) { + switch ($bulletType) { + case LEGEND_FAMILY_LINE: + $bulletWidth = self::LEGEND_LINE_BULLET_WIDTH; + + // measured using a picture editing software + $iconOffsetAboveLabelSymmetryAxis = -2; + break; + + case LEGEND_FAMILY_BOX: + $bulletWidth = self::LEGEND_BOX_BULLET_WIDTH; + + // measured using a picture editing software + $iconOffsetAboveLabelSymmetryAxis = 3; + break; + } + + // pChart requires two coordinates to draw the legend $legendTopLeftXValue & $legendTopLeftYValue + // $legendTopLeftXValue = legend's left padding + $legendTopLeftXValue = $topLeftXValue + ($verticalLegend ? self::VERTICAL_LEGEND_LEFT_MARGIN : self::HORIZONTAL_LEGEND_LEFT_MARGIN); + + // $legendTopLeftYValue = y coordinate of the top edge of the legend's icons + // Caution : + // - pChart will silently add some value (see $paddingAddedByPChart) to $legendTopLeftYValue depending on multiple criterias + // - pChart will not take into account the size of the text. Setting $legendTopLeftYValue = 0 will crop the legend's labels + // The following section of code determines the value of $legendTopLeftYValue while taking into account the following paremeters : + // - whether legend items have icons + // - whether icons are bigger than the legend's labels + // - how much colored shadow padding is required + list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize(array_values($this->ordinateLogos)); + if ($maxLogoHeight >= $this->legendFontSize) { + $heightOfTextAboveBulletTop = 0; + $paddingCreatedByLogo = $maxLogoHeight - $this->legendFontSize; + $effectiveShadowPadding = $paddingCreatedByLogo < self::LEGEND_VERTICAL_SHADOW_PADDING * 2 ? self::LEGEND_VERTICAL_SHADOW_PADDING - ($paddingCreatedByLogo / 2) : 0; + } else { + if ($maxLogoHeight) { + // measured using a picture editing software + $iconOffsetAboveLabelSymmetryAxis = 5; + } + $heightOfTextAboveBulletTop = $this->legendFontSize / 2 - $iconOffsetAboveLabelSymmetryAxis; + $effectiveShadowPadding = self::LEGEND_VERTICAL_SHADOW_PADDING; + } + + $effectiveLegendItemVerticalInterstice = $this->legendFontSize + self::LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET; + $effectiveLegendItemHorizontalInterstice = self::LEGEND_ITEM_HORIZONTAL_INTERSTICE + self::LEGEND_HORIZONTAL_SHADOW_PADDING; + + $legendTopMargin = $verticalLegend ? self::VERTICAL_LEGEND_TOP_MARGIN : self::HORIZONTAL_LEGEND_TOP_MARGIN; + $requiredPaddingAboveItemBullet = $legendTopMargin + $heightOfTextAboveBulletTop + $effectiveShadowPadding; + + $paddingAddedByPChart = 0; + if ($verticalLegend) { + if ($maxLogoHeight) { + // see line 1691 of pDraw.class.php + if ($maxLogoHeight < $effectiveLegendItemVerticalInterstice) { + $paddingAddedByPChart = ($effectiveLegendItemVerticalInterstice / 2) - ($maxLogoHeight / 2); + } + } else { + // see line 1711 of pDraw.class.php ($Y+$IconAreaHeight/2) + $paddingAddedByPChart = $effectiveLegendItemVerticalInterstice / 2; + } + } + + $legendTopLeftYValue = $paddingAddedByPChart < $requiredPaddingAboveItemBullet ? $requiredPaddingAboveItemBullet - $paddingAddedByPChart : 0; + + // add colored background to each legend item + if (count($this->ordinateLabels) > 1) { + $currentPosition = $verticalLegend ? $legendTopMargin : $legendTopLeftXValue; + $colorIndex = 1; + foreach ($this->ordinateLabels as $metricCode => &$label) { + $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; + + $paddedBulletWidth = $bulletWidth; + if (isset($this->ordinateLogos[$metricCode])) { + $paddedBulletWidth = $maxLogoWidth; + } + $paddedBulletWidth += self::LEGEND_BULLET_RIGHT_PADDING; + + // truncate labels if required + if ($verticalLegend) { + $label = $this->truncateLabel($label, ($this->width * self::VERTICAL_LEGEND_MAX_WIDTH_PCT) - $legendTopLeftXValue - $paddedBulletWidth, $this->legendFontSize); + $this->pData->setSerieDescription($metricCode, $label); + } + + $rectangleTopLeftXValue = ($verticalLegend ? $legendTopLeftXValue : $currentPosition) + $paddedBulletWidth - self::LEGEND_HORIZONTAL_SHADOW_PADDING; + $rectangleTopLeftYValue = $verticalLegend ? $currentPosition : $legendTopMargin; + + list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $this->legendFontSize); + $legendItemWidth = $paddedBulletWidth + $labelWidth + $effectiveLegendItemHorizontalInterstice; + $rectangleBottomRightXValue = $rectangleTopLeftXValue + $labelWidth + (self::LEGEND_HORIZONTAL_SHADOW_PADDING * 2); + + $legendItemHeight = max($maxLogoHeight, $this->legendFontSize) + ($effectiveShadowPadding * 2); + $rectangleBottomRightYValue = $rectangleTopLeftYValue + $legendItemHeight; + + $this->pImage->drawFilledRectangle( + $rectangleTopLeftXValue, + $rectangleTopLeftYValue, + $rectangleBottomRightXValue, + $rectangleBottomRightYValue, + array( + 'Alpha' => self::LEGEND_SHADOW_OPACITY, + 'R' => $color['R'], + 'G' => $color['G'], + 'B' => $color['B'], + ) + ); + + if ($verticalLegend) { + $currentPositionIncrement = max($maxLogoHeight, $effectiveLegendItemVerticalInterstice, $this->legendFontSize) + self::PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE; + } else { + $currentPositionIncrement = $legendItemWidth; + } + + $currentPosition += $currentPositionIncrement; + } + } + + // draw legend + $legendColor = $this->colors[self::VALUE_COLOR_KEY]; + $this->pImage->drawLegend( + $legendTopLeftXValue, + $legendTopLeftYValue, + array( + 'Style' => LEGEND_NOBORDER, + 'FontSize' => $this->legendFontSize, + 'BoxWidth' => $bulletWidth, + 'XSpacing' => $effectiveLegendItemHorizontalInterstice, // not effective when vertical + 'Mode' => $verticalLegend ? LEGEND_VERTICAL : LEGEND_HORIZONTAL, + 'BoxHeight' => $verticalLegend ? $effectiveLegendItemVerticalInterstice : null, + 'Family' => $bulletType, + 'FontR' => $legendColor['R'], + 'FontG' => $legendColor['G'], + 'FontB' => $legendColor['B'], + ) + ); + } + } + + protected static function getMaxLogoSize($logoPaths) + { + $maxLogoWidth = 0; + $maxLogoHeight = 0; + foreach ($logoPaths as $logoPath) { + list($logoWidth, $logoHeight) = self::getLogoSize($logoPath); + + if ($logoWidth > $maxLogoWidth) { + $maxLogoWidth = $logoWidth; + } + if ($logoHeight > $maxLogoHeight) { + $maxLogoHeight = $logoHeight; + } + } + + return array($maxLogoWidth, $maxLogoHeight); + } + + protected static function getLogoSize($logoPath) + { + $pathInfo = getimagesize($logoPath); + return array($pathInfo[0], $pathInfo[1]); + } + + protected function getGridLeftMargin($horizontalGraph, $withLabel) + { + $gridLeftMargin = self::LEFT_GRID_MARGIN + self::OUTER_TICK_WIDTH; + + if ($withLabel) { + list($maxTextWidth, $maxTextHeight) = $this->getMaximumTextWidthHeight($horizontalGraph ? $this->abscissaSeries : $this->ordinateSeries); + $gridLeftMargin += $maxTextWidth; + } + + return $gridLeftMargin; + } + + protected function getGridTopMargin($horizontalGraph, $verticalLegend) + { + list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); + + if ($horizontalGraph) { + $topMargin = $ordinateMaxHeight + self::TOP_GRID_MARGIN_HORIZONTAL_GRAPH + self::OUTER_TICK_WIDTH; + } else { + $topMargin = $ordinateMaxHeight / 2; + } + + if ($this->showLegend && !$verticalLegend) { + $topMargin += $this->getHorizontalLegendHeight(); + } + + return $topMargin; + } + + private function getHorizontalLegendHeight() + { + list($maxMetricLegendWidth, $maxMetricLegendHeight) = + $this->getMaximumTextWidthHeight(array_values($this->ordinateLabels), $this->legendFontSize); + + return $maxMetricLegendHeight + self::HORIZONTAL_LEGEND_BOTTOM_MARGIN + self::HORIZONTAL_LEGEND_TOP_MARGIN; + } + + protected function getGraphHeight($horizontalGraph, $verticalLegend) + { + return $this->getGraphBottom($horizontalGraph) - $this->getGridTopMargin($horizontalGraph, $verticalLegend); + } + + private function getGridBottomMargin($horizontalGraph) + { + $gridBottomMargin = self::BOTTOM_GRID_MARGIN; + if (!$horizontalGraph) { + list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); + $gridBottomMargin += $abscissaMaxHeight; + } + return $gridBottomMargin; + } + + protected function getGridRightMargin($horizontalGraph) + { + if ($horizontalGraph) { + // in horizontal graphs, metric values are displayed on the far right of the bar + list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); + return self::RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH + $ordinateMaxWidth; + } else { + return 0; + } + } + + protected function getGraphBottom($horizontalGraph) + { + return $this->height - $this->getGridBottomMargin($horizontalGraph); + } + + protected function truncateLabel($label, $labelWidthLimit, $fontSize = false) + { + list($truncationTextWidth, $truncationTextHeight) = $this->getTextWidthHeight(self::TRUNCATION_TEXT, $fontSize); + list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $fontSize); + + if ($labelWidth > $labelWidthLimit) { + $averageCharWidth = $labelWidth / strlen($label); + $charsToKeep = floor(($labelWidthLimit - $truncationTextWidth) / $averageCharWidth); + $label = substr($label, 0, $charsToKeep) . self::TRUNCATION_TEXT; + } + return $label; + } + + // display min & max values + // can not currently be used because pChart's label design is not flexible enough + // e.g: it is not possible to remove the box border & the square icon + // it would require modifying pChart code base which we try to avoid + // see http://dev.piwik.org/trac/ticket/3396 // protected function displayMinMaxValues() // { // if($displayMinMax) |