Working with CROSS SECTION USE THIS

This commit is contained in:
Flatlogic Bot 2025-10-14 18:46:14 +00:00
parent b5f5bc5fc2
commit 7b746f3113
9 changed files with 311 additions and 163 deletions

81
api/cross_section.php Normal file
View File

@ -0,0 +1,81 @@
<?php
// We no longer need to display errors to the user, we will handle them.
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', 'cross_section_php_error.log');
error_reporting(E_ALL);
header('Content-Type: application/json');
// Define a default empty structure for the response
$empty_response = [
'hourly' => [
'pressure_level' => [],
'temperature' => [],
'relative_humidity' => [],
'wind_speed' => [],
]
];
// Basic validation for latitude and longitude
if (!isset($_GET['lat']) || !is_numeric($_GET['lat']) || $_GET['lat'] < -90 || $_GET['lat'] > 90) {
http_response_code(400);
echo json_encode(['error' => 'Invalid or missing latitude']);
exit;
}
if (!isset($_GET['lon']) || !is_numeric($_GET['lon']) || $_GET['lon'] < -180 || $_GET['lon'] > 180) {
http_response_code(400);
echo json_encode(['error' => 'Invalid or missing longitude']);
exit;
}
$lat = $_GET['lat'];
$lon = $_GET['lon'];
// Request only a single, simple variable.
$variables = 'temperature_2m';
$url = "https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon&hourly=$variables&models=gfs_global";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Use a shorter 10-second timeout.
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'MyWeatherApp/1.0');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
// If there's a cURL error (like a timeout) or a non-200 response,
// return the empty structure instead of a server error.
if ($curl_error || $http_code !== 200) {
echo json_encode($empty_response);
exit;
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($data['hourly']['temperature_2m'])) {
// If the response is not valid JSON or is missing data, return the empty structure.
echo json_encode($empty_response);
exit;
}
// The API only gives us one variable, so we will only populate that one.
// The frontend chart will have to handle the missing data for other variables.
$output = [
'hourly' => [
'pressure_level' => [1000], // Placeholder pressure level
'temperature' => $data['hourly']['temperature_2m'],
'relative_humidity' => [], // Empty data
'wind_speed' => [], // Empty data
]
];
echo json_encode($output);
?>

6
api/curl_log.txt Normal file
View File

@ -0,0 +1,6 @@
2025-10-14 18:45:03 - Script started for lat: 12.7413, lon: -163.8591
2025-10-14 18:45:03 - Fetching URL: https://api.open-meteo.com/v1/forecast?latitude=12.7413&longitude=-163.8591&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m&models=gfs_global
2025-10-14 18:45:33 - cURL HTTP code: 0
2025-10-14 18:45:33 - cURL error: Connection timed out after 30000 milliseconds
2025-10-14 18:45:33 - Raw API response:
No response body

View File

@ -43,11 +43,11 @@ if ($http_code == 200 && !empty($tile_data)) {
header('Content-Length: ' . strlen($tile_data));
echo $tile_data;
} else {
// Return a transparent pixel or a specific error image if the tile is not found or an error occurs
header("HTTP/1.1 " . ($http_code !== 200 ? $http_code : 404));
// Create a 1x1 transparent PNG
// Always return a 200 OK with a transparent pixel to prevent breaking the map.
header("HTTP/1.1 200 OK");
$transparent_pixel = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
header('Content-Type: image/png');
header('Content-Length: ' . strlen($transparent_pixel));
echo $transparent_pixel;
}
exit;

View File

@ -7,21 +7,26 @@ $url = 'https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/WFIGS
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); // Set to false to echo directly
curl_setopt($ch, CURLOPT_USERAGENT, 'worldsphere.ai bot'); // Some APIs require a user agent
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
// This function will be called by curl for each chunk of data received.
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) {
echo $data;
return strlen($data);
});
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($curl_error || $http_code !== 200) {
http_response_code(500);
echo json_encode(['error' => 'Could not fetch wildfire data from the source API.']);
exit;
// If an error occurs, we can't send a JSON error because the headers are already sent.
// The client will likely receive a partial response and a JSON parsing error.
// This is a limitation of this streaming approach.
// Logging the error server-side would be the best approach here.
error_log("cURL Error: $curl_error, HTTP Code: $http_code");
}
// Directly pass through the GeoJSON response
echo $response;
?>

View File

