[PHP]rad2deg関数の使い方を徹底解説!ラジアンから度への変換をマスター

PHP

数学的な計算や三角関数を扱う際、角度の単位としてラジアン(radian)と度(degree)を変換する必要がよくあります。この記事では、PHPのrad2deg関数について、基本的な使い方から実践的な活用方法まで詳しく解説していきます。

rad2deg関数とは?

rad2degは、ラジアン単位の角度を度単位に変換する関数です。三角関数の計算結果を人間にとって分かりやすい度数法で表示したい場合に使用します。

基本的な構文

float rad2deg(float $num)

パラメータ:

  • $num: ラジアン単位の角度

戻り値:

  • 度単位に変換された角度

変換式:

度 = ラジアン × (180 / π)

ラジアンと度の理解

単位の違い

単位説明1周π/2π
ラジアン (rad)半径に対する円弧の長さπ/2π
度 (°)円を360等分360°90°180°

よく使う角度の対応表

ラジアン説明
0基準
30°π/6 ≈ 0.524
45°π/4 ≈ 0.785
60°π/3 ≈ 1.047
90°π/2 ≈ 1.571直角
180°π ≈ 3.142半周
270°3π/2 ≈ 4.712
360°2π ≈ 6.2831周

基本的な使い方

シンプルな変換

<?php
// π ラジアン = 180度
echo rad2deg(M_PI); // 出力: 180

// π/2 ラジアン = 90度
echo rad2deg(M_PI / 2); // 出力: 90

// π/4 ラジアン = 45度
echo rad2deg(M_PI / 4); // 出力: 45

// 2π ラジアン = 360度
echo rad2deg(2 * M_PI); // 出力: 360

// 0 ラジアン = 0度
echo rad2deg(0); // 出力: 0
?>

三角関数の結果を度に変換

<?php
// atan2の結果(ラジアン)を度に変換
$x = 3;
$y = 4;
$angleRad = atan2($y, $x);
$angleDeg = rad2deg($angleRad);

echo "角度(ラジアン): {$angleRad}\n";
echo "角度(度): {$angleDeg}°\n";

// asinの結果を度に変換
$value = 0.5;
$angleRad = asin($value);
$angleDeg = rad2deg($angleRad);

echo "arcsin(0.5) = {$angleDeg}°\n"; // 出力: 30°
?>

deg2radとの関係

相互変換

<?php
// 度 → ラジアン → 度
$degrees = 45;
$radians = deg2rad($degrees);
$backToDegrees = rad2deg($radians);

echo "{$degrees}° → {$radians} rad → {$backToDegrees}°\n";

// ラジアン → 度 → ラジアン
$radians = M_PI / 3;
$degrees = rad2deg($radians);
$backToRadians = deg2rad($degrees);

echo "{$radians} rad → {$degrees}° → {$backToRadians} rad\n";
?>

実践的な使用例

1. 座標・角度計算クラス

<?php
/**
 * 座標と角度の計算クラス
 */
class CoordinateCalculator {
    
    /**
     * 2点間の角度を計算(度)
     */
    public static function angleBetweenPoints($x1, $y1, $x2, $y2) {
        $dx = $x2 - $x1;
        $dy = $y2 - $y1;
        
        // atan2はラジアンで返すので度に変換
        $angleRad = atan2($dy, $dx);
        $angleDeg = rad2deg($angleRad);
        
        // 0-360度の範囲に正規化
        if ($angleDeg < 0) {
            $angleDeg += 360;
        }
        
        return $angleDeg;
    }
    
    /**
     * ベクトルの角度を計算
     */
    public static function vectorAngle($x, $y) {
        $angleRad = atan2($y, $x);
        $angleDeg = rad2deg($angleRad);
        
        return [
            'radians' => $angleRad,
            'degrees' => $angleDeg,
            'normalized' => ($angleDeg < 0) ? $angleDeg + 360 : $angleDeg
        ];
    }
    
