Her er koden fremstillet med en dedikeret AI – Vo.dev -beregnet til udvikling af programmer :
Den var meget skarp . Bedre end de generelle.
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CPA/TCPA Navigator</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #34495e;
}
input[type="number"] {
width: 100%;
padding: 10px;
border: 2px solid #bdc3c7;
border-radius: 5px;
font-size: 16px;
}
input[type="number"]:focus {
border-color: #3498db;
outline: none;
}
button {
background-color: #3498db;
color: white;
padding: 12px 30px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
width: 100%;
margin-top: 20px;
}
button:hover {
background-color: #2980b9;
}
.results {
margin-top: 30px;
padding: 15px;
background-color: #ecf0f1;
border-radius: 5px;
font-size: 14px;
}
.results h2 {
font-size: 16px;
margin-bottom: 15px;
}
.result-item {
margin-bottom: 8px;
font-size: 14px;
}
.result-value {
font-weight: bold;
color: #e74c3c;
}
.two-column {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.section-title {
color: #2c3e50;
font-size: 18px;
margin-bottom: 15px;
border-bottom: 2px solid #3498db;
padding-bottom: 5px;
}
/* Added styles for compass diagram */
.results-with-diagram {
display: grid;
grid-template-columns: 300px 1fr;
gap: 30px;
margin-top: 30px;
}
.compass-container {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.compass-title {
color: #2c3e50;
font-size: 18px;
margin-bottom: 15px;
font-weight: bold;
}
.legend {
margin-top: 15px;
text-align: left;
font-size: 12px;
}
.legend-item {
margin-bottom: 5px;
display: flex;
align-items: center;
}
.legend-color {
width: 20px;
height: 3px;
margin-right: 8px;
}
@media (max-width: 768px) {
.results-with-diagram {
grid-template-columns: 1fr;
}
.compass-container {
order: -1;
}
}
</style>
</head>
<body>
<div class="container">
<h1>CPA/TCPA Navigator</h1>
<form method="POST">
<div class="two-column">
<div>
<div class="section-title">Observation 1</div>
<div class="form-group">
<label for="bearing1">Pejling 1 (grader):</label>
<input type="number" id="bearing1" name="bearing1" step="0.1" value="<?php echo isset($_POST['bearing1']) ? $_POST['bearing1'] : ''; ?>" required>
</div>
<div class="form-group">
<label for="distance1">Afstand 1 (sømil):</label>
<input type="number" id="distance1" name="distance1" step="0.1" value="<?php echo isset($_POST['distance1']) ? $_POST['distance1'] : ''; ?>" required>
</div>
</div>
<div>
<div class="section-title">Observation 2</div>
<div class="form-group">
<label for="bearing2">Pejling 2 (grader):</label>
<input type="number" id="bearing2" name="bearing2" step="0.1" value="<?php echo isset($_POST['bearing2']) ? $_POST['bearing2'] : ''; ?>" required>
</div>
<div class="form-group">
<label for="distance2">Afstand 2 (sømil):</label>
<input type="number" id="distance2" name="distance2" step="0.1" value="<?php echo isset($_POST['distance2']) ? $_POST['distance2'] : ''; ?>" required>
</div>
</div>
</div>
<div class="two-column">
<div>
<div class="section-title">Eget Fartøj</div>
<div class="form-group">
<label for="own_speed">Egen fart (knob):</label>
<input type="number" id="own_speed" name="own_speed" step="0.1" value="<?php echo isset($_POST['own_speed']) ? $_POST['own_speed'] : ''; ?>" required>
</div>
<div class="form-group">
<label for="own_course">Egen kurs (grader):</label>
<input type="number" id="own_course" name="own_course" step="0.1" value="<?php echo isset($_POST['own_course']) ? $_POST['own_course'] : ''; ?>" required>
</div>
</div>
<div>
<div class="section-title">Tidsinterval</div>
<div class="form-group">
<label for="time_interval">Tidsinterval (minutter):</label>
<input type="number" id="time_interval" name="time_interval" step="0.1" value="<?php echo isset($_POST['time_interval']) ? $_POST['time_interval'] : ''; ?>" required>
</div>
</div>
</div>
<button type="submit">Beregn CPA/TCPA</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Hent input værdier
$bearing1 = floatval($_POST['bearing1']);
$distance1 = floatval($_POST['distance1']);
$bearing2 = floatval($_POST['bearing2']);
$distance2 = floatval($_POST['distance2']);
$own_speed = floatval($_POST['own_speed']);
$own_course = floatval($_POST['own_course']);
$time_interval = floatval($_POST['time_interval']);
// Konverter til radianer
$bearing1_rad = deg2rad($bearing1);
$bearing2_rad = deg2rad($bearing2);
$own_course_rad = deg2rad($own_course);
// Beregn positioner i kartesisk koordinatsystem
$x1 = $distance1 * sin($bearing1_rad);
$y1 = $distance1 * cos($bearing1_rad);
$x2 = $distance2 * sin($bearing2_rad);
$y2 = $distance2 * cos($bearing2_rad);
// Beregn egen bevægelse i tidsintervallet
$time_hours = $time_interval / 60;
$own_distance = $own_speed * $time_hours;
$own_dx = $own_distance * sin($own_course_rad);
$own_dy = $own_distance * cos($own_course_rad);
// Kompenser for egen bevægelse
$target_dx = ($x2 - $x1) + $own_dx;
$target_dy = ($y2 - $y1) + $own_dy;
// Beregn målets fart og kurs
$target_distance = sqrt($target_dx * $target_dx + $target_dy * $target_dy);
$target_speed = $target_distance / $time_hours;
$target_course = rad2deg(atan2($target_dx, $target_dy));
if ($target_course < 0) $target_course += 360;
// Beregn relativ kurs (målets kurs relativt til eget fartøj)
$relative_course = $target_course - $own_course;
if ($relative_course < 0) $relative_course += 360;
if ($relative_course > 360) $relative_course -= 360;
// Beregn relativ hastighed
$rel_dx = $target_dx / $time_hours - $own_speed * sin($own_course_rad);
$rel_dy = $target_dy / $time_hours - $own_speed * cos($own_course_rad);
// Beregn CPA og TCPA
$rel_speed = sqrt($rel_dx * $rel_dx + $rel_dy * $rel_dy);
if ($rel_speed > 0.01) {
// Dot product for at finde TCPA
$dot_product = $x2 * $rel_dx + $y2 * $rel_dy;
$tcpa_hours = -$dot_product / ($rel_speed * $rel_speed);
$tcpa_minutes = $tcpa_hours * 60;
// Beregn CPA position
$cpa_x = $x2 + $rel_dx * $tcpa_hours;
$cpa_y = $y2 + $rel_dy * $tcpa_hours;
$cpa_distance = sqrt($cpa_x * $cpa_x + $cpa_y * $cpa_y);
} else {
$tcpa_minutes = 0;
$cpa_distance = $distance2;
}
function generateCompassDiagram($own_course, $bearing1, $bearing2, $distance1, $distance2, $target_course, $relative_course, $cpa_distance, $tcpa_hours, $x2, $y2, $rel_dx, $rel_dy) {
$svg = '<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">';
// Distance rings (1, 2, 3, 4, 5 nautical miles)
$max_distance = max($distance1, $distance2, 5); // Ensure at least 5 rings
$scale = 200 / $max_distance; // Scale to fit in larger diagram
for ($ring = 1; $ring <= 5; $ring++) {
$radius = $ring * $scale;
if ($radius <= 200) {
$svg .= '<circle cx="250" cy="250" r="' . $radius . '" fill="none" stroke="#ecf0f1" stroke-width="1" stroke-dasharray="2,2"/>';
// Distance labels
$svg .= '<text x="' . (250 + $radius - 10) . '" y="255" font-size="12" fill="#7f8c8d">' . $ring . 'nm</text>';
}
}
// Background circle
$svg .= '<circle cx="250" cy="250" r="230" fill="none" stroke="#bdc3c7" stroke-width="2"/>';
// Degree markings
for ($i = 0; $i < 360; $i += 30) {
$x1 = 250 + 220 * sin(deg2rad($i));
$y1 = 250 - 220 * cos(deg2rad($i));
$x2 = 250 + 230 * sin(deg2rad($i));
$y2 = 250 - 230 * cos(deg2rad($i));
$svg .= '<line x1="' . $x1 . '" y1="' . $y1 . '" x2="' . $x2 . '" y2="' . $y2 . '" stroke="#7f8c8d" stroke-width="2"/>';
// Degree labels
$label_x = 250 + 200 * sin(deg2rad($i));
$label_y = 250 - 200 * cos(deg2rad($i)) + 5;
$svg .= '<text x="' . $label_x . '" y="' . $label_y . '" text-anchor="middle" font-size="14" fill="#2c3e50">' . $i . '°</text>';
}
// Minor degree markings
for ($i = 0; $i < 360; $i += 10) {
if ($i % 30 != 0) {
$x1 = 250 + 225 * sin(deg2rad($i));
$y1 = 250 - 225 * cos(deg2rad($i));
$x2 = 250 + 230 * sin(deg2rad($i));
$y2 = 250 - 230 * cos(deg2rad($i));
$svg .= '<line x1="' . $x1 . '" y1="' . $y1 . '" x2="' . $x2 . '" y2="' . $y2 . '" stroke="#bdc3c7" stroke-width="1"/>';
}
}
// North indicator
$svg .= '<text x="250" y="30" text-anchor="middle" font-size="18" font-weight="bold" fill="#e74c3c">N</text>';
$p1_x = 250 + $distance1 * $scale * sin(deg2rad($bearing1));
$p1_y = 250 - $distance1 * $scale * cos(deg2rad($bearing1));
$p2_x = 250 + $distance2 * $scale * sin(deg2rad($bearing2));
$p2_y = 250 - $distance2 * $scale * cos(deg2rad($bearing2));
// Calculate direction vector from P1 to P2
$trajectory_dx = $p2_x - $p1_x;
$trajectory_dy = $p2_y - $p1_y;
$trajectory_length = sqrt($trajectory_dx * $trajectory_dx + $trajectory_dy * $trajectory_dy);
if ($trajectory_length > 0) {
// Normalize direction vector
$trajectory_unit_x = $trajectory_dx / $trajectory_length;
$trajectory_unit_y = $trajectory_dy / $trajectory_length;
// Extend line backwards from P1 and forwards from P2
$extend_distance = 300; // Extend line well beyond diagram
$line_start_x = $p1_x - $trajectory_unit_x * $extend_distance;
$line_start_y = $p1_y - $trajectory_unit_y * $extend_distance;
$line_end_x = $p2_x + $trajectory_unit_x * $extend_distance;
$line_end_y = $p2_y + $trajectory_unit_y * $extend_distance;
// Draw extended trajectory line
$svg .= '<line x1="' . $line_start_x . '" y1="' . $line_start_y . '" x2="' . $line_end_x . '" y2="' . $line_end_y . '" stroke="#8e44ad" stroke-width="3" stroke-dasharray="10,5" opacity="0.8"/>';
// Find the closest point on the trajectory line to the center (250, 250)
$center_x = 250;
$center_y = 250;
// Vector from P1 to center
$p1_to_center_x = $center_x - $p1_x;
$p1_to_center_y = $center_y - $p1_y;
// Project center onto trajectory line
$dot_product = $p1_to_center_x * $trajectory_unit_x + $p1_to_center_y * $trajectory_unit_y;
$closest_point_x = $p1_x + $dot_product * $trajectory_unit_x;
$closest_point_y = $p1_y + $dot_product * $trajectory_unit_y;
// Draw perpendicular line from center to closest point on trajectory
$svg .= '<line x1="' . $center_x . '" y1="' . $center_y . '" x2="' . $closest_point_x . '" y2="' . $closest_point_y . '" stroke="#e67e22" stroke-width="4" stroke-dasharray="8,4"/>';
// Mark the closest point on trajectory
$svg .= '<circle cx="' . $closest_point_x . '" cy="' . $closest_point_y . '" r="6" fill="#e67e22" stroke="#fff" stroke-width="2"/>';
// Add label for geometric CPA
$label_offset_x = ($closest_point_x > $center_x) ? 15 : -25;
$label_offset_y = ($closest_point_y > $center_y) ? -10 : 20;
$svg .= '<text x="' . ($closest_point_x + $label_offset_x) . '" y="' . ($closest_point_y + $label_offset_y) . '" font-size="12" font-weight="bold" fill="#e67e22">CPA</text>';
}
// Own course (blue arrow)
$own_x = 250 + 160 * sin(deg2rad($own_course));
$own_y = 250 - 160 * cos(deg2rad($own_course));
$svg .= '<line x1="250" y1="250" x2="' . $own_x . '" y2="' . $own_y . '" stroke="#3498db" stroke-width="5" marker-end="url(#arrowBlue)"/>';
// Bearing 1 (green line) - scaled to actual distance
$bear1_length = min($distance1 * $scale, 200);
$bear1_x = 250 + $bear1_length * sin(deg2rad($bearing1));
$bear1_y = 250 - $bear1_length * cos(deg2rad($bearing1));
$svg .= '<line x1="250" y1="250" x2="' . $bear1_x . '" y2="' . $bear1_y . '" stroke="#27ae60" stroke-width="4"/>';
$svg .= '<circle cx="' . $bear1_x . '" cy="' . $bear1_y . '" r="6" fill="#27ae60" stroke="#fff" stroke-width="2"/>';
$svg .= '<text x="' . ($bear1_x + 10) . '" y="' . ($bear1_y - 10) . '" font-size="12" font-weight="bold" fill="#27ae60">P1</text>';
// Bearing 2 (orange line) - scaled to actual distance
$bear2_length = min($distance2 * $scale, 200);
$bear2_x = 250 + $bear2_length * sin(deg2rad($bearing2));
$bear2_y = 250 - $bear2_length * cos(deg2rad($bearing2));
$svg .= '<line x1="250" y1="250" x2="' . $bear2_x . '" y2="' . $bear2_y . '" stroke="#f39c12" stroke-width="4"/>';
$svg .= '<circle cx="' . $bear2_x . '" cy="' . $bear2_y . '" r="6" fill="#f39c12" stroke="#fff" stroke-width="2"/>';
$svg .= '<text x="' . ($bear2_x + 10) . '" y="' . ($bear2_y - 10) . '" font-size="12" font-weight="bold" fill="#f39c12">P2</text>';
// Target true course (red arrow)
$target_x = 250 + 120 * sin(deg2rad($target_course));
$target_y = 250 - 120 * cos(deg2rad($target_course));
$svg .= '<line x1="250" y1="250" x2="' . $target_x . '" y2="' . $target_y . '" stroke="#e74c3c" stroke-width="5" marker-end="url(#arrowRed)"/>';
// Arrow markers
$svg .= '<defs>';
$svg .= '<marker id="arrowBlue" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">';
$svg .= '<path d="M0,0 L0,6 L9,3 z" fill="#3498db"/>';
$svg .= '</marker>';
$svg .= '<marker id="arrowRed" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">';
$svg .= '<path d="M0,0 L0,6 L9,3 z" fill="#e74c3c"/>';
$svg .= '</marker>';
$svg .= '</defs>';
$svg .= '</svg>';
return $svg;
}
echo '<div class="results-with-diagram">';
echo '<div class="results">';
echo '<h2>Beregningsresultater:</h2>';
echo '<div class="result-item">Målets sande kurs: <span class="result-value">' . number_format($target_course, 1) . '°</span></div>';
echo '<div class="result-item">Målets relative kurs: <span class="result-value">' . number_format($relative_course, 1) . '°</span></div>';
echo '<div class="result-item">Målets fart: <span class="result-value">' . number_format($target_speed, 1) . ' knob</span></div>';
echo '<div class="result-item">TCPA: <span class="result-value">' . number_format($tcpa_minutes, 1) . ' minutter</span></div>';
echo '<div class="result-item">CPA: <span class="result-value">' . number_format($cpa_distance, 2) . ' sømil</span></div>';
if ($cpa_distance < 0.5) {
echo '<div style="color: red; font-weight: bold; margin-top: 15px;">⚠️ ADVARSEL: Kollisionsrisiko!</div>';
} elseif ($cpa_distance < 1.0) {
echo '<div style="color: orange; font-weight: bold; margin-top: 15px;">⚠️ FORSIGTIG: Tæt passage!</div>';
}
echo '</div>';
echo '<div class="compass-container">';
echo '<div class="compass-title">360° Navigationsdiagram</div>';
echo generateCompassDiagram($own_course, $bearing1, $bearing2, $distance1, $distance2, $target_course, $relative_course, $cpa_distance, $tcpa_hours, $x2, $y2, $rel_dx, $rel_dy);
echo '<div class="legend">';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #3498db;"></div>Egen kurs (' . number_format($own_course, 1) . '°)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #27ae60;"></div>Pejling 1 - P1 (' . number_format($bearing1, 1) . '° - ' . number_format($distance1, 1) . 'nm)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #f39c12;"></div>Pejling 2 - P2 (' . number_format($bearing2, 1) . '° - ' . number_format($distance2, 1) . 'nm)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #e74c3c;"></div>Målets sande kurs (' . number_format($target_course, 1) . '°)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #8e44ad; background-image: repeating-linear-gradient(45deg, transparent, transparent 3px, white 3px, white 6px);"></div>Målets relative kurs (P1→P2 linje)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #e67e22; background-image: repeating-linear-gradient(45deg, transparent, transparent 3px, white 3px, white 6px);"></div>CPA - korteste afstand til målets bane</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #ecf0f1; border: 1px dashed #bdc3c7;"></div>Afstandsringe (1nm intervaller)</div>';
echo '<div class="legend-item"><div class="legend-color" style="background-color: #8e44ad; background-image: repeating-linear-gradient(45deg, transparent, transparent 3px, white 3px, white 6px);"></div>Målets bane (P1→P2→CPA)</div>';
echo '</div>';
echo '</div>';
echo '</div>';
}
?>
</div>
</body>
</html>