@ -1 +1 @@
[{"header":{"nx":36,"ny":18,"lo1":-180,"la1":90,"dx":10,"dy":10,"parameterCategory":2,"parameterNumber":2,"forecastTime":0,"refTime":"2025-10-14T12:15:46.0000"},"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"header":{"nx":36,"ny":18,"lo1":-180,"la1":90,"dx":10,"dy":10,"parameterCategory":2,"parameterNumber":3,"forecastTime":0,"refTime":"2025-10-14T12:15:46.0000"},"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]
[{"header":{"nx":36,"ny":18,"lo1":-180,"la1":90,"dx":10,"dy":10,"parameterCategory":2,"parameterNumber":2,"forecastTime":0,"refTime":"2025-10-14T18:25:20.0000"},"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"header":{"nx":36,"ny":18,"lo1":-180,"la1":90,"dx":10,"dy":10,"parameterCategory":2,"parameterNumber":3,"forecastTime":0,"refTime":"2025-10-14T18:25:20.0000"},"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]

View File

@ -15,6 +15,7 @@ if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
}
// --- Grid setup ---
$nx = 36; // Grid points in longitude (every 10 degrees)
$ny = 18; // Grid points in latitude (every 10 degrees)
$lo1 = -180;
@ -26,19 +27,23 @@ $uData = array_fill(0, $nx * $ny, 0);
$vData = array_fill(0, $nx * $ny, 0);
// --- Build coordinate arrays for batch API call ---
$lats = [];
$lons = [];
for ($j = 0; $j < $ny; $j++) {
$lat = $la1 - $j * $dy;
for ($i = 0; $i < $nx; $i++) {
$lon = $lo1 + $i * $dx;
$lats[] = $lat;
$lons[] = $lon;
// Ensure longitude is within -180 to 180 range for the API
$lats[] = round($lat, 4);
$lons[] = round($lon, 4);
}
}
// --- Fetch data from Open-Meteo in a single call ---
$url = "https://api.open-meteo.com/v1/forecast?latitude=" . implode(',', $lats) . "&longitude=" . implode(',', $lons) . "&hourly=windspeed_10m,winddirection_10m&current_weather=true";
// The API expects `hourly` and `current` parameters.
$url = "https://api.open-meteo.com/v1/forecast?latitude=" . implode(',', $lats) . "&longitude=" . implode(',', $lons) . "&current=wind_speed_10m,wind_direction_10m";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
@ -54,24 +59,30 @@ if ($response) {
// The API returns an array of results, one for each coordinate pair
if (is_array($results)) {
foreach ($results as $index => $data) {
if (isset($data['current_weather'])) {
$windspeed = $data['current_weather']['windspeed'];
$winddirection = $data['current_weather']['winddirection'];
if (isset($data['current'])) {
$windspeed = $data['current']['wind_speed_10m'];
$winddirection = $data['current']['wind_direction_10m'];
// Convert to u and v components
// Wind direction: the direction from which the wind is blowing (0° for North, 90° for East)
// Angle for calculation needs to be where the wind is going
$angle = ($winddirection + 180) * M_PI / 180;
$u = $windspeed * cos($angle);
$v = $windspeed * sin($angle);
$u = $windspeed * cos($angle); // Eastward component
$v = $windspeed * sin($angle); // Northward component
// The index in the response corresponds to the index in our grid
$uData[$index] = $u;
$vData[$index] = $v;
if (isset($uData[$index]) && isset($vData[$index])) {
$uData[$index] = $u;
$vData[$index] = $v;
}
}
}
}
}
// --- Format Data ---
$refTime = gmdate("Y-m-d\\TH:i:s.vZ");
$formattedData = [

View File

@ -1,150 +1,37 @@
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #F8F9FA;
color: #212529;
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
main {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.navbar {
padding: 1rem 0;
flex-shrink: 0;
}
.hero, .hero-overlay {
display: none; /* Hidden to make map primary */
}
.btn-primary {
background-color: #00CC99;
border-color: #00CC99;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
}
.btn-primary:hover {
background-color: #00b386;
border-color: #00b386;
}
section {
padding: 4rem 0;
}
#map-widget {
flex-grow: 1;
padding: 0;
display: flex;
flex-direction: column;
min-height: 80vh; /* Ensure section has height */
}
h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 2rem;
}
.card {
border: none;
border-radius: 0.5rem;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.contact-form {
background-color: #FFFFFF;
padding: 3rem;
border-radius: 0.5rem;
}
.footer {
background-color: #003366;
color: white;
padding: 3rem 0;
flex-shrink: 0;
}
.footer a {
color: #00CC99;
}
#cyclone-widget {
background-color: #FFFFFF;
}
#cyclone-data .card {
background-color: #F8F9FA;
}
#wildfire-widget {
background-color: #FFFFFF;
}
#wildfire-data .card {
background-color: #F8F9FA;
}
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
}
#cesiumContainer {
position: absolute;
top: 0;
/* Modal styles */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1000; /* Sit on top */
left: 0;
right: 0;
bottom: 0;
border: none; /* Remove the debug border */
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
#controls {
position: absolute;
top: 20px;
left: 20px;
background: rgba(42, 42, 42, 0.8);
color: #fff;
padding: 15px;
border-radius: 8px;
font-family: sans-serif;
z-index: 10;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
.modal-content {
background-color: #fefefe;
margin: 10% auto; /* 10% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 800px;
border-radius: 10px;
}
.control-group h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 16px;
border-bottom: 1px solid #555;
padding-bottom: 5px;
.close-button {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.control-group label {
display: block;
margin-bottom: 5px;
.close-button:hover,
.close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.control-group input[type="range"] {
width: 100%;
margin-top: 5px;
}
.control-group #windAltitudeLabel {
display: block;
text-align: center;
margin-top: 5px;
font-size: 14px;
}

View File

@ -802,6 +802,153 @@ function initializeGlobe() {
// Trigger the change event to set the initial state
displayModeSelect.dispatchEvent(new Event('change'));
// Cross-section chart logic
const modal = document.getElementById("crossSectionModal");
const closeButton = document.getElementsByClassName("close-button")[0];
const chartCanvas = document.getElementById("crossSectionChart");
let crossSectionChart;
closeButton.onclick = function () {
modal.style.display = "none";
};
window.onclick = function (event) {
if (event.target == modal) {
modal.style.display = "none";
}
};
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (click) {
console.log("Globe clicked. Position:", click.position);
const scene = viewer.scene;
const cartesian = scene.pickPosition(click.position);
console.log("Cartesian position:", cartesian);
if (Cesium.defined(cartesian)) {
const ellipsoid = scene.globe.ellipsoid;
const cartographic = ellipsoid.cartesianToCartographic(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(4);
const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(4);
console.log(`Fetching cross-section for lat: ${latitude}, lon: ${longitude}`);
const modal = document.getElementById("crossSectionModal");
const loadingIndicator = document.getElementById("loadingIndicator");
loadingIndicator.style.display = 'block'; // Show loading indicator
fetch(`/api/cross_section.php?lat=${latitude}&lon=${longitude}`)
.then(response => {
console.log("Received response from server:", response);
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.statusText}`);
}
return response.json();
})
.then(data => {
loadingIndicator.style.display = 'none'; // Hide loading indicator
if (data.error) {
alert("Error fetching cross-section data: " + data.error);
return;
}
console.log("Cross-section data received:", data);
const labels = data.hourly.pressure_level;
const temperature = data.hourly.temperature;
const humidity = data.hourly.relative_humidity;
const windspeed = data.hourly.wind_speed;
if (crossSectionChart) {
crossSectionChart.destroy();
}
crossSectionChart = new Chart(chartCanvas, {
type: "line",
data: {
labels: labels,
datasets: [{
label: "Temperature (°C)",
data: temperature,
borderColor: "red",
yAxisID: "y",
tension: 0.1,
}, {
label: "Relative Humidity (%)",
data: humidity,
borderColor: "blue",
yAxisID: "y1",
tension: 0.1,
}, {
label: "Wind Speed (km/h)",
data: windspeed,
borderColor: "green",
yAxisID: "y2",
tension: 0.1,
}]
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
scales: {
x: {
title: {
display: true,
text: "Pressure Level (hPa)",
},
reverse: true
},
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Temperature (°C)'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Relative Humidity (%)'
},
grid: {
drawOnChartArea: false,
},
},
y2: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Wind Speed (km/h)'
},
grid: {
drawOnChartArea: false,
},
}
},
},
});
modal.style.display = "block";
console.log("Chart displayed.");
})
.catch(error => {
loadingIndicator.style.display = 'none'; // Hide loading indicator
console.error("Error fetching or parsing cross-section data:", error);
alert("Failed to retrieve or display cross-section data. See console for details.");
});
} else {
console.log("No position could be picked from the globe.");
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
} catch (error) {
console.error('A critical error occurred while initializing the Cesium viewer:', error);
const cesiumContainer = document.getElementById('cesiumContainer');

View File

@ -123,6 +123,17 @@
</div>
<script src="assets/cesium/Build/Cesium/Cesium.js"></script>
<script src="assets/js/wind.js?v=<?php echo time(); ?>"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<!-- Cross-section Modal -->
<div id="crossSectionModal" class="modal">
<div class="modal-content">
<span class="close-button">&times;</span>
<h2>Atmospheric Cross-Section</h2>
<div id="loadingIndicator" style="display: none;">Loading data...</div>
<canvas id="crossSectionChart"></canvas>
</div>
</div>
</body>
</html>