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';
|
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';
|
$bulkFileName = 'weather_14.json.gz';
|
||||||
$bulkUrl = "https://bulk.openweathermap.org/snapshot/{$bulkFileName}?appid=" . OWM_API_KEY;
|
$bulkUrl = "https://bulk.openweathermap.org/snapshot/{$bulkFileName}?appid=" . OWM_API_KEY;
|
||||||
|
|
||||||
$gzData = @file_get_contents($bulkUrl);
|
// Use a stream context to capture HTTP status headers
|
||||||
if ($gzData === false) {
|
$context = stream_context_create(['http' => ['ignore_errors' => true]]);
|
||||||
http_response_code(500);
|
$gzData = @file_get_contents($bulkUrl, false, $context);
|
||||||
echo json_encode(['error' => 'Failed to download bulk weather data. Check API key and URL.']);
|
|
||||||
|
// 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;
|
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() {
|
function initializeGlobe() {
|
||||||
let epCurveChart = null; // To hold the chart instance
|
let epCurveChart = null; // To hold the chart instance
|
||||||
console.log('Cesium is defined, initializing globe.');
|
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 {
|
try {
|
||||||
console.log('Initializing Cesium Viewer');
|
console.log('Initializing Cesium Viewer');
|
||||||
@ -575,7 +576,8 @@ function initializeGlobe() {
|
|||||||
}
|
}
|
||||||
const stochasticEvents = await response.json();
|
const stochasticEvents = await response.json();
|
||||||
console.log(`Fetched ${stochasticEvents.length} stochastic events.`);
|
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;
|
let averageAnnualLoss = 0;
|
||||||
const eventLosses = [];
|
const eventLosses = [];
|
||||||
@ -597,7 +599,7 @@ function initializeGlobe() {
|
|||||||
return;
|
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)) {
|
if (!windData || windData.closestTrackPoint === null || isNaN(windData.windSpeed)) {
|
||||||
console.error('Could not calculate valid wind data for property, skipping.', {property, event});
|
console.error('Could not calculate valid wind data for property, skipping.', {property, event});
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
// Main configuration file
|
// 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');
|
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` (
|
CREATE TABLE IF NOT EXISTS hurricanes (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
`name` varchar(255) NOT NULL,
|
storm_id VARCHAR(255) NOT NULL,
|
||||||
`year` int(11) NOT NULL,
|
season INT NOT NULL,
|
||||||
PRIMARY KEY (`id`)
|
name VARCHAR(255) NOT NULL,
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
iso_time DATETIME NOT NULL,
|
||||||
|
lat DECIMAL(10, 5) NOT NULL,
|
||||||
CREATE TABLE IF NOT EXISTS `hurricane_tracks` (
|
lon DECIMAL(10, 5) NOT NULL,
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
wind_speed INT,
|
||||||
`hurricane_id` int(11) NOT NULL,
|
pressure INT,
|
||||||
`lat` decimal(9,6) NOT NULL,
|
INDEX(storm_id),
|
||||||
`lon` decimal(9,6) NOT NULL,
|
INDEX(season),
|
||||||
`wind_speed_mph` int(11) NOT NULL,
|
INDEX(iso_time)
|
||||||
`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);
|
|
||||||
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