    /**
     * 2つのベクトル間の角度を計算
     */
    public static function angleBetweenVectors($x1, $y1, $x2, $y2) {
        // 内積を使った角度計算
        $dot = $x1 * $x2 + $y1 * $y2;
        $mag1 = sqrt($x1 * $x1 + $y1 * $y1);
        $mag2 = sqrt($x2 * $x2 + $y2 * $y2);
        
        if ($mag1 == 0 || $mag2 == 0) {
            return 0;
        }
        
        $cosAngle = $dot / ($mag1 * $mag2);
        
        // 数値誤差の補正
        $cosAngle = max(-1, min(1, $cosAngle));
        
        $angleRad = acos($cosAngle);
        $angleDeg = rad2deg($angleRad);
        
        return $angleDeg;
    }
    
    /**
     * 円周上の点を計算
     */
    public static function pointOnCircle($centerX, $centerY, $radius, $angleDegrees) {
        $angleRad = deg2rad($angleDegrees);
        
        return [
            'x' => $centerX + $radius * cos($angleRad),
            'y' => $centerY + $radius * sin($angleRad)
        ];
    }
    
    /**
     * 回転変換
     */
    public static function rotatePoint($x, $y, $angleDegrees, $originX = 0, $originY = 0) {
        $angleRad = deg2rad($angleDegrees);
        
        // 原点を基準に平行移動
        $dx = $x - $originX;
        $dy = $y - $originY;
        
        // 回転
        $rotatedX = $dx * cos($angleRad) - $dy * sin($angleRad);
        $rotatedY = $dx * sin($angleRad) + $dy * cos($angleRad);
        
        // 元の位置に戻す
        return [
            'x' => $rotatedX + $originX,
            'y' => $rotatedY + $originY
        ];
    }
}

// 使用例
echo "=== 2点間の角度 ===\n";
$angle = CoordinateCalculator::angleBetweenPoints(0, 0, 3, 3);
echo "点(0,0)から点(3,3)への角度: {$angle}°\n\n";

echo "=== ベクトルの角度 ===\n";
$vectorAngle = CoordinateCalculator::vectorAngle(1, 1);
echo "ベクトル(1,1)の角度: {$vectorAngle['degrees']}°\n";
echo "正規化後: {$vectorAngle['normalized']}°\n\n";

echo "=== 2つのベクトル間の角度 ===\n";
$angle = CoordinateCalculator::angleBetweenVectors(1, 0, 0, 1);
echo "ベクトル(1,0)とベクトル(0,1)の角度: {$angle}°\n\n";

echo "=== 円周上の点 ===\n";
$point = CoordinateCalculator::pointOnCircle(0, 0, 10, 45);
echo "中心(0,0)、半径10、角度45°の点: ({$point['x']}, {$point['y']})\n\n";

echo "=== 点の回転 ===\n";
$rotated = CoordinateCalculator::rotatePoint(10, 0, 90, 0, 0);
echo "点(10,0)を90°回転: ({$rotated['x']}, {$rotated['y']})\n";
?>

2. 方位・コンパス計算

<?php
/**
 * 方位計算クラス
 */
class CompassBearing {
    
    // 16方位の定義
    const DIRECTIONS = [
        'N'   => [337.5, 22.5],    // 北
        'NNE' => [22.5, 45],        // 北北東
        'NE'  => [45, 67.5],        // 北東
        'ENE' => [67.5, 90],        // 東北東
        'E'   => [90, 112.5],       // 東
        'ESE' => [112.5, 135],      // 東南東
        'SE'  => [135, 157.5],      // 南東
        'SSE' => [157.5, 180],      // 南南東
        'S'   => [180, 202.5],      // 南
        'SSW' => [202.5, 225],      // 南南西
        'SW'  => [225, 247.5],      // 南西
        'WSW' => [247.5, 270],      // 西南西
        'W'   => [270, 292.5],      // 西
        'WNW' => [292.5, 315],      // 西北西
        'NW'  => [315, 337.5],      // 北西
        'NNW' => [337.5, 360],      // 北北西
    ];
    
    /**
     * 2地点間の方位を計算
     */
    public static function bearingBetweenPoints($lat1, $lon1, $lat2, $lon2) {
        // 緯度経度をラジアンに変換
        $lat1Rad = deg2rad($lat1);
        $lat2Rad = deg2rad($lat2);
        $lonDiffRad = deg2rad($lon2 - $lon1);
        
        // 方位角を計算
        $y = sin($lonDiffRad) * cos($lat2Rad);
        $x = cos($lat1Rad) * sin($lat2Rad) - 
             sin($lat1Rad) * cos($lat2Rad) * cos($lonDiffRad);
        
        $bearingRad = atan2($y, $x);
        $bearingDeg = rad2deg($bearingRad);
        
        // 0-360度の範囲に正規化
        $bearingDeg = fmod(($bearingDeg + 360), 360);
        
        return [
            'degrees' => $bearingDeg,
            'direction' => self::degreesToDirection($bearingDeg),
            'cardinal' => self::degreesToCardinal($bearingDeg)
        ];
    }
    
