Working with IBTRACS

This commit is contained in:
Flatlogic Bot 2025-10-14 11:20:42 +00:00
parent e4f30e4fa3
commit 3f8d192ba7
6 changed files with 716289 additions and 163 deletions

View File

@ -6,107 +6,28 @@ error_reporting(E_ALL);
require_once __DIR__ . '/../config.php';
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$cacheFile = __DIR__ . '/../assets/cache/wind.json';
$cacheTime = 3600; // 1 hour in seconds
// Check if a valid cache file exists
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
readfile($cacheFile);
exit;
}
// --- Helper Functions ---
/**
* Calculates the great-circle distance between two points on a sphere.
* @param float $lat1 Latitude of point 1
* @param float $lon1 Longitude of point 1
* @param float $lat2 Latitude of point 2
* @param float $lon2 Longitude of point 2
* @return float Distance in kilometers
*/
function haversineDistance($lat1, $lon1, $lat2, $lon2) {
$earthRadius = 6371; // km
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) * sin($dLat / 2) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
sin($dLon / 2) * sin($dLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $earthRadius * $c;
}
/**
* Interpolates wind data for a grid point using Inverse Distance Weighting.
* @param float $gridLat Latitude of the grid point
* @param float $gridLon Longitude of the grid point
* @param array $cities Array of city weather data
* @param int $numNearest Number of nearest cities to use
* @param float $power Power parameter for IDW
* @return array ['u' => float, 'v' => float]
*/
function interpolatePoint($gridLat, $gridLon, &$cities, $numNearest = 4, $power = 2) {
// Find nearest cities
usort($cities, function($a, $b) use ($gridLat, $gridLon) {
$distA = haversineDistance($gridLat, $gridLon, $a['coord']['Lat'], $a['coord']['Lon']);
$distB = haversineDistance($gridLat, $gridLon, $b['coord']['Lat'], $b['coord']['Lon']);
return $distA - $distB;
});
$nearestCities = array_slice($cities, 0, $numNearest);
$totalWeight = 0;
$weightedU = 0;
$weightedV = 0;
foreach ($nearestCities as $city) {
$dist = haversineDistance($gridLat, $gridLon, $city['coord']['Lat'], $city['coord']['Lon']);
if ($dist == 0) { // If the grid point is exactly at a city, use its data directly
$windSpeed = $city['wind']['speed']; // m/s
$windDeg = $city['wind']['deg'];
$windRad = deg2rad($windDeg);
return [
'u' => -$windSpeed * sin($windRad),
'v' => -$windSpeed * cos($windRad)
];
}
$weight = 1 / pow($dist, $power);
$windSpeed = $city['wind']['speed']; // m/s
$windDeg = $city['wind']['deg'];
$windRad = deg2rad($windDeg);
// Convert to U/V components (meteorological convention)
$u = -$windSpeed * sin($windRad);
$v = -$windSpeed * cos($windRad);
$weightedU += $u * $weight;
$weightedV += $v * $weight;
$totalWeight += $weight;
}
if ($totalWeight == 0) return ['u' => 0, 'v' => 0];
return [
'u' => $weightedU / $totalWeight,
'v' => $weightedV / $totalWeight
];
}
// --- Data Fetching and Processing ---
$bulkFileName = 'weather_14.json.gz';
$bulkUrl = "https://bulk.openweathermap.org/snapshot/{$bulkFileName}?appid=" . OWM_API_KEY;
$gzData = @file_get_contents($bulkUrl);
if ($gzData === false) {
http_response_code(500);
echo json_encode(['error' => 'Failed to download bulk weather data. Check API key and URL.']);
// Use a stream context to capture HTTP status headers
$context = stream_context_create(['http' => ['ignore_errors' => true]]);
$gzData = @file_get_contents($bulkUrl, false, $context);
// Check for errors and specific HTTP status codes
if ($gzData === false || !isset($http_response_header[0]) || strpos($http_response_header[0], '200 OK') === false) {
$error_message = 'Failed to download bulk weather data.';
if (isset($http_response_header[0])) {
if (strpos($http_response_header[0], '401 Unauthorized') !== false) {
$error_message = 'OpenWeatherMap API key is invalid for the Bulk Data service. The key may be correct for other services like weather tiles, but lacks permission for bulk downloads.';
http_response_code(401);
} else {
$error_message .= ' Server responded with: ' . $http_response_header[0];
http_response_code(500);
}
} else {
http_response_code(500);
}
echo json_encode(['error' => $error_message]);
exit;
}

View File

@ -1,8 +1,9 @@
// Set your Cesium Ion default access token immediately
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTY0ZTQ1Yi0zYmYxLTQ5MjItODdkOS05ZDY0ZGRjYjQwM2QiLCJpZCI6MjA5ODgwLCJpYXQiOjE3MTM4MTY3OTB9.A-3Jt_G0K81s-A-XLpT2bn5aY2H3s-n2p-2jYf-i-g';
function initializeGlobe() {
let epCurveChart = null; // To hold the chart instance
console.log('Cesium is defined, initializing globe.');
// Set your Cesium Ion default access token
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTY0ZTQ1Yi0zYmYxLTQ5MjItODdkOS05ZDY0ZGRjYjQwM2QiLCJpZCI6MjA5ODgwLCJpYXQiOjE3MTM4MTY3OTB9.A-3Jt_G0K81s-A-XLpT2bn5aY2H3s-n2p-2jYf-i-g';
try {
console.log('Initializing Cesium Viewer');
@ -575,7 +576,8 @@ function initializeGlobe() {
}
const stochasticEvents = await response.json();
console.log(`Fetched ${stochasticEvents.length} stochastic events.`);
console.log('Stochastic Events:', stochasticEvents);
console.log('Full stochastic events data:', stochasticEvents);
console.log('Stochastic Events:', JSON.stringify(stochasticEvents, null, 2));
let averageAnnualLoss = 0;
const eventLosses = [];
@ -597,7 +599,7 @@ function initializeGlobe() {
return;
}
const windData = getEstimatedWindSpeed(propertyPosition, event.track_data, property.name, event.id);
const windData = getEstimatedWindSpeed(propertyPosition, event.track_data, property.name, event.event_id);
if (!windData || windData.closestTrackPoint === null || isNaN(windData.windSpeed)) {
console.error('Could not calculate valid wind data for property, skipping.', {property, event});

View File

@ -1,6 +1,8 @@
<?php
// Main configuration file
// OpenWeatherMap API Key
// The API key for OpenWeatherMap.
// You can get a free key from https://openweathermap.org/price.
// Please replace 'YOUR_OWM_API_KEY' with your actual key.
define('OWM_API_KEY', '1dfc5acd07d74b93a2ebdcccf3181cab');
?>

80
db/import_ibtracs.php Normal file
View File

@ -0,0 +1,80 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/config.php';
$pdo = db();
$csv_file = __DIR__ . '/../ibtracs.csv';
echo "Importing data into the database...";
$pdo->exec("SET FOREIGN_KEY_CHECKS=0");
$pdo->exec("TRUNCATE TABLE hurricanes");
$pdo->exec("SET FOREIGN_KEY_CHECKS=1");
$file = fopen($csv_file, 'r');
// Get header rows
$header1 = fgetcsv($file);
$header2 = fgetcsv($file);
// Find column indices
$sid_index = array_search('SID', $header1);
$season_index = array_search('SEASON', $header1);
$name_index = array_search('NAME', $header1);
$iso_time_index = array_search('ISO_TIME', $header1);
$lat_index = array_search('LAT', $header1);
$lon_index = array_search('LON', $header1);
$wind_index = array_search('WMO_WIND', $header1);
$pres_index = array_search('WMO_PRES', $header1);
$basin_index = array_search('BASIN', $header1);
$current_year = date('Y');
$ten_years_ago = $current_year - 10;
$count = 0;
$processed_sids = [];
$pdo->beginTransaction();
while (($row = fgetcsv($file)) !== FALSE) {
$season = (int)$row[$season_index];
$basin = $row[$basin_index];
$sid = $row[$sid_index];
if ($season >= $ten_years_ago && $basin === 'NA') {
if (!in_array($sid, $processed_sids)) {
if (count($processed_sids) >= 2) {
break; // Stop after processing two storms
}
$processed_sids[] = $sid;
}
$stmt = $pdo->prepare(
'INSERT INTO `hurricanes` (`storm_id`, `season`, `name`, `iso_time`, `lat`, `lon`, `wind_speed`, `pressure`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
);
$stmt->execute([
$sid,
$season,
$row[$name_index],
$row[$iso_time_index],
(float)$row[$lat_index],
(float)$row[$lon_index],
(int)$row[$wind_index],
(int)$row[$pres_index]
]);
$count++;
if ($count % 1000 === 0) {
echo "Inserted $count records...\n";
}
}
}
$pdo->commit();
fclose($file);
echo "Import complete. Inserted a total of $count records.\n";
?>

View File

@ -1,60 +1,14 @@
CREATE TABLE IF NOT EXISTS `hurricanes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`year` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hurricane_tracks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`hurricane_id` int(11) NOT NULL,
`lat` decimal(9,6) NOT NULL,
`lon` decimal(9,6) NOT NULL,
`wind_speed_mph` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `hurricane_id` (`hurricane_id`),
CONSTRAINT `hurricane_tracks_ibfk_1` FOREIGN KEY (`hurricane_id`) REFERENCES `hurricanes` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Clear existing data to prevent duplicates on re-run
SET FOREIGN_KEY_CHECKS = 0;
DELETE FROM `hurricanes`;
DELETE FROM `hurricane_tracks`;
SET FOREIGN_KEY_CHECKS = 1;
ALTER TABLE `hurricanes` AUTO_INCREMENT = 1;
ALTER TABLE `hurricane_tracks` AUTO_INCREMENT = 1;
-- Populate data
-- Hurricane Andrew (1992)
INSERT INTO `hurricanes` (`name`, `year`) VALUES ('Andrew', 1992);
SET @andrew_id = LAST_INSERT_ID();
INSERT INTO `hurricane_tracks` (`hurricane_id`, `lat`, `lon`, `wind_speed_mph`, `timestamp`) VALUES
(@andrew_id, 25.200000, -75.700000, 150, 1661299200),
(@andrew_id, 25.400000, -77.700000, 160, 1661310000),
(@andrew_id, 25.500000, -79.700000, 175, 1661320800),
(@andrew_id, 25.600000, -81.700000, 165, 1661331600),
(@andrew_id, 25.800000, -83.700000, 145, 1661342400),
(@andrew_id, 26.200000, -85.700000, 135, 1661353200);
-- Hurricane Katrina (2005)
INSERT INTO `hurricanes` (`name`, `year`) VALUES ('Katrina', 2005);
SET @katrina_id = LAST_INSERT_ID();
INSERT INTO `hurricane_tracks` (`hurricane_id`, `lat`, `lon`, `wind_speed_mph`, `timestamp`) VALUES
(@katrina_id, 23.100000, -80.100000, 80, 1124928000),
(@katrina_id, 24.500000, -81.800000, 95, 1124985600),
(@katrina_id, 26.000000, -85.000000, 110, 1125043200),
(@katrina_id, 28.200000, -89.600000, 175, 1125100800),
(@katrina_id, 30.200000, -89.600000, 125, 1125158400),
(@katrina_id, 31.100000, -89.600000, 75, 1125216000);
-- Hurricane Irma (2017)
INSERT INTO `hurricanes` (`name`, `year`) VALUES ('Irma', 2017);
SET @irma_id = LAST_INSERT_ID();
INSERT INTO `hurricane_tracks` (`hurricane_id`, `lat`, `lon`, `wind_speed_mph`, `timestamp`) VALUES
(@irma_id, 16.100000, -55.000000, 175, 1504656000),
(@irma_id, 18.200000, -63.000000, 185, 1504742400),
(@irma_id, 22.000000, -75.000000, 160, 1504828800),
(@irma_id, 23.500000, -81.500000, 130, 1504915200),
(@irma_id, 25.900000, -81.700000, 115, 1505001600),
(@irma_id, 28.900000, -82.400000, 75, 1505088000);
CREATE TABLE IF NOT EXISTS hurricanes (
id INT AUTO_INCREMENT PRIMARY KEY,
storm_id VARCHAR(255) NOT NULL,
season INT NOT NULL,
name VARCHAR(255) NOT NULL,
iso_time DATETIME NOT NULL,
lat DECIMAL(10, 5) NOT NULL,
lon DECIMAL(10, 5) NOT NULL,
wind_speed INT,
pressure INT,
INDEX(storm_id),
INDEX(season),
INDEX(iso_time)
);

716167
ibtracs.csv Normal file

File diff suppressed because it is too large Load Diff