Public
Edited
Sep 7, 2023
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
feed = {
const res = await proxyFetch("https://www.nhc.noaa.gov/CurrentStorms.json");
const data = await res.json();

return parseJsonFeed(data);
}
Insert cell
Insert cell
activeStormFeed = {
const data = await d3.json(
`https://mapservices.weather.noaa.gov/tropical/rest/services/tropical/${mapServer}/MapServer?f=json`
);

return data.layers.filter(layer => !layer.subLayerIds);
}
Insert cell
Insert cell
function getLayers(walletId, stormName) {
const layers = activeStormFeed.filter(layer =>
layer.name.startsWith(walletId)
);

return layers.map(layer => ({
name: layer.name.replace(`${walletId} `, ''),
id: layer.id,
stormName,
url: buildURL(layer.id)
}));
}
Insert cell
Insert cell
function buildURL(id) {
return `https://mapservices.weather.noaa.gov/tropical/rest/services/tropical/${mapServer}/MapServer/${id}/query?where=1%3D1&f=geojson&geometryPrecision=6&outFields=*&orderByFields=objectid`;
}
Insert cell
mapServer = "NHC_tropical_weather"
Insert cell
Insert cell
function DownloadList({ layers }) {
return html`
<dl class="download-list">${layers.map(
({ name, id, stormName, url }) =>
html`
<div>
<dt>${name} (${id})</dt>
<dd>${DOM.download(
async () => serializeJSON(await d3.json(url), name),
slugify(`${name}_${stormName}.json`, {
lower: true,
replacement: "_"
}),
"Prepare GeoJSON"
)}
</dd>
</div>`
)}
</dl>`;
}
Insert cell
Insert cell
Insert cell
falseFeed = parseJsonFeed(
await (
await proxyFetch(
"https://www.nhc.noaa.gov/productexamples/NHC_JSON_Sample.json"
)
).json()
)
Insert cell
function parseJsonFeed(feed) {
const storms = feed.activeStorms
.filter((d) => d.binNumber.startsWith("AT")) // only care about Atlantic Basin for now
.map((storm) => {
const id = storm.id.toUpperCase();
const wallet = storm.binNumber;
const parsedDate = DateTime.fromISO(storm.lastUpdate);
const lastUpdated = parsedDate.toLocaleString(DateTime.DATETIME_SHORT);

return {
name: storm.name,
type: parseClassification(storm.classification),
wind: knotsToMph(+storm.intensity),
pressure: +storm.pressure,
wallet,
id,
graphic: buildGraphicUrl(id),
lastUpdated
};
});

return { storms };
}
Insert cell
function proxyFetch(urlToProxy) {
// const url = new URL('https://nhc-data-proxy.glitch.me/proxy');
const url = new URL("https://nhc-data-proxy.deno.dev");
url.searchParams.append("url", urlToProxy);

return fetch(url);
}
Insert cell
function serializeJSON(obj, name) {
const str = JSON.stringify(obj);
return new Blob([str], { type: "application/json" });
}
Insert cell
function knotsToMph(knots) {
return +(knots * 1.15078).toFixed(1);
}
Insert cell
function parseClassification(code) {
// Storm Classification abbreviation; one of the following:
// TD - Tropical Depression
// STD - Subtropical Depression
// TS - Tropical Storm
// HU - Hurricane
// STS - Subtropical Storm
// PTC - Post-tropical Cyclone / Remnants
// TY - Typhoon (we don't use this currently)
// PC - Potential Tropical Cyclone
switch (code) {
case "TD":
return "Tropical Depression";
case "STD":
return "Subtropical Depression";
case "TS":
return "Tropical Storm";
case "HU":
return "Hurricane";
case "STS":
return "Subtropical Storm";
case "PTC":
return "Post-tropical Cyclone";
case "TY":
return "Typhoon";
case "PC":
return "Potential Tropical Cyclone";
default:
throw new Error(`No match found for code "${code}"`);
}
}
Insert cell
function buildGraphicUrl(id) {
const number = id.slice(2, 4);
return `https://www.nhc.noaa.gov/storm_graphics/AT${number}/${id}_5day_cone_with_line_and_wind_sm2.png`;
}
Insert cell
slugify = require('slugify@1')
Insert cell
journalize = require('journalize@2')
Insert cell
DateTime = require("luxon@3/build/amd/luxon.js").then((m) => m.DateTime)
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more