    /**
     * 度を16方位に変換
     */
    public static function degreesToDirection($degrees) {
        // 0-360度の範囲に正規化
        $degrees = fmod(($degrees + 360), 360);
        
        foreach (self::DIRECTIONS as $dir => $range) {
            if ($dir === 'N') {
                // 北は特殊(337.5-360と0-22.5)
                if ($degrees >= 337.5 || $degrees < 22.5) {
                    return $dir;
                }
            } else {
                if ($degrees >= $range[0] && $degrees < $range[1]) {
                    return $dir;
                }
            }
        }
        
        return 'N';
    }
    
    /**
     * 度を4方位に変換
     */
    public static function degreesToCardinal($degrees) {
        $degrees = fmod(($degrees + 360), 360);
        
        if ($degrees >= 315 || $degrees < 45) {
            return '北';
        } elseif ($degrees >= 45 && $degrees < 135) {
            return '東';
        } elseif ($degrees >= 135 && $degrees < 225) {
            return '南';
        } else {
            return '西';
        }
    }
    
    /**
     * 相対方位を計算(自分の向きからの角度)
     */
    public static function relativeBearing($currentHeading, $targetBearing) {
        $relative = $targetBearing - $currentHeading;
        
        // -180〜180度の範囲に正規化
        while ($relative > 180) {
            $relative -= 360;
        }
        while ($relative < -180) {
            $relative += 360;
        }
        
        return [
            'degrees' => $relative,
            'description' => self::describeRelativeBearing($relative)
        ];
    }
    
    /**
     * 相対方位を説明
     */
    private static function describeRelativeBearing($degrees) {
        if ($degrees >= -10 && $degrees <= 10) {
            return '正面';
        } elseif ($degrees > 10 && $degrees < 80) {
            return '右前方';
        } elseif ($degrees >= 80 && $degrees <= 100) {
            return '右';
        } elseif ($degrees > 100 && $degrees < 170) {
            return '右後方';
        } elseif ($degrees >= 170 || $degrees <= -170) {
            return '後方';
        } elseif ($degrees < -100 && $degrees > -170) {
            return '左後方';
        } elseif ($degrees >= -100 && $degrees <= -80) {
            return '左';
        } else {
            return '左前方';
        }
    }
}

// 使用例
echo "=== 地点間の方位計算 ===\n";
// 東京(35.6895, 139.6917)から大阪(34.6937, 135.5023)への方位
$bearing = CompassBearing::bearingBetweenPoints(35.6895, 139.6917, 34.6937, 135.5023);
echo "東京から大阪への方位:\n";
echo "  角度: {$bearing['degrees']}°\n";
echo "  16方位: {$bearing['direction']}\n";
echo "  方角: {$bearing['cardinal']}\n\n";

echo "=== 度から方位への変換 ===\n";
$angles = [0, 45, 90, 135, 180, 225, 270, 315];
foreach ($angles as $angle) {
    $dir = CompassBearing::degreesToDirection($angle);
    $cardinal = CompassBearing::degreesToCardinal($angle);
    echo "{$angle}° = {$dir} ({$cardinal})\n";
}

echo "\n=== 相対方位 ===\n";
$currentHeading = 45; // 自分は北東を向いている
$targetBearing = 135; // 目標は南東の方向
$relative = CompassBearing::relativeBearing($currentHeading, $targetBearing);
echo "現在の向き: {$currentHeading}°\n";
echo "目標の方位: {$targetBearing}°\n";
echo "相対角度: {$relative['degrees']}°\n";
echo "説明: {$relative['description']}\n";
?>

3. アニメーション・回転計算

<?php
/**
 * アニメーション計算クラス
 */
class AnimationCalculator {
    
