Revert to version 73d0602
This commit is contained in:
parent
9feea426a6
commit
37c09adfe4
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('User-Agent: worldsphere.ai, contact@worldsphere.ai');
|
||||
|
||||
// URL for the SPC RSS feed
|
||||
$url = 'http://www.spc.noaa.gov/products/spcrss.xml';
|
||||
|
||||
// Fetch the RSS feed content
|
||||
$rss = @file_get_contents($url);
|
||||
|
||||
if ($rss === false) {
|
||||
echo json_encode(['error' => 'Failed to fetch SPC data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parse the XML
|
||||
$xml = @simplexml_load_string($rss);
|
||||
|
||||
if ($xml === false) {
|
||||
echo json_encode(['error' => 'Failed to parse SPC XML.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$alerts = [];
|
||||
if (isset($xml->channel->item)) {
|
||||
foreach ($xml->channel->item as $item) {
|
||||
// The title often contains the most succinct information
|
||||
$title = (string)$item->title;
|
||||
|
||||
// The description can be long, let's create a summary or just use the title
|
||||
$description = (string)$item->description;
|
||||
|
||||
$alerts[] = [
|
||||
'headline' => $title,
|
||||
'description' => strip_tags($description), // Basic sanitization
|
||||
'link' => (string)$item->link
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Return the alerts as JSON
|
||||
echo json_encode($alerts);
|
||||
?>
|
||||
91
api/gfs.php
Normal file
91
api/gfs.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// --- Configuration ---
|
||||
$gfs_url = 'https://nomads.ncep.noaa.gov/pub/data/nccf/com/gfs/prod/gfs.20251013/00/atmos/gfs.t00z.pgrb2.0p25.f000';
|
||||
$tmp_dir = '/tmp';
|
||||
$grib_file = $tmp_dir . '/' . basename($gfs_url);
|
||||
$xyz_file = $tmp_dir . '/gfs.xyz';
|
||||
|
||||
// --- Download the GFS file ---
|
||||
if (!file_exists($grib_file)) {
|
||||
$fp = fopen($grib_file, 'w');
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $gfs_url);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
if ($http_code !== 200) {
|
||||
unlink($grib_file); // Clean up failed download
|
||||
echo json_encode(['error' => 'Failed to download GFS file', 'http_code' => $http_code]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Convert GRIB to XYZ using gdal_translate ---
|
||||
// We'll use band 1 for this example. You can use gdalinfo to see available bands.
|
||||
$gdal_command = 'gdal_translate -b 1 -of XYZ ' . escapeshellarg($grib_file) . ' ' . escapeshellarg($xyz_file);
|
||||
$gdal_output = shell_exec($gdal_command);
|
||||
|
||||
if (!file_exists($xyz_file)) {
|
||||
echo json_encode(['error' => 'Failed to convert GRIB to XYZ', 'gdal_output' => $gdal_output]);
|
||||
unlink($grib_file);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Read the XYZ file and create GeoJSON features ---
|
||||
$features = [];
|
||||
$handle = fopen($xyz_file, 'r');
|
||||
$line_count = 0;
|
||||
$sample_rate = 100; // Process 1 in every 100 lines
|
||||
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line_count++;
|
||||
if ($line_count % $sample_rate !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = preg_split('/\s+/', trim($line));
|
||||
if (count($parts) === 3) {
|
||||
$lon = floatval($parts[0]);
|
||||
$lat = floatval($parts[1]);
|
||||
$value = floatval($parts[2]);
|
||||
|
||||
// Skip points that are exactly zero, often represent no data
|
||||
if ($value === 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$features[] = [
|
||||
'type' => 'Feature',
|
||||
'properties' => ['value' => $value],
|
||||
'geometry' => [
|
||||
'type' => 'Point',
|
||||
'coordinates' => [$lon, $lat]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
// --- Clean up temporary files ---
|
||||
unlink($grib_file);
|
||||
unlink($xyz_file);
|
||||
|
||||
// --- Output the GeoJSON ---
|
||||
echo json_encode([
|
||||
'type' => 'FeatureCollection',
|
||||
'features' => $features
|
||||
]);
|
||||
|
||||
?>
|
||||
118
api/hurricanes.php
Normal file
118
api/hurricanes.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// URL for the NHC active hurricane KML data
|
||||
$nhc_kmz_url = 'https://www.nhc.noaa.gov/gis/kml/nhc.kmz';
|
||||
|
||||
// Temporary file to store the KMZ
|
||||
$tmp_kmz_file = tempnam(sys_get_temp_dir(), 'nhc_kmz');
|
||||
|
||||
// Fetch the KMZ file
|
||||
$kmz_data = file_get_contents($nhc_kmz_url);
|
||||
|
||||
if ($kmz_data === false) {
|
||||
echo json_encode(['error' => 'Failed to fetch NHC data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Save the data to the temporary file
|
||||
file_put_contents($tmp_kmz_file, $kmz_data);
|
||||
|
||||
if (!class_exists('ZipArchive')) {
|
||||
echo json_encode(['error' => 'ZipArchive class does not exist.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Use ZipArchive to open the KMZ file
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($tmp_kmz_file) === TRUE) {
|
||||
// NHC KMZ files typically contain a single KML file, often named doc.kml or similar.
|
||||
// We will look for the first .kml file in the archive.
|
||||
$kml_content = false;
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
if (strtolower(substr($filename, -4)) === '.kml') {
|
||||
$kml_content = $zip->getFromIndex($i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
if ($kml_content === false) {
|
||||
echo json_encode(['error' => 'KML file not found in the KMZ archive.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parse the KML content
|
||||
$xml = simplexml_load_string($kml_content, "SimpleXMLElement", LIBXML_NOCDATA);
|
||||
if ($xml === false) {
|
||||
echo json_encode(['error' => 'Failed to parse KML data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Register the KML namespace
|
||||
$xml->registerXPathNamespace('kml', 'http://www.opengis.net/kml/2.2');
|
||||
|
||||
$features = [];
|
||||
|
||||
// Find all Placemarks in the KML
|
||||
foreach ($xml->xpath('//kml:Placemark') as $placemark) {
|
||||
$placemark->registerXPathNamespace('kml', 'http://www.opengis.net/kml/2.2');
|
||||
$name = (string)$placemark->name;
|
||||
|
||||
// Look for Polygon
|
||||
$polygon = $placemark->xpath('.//kml:Polygon');
|
||||
if ($polygon && isset($polygon[0]->outerBoundaryIs->LinearRing->coordinates)) {
|
||||
$coordinates_str = (string)$polygon[0]->outerBoundaryIs->LinearRing->coordinates;
|
||||
$coordinates = parse_coordinates($coordinates_str);
|
||||
if (!empty($coordinates)) {
|
||||
$features[] = [
|
||||
'name' => $name,
|
||||
'type' => 'Polygon',
|
||||
'coordinates' => $coordinates
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Look for LineString
|
||||
$linestring = $placemark->xpath('.//kml:LineString');
|
||||
if ($linestring && isset($linestring[0]->coordinates)) {
|
||||
$coordinates_str = (string)$linestring[0]->coordinates;
|
||||
$coordinates = parse_coordinates($coordinates_str);
|
||||
if (!empty($coordinates)) {
|
||||
$features[] = [
|
||||
'name' => $name,
|
||||
'type' => 'LineString',
|
||||
'coordinates' => $coordinates
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($features);
|
||||
|
||||
} else {
|
||||
echo json_encode(['error' => 'Failed to open KMZ file.']);
|
||||
}
|
||||
|
||||
// Clean up the temporary file
|
||||
unlink($tmp_kmz_file);
|
||||
|
||||
function parse_coordinates($coordinates_str) {
|
||||
$coords = [];
|
||||
$pairs = explode(' ', trim($coordinates_str));
|
||||
foreach ($pairs as $pair) {
|
||||
$parts = explode(',', $pair);
|
||||
if (count($parts) >= 2) {
|
||||
$lon = floatval($parts[0]);
|
||||
$lat = floatval($parts[1]);
|
||||
// Ensure coordinates are valid
|
||||
if (is_finite($lat) && is_finite($lon)) {
|
||||
$coords[] = $lon;
|
||||
$coords[] = $lat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $coords;
|
||||
}
|
||||
?>
|
||||
66
api/spc.php
Normal file
66
api/spc.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// URL for the SPC Day 1 Outlook KML data
|
||||
$spc_kml_url = 'https://www.spc.noaa.gov/products/outlook/day1otlk_cat.kml';
|
||||
|
||||
// Fetch the KML file
|
||||
$kml_content = file_get_contents($spc_kml_url);
|
||||
|
||||
if ($kml_content === false) {
|
||||
echo json_encode(['error' => 'Failed to fetch SPC data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parse the KML content
|
||||
$xml = simplexml_load_string($kml_content, "SimpleXMLElement", LIBXML_NOCDATA);
|
||||
if ($xml === false) {
|
||||
echo json_encode(['error' => 'Failed to parse KML data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Register the KML namespace
|
||||
$xml->registerXPathNamespace('kml', 'http://www.opengis.net/kml/2.2');
|
||||
|
||||
$features = [];
|
||||
|
||||
// Find all Placemarks in the KML
|
||||
foreach ($xml->xpath('//kml:Placemark') as $placemark) {
|
||||
$placemark->registerXPathNamespace('kml', 'http://www.opengis.net/kml/2.2');
|
||||
$name = (string)$placemark->name;
|
||||
|
||||
// Look for Polygon
|
||||
$polygon = $placemark->xpath('.//kml:Polygon');
|
||||
if ($polygon && isset($polygon[0]->outerBoundaryIs->LinearRing->coordinates)) {
|
||||
$coordinates_str = (string)$polygon[0]->outerBoundaryIs->LinearRing->coordinates;
|
||||
$coordinates = parse_coordinates($coordinates_str);
|
||||
if (!empty($coordinates)) {
|
||||
$features[] = [
|
||||
'name' => $name,
|
||||
'type' => 'Polygon',
|
||||
'coordinates' => $coordinates
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($features);
|
||||
|
||||
function parse_coordinates($coordinates_str) {
|
||||
$coords = [];
|
||||
$pairs = explode(' ', trim($coordinates_str));
|
||||
foreach ($pairs as $pair) {
|
||||
$parts = explode(',', $pair);
|
||||
if (count($parts) >= 2) {
|
||||
$lon = floatval($parts[0]);
|
||||
$lat = floatval($parts[1]);
|
||||
// Ensure coordinates are valid
|
||||
if (is_finite($lat) && is_finite($lon)) {
|
||||
$coords[] = $lon;
|
||||
$coords[] = $lat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $coords;
|
||||
}
|
||||
?>
|
||||
@ -17,25 +17,6 @@ if ($response === false) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
echo json_encode(['error' => 'Could not parse wildfire data.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$features = $data['features'] ?? [];
|
||||
|
||||
$wildfires = [];
|
||||
foreach ($features as $feature) {
|
||||
$properties = $feature['properties'];
|
||||
$wildfires[] = [
|
||||
'name' => $properties['poly_IncidentName'],
|
||||
'acres' => $properties['poly_Acres_AutoCalc'],
|
||||
'started' => $properties['attr_InitialResponseDateTime'],
|
||||
'percent_contained' => $properties['attr_PercentContained'],
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($wildfires);
|
||||
// Directly pass through the GeoJSON response
|
||||
echo $response;
|
||||
?>
|
||||
@ -1,44 +1,26 @@
|
||||
|
||||
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 {
|
||||
position: relative;
|
||||
padding: 8rem 0;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hero-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 25, 51, 0.7);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero .container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.25rem;
|
||||
.hero, .hero-overlay {
|
||||
display: none; /* Hidden to make map primary */
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@ -58,6 +40,14 @@ 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;
|
||||
@ -80,6 +70,7 @@ h2 {
|
||||
background-color: #003366;
|
||||
color: white;
|
||||
padding: 3rem 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
@ -101,3 +92,20 @@ h2 {
|
||||
#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;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: none; /* Remove the debug border */
|
||||
}
|
||||
@ -1,104 +1,119 @@
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const contactForm = document.getElementById('contactForm');
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
const status = document.getElementById('form-status');
|
||||
console.log('DOM fully loaded and parsed');
|
||||
// Set your Cesium Ion default access token
|
||||
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTY0ZTQ1Yi0zYmYxLTQ5MjItODdkOS05ZDY0ZGRjYjQwM2QiLCJpZCI6MjA5ODgwLCJpYXQiOjE3MTM4MTY3OTB9.A-3Jt_G0K81s-A-XLpT2bn5aY2H3s-n2p-2jYf-i-g';
|
||||
|
||||
// Basic client-side validation
|
||||
const name = formData.get('name');
|
||||
const email = formData.get('email');
|
||||
const message = formData.get('message');
|
||||
|
||||
if (!name || !email || !message) {
|
||||
status.innerHTML = '<div class="alert alert-danger">Please fill out all fields.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('contact.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
status.innerHTML = `<div class="alert alert-success">${data.message}</div>`;
|
||||
form.reset();
|
||||
} else {
|
||||
status.innerHTML = `<div class="alert alert-danger">${data.message}</div>`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
status.innerHTML = '<div class="alert alert-danger">An error occurred. Please try again.</div>';
|
||||
console.error('Error:', error);
|
||||
});
|
||||
try {
|
||||
console.log('Initializing Cesium Viewer');
|
||||
// Initialize a Cesium Viewer with a map
|
||||
const viewer = new Cesium.Viewer('cesiumContainer', {
|
||||
imageryProvider: new Cesium.OpenStreetMapImageryProvider({
|
||||
url : 'https://a.tile.openstreetmap.org/'
|
||||
}),
|
||||
animation: false,
|
||||
baseLayerPicker: false,
|
||||
fullscreenButton: false,
|
||||
geocoder: false,
|
||||
homeButton: false,
|
||||
infoBox: true,
|
||||
sceneModePicker: false,
|
||||
selectionIndicator: false,
|
||||
timeline: false,
|
||||
navigationHelpButton: false,
|
||||
scene3DOnly: true
|
||||
});
|
||||
}
|
||||
viewer.scene.globe.depthTestAgainstTerrain = false;
|
||||
console.log('Cesium Viewer initialized successfully');
|
||||
|
||||
// Fetch and display cyclone data
|
||||
const cycloneDataContainer = document.getElementById('cyclone-data');
|
||||
if (cycloneDataContainer) {
|
||||
fetch('api/cyclone.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(alert => {
|
||||
html += `
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">${alert.headline}</h5>
|
||||
<p class="card-text">${alert.description.substring(0, 150)}...</p>
|
||||
<a href="${alert.link}" target="_blank" rel="noopener noreferrer" class="btn btn-primary btn-sm mt-auto">Read More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
cycloneDataContainer.innerHTML = html;
|
||||
} else {
|
||||
cycloneDataContainer.innerHTML = '<div class="col-12"><p>No active severe weather alerts from the SPC at the moment.</p></div>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
cycloneDataContainer.innerHTML = '<p>Could not load cyclone data. Please try again later.</p>';
|
||||
console.error('Error fetching cyclone data:', error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Fetch and display wildfire data
|
||||
const wildfireDataContainer = document.getElementById('wildfire-data');
|
||||
if (wildfireDataContainer) {
|
||||
fetch('api/wildfires.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(fire => {
|
||||
html += `
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${fire.name}</h5>
|
||||
<p class="card-text">${fire.acres ? Math.round(fire.acres) + ' acres' : 'Size not available'}</p>
|
||||
<p class="card-text">${fire.percent_contained !== null ? fire.percent_contained + '% contained' : 'Containment not available'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
wildfireDataContainer.innerHTML = html;
|
||||
} else {
|
||||
wildfireDataContainer.innerHTML = '<div class="col-12"><p>No active wildfires reported at the moment.</p></div>';
|
||||
// Function to load wildfire data
|
||||
const loadWildfireData = async () => {
|
||||
try {
|
||||
console.log('Fetching wildfire data...');
|
||||
const response = await fetch('api/wildfires.php');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
wildfireDataContainer.innerHTML = '<p>Could not load wildfire data. Please try again later.</p>';
|
||||
console.error('Error fetching wildfire data:', error);
|
||||
});
|
||||
const geojsonData = await response.json();
|
||||
console.log('Wildfire data fetched successfully.');
|
||||
|
||||
const wildfireDataSource = new Cesium.GeoJsonDataSource();
|
||||
await wildfireDataSource.load(geojsonData, {
|
||||
stroke: Cesium.Color.RED,
|
||||
fill: Cesium.Color.RED.withAlpha(0.5),
|
||||
strokeWidth: 2
|
||||
});
|
||||
|
||||
viewer.dataSources.add(wildfireDataSource);
|
||||
console.log('Wildfire data source added to viewer.');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading wildfire data:', error);
|
||||
// This catch block prevents the globe from crashing if the API fails.
|
||||
}
|
||||
};
|
||||
|
||||
// Function to load hurricane data
|
||||
const loadHurricaneData = async () => {
|
||||
try {
|
||||
console.log('Fetching hurricane data...');
|
||||
// Use KmlDataSource for the KML data from the hurricanes API
|
||||
const hurricaneDataSource = await Cesium.KmlDataSource.load('api/hurricanes.php', {
|
||||
camera: viewer.camera,
|
||||
canvas: viewer.canvas
|
||||
});
|
||||
await viewer.dataSources.add(hurricaneDataSource);
|
||||
console.log('Hurricane data source added to viewer.');
|
||||
} catch (error) {
|
||||
console.error('Error loading hurricane data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to load GFS data
|
||||
const loadGfsData = async () => {
|
||||
try {
|
||||
console.log('Fetching GFS data...');
|
||||
const response = await fetch('api/gfs.php');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const geojsonData = await response.json();
|
||||
console.log('GFS data fetched successfully.');
|
||||
|
||||
const gfsDataSource = new Cesium.GeoJsonDataSource();
|
||||
await gfsDataSource.load(geojsonData, {
|
||||
stroke: Cesium.Color.BLUE.withAlpha(0.3),
|
||||
fill: Cesium.Color.BLUE.withAlpha(0.3),
|
||||
strokeWidth: 1
|
||||
});
|
||||
|
||||
// Simple heatmap-like styling for GFS points
|
||||
gfsDataSource.entities.values.forEach(entity => {
|
||||
const value = entity.properties.value.getValue();
|
||||
const color = Cesium.Color.fromHsl(0.6 - (value - 100000) / 2000 * 0.5, 1.0, 0.5).withAlpha(0.5);
|
||||
entity.point = new Cesium.PointGraphics({
|
||||
color: color,
|
||||
pixelSize: 5
|
||||
});
|
||||
});
|
||||
|
||||
viewer.dataSources.add(gfsDataSource);
|
||||
console.log('GFS data source added to viewer.');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading GFS data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load all data sources
|
||||
loadWildfireData();
|
||||
// loadHurricaneData();
|
||||
// loadGfsData();
|
||||
|
||||
} catch (error) {
|
||||
console.error('A critical error occurred while initializing the Cesium viewer:', error);
|
||||
const cesiumContainer = document.getElementById('cesiumContainer');
|
||||
cesiumContainer.innerHTML = '<div class="alert alert-danger">Error: Could not load the 3D scene. Please check the console for details.</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
BIN
assets/pasted-20251014-012612-96ac69da.png
Normal file
BIN
assets/pasted-20251014-012612-96ac69da.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 431 KiB |
155
index.php
155
index.php
@ -1,162 +1,15 @@
|
||||
<?php
|
||||
// Fetch a hurricane image from our local API endpoint
|
||||
$image_api_url = "http://" . $_SERVER['HTTP_HOST'] . "/api/pexels.php?query=hurricane&orientation=landscape";
|
||||
@$image_data_json = file_get_contents($image_api_url);
|
||||
$image_data = $image_data_json ? json_decode($image_data_json, true) : null;
|
||||
$hero_image_url = $image_data ? $image_data['local_path'] : 'https://images.pexels.com/photos/15033937/pexels-photo-15033937.jpeg'; // Fallback
|
||||
$hero_image_alt = $image_data ? $image_data['alt'] : 'Satellite view of a hurricane';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Worldsphere.ai - AI-Powered Weather Prediction</title>
|
||||
<meta name="description" content="Worldsphere.ai is revolutionizing climate resilience with AI-powered weather prediction.">
|
||||
<meta name="keywords" content="ai weather prediction, climate resilience, hurricane forecasting, climate tech, big data analytics, weather risk management, diffusion models, satellite imagery analysis, Built with Flatlogic Generator">
|
||||
<meta property="og:title" content="Worldsphere.ai - AI-Powered Weather Prediction">
|
||||
<meta property="og:description" content="Revolutionizing Climate Resilience with AI-Powered Weather Prediction.">
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<title>Worldsphere.ai - 3D Weather Map</title>
|
||||
<link href="https://cesium.com/downloads/cesiumjs/releases/1.117/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container">
|
||||
<a class="navbar-brand fw-bold" href="#">Worldsphere.ai</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="#solutions">Solutions</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#mission">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero text-center text-white" style="background-image: url('<?php echo htmlspecialchars($hero_image_url); ?>');">
|
||||
<div class="hero-overlay">
|
||||
<div class="container">
|
||||
<p class="mb-3">First Beta Version now available! Download now</p>
|
||||
<h1 class="display-3 fw-bold">Revolutionizing Climate Resilience with AI-Powered Weather Prediction</h1>
|
||||
<p class="lead mt-4">Where Cutting-Edge Technology Meets Climate Action. At Worldsphere.ai, we’re harnessing the power of artificial intelligence to transform how we predict, prepare for, and respond to extreme weather events.</p>
|
||||
<a href="#contact" class="btn btn-primary mt-4">Join the Climate Tech Revolution</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="cyclone-widget" class="container text-center">
|
||||
<h2>Active Tropical Cyclones</h2>
|
||||
<div id="cyclone-data" class="row">
|
||||
<p>Loading real-time cyclone data...</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="wildfire-widget" class="container text-center">
|
||||
<h2>Active Wildfires</h2>
|
||||
<div id="wildfire-data" class="row">
|
||||
<p>Loading real-time wildfire data...</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="solutions" class="container text-center">
|
||||
<h2>Technology & Innovation</h2>
|
||||
<p class="lead mb-5">Our innovative platform combines state-of-the-art AI models, big data analytics, and immersive visualization techniques to provide unparalleled insights into weather risks and climate patterns.</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100 p-4">
|
||||
<h5 class="fw-bold">AI-Powered Hurricane Prediction</h5>
|
||||
<p>Our flagship technology utilizes advanced diffusion models to revolutionize hurricane forecasting.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100 p-4">
|
||||
<h5 class="fw-bold">Satellite-to-Wind Technology</h5>
|
||||
<p>Generate realistic 2D wind fields from infrared satellite imagery and automatically remove land masses.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100 p-4">
|
||||
<h5 class="fw-bold">Model Interpretability</h5>
|
||||
<p>We bridge traditional meteorological frameworks and AI-generated insights by mapping atmospheric patterns to model activations.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="mission" class="bg-white">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<h2>Our Mission</h2>
|
||||
<p class="lead">Empowering Global Resilience in the Face of Climate Change.</p>
|
||||
<p>We recognize the urgency of the climate crisis. At Worldsphere.ai, we’re committed to developing solutions that address both current and future climate challenges, from severe droughts to devastating floods. Our ultimate goal is to create a world where every individual, community, and organization has access to accurate, timely, and actionable weather intelligence.</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5 class="fw-bold">Our Core Objectives</h5>
|
||||
<ul>
|
||||
<li>Advance the field of Al-powered weather prediction</li>
|
||||
<li>Improve early warning systems for extreme weather events</li>
|
||||
<li>Support businesses and communities in climate risk management</li>
|
||||
<li>Foster collaboration between technology and climate science</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contact">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="contact-form text-center">
|
||||
<h2>Contact Us</h2>
|
||||
<p>Sign up for our beta testing program and experience the future of weather risk management.</p>
|
||||
<form id="contactForm" novalidate>
|
||||
<div class="mb-3">
|
||||
<input type="text" class="form-control" id="name" name="name" placeholder="Your Name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input type="email" class="form-control" id="email" name="email" placeholder="Your Email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" id="message" name="message" rows="5" placeholder="Your Message" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||
</form>
|
||||
<div id="form-status" class="mt-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container text-center">
|
||||
<ul class="nav justify-content-center mb-3">
|
||||
<li class="nav-item"><a href="#" class="nav-link px-2 text-white">LinkedIn</a></li>
|
||||
<li class="nav-item"><a href="#" class="nav-link px-2 text-white">Instagram</a></li>
|
||||
<li class="nav-item"><a href="#" class="nav-link px-2 text-white">x.com</a></li>
|
||||
<li class="nav-item"><a href="privacy.php" class="nav-link px-2 text-white">Privacy Policy</a></li>
|
||||
</ul>
|
||||
<p>© 2025 Worldsphere.ai. All Rights Reserved.</p>
|
||||
<p>Reminder: click Save in the editor to sync changes.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<div id="cesiumContainer"></div>
|
||||
<script src="https://cesium.com/downloads/cesiumjs/releases/1.117/Build/Cesium/Cesium.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user