[PHP]レーダーチャート(クモの巣グラフ)をGDで描画する

以前の記事で座標の回転を行う関数を作ったので、
それを利用する形で本格的なレーダーチャートを作ります。

rader

<?php
//値
$values = array(6,7,8,10,7,8);

//ラベル
$labels = array("red", "green", "blue", "yellow", "black", "white");

$max         = 10;    //上限
$step        = 2;     //目盛の間隔
$margin	     = 80;    //グラフの余白
$text_margin = 20;    //ラベルの余白
$size        = 240;   //サイズ(正方形)

//フォント
$font = "migmix-1p-regular.ttf";
$font_size = 10;

//画像
$image = imagecreatetruecolor( $size + $margin , $size + $margin);
imageantialias($image, true);

//色
$bg   = imagecolorallocate($image, 10, 10, 10);    // 背景
$line = imagecolorallocate($image, 255, 255, 255); // チャートの線
$grid = imagecolorallocate($image, 50, 50, 50);	   // グリッドの色
$font_color = imagecolorallocate($image, 255, 160, 200);

$center_x = round(($size + $margin) / 2);
$center_y = round(($size + $margin) / 2);
$count = count($values);
$div = round(360 / $count);
$length = round($size / 2);

// 背景の描画
imagefill($image, 0, 0, $bg);
for($i = 1;$i<=$max;$i++){	
	if($i%$step != 0) continue;
	$points = array();
	for($j=0;$j<$count;$j++){
		list($x, $y) = point_rotate($length * ($i / $max), $div * $j - 90);

		$point = array($x + $center_x, $y + $center_y);
		imageline($image, $center_x, $center_y, $point[0], $point[1], $grid);
		$points = array_merge($points, $point);
	}
	imagepolygon($image, $points, $count, $grid);
}


// 文字の描画
for($i = 0;$i<$count;$i++){
	$box = imagettfbbox($font_size, 0, $font, $labels[$i]);
	$text_width = $box[2] - $box[6];
	$text_height = $box[3] - $box[7];

	list($x, $y) = point_rotate($length + $text_margin, $div * $i - 90);
	
	$text_x = (-1 * $text_width / 2) + $center_x + $x;
	$text_y = ($text_height / 2) + $center_y + $y;
	imagettftext($image, $font_size, 0, $text_x, $text_y, $font_color, $font, $labels[$i]);
}

for($i=0;$i<=$max;$i=$i+$step){
	$box = imagettfbbox($font_size, 0, $font, $i);
	$text_width = $box[2] - $box[6];
	$text_height = $box[3] - $box[7];
	
	$text_x = (-1 * $text_width) + $center_x - $font_size;
	$text_y = ($text_height / 2) + $center_y - ($length * ($i / $max));
	imagettftext($image, $font_size, 0, $text_x, $text_y, $grid, $font, $i);
}

// グラフの描画
$points = array();
for($i=0;$i<$count;$i++){
	$value = $length * $values[$i] / $max;
	list($x, $y) = point_rotate($value, $div * $i - 90);
	$point = array($x + $center_x, $y + $center_y);
	$points = array_merge($points, $point);
	
}
imagepolygon($image, $points, $count, $line);

// 画像の出力
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);

function point_rotate($length, $angle){
  $angle = deg2rad($angle);
  $x = round($length * cos($angle));
  $y = round($length * sin($angle));
  return array($x, $y);
}
?>

このプログラムを利用するには同じフォルダにフォントファイルをアップロードしておく必要があります。
今回はフリーフォント MigMix を利用しました。

MigMix フォント
http://mix-mplus-ipa.sourceforge.jp/

このプログラムでは項目数に応じた多角形のレーダーチャートを生成できます。
頂点には項目名が表示され、垂直の軸にはメモリが表示されます。
目盛の間隔(step)を調整することで、10 ごとに目盛線を入れる等の設定ができます。

項目名が長くなる場合はテキストの余白($text_margin)を大きめにとって下さい。

[PHP]中心から角度を指定して線を引く(座標の回転)

360

ある座標から角度を指定して線を引くサンプルです。
線の長さを同じにして角度をすこしずつ変えていくと円のようになります。
時計の針のようなものをイメージするとわかりやすいと思います。

サンプルとして中心から 80 ピクセルの線を 45 度で描画してみます。