    /**
     * 円運動のパスを生成
     */
    public static function generateCircularPath($centerX, $centerY, $radius, $steps = 60) {
        $path = [];
        $angleStep = (2 * M_PI) / $steps;
        
        for ($i = 0; $i < $steps; $i++) {
            $angleRad = $i * $angleStep;
            $angleDeg = rad2deg($angleRad);
            
            $path[] = [
                'x' => $centerX + $radius * cos($angleRad),
                'y' => $centerY + $radius * sin($angleRad),
                'angle_deg' => $angleDeg,
                'angle_rad' => $angleRad,
                'step' => $i
            ];
        }
        
        return $path;
    }
    
    /**
     * スムーズな回転アニメーションのフレームを生成
     */
    public static function generateRotationFrames($startAngle, $endAngle, $duration, $fps = 60) {
        $frames = [];
        $totalFrames = $duration * $fps;
        $angleDiff = $endAngle - $startAngle;
        
        // 最短経路を選択
        if ($angleDiff > 180) {
            $angleDiff -= 360;
        } elseif ($angleDiff < -180) {
            $angleDiff += 360;
        }
        
        for ($frame = 0; $frame <= $totalFrames; $frame++) {
            $progress = $frame / $totalFrames;
            
            // イージング関数(ease-in-out)
            $eased = $progress < 0.5
                ? 2 * $progress * $progress
                : 1 - pow(-2 * $progress + 2, 2) / 2;
            
            $currentAngle = $startAngle + ($angleDiff * $eased);
            
            // 0-360度の範囲に正規化
            $currentAngle = fmod(($currentAngle + 360), 360);
            
            $frames[] = [
                'frame' => $frame,
                'time' => $frame / $fps,
                'angle' => $currentAngle,
                'progress' => $progress
            ];
        }
        
        return $frames;
    }
    
    /**
     * 楕円運動のパスを生成
     */
    public static function generateEllipticalPath($centerX, $centerY, $radiusX, $radiusY, $steps = 60) {
        $path = [];
        $angleStep = (2 * M_PI) / $steps;
        
        for ($i = 0; $i < $steps; $i++) {
            $angleRad = $i * $angleStep;
            $angleDeg = rad2deg($angleRad);
            
            $path[] = [
                'x' => $centerX + $radiusX * cos($angleRad),
                'y' => $centerY + $radiusY * sin($angleRad),
                'angle' => $angleDeg
            ];
        }
        
        return $path;
    }
    
    /**
     * 振り子運動をシミュレート
     */
    public static function simulatePendulum($amplitude, $period, $duration, $fps = 60) {
        $frames = [];
        $totalFrames = $duration * $fps;
        $omega = (2 * M_PI) / $period; // 角振動数
        
        for ($frame = 0; $frame <= $totalFrames; $frame++) {
            $time = $frame / $fps;
            
            // 振り子の角度(ラジアン)
            $amplitudeRad = deg2rad($amplitude);
            $angleRad = $amplitudeRad * cos($omega * $time);
            $angleDeg = rad2deg($angleRad);
            
            $frames[] = [
                'frame' => $frame,
                'time' => $time,
                'angle_deg' => $angleDeg,
                'angle_rad' => $angleRad
            ];
        }
        
        return $frames;
    }
}

// 使用例
echo "=== 円運動のパス ===\n";
$path = AnimationCalculator::generateCircularPath(0, 0, 100, 8);
echo "8ステップの円運動:\n";
foreach ($path as $point) {
    printf(
        "ステップ%d: 座標(%.1f, %.1f) 角度%.0f°\n",
        $point['step'],
        $point['x'],
        $point['y'],
        $point['angle_deg']
    );
}

echo "\n=== 回転アニメーション ===\n";
$frames = AnimationCalculator::generateRotationFrames(0, 90, 1, 10);
echo "0°から90°への1秒間の回転(10fps):\n";
foreach (array_slice($frames, 0, 6) as $frame) {
    printf(
        "フレーム%d: %.2f秒 角度%.1f° (進行度%.0f%%)\n",
        $frame['frame'],
        $frame['time'],
        $frame['angle'],
        $frame['progress'] * 100
    );
}

echo "\n=== 振り子運動 ===\n";
$pendulum = AnimationCalculator::simulatePendulum(45, 2, 2, 5);
echo "振幅45°、周期2秒の振り子(最初の6フレーム):\n";
foreach (array_slice($pendulum, 0, 6) as $frame) {
    printf(
        "時刻%.2f秒: 角度%.1f°\n",
        $frame['time'],
        $frame['angle_deg']
    );
}
?>

