predictor/examples/wind-demo/index.html

98 lines
3.6 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>stratoflights-predictor — wind layer demo</title>
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- leaflet-velocity: consumes the wind-js-server JSON this API emits -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-velocity@1.7.0/dist/leaflet-velocity.css" />
<script src="https://unpkg.com/leaflet-velocity@1.7.0/dist/leaflet-velocity.js"></script>
<style>
html, body, #map { height: 100%; margin: 0; }
#controls {
position: absolute; z-index: 1000; top: 10px; right: 10px;
background: #fff; padding: 8px 10px; border-radius: 6px;
font: 13px sans-serif; box-shadow: 0 1px 4px rgba(0,0,0,.3);
}
#controls label { display: block; margin: 4px 0; }
</style>
</head>
<body>
<div id="map"></div>
<div id="controls">
<strong>Wind layer</strong>
<label>Altitude (m):
<input id="altitude" type="number" value="10000" step="1000" style="width:80px">
</label>
<label>Step (deg):
<input id="step" type="number" value="2" step="0.5" min="0.25" style="width:60px">
</label>
<button id="reload">Reload</button>
<div id="status"></div>
</div>
<script>
// Base URL of the predictor API. Same-origin by default.
const API = "";
const map = L.map("map").setView([30, 0], 2);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "&copy; OpenStreetMap",
}).addTo(map);
let velocityLayer = null;
// fetchWindField pulls the leaflet-velocity-compatible grid from the API.
//
// The endpoint returns a two-element array [uComponent, vComponent], each
// with a {header, data} object — exactly the gfs.json / wind-js-server
// shape leaflet-velocity and wind-layer expect.
async function fetchWindField({ altitude, step, time, bbox } = {}) {
const q = new URLSearchParams();
if (altitude != null) q.set("altitude", altitude);
if (step != null) q.set("step", step);
if (time) q.set("time", time);
if (bbox) {
q.set("min_lat", bbox.minLat); q.set("max_lat", bbox.maxLat);
q.set("min_lng", bbox.minLng); q.set("max_lng", bbox.maxLng);
}
const res = await fetch(`${API}/api/v1/wind/field?` + q.toString());
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
return res.json();
}
async function reload() {
const status = document.getElementById("status");
const altitude = Number(document.getElementById("altitude").value);
const step = Number(document.getElementById("step").value);
status.textContent = "loading…";
try {
const data = await fetchWindField({ altitude, step });
if (velocityLayer) map.removeLayer(velocityLayer);
velocityLayer = L.velocityLayer({
displayValues: true,
displayOptions: {
velocityType: "Wind",
displayPosition: "bottomleft",
displayEmptyString: "No wind data",
},
data,
maxVelocity: 60,
}).addTo(map);
status.textContent = `loaded ${data[0].header.nx}×${data[0].header.ny} grid`;
} catch (err) {
status.textContent = "error: " + err.message;
console.error(err);
}
}
document.getElementById("reload").addEventListener("click", reload);
reload();
</script>
</body>
</html>