Working with IBTRACS
This commit is contained in:
parent
e4f30e4fa3
commit
3f8d192ba7
117
api/wind.php
117
api/wind.php
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -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
80
db/import_ibtracs.php
Normal 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";
|
||||
?>
|
||||
@ -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
716167
ibtracs.csv
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user