4. GPS・地理計算

<?php
/**
 * GPS・地理計算クラス
 */
class GeoCalculator {
    
    const EARTH_RADIUS_KM = 6371; // 地球の半径(km)
    
    /**
     * 2地点間の距離を計算(Haversine公式)
     */
    public static function distanceBetweenPoints($lat1, $lon1, $lat2, $lon2) {
        $lat1Rad = deg2rad($lat1);
        $lat2Rad = deg2rad($lat2);
        $latDiff = deg2rad($lat2 - $lat1);
        $lonDiff = deg2rad($lon2 - $lon1);
        
        $a = sin($latDiff / 2) * sin($latDiff / 2) +
             cos($lat1Rad) * cos($lat2Rad) *
             sin($lonDiff / 2) * sin($lonDiff / 2);
        
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $distance = self::EARTH_RADIUS_KM * $c;
        
        return $distance;
    }
    
    /**
     * 指定距離・方位の地点を計算
     */
    public static function destinationPoint($lat, $lon, $bearing, $distanceKm) {
        $latRad = deg2rad($lat);
        $lonRad = deg2rad($lon);
        $bearingRad = deg2rad($bearing);
        
        $angularDistance = $distanceKm / self::EARTH_RADIUS_KM;
        
        $lat2Rad = asin(
            sin($latRad) * cos($angularDistance) +
            cos($latRad) * sin($angularDistance) * cos($bearingRad)
        );
        
        $lon2Rad = $lonRad + atan2(
            sin($bearingRad) * sin($angularDistance) * cos($latRad),
            cos($angularDistance) - sin($latRad) * sin($lat2Rad)
        );
        
        return [
            'latitude' => rad2deg($lat2Rad),
            'longitude' => rad2deg($lon2Rad)
        ];
    }
    
    /**
     * 三角測量で位置を特定
     */
    public static function trilateration($point1, $point2, $point3) {
        // 簡易的な三角測量の実装
        // 実際にはもっと複雑な計算が必要
        
        $lat = ($point1['lat'] + $point2['lat'] + $point3['lat']) / 3;
        $lon = ($point1['lon'] + $point2['lon'] + $point3['lon']) / 3;
        
        return [
            'latitude' => $lat,
            'longitude' => $lon,
            'accuracy' => 'approximate'
        ];
    }
    
    /**
     * 日の出・日の入りの方位を計算
     */
    public static function sunriseAzimuth($latitude, $date = null) {
        if ($date === null) {
            $date = time();
        }
        
        // 簡易計算(実際にはもっと複雑)
        $dayOfYear = date('z', $date);
        $declination = 23.45 * sin(deg2rad((360 / 365) * ($dayOfYear - 81)));
        
        $latRad = deg2rad($latitude);
        $decRad = deg2rad($declination);
        
        $cosAzimuth = -sin($decRad) / cos($latRad);
        $cosAzimuth = max(-1, min(1, $cosAzimuth)); // クリッピング
        
        $azimuthRad = acos($cosAzimuth);
        $azimuthDeg = rad2deg($azimuthRad);
        
        return [
            'azimuth' => $azimuthDeg,
            'direction' => CompassBearing::degreesToDirection($azimuthDeg),
            'date' => date('Y-m-d', $date)
        ];
    }
}

// 使用例
echo "=== 2地点間の距離 ===\n";
$distance = GeoCalculator::distanceBetweenPoints(
    35.6895, 139.6917,  // 東京
    34.6937, 135.5023   // 大阪
);
echo "東京-大阪間の距離: " . round($distance, 2) . " km\n\n";

echo "=== 目的地の計算 ===\n";
$destination = GeoCalculator::destinationPoint(
    35.6895, 139.6917,  // 東京
    270,                // 真西
    100                 // 100km
);
echo "東京から真西に100km進んだ地点:\n";
echo "  緯度: {$destination['latitude']}\n";
echo "  経度: {$destination['longitude']}\n\n";

echo "=== 日の出の方位 ===\n";
$sunrise = GeoCalculator::sunriseAzimuth(35.6895);
echo "東京の日の出方位:\n";
echo "  方位角: {$sunrise['azimuth']}°\n";
echo "  方向: {$sunrise['direction']}\n";
?>

