The result is little crude but its a good starting point to add more designs and make it prettier. This is the effect:
To get numbers around the circle like that you will need to know maximum number and number of divisions, or how many numbers you want showing around the semicircle. For the arrow, need to know where the arrow should fall on the curve. Also need to know the dimensions of the image. I will use a rectangle 150 x 100 with the middle of the speedometer and arrow origin at around bottom center. Lines and text will be black. Don’t forget about a font file and font size. These are the variables to hold the data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// image dimensions $imageWidth = 150; $imageHeight = 100; // font file $font = 'arial.ttf'; // maximum number $max = 25; // current number $current = 10; // how many numbers to be displayed $divisions = 6; // speedometer semicircle center $arcCenterX = 70; $arcCenterY = 90; |
With that lets begin creating the image!
01Set up the image itself, and allocate colors:
18 19 20 21 22 23 24 25 |
// create image $image = imagecreate($imageWidth, $imageHeight); // make line appear smoother imageantialias($image, true); // allocate colors $black = imagecolorallocate($image, 0, 0, 0); $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); |
02Draw an arc. Its a semicircle so it will start at 180 degrees and finish at 360, since PHP counts angles clockwise from right most part of the circle. PHP can draw arc of any shape so for it to resemble a circle it needs to have both width and height 100. Its position is determined from the variables listed in the introduction.
26 |
imagearc($image, $arcCenterX, $arcCenterY, 100, 100, 180, 360, $black);
|
Note, that PHP’s default arc is not smooth. For better results use a library from Mierendo.com.
This is what it should look like:

03To calculate positions of the numbers and arrow will use some trigonometry. PHP’s trigonometrical functions work in radians, so the rest of calculations and variables will store angles in radians. Here, we allocate how many radians does each one division takes (remember, semicircle is 180 degrees or pi radians):
27 |
$oneDivision = pi() / $max;
|
04The number to be displayed in first division is easy enough to calculate…
28 |
$firstNum = round($max / $divisions);
|
05…but for rest, need to multiply that first number by the current division until resultant number is equal to or above maximum. In this example, maximum number will be put on manually at the end so to avoid overlap will include a condition that the current division number is not equal to maximum. Begin the loop with
29 30 31 |
for($i = 1; $i < $divisions; $i++){ $num = $firstNum * $i; if ($num != $max) { |
06Pixel coordinates are used to position the text on the image. To find the position along the arc where the text should go for the current division, need to know the angle and then use trig functions to find the exact coordinates.
32 |
$numAngle = $num * $oneDivision;
|
Its a bit hard to visualize, so think of a triangle like this:

To find the x and y coordinates remember your right triangle relations: use cos(angle) * hypotenuse for x and sin(angle) * hypotenuse for y. From that image, we are basically trying to find the length of the base. However, this will calculate x,y from the speedometer center’s point of view but PHP counts from the image edges. The actual x position then becomes the center of the circle minus the length of the base of that triangle. Same goes for y position. The whole thing then becomes:
07If you just leave the coordinates like that then you would eventually come up with something like this:

Numbers look rather silly like that. What needs to be done is to rotate them so that they curve more naturally around the curve. To find out how much degrees they need to be rotated, need to find the exact tangent at the point where the number should go, take arctan of that which will give radians that we convert into degrees. How do we find tangent at a point? Need to come up with a general expression that will help us do that at any position on the curve. Here, we are going to call calculus for help.In this example diameter of the circle that this semicircle was originated from is 100, hence the radius is 50. Recall that equation for any circle is (x-a)^2 + (y-b)^2 = r^2 where a and b are coordinates of the center and r is radius so equation of this circle is (x-70)^2 + (y-90)^2 = 50^2. To find tangent at any point need to implicitly differentiate that:
and thats the formula we will use. So plugging the current x and y coordinates into that, take arctan and convert to degrees…
… will find the angle needed to rotate the text to make it flow along with the curve.
37 |
imagettftext($image, $fontSize, $angle, $x, $y, $black, $font, $num);
|
and close the loop
38 39 |
} } |
And yes, it is necessary that you know how to do this because you are going to have to adjust all the values depending on the size of your image.
08putting total number at the edge of the circle is just a matter of
40 |
imagettftext($image, $fontSize, 0, 123, $arcCenterY, $black, $font, $max);
|
Rotate it however you want it to fit best. So far we have:
09 Now for the arrow. It will start at the center of the semicircle which we already have. To find its point on the arch, have to go back to those triangle relations again. The principle is exactly the same as when we did it in a loop for each division, only now its done for variable $current:
And now we draw it:
43 |
imageline($image, $arcCenterX, $arcCenterY, $x, $y, $black);
|
10To finish it off generate the image and send an image header:
And there you have it, a speedometer style meter!
| < Prev |
|---|