45

<?php
//線分の長さ
$r = 80;

//角度
$angle = 45;

//キャンバスサイズ
$width	 = 200;
$height	 = 200;

//キャンバスの中心を原点とする
$origin = array( round($width / 2), round($height / 2) );

$image = imagecreatetruecolor($width, $height);

//線の色
$color = imagecolorallocate( $image, 255, 255, 255 );

list($x, $y) = point_rotate($r, $angle);
imageline($image, $origin[0], $origin[1], $origin[0] + $x, $origin[1] + $y, $color);

header('Content-Type: image/jpeg');
imagejpeg($image);
imagedestroy($image);

function point_rotate($r, $angle){
	$angle = deg2rad($angle);
	$x = round($r * cos($angle));
	$y = round($r * sin($angle));
	return array($x, $y);
}

0 度の時は水平線で、角度が増えるごとに時計回りに回転します。
時計のように 12 時から始める場合は 90 度引いて計算します。

[PHP]マスク画像で写真を好きな形に切り抜く(クリッピングマスク)

result

写真と図形などのマスク画像を組み合わせて写真を自由な形にくり抜くサンプルです。
例ではアルファベットの「A」の形の透過 PNG に合わせて森の写真をはめ込み合成しています。
合成する写真よりもマスク画像が大きくならないように気をつけて下さい。

mask.png
mask

sample.jpg
sample

<?php
$canvas	 = new stdClass();
$image	 = new stdClass();
$mask	 = new stdClass();

$image->image	 = imagecreatefromjpeg('sample.jpg');
$mask->image	 = imagecreatefrompng('mask.png');

$image->width	 = imagesx($image->image);
$image->height	 = imagesy($image->image);

$mask->width	 = imagesx($mask->image);
$mask->height	 = imagesy($mask->image);

$canvas->width	 = $mask->width;
$canvas->height	 = $mask->height;
$canvas->image = imagecreatetruecolor($canvas->width, $canvas->height);

imagealphablending($canvas->image, false);
imagesavealpha($canvas->image, true);
$transparent = imagecolorallocatealpha( $canvas->image, 0, 0, 0, 127 );
imagefill( $canvas->image, 0, 0, $transparent );

//中心から切り抜くための調整
$top	 = round(($image->width - $mask->width) / 2);
$left	 = round(($image->height - $mask->height) / 2);

for($y=0;$y<$canvas->height;$y++){
	for($x=0;$x<$canvas->width;$x++){
		$rgb	 = imagecolorat($mask->image, $x, $y);
		$index	 = imagecolorsforindex($mask->image, $rgb);
		
		$alpha	 = $index['alpha'];
		//$alpha	 = ($index['red'] + $index['green'] + $index['blue']) / 765 * 127 ;

		$current = imagecolorat($image->image, $x + $top, $y + $left);
		$index	 = imagecolorsforindex($image->image, $current);
		$color	 = imagecolorallocatealpha($canvas->image, $index['red'], $index['green'], $index['blue'], $alpha);
		imagesetpixel($canvas->image, $x, $y, $color);
	}
}
header("Content-type: image/png");
imagepng($canvas->image);

imagedestroy($canvas->image);
imagedestroy($image->image);
imagedestroy($mask->image);

今回画像データの管理に stdClass() を用いましたが、PHP のバージョンが古い場合は連想配列を使って書きなおして下さい。
マスクの透明部分をもとに画像を切り抜くので、透明度を徐々に変化させてグラデーション状にすると切り抜かれる画像にもグラデーションがかかります。

・透明度のグラデーションを利用したマスク
gradient

grad_result

例では透過 PNG の不透明な部分に写真を合成していますが、色の黒い部分に画像を合成する場合は下記のように変更します。

//PNGの透明度を利用する方法
//$alpha	 = $index['alpha'];

//色の黒さを利用する方法
$alpha	 = ($index['red'] + $index['green'] + $index['blue']) / 765 * 127 ;

最初のサンプルでは文字の形に背景を切り出しましたが、逆に背景から文字部分を透明にするには、「$alpha = 127 – $alpha;」を付け足して反転させます。

$alpha	 = $index['alpha'];
$alpha   = 127 - $alpha;

result2


写真素材 足成
http://www.ashinari.com/