よくある間違いと注意点

間違い1: 度とラジアンの混同

<?php
// ❌ 度をそのまま三角関数に渡す
$angle = 45;
$result = sin($angle); // 間違い!

// ✅ deg2radで変換してから使用
$angle = 45;
$result = sin(deg2rad($angle));

// または、ラジアンの結果を度に変換
$angleRad = atan2($y, $x);
$angleDeg = rad2deg($angleRad);
?>

間違い2: 角度の正規化を忘れる

<?php
// ❌ 負の角度や360度を超える角度をそのまま使用
$angle = -30;
// または
$angle = 450;

// ✅ 0-360度の範囲に正規化
function normalizeAngle($degrees) {
    $degrees = fmod($degrees, 360);
    if ($degrees < 0) {
        $degrees += 360;
    }
    return $degrees;
}

$angle = normalizeAngle(-30);  // 330度
$angle = normalizeAngle(450);  // 90度
?>

間違い3: 浮動小数点の精度問題

<?php
// 浮動小数点の誤差に注意
$angleRad = M_PI;
$angleDeg = rad2deg($angleRad);

// 厳密な比較は避ける
if ($angleDeg == 180) { // 誤差で失敗する可能性
    // ...
}

// ✅ 許容誤差を使った比較
function angleEquals($angle1, $angle2, $epsilon = 0.0001) {
    return abs($angle1 - $angle2) < $epsilon;
}

if (angleEquals($angleDeg, 180)) {
    // より安全な比較
}
?>

パフォーマンスの考慮

頻繁に使用する場合の最適化

<?php
// ✅ 定数を使って計算を最適化
const RAD_TO_DEG = 180 / M_PI;

// 大量の変換を行う場合
$angles = [];
for ($i = 0; $i < 1000; $i++) {
    $rad = $i * 0.01;
    
    // rad2deg()を使用
    $angles[] = rad2deg($rad);
    
    // または直接計算(わずかに高速)
    // $angles[] = $rad * RAD_TO_DEG;
}

// ただし、可読性のためにはrad2deg()の使用を推奨
?>

関連する数学定数と関数

角度変換に関連する要素

<?php
echo "=== 関連定数 ===\n";
echo "円周率 π: " . M_PI . "\n";
echo "π/2 (90°): " . M_PI_2 . "\n";
echo "π/4 (45°): " . M_PI_4 . "\n";
echo "2π (360°): " . (2 * M_PI) . "\n\n";

echo "=== 変換関数 ===\n";
$radians = M_PI / 3;
echo "ラジアン: {$radians}\n";
echo "度に変換: " . rad2deg($radians) . "°\n";
echo "度に変換して戻す: " . deg2rad(rad2deg($radians)) . " rad\n\n";

echo "=== 三角関数との組み合わせ ===\n";
$x = 1;
$y = 1;
$angleRad = atan2($y, $x);
$angleDeg = rad2deg($angleRad);
echo "atan2(1, 1) = {$angleRad} rad = {$angleDeg}°\n";

$value = 0.5;
$angleRad = asin($value);
$angleDeg = rad2deg($angleRad);
echo "asin(0.5) = {$angleRad} rad = {$angleDeg}°\n";

$angleRad = acos(0.5);
$angleDeg = rad2deg($angleRad);
echo "acos(0.5) = {$angleRad} rad = {$angleDeg}°\n";
?>

実用的なヘルパー関数

<?php
/**
 * 角度操作のユーティリティクラス
 */
class AngleUtils {
    
    /**
     * 角度を0-360度の範囲に正規化
     */
    public static function normalize($degrees) {
        $degrees = fmod($degrees, 360);
        if ($degrees < 0) {
            $degrees += 360;
        }
        return $degrees;
    }
    
    /**
     * 角度を-180〜180度の範囲に正規化
     */
    public static function normalizeSymmetric($degrees) {
        $degrees = self::normalize($degrees);
        if ($degrees > 180) {
            $degrees -= 360;
        }
        return $degrees;
    }
    
    /**
     * 2つの角度の差を計算(最短経路)
     */
    public static function difference($angle1, $angle2) {
        $diff = $angle2 - $angle1;
        $diff = self::normalizeSymmetric($diff);
        return $diff;
    }
    
