1: <?php
2: include 'PieChart.php';
3:
4: 5: 6: 7: 8: 9: 10: 11:
12: class PieChartImagick extends PieChart {
13: public function destroy() {
14: $this->canvas->destroy();
15: }
16:
17: public function draw() {
18: $this->canvas = new Imagick;
19: $this->canvas->newImage($this->width, $this->height, $this->backgroundColor->toHex());
20:
21: $total = 0;
22: $sliceStart = -90;
23:
24: $titleHeight = $this->_drawTitle();
25: $legendWidth = $this->_drawLegend($titleHeight);
26:
27:
28: $pieCenterX = ($this->width - $legendWidth) / 2;
29:
30:
31: $pieCenterY = $titleHeight + ($this->height - $titleHeight) / 2;
32:
33:
34: $pieDiameter = round(
35: min($this->width - $legendWidth, $this->height - $titleHeight) * 0.85
36: );
37:
38: $pieRadius = $pieDiameter / 2;
39:
40: foreach ($this->slices as $slice)
41: $total += $slice['value'];
42:
43:
44: foreach ($this->slices as &$slice) {
45: $sliceWidth = 360 * $slice['value'] / $total;
46:
47:
48: if ($sliceWidth == 0) continue;
49:
50: $sliceEnd = $sliceStart + $sliceWidth;
51:
52: $this->_drawSlice(
53: array('x' => $pieCenterX, 'y' => $pieCenterY),
54: $pieRadius,
55: $sliceStart,
56: $sliceEnd,
57: $slice['color']
58: );
59:
60:
61: $sliceStart = $sliceEnd;
62: }
63: }
64:
65: protected function _output($method, $format, $filename) {
66: switch ($format) {
67: case parent::FORMAT_GIF:
68: $this->canvas->setImageFormat('gif');
69:
70: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
71: return print $this->canvas;
72: }
73: else if ($method == parent::OUTPUT_SAVE) {
74: return $this->canvas->writeImage($filename);
75: }
76: break;
77:
78: case parent::FORMAT_JPEG:
79: $this->canvas->setImageFormat('jpeg');
80: $this->canvas->setImageCompression(imagick::COMPRESSION_JPEG);
81: $this->canvas->setImageCompressionQuality($this->quality);
82:
83: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
84: return print $this->canvas;
85: }
86: else if ($method == parent::OUTPUT_SAVE) {
87: return $this->canvas->writeImage($filename);
88: }
89: break;
90:
91: case parent::FORMAT_PNG:
92: $this->canvas->setImageFormat('png');
93:
94: if ($method == parent::OUTPUT_INLINE || $method == parent::OUTPUT_DOWNLOAD) {
95: return print $this->canvas;
96: }
97: else if ($method == parent::OUTPUT_SAVE) {
98: return $this->canvas->writeImage($filename);
99: }
100: break;
101: }
102:
103: return false;
104: }
105:
106: 107: 108: 109: 110:
111: protected function _drawLegend($legendOffset) {
112: if (!$this->hasLegend)
113: return 0;
114:
115:
116: $legendFontSize = $this->width * 0.0325;
117:
118:
119: if (ceil($legendFontSize) < 8)
120: return 0;
121:
122:
123: $squareSize = $this->height * 0.060;
124: $squarePadding = $this->height * 0.025;
125: $labelPadding = $this->height * 0.025;
126:
127: $sliceCount = count($this->slices);
128:
129: $legendPadding = 0.075 * $this->width;
130:
131:
132: $maxLabelWidth = $this->_maxLabelWidth($legendFontSize);
133:
134:
135: $legendWidth = $squareSize + $labelPadding + $maxLabelWidth;
136: $legendHeight = $sliceCount * ($squareSize + $squarePadding) - $squarePadding;
137:
138:
139: if ($legendWidth + $legendPadding * 2 > $this->width / 2)
140: return 0;
141:
142: if ($legendHeight > $this->height - $legendOffset - $legendPadding * 2)
143: return 0;
144:
145: $legendX = $this->width - $legendWidth - $legendPadding;
146: $legendY = ($this->height - $legendOffset) / 2 + $legendOffset - $legendHeight / 2;
147:
148: $labelSettings = new ImagickDraw;
149:
150: $labelSettings->setFont($this->legendFont);
151: $labelSettings->setFillColor($this->textColor->toHex());
152: $labelSettings->setFontSize($legendFontSize);
153: $labelSettings->setGravity(Imagick::GRAVITY_NORTHWEST);
154:
155: $i = 0;
156: foreach ($this->slices as $sliceName => $slice) {
157:
158: $OffsetY = $i++ * ($squareSize + $squarePadding);
159:
160: $keyPosX = $legendX;
161: $keyPosY = $legendY + $OffsetY;
162:
163:
164: $keySquare = new ImagickDraw;
165: $keySquare->setFillColor($slice['color']->toHex());
166: $keySquare->rectangle(
167: $keyPosX,
168: $keyPosY,
169: $keyPosX + $squareSize,
170: $keyPosY + $squareSize
171: );
172:
173: $this->canvas->drawImage($keySquare);
174:
175:
176: $labelMetrics = $this->canvas->queryFontMetrics($labelSettings, $sliceName);
177:
178: $this->canvas->annotateImage(
179: $labelSettings,
180: $keyPosX + $squareSize + $squarePadding,
181: $keyPosY + $labelMetrics['descender'] / 2,
182: 0,
183: $sliceName
184: );
185: }
186:
187: return $legendWidth + $legendPadding;
188: }
189:
190: protected function _drawSlice($center, $radius, $start, $end, $color){
191:
192: $radius -= 1;
193: $center['x'] -= 1;
194: $center['y'] -= 1;
195:
196:
197: $arc = new ImagickDraw;
198: $color = $color->toHex();
199:
200: $arc->setFillColor($color);
201: $arc->setStrokeColor($color);
202: $arc->setStrokeWidth(1);
203:
204: $arc->arc(
205: $center['x'] - $radius,
206: $center['y'] - $radius,
207: $center['x'] + $radius,
208: $center['y'] + $radius,
209: $start,
210: $end
211: );
212:
213: $this->canvas->drawImage($arc);
214:
215:
216: $startRadians = deg2rad($start);
217: $endRadians = deg2rad($end);
218:
219:
220: $startPosition = array(
221: 'x' => $center['x'] + $radius * cos($startRadians),
222: 'y' => $center['y'] + $radius * sin($startRadians)
223: );
224:
225:
226: $endPosition = array(
227: 'x' => $center['x'] + $radius * cos($endRadians),
228: 'y' => $center['y'] + $radius * sin($endRadians)
229: );
230:
231: $trianglePoints = array($startPosition, $endPosition, $center);
232:
233: $triangle = new ImagickDraw;
234:
235: $triangle->setFillColor($color);
236: $triangle->setStrokeColor($color);
237: $triangle->setStrokeWidth(1);
238:
239: $triangle->polygon($trianglePoints);
240:
241: $this->canvas->drawImage($triangle);
242: }
243:
244: 245: 246: 247:
248: protected function _maxLabelWidth($fontSize) {
249: $labelSettings = new ImagickDraw;
250: $labelSettings->setFontSize($fontSize);
251: $widestLabelWidth = 0;
252:
253: foreach ($this->slices as $sliceName => $slice) {
254:
255: $labelMetrics = $this->canvas->queryFontMetrics($labelSettings, $sliceName);
256: $labelWidth = $labelMetrics['textWidth'];
257:
258: if ($labelWidth > $widestLabelWidth)
259: $widestLabelWidth = $labelWidth;
260: }
261:
262: return $widestLabelWidth;
263: }
264:
265: 266: 267: 268: 269:
270: protected function _drawTitle() {
271: if (!$this->title) return 0;
272:
273: $titleSettings = new ImagickDraw;
274:
275: $titleSettings->setFont($this->titleFont);
276: $titleSettings->setFillColor($this->textColor->toHex());
277: $titleSettings->setGravity(Imagick::GRAVITY_NORTH);
278:
279:
280: $titleSize = 0.08 * $this->height;
281: $minTitleSize = 10;
282:
283: do {
284: $titleSettings->setFontSize($titleSize);
285:
286:
287: $titleBBox = $this->canvas->queryFontMetrics($titleSettings, $this->title);
288: $titleWidth = $titleBBox['textWidth'];
289:
290:
291: if ($titleWidth <= ($this->width * 0.9))
292: break;
293:
294: $titleSize -= 0.5;
295: } while ($titleSize >= $minTitleSize);
296:
297: $titleHeight = $titleBBox['textHeight'];
298:
299:
300: if ($titleSize < $minTitleSize) return 0;
301:
302:
303: $titleTopPadding = 0.075 * $this->height;
304:
305:
306: $this->canvas->annotateImage($titleSettings, 0, $titleTopPadding, 0, $this->title);
307:
308: return $titleHeight + $titleTopPadding;
309: }
310: }