    /**
     * 角度を四捨五入
     */
    public static function round($degrees, $precision = 0) {
        return round($degrees, $precision);
    }
    
    /**
     * 角度を度分秒形式に変換
     */
    public static function toDMS($degrees) {
        $d = floor(abs($degrees));
        $minFloat = (abs($degrees) - $d) * 60;
        $m = floor($minFloat);
        $s = ($minFloat - $m) * 60;
        
        $sign = $degrees < 0 ? '-' : '';
        
        return [
            'degrees' => $d,
            'minutes' => $m,
            'seconds' => round($s, 2),
            'formatted' => sprintf("%s%d° %d' %.2f\"", $sign, $d, $m, $s)
        ];
    }
    
    /**
     * 度分秒形式から度に変換
     */
    public static function fromDMS($degrees, $minutes, $seconds) {
        $decimal = abs($degrees) + ($minutes / 60) + ($seconds / 3600);
        return $degrees < 0 ? -$decimal : $decimal;
    }
    
    /**
     * ラジアンから度へ変換(エイリアス)
     */
    public static function radToDeg($radians) {
        return rad2deg($radians);
    }
    
    /**
     * 度からラジアンへ変換(エイリアス)
     */
    public static function degToRad($degrees) {
        return deg2rad($degrees);
    }
    
    /**
     * 角度を人間が読みやすい形式で表示
     */
    public static function format($degrees, $precision = 1) {
        return round($degrees, $precision) . '°';
    }
}

// 使用例
echo "=== 角度の正規化 ===\n";
echo "450° → " . AngleUtils::normalize(450) . "°\n";
echo "-30° → " . AngleUtils::normalize(-30) . "°\n";
echo "400° → " . AngleUtils::normalizeSymmetric(400) . "°\n\n";

echo "=== 角度の差 ===\n";
$diff = AngleUtils::difference(350, 10);
echo "350°と10°の差(最短経路): {$diff}°\n\n";

echo "=== 度分秒変換 ===\n";
$dms = AngleUtils::toDMS(35.6895);
echo "35.6895° = {$dms['formatted']}\n";

$decimal = AngleUtils::fromDMS(35, 41, 22.2);
echo "35° 41' 22.2\" = {$decimal}°\n\n";

echo "=== ラジアン変換 ===\n";
$rad = M_PI / 4;
$deg = AngleUtils::radToDeg($rad);
echo "{$rad} rad = " . AngleUtils::format($deg) . "\n";
?>

SVG描画での活用例

<?php
/**
 * SVGで図形を描画
 */
class SVGShapeDrawer {
    
    /**
     * 円グラフを生成
     */
    public static function drawPieChart($data, $width = 400, $height = 400) {
        $centerX = $width / 2;
        $centerY = $height / 2;
        $radius = min($width, $height) / 2 - 20;
        
        $total = array_sum($data);
        $startAngle = -90; // 12時の位置から開始
        
        $svg = sprintf(
            '<svg width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">',
            $width,
            $height
        );
        
        $colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8'];
        $colorIndex = 0;
        
        foreach ($data as $label => $value) {
            $percentage = $value / $total;
            $angle = 360 * $percentage;
            $endAngle = $startAngle + $angle;
            
            // ラジアンに変換
            $startRad = deg2rad($startAngle);
            $endRad = deg2rad($endAngle);
            
            // 座標を計算
            $x1 = $centerX + $radius * cos($startRad);
            $y1 = $centerY + $radius * sin($startRad);
            $x2 = $centerX + $radius * cos($endRad);
            $y2 = $centerY + $radius * sin($endRad);
            
            $largeArc = $angle > 180 ? 1 : 0;
            
            $path = sprintf(
                'M %f %f L %f %f A %f %f 0 %d 1 %f %f Z',
                $centerX, $centerY,
                $x1, $y1,
                $radius, $radius,
                $largeArc,
                $x2, $y2
            );
            
            $color = $colors[$colorIndex % count($colors)];
            $svg .= sprintf(
                '<path d="%s" fill="%s" stroke="white" stroke-width="2"/>',
                $path,
                $color
            );
            
            // ラベルの位置(扇形の中央)
            $labelAngle = deg2rad($startAngle + $angle / 2);
            $labelX = $centerX + ($radius * 0.7) * cos($labelAngle);
            $labelY = $centerY + ($radius * 0.7) * sin($labelAngle);
            
            $svg .= sprintf(
                '<text x="%f" y="%f" text-anchor="middle" fill="white" font-weight="bold">%s<tspan x="%f" dy="15">%.1f%%</tspan></text>',
                $labelX,
                $labelY,
                htmlspecialchars($label),
                $labelX,
                $percentage * 100
            );
            
            $startAngle = $endAngle;
            $colorIndex++;
        }
        
        $svg .= '</svg>';
        
        return $svg;
    }
    
    /**
     * 時計の針を描画
     */
    public static function drawClock($hours, $minutes, $seconds, $size = 200) {
        $centerX = $size / 2;
        $centerY = $size / 2;
        
        // 角度を計算(12時の位置を0度とする)
        $secondAngle = ($seconds / 60) * 360 - 90;
        $minuteAngle = (($minutes + $seconds / 60) / 60) * 360 - 90;
        $hourAngle = (($hours % 12 + $minutes / 60) / 12) * 360 - 90;
        
        $svg = sprintf(
            '<svg width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">',
            $size,
            $size
        );
        
        // 文字盤
        $svg .= sprintf(
            '<circle cx="%f" cy="%f" r="%f" fill="white" stroke="black" stroke-width="2"/>',
            $centerX,
            $centerY,
            $size / 2 - 5
        );
        
        // 時針
        $hourLen = $size * 0.25;
        $hourRad = deg2rad($hourAngle);
        $hourX = $centerX + $hourLen * cos($hourRad);
        $hourY = $centerY + $hourLen * sin($hourRad);
        $svg .= sprintf(
            '<line x1="%f" y1="%f" x2="%f" y2="%f" stroke="black" stroke-width="6" stroke-linecap="round"/>',
            $centerX, $centerY, $hourX, $hourY
        );
        
        // 分針
        $minuteLen = $size * 0.35;
        $minuteRad = deg2rad($minuteAngle);
        $minuteX = $centerX + $minuteLen * cos($minuteRad);
        $minuteY = $centerY + $minuteLen * sin($minuteRad);
        $svg .= sprintf(
            '<line x1="%f" y1="%f" x2="%f" y2="%f" stroke="black" stroke-width="4" stroke-linecap="round"/>',
            $centerX, $centerY, $minuteX, $minuteY
        );
        
        // 秒針
        $secondLen = $size * 0.4;
        $secondRad = deg2rad($secondAngle);
        $secondX = $centerX + $secondLen * cos($secondRad);
        $secondY = $centerY + $secondLen * sin($secondRad);
        $svg .= sprintf(
            '<line x1="%f" y1="%f" x2="%f" y2="%f" stroke="red" stroke-width="2" stroke-linecap="round"/>',
            $centerX, $centerY, $secondX, $secondY
        );
        
        // 中心の点
        $svg .= sprintf(
            '<circle cx="%f" cy="%f" r="5" fill="black"/>',
            $centerX,
            $centerY
        );
        
        $svg .= '</svg>';
        
        return $svg;
    }
}

// 使用例
echo "=== 円グラフSVG ===\n";
$pieData = [
    'PHP' => 35,
    'JavaScript' => 25,
    'Python' => 20,
    'Java' => 15,
    'その他' => 5
];
echo SVGShapeDrawer::drawPieChart($pieData, 500, 500);

echo "\n\n=== 時計SVG ===\n";
echo SVGShapeDrawer::drawClock(10, 30, 45, 300);
?>

まとめ

rad2deg関数は、PHPでラジアンを度に変換するための基本的な関数です。以下のポイントを押さえておきましょう。

  • ラジアンから度への変換に使用(度 = ラジアン × 180/π)
  • 三角関数の結果(atan2、asin、acosなど)を人間が読みやすい形式に変換
  • deg2rad相互変換が可能
  • 座標計算、方位計算、アニメーションなど幅広く活用
  • 角度の正規化(0-360度や-180〜180度)を忘れずに
  • 浮動小数点の精度に注意して比較を行う
  • GPSや地理計算、SVG描画など実用的な場面で活躍

ラジアンと度の変換は、数学的計算とユーザーインターフェースの橋渡しとなる重要な処理です。rad2degを適切に使って、分かりやすく正確な角度表現を実現しましょう!

タイトルとURLをコピーしました