mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-20 18:49:31 -07:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3faf95e39 | ||
|
|
3a304d7c08 | ||
|
|
c6918f0c6c | ||
|
|
fb93ade4d2 | ||
|
|
6f260a6ac7 | ||
|
|
50e526639c | ||
|
|
38db5cb6a6 | ||
|
|
8c278928b9 | ||
|
|
130a1bfad3 | ||
|
|
bd6c5430c4 | ||
|
|
cc896bf18d | ||
|
|
f7a15a93c6 | ||
|
|
0baa31a92c | ||
|
|
8fa00b34b4 | ||
|
|
23cc1a1f7a | ||
|
|
b272aa298a | ||
|
|
f0166ec2df | ||
|
|
1983d025a4 |
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -18,7 +18,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Data:stations",
|
||||
"program": "${workspaceFolder}/datagenerators/stations.js",
|
||||
"program": "${workspaceFolder}/datagenerators/stations.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Data:regionalcities",
|
||||
"program": "${workspaceFolder}/datagenerators/regionalcities.js",
|
||||
"program": "${workspaceFolder}/datagenerators/regionalcities.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Data:travelcities",
|
||||
"program": "${workspaceFolder}/datagenerators/travelcities.js",
|
||||
"program": "${workspaceFolder}/datagenerators/travelcities.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -22,5 +22,8 @@
|
||||
},
|
||||
"eslint.validate": [
|
||||
"javascript"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"Tucsan"
|
||||
]
|
||||
}
|
||||
@@ -19,4 +19,4 @@ const chunk = (data, chunkSize = 10) => {
|
||||
return chunks;
|
||||
};
|
||||
|
||||
module.exports = chunk;
|
||||
export default chunk;
|
||||
@@ -1,8 +1,7 @@
|
||||
// async https wrapper
|
||||
import https from 'https';
|
||||
|
||||
const https = require('https');
|
||||
|
||||
module.exports = (url) => new Promise((resolve, reject) => {
|
||||
const get = (url) => new Promise((resolve, reject) => {
|
||||
const headers = {};
|
||||
headers['user-agent'] = '(WeatherStar 4000+ data generator, ws4000@netbymatt.com)';
|
||||
|
||||
@@ -22,3 +21,5 @@ module.exports = (url) => new Promise((resolve, reject) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
|
||||
export default get;
|
||||
@@ -4,8 +4,8 @@
|
||||
"lat": 33.749,
|
||||
"lon": -84.388,
|
||||
"point": {
|
||||
"x": 50,
|
||||
"y": 86,
|
||||
"x": 51,
|
||||
"y": 87,
|
||||
"wfo": "FFC"
|
||||
}
|
||||
},
|
||||
@@ -24,8 +24,8 @@
|
||||
"lat": 41.9796,
|
||||
"lon": -87.9045,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 76,
|
||||
"x": 66,
|
||||
"y": 77,
|
||||
"wfo": "LOT"
|
||||
}
|
||||
},
|
||||
@@ -34,8 +34,8 @@
|
||||
"lat": 41.4995,
|
||||
"lon": -81.6954,
|
||||
"point": {
|
||||
"x": 82,
|
||||
"y": 64,
|
||||
"x": 83,
|
||||
"y": 65,
|
||||
"wfo": "CLE"
|
||||
}
|
||||
},
|
||||
@@ -44,8 +44,8 @@
|
||||
"lat": 32.8959,
|
||||
"lon": -97.0372,
|
||||
"point": {
|
||||
"x": 79,
|
||||
"y": 108,
|
||||
"x": 80,
|
||||
"y": 109,
|
||||
"wfo": "FWD"
|
||||
}
|
||||
},
|
||||
@@ -54,8 +54,8 @@
|
||||
"lat": 39.7391,
|
||||
"lon": -104.9847,
|
||||
"point": {
|
||||
"x": 62,
|
||||
"y": 60,
|
||||
"x": 63,
|
||||
"y": 61,
|
||||
"wfo": "BOU"
|
||||
}
|
||||
},
|
||||
@@ -64,8 +64,8 @@
|
||||
"lat": 42.3314,
|
||||
"lon": -83.0457,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 33,
|
||||
"x": 66,
|
||||
"y": 34,
|
||||
"wfo": "DTX"
|
||||
}
|
||||
},
|
||||
@@ -94,8 +94,8 @@
|
||||
"lat": 39.7684,
|
||||
"lon": -86.158,
|
||||
"point": {
|
||||
"x": 57,
|
||||
"y": 68,
|
||||
"x": 58,
|
||||
"y": 69,
|
||||
"wfo": "IND"
|
||||
}
|
||||
},
|
||||
@@ -104,8 +104,8 @@
|
||||
"lat": 34.0522,
|
||||
"lon": -118.2437,
|
||||
"point": {
|
||||
"x": 154,
|
||||
"y": 44,
|
||||
"x": 155,
|
||||
"y": 45,
|
||||
"wfo": "LOX"
|
||||
}
|
||||
},
|
||||
@@ -114,8 +114,8 @@
|
||||
"lat": 25.7743,
|
||||
"lon": -80.1937,
|
||||
"point": {
|
||||
"x": 109,
|
||||
"y": 50,
|
||||
"x": 110,
|
||||
"y": 51,
|
||||
"wfo": "MFL"
|
||||
}
|
||||
},
|
||||
@@ -124,8 +124,8 @@
|
||||
"lat": 44.98,
|
||||
"lon": -93.2638,
|
||||
"point": {
|
||||
"x": 107,
|
||||
"y": 71,
|
||||
"x": 108,
|
||||
"y": 72,
|
||||
"wfo": "MPX"
|
||||
}
|
||||
},
|
||||
@@ -134,8 +134,8 @@
|
||||
"lat": 40.78,
|
||||
"lon": -73.88,
|
||||
"point": {
|
||||
"x": 36,
|
||||
"y": 38,
|
||||
"x": 37,
|
||||
"y": 39,
|
||||
"wfo": "OKX"
|
||||
}
|
||||
},
|
||||
@@ -144,8 +144,8 @@
|
||||
"lat": 36.8468,
|
||||
"lon": -76.2852,
|
||||
"point": {
|
||||
"x": 89,
|
||||
"y": 51,
|
||||
"x": 90,
|
||||
"y": 52,
|
||||
"wfo": "AKQ"
|
||||
}
|
||||
},
|
||||
@@ -164,8 +164,8 @@
|
||||
"lat": 39.9523,
|
||||
"lon": -75.1638,
|
||||
"point": {
|
||||
"x": 49,
|
||||
"y": 75,
|
||||
"x": 50,
|
||||
"y": 76,
|
||||
"wfo": "PHI"
|
||||
}
|
||||
},
|
||||
@@ -174,8 +174,8 @@
|
||||
"lat": 40.4406,
|
||||
"lon": -79.9959,
|
||||
"point": {
|
||||
"x": 77,
|
||||
"y": 65,
|
||||
"x": 78,
|
||||
"y": 66,
|
||||
"wfo": "PBZ"
|
||||
}
|
||||
},
|
||||
@@ -184,8 +184,8 @@
|
||||
"lat": 38.6273,
|
||||
"lon": -90.1979,
|
||||
"point": {
|
||||
"x": 94,
|
||||
"y": 73,
|
||||
"x": 95,
|
||||
"y": 74,
|
||||
"wfo": "LSX"
|
||||
}
|
||||
},
|
||||
@@ -204,8 +204,8 @@
|
||||
"lat": 47.6062,
|
||||
"lon": -122.3321,
|
||||
"point": {
|
||||
"x": 124,
|
||||
"y": 67,
|
||||
"x": 125,
|
||||
"y": 68,
|
||||
"wfo": "SEW"
|
||||
}
|
||||
},
|
||||
@@ -214,8 +214,8 @@
|
||||
"lat": 43.0481,
|
||||
"lon": -76.1474,
|
||||
"point": {
|
||||
"x": 51,
|
||||
"y": 98,
|
||||
"x": 52,
|
||||
"y": 99,
|
||||
"wfo": "BGM"
|
||||
}
|
||||
},
|
||||
@@ -224,8 +224,8 @@
|
||||
"lat": 27.9756,
|
||||
"lon": -82.5329,
|
||||
"point": {
|
||||
"x": 67,
|
||||
"y": 97,
|
||||
"x": 68,
|
||||
"y": 98,
|
||||
"wfo": "TBW"
|
||||
}
|
||||
},
|
||||
@@ -244,8 +244,8 @@
|
||||
"lat": 42.6526,
|
||||
"lon": -73.7562,
|
||||
"point": {
|
||||
"x": 58,
|
||||
"y": 58,
|
||||
"x": 72,
|
||||
"y": 63,
|
||||
"wfo": "ALY"
|
||||
}
|
||||
},
|
||||
@@ -254,8 +254,8 @@
|
||||
"lat": 35.0845,
|
||||
"lon": -106.6511,
|
||||
"point": {
|
||||
"x": 97,
|
||||
"y": 118,
|
||||
"x": 98,
|
||||
"y": 121,
|
||||
"wfo": "ABQ"
|
||||
}
|
||||
},
|
||||
@@ -264,8 +264,8 @@
|
||||
"lat": 35.222,
|
||||
"lon": -101.8313,
|
||||
"point": {
|
||||
"x": 47,
|
||||
"y": 25,
|
||||
"x": 48,
|
||||
"y": 26,
|
||||
"wfo": "AMA"
|
||||
}
|
||||
},
|
||||
@@ -284,8 +284,8 @@
|
||||
"lat": 30.2671,
|
||||
"lon": -97.7431,
|
||||
"point": {
|
||||
"x": 155,
|
||||
"y": 90,
|
||||
"x": 156,
|
||||
"y": 91,
|
||||
"wfo": "EWX"
|
||||
}
|
||||
},
|
||||
@@ -294,8 +294,8 @@
|
||||
"lat": 44.7502,
|
||||
"lon": -117.6677,
|
||||
"point": {
|
||||
"x": 93,
|
||||
"y": 145,
|
||||
"x": 94,
|
||||
"y": 146,
|
||||
"wfo": "BOI"
|
||||
}
|
||||
},
|
||||
@@ -314,7 +314,7 @@
|
||||
"lat": 44.8012,
|
||||
"lon": -68.7778,
|
||||
"point": {
|
||||
"x": 72,
|
||||
"x": 66,
|
||||
"y": 62,
|
||||
"wfo": "CAR"
|
||||
}
|
||||
@@ -324,8 +324,8 @@
|
||||
"lat": 33.5207,
|
||||
"lon": -86.8025,
|
||||
"point": {
|
||||
"x": 58,
|
||||
"y": 83,
|
||||
"x": 59,
|
||||
"y": 84,
|
||||
"wfo": "BMX"
|
||||
}
|
||||
},
|
||||
@@ -334,8 +334,8 @@
|
||||
"lat": 46.8083,
|
||||
"lon": -100.7837,
|
||||
"point": {
|
||||
"x": 109,
|
||||
"y": 46,
|
||||
"x": 110,
|
||||
"y": 47,
|
||||
"wfo": "BIS"
|
||||
}
|
||||
},
|
||||
@@ -344,8 +344,8 @@
|
||||
"lat": 43.6135,
|
||||
"lon": -116.2034,
|
||||
"point": {
|
||||
"x": 132,
|
||||
"y": 85,
|
||||
"x": 133,
|
||||
"y": 86,
|
||||
"wfo": "BOI"
|
||||
}
|
||||
},
|
||||
@@ -354,8 +354,8 @@
|
||||
"lat": 42.8864,
|
||||
"lon": -78.8784,
|
||||
"point": {
|
||||
"x": 35,
|
||||
"y": 46,
|
||||
"x": 36,
|
||||
"y": 47,
|
||||
"wfo": "BUF"
|
||||
}
|
||||
},
|
||||
@@ -374,8 +374,8 @@
|
||||
"lat": 32.7766,
|
||||
"lon": -79.9309,
|
||||
"point": {
|
||||
"x": 86,
|
||||
"y": 76,
|
||||
"x": 87,
|
||||
"y": 77,
|
||||
"wfo": "CHS"
|
||||
}
|
||||
},
|
||||
@@ -384,8 +384,8 @@
|
||||
"lat": 38.3498,
|
||||
"lon": -81.6326,
|
||||
"point": {
|
||||
"x": 62,
|
||||
"y": 66,
|
||||
"x": 63,
|
||||
"y": 67,
|
||||
"wfo": "RLX"
|
||||
}
|
||||
},
|
||||
@@ -394,8 +394,8 @@
|
||||
"lat": 35.2271,
|
||||
"lon": -80.8431,
|
||||
"point": {
|
||||
"x": 118,
|
||||
"y": 64,
|
||||
"x": 119,
|
||||
"y": 65,
|
||||
"wfo": "GSP"
|
||||
}
|
||||
},
|
||||
@@ -404,8 +404,8 @@
|
||||
"lat": 41.14,
|
||||
"lon": -104.8202,
|
||||
"point": {
|
||||
"x": 109,
|
||||
"y": 13,
|
||||
"x": 110,
|
||||
"y": 14,
|
||||
"wfo": "CYS"
|
||||
}
|
||||
},
|
||||
@@ -414,8 +414,8 @@
|
||||
"lat": 39.162,
|
||||
"lon": -84.4569,
|
||||
"point": {
|
||||
"x": 36,
|
||||
"y": 40,
|
||||
"x": 37,
|
||||
"y": 41,
|
||||
"wfo": "ILN"
|
||||
}
|
||||
},
|
||||
@@ -434,8 +434,8 @@
|
||||
"lat": 39.9612,
|
||||
"lon": -82.9988,
|
||||
"point": {
|
||||
"x": 84,
|
||||
"y": 80,
|
||||
"x": 85,
|
||||
"y": 81,
|
||||
"wfo": "ILN"
|
||||
}
|
||||
},
|
||||
@@ -444,8 +444,8 @@
|
||||
"lat": 41.6005,
|
||||
"lon": -93.6091,
|
||||
"point": {
|
||||
"x": 73,
|
||||
"y": 49,
|
||||
"x": 74,
|
||||
"y": 50,
|
||||
"wfo": "DMX"
|
||||
}
|
||||
},
|
||||
@@ -454,8 +454,8 @@
|
||||
"lat": 42.5006,
|
||||
"lon": -90.6646,
|
||||
"point": {
|
||||
"x": 62,
|
||||
"y": 110,
|
||||
"x": 63,
|
||||
"y": 111,
|
||||
"wfo": "DVN"
|
||||
}
|
||||
},
|
||||
@@ -474,7 +474,7 @@
|
||||
"lat": 44.9062,
|
||||
"lon": -66.99,
|
||||
"point": {
|
||||
"x": 129,
|
||||
"x": 123,
|
||||
"y": 79,
|
||||
"wfo": "CAR"
|
||||
}
|
||||
@@ -484,8 +484,8 @@
|
||||
"lat": 32.792,
|
||||
"lon": -115.563,
|
||||
"point": {
|
||||
"x": 26,
|
||||
"y": 46,
|
||||
"x": 27,
|
||||
"y": 47,
|
||||
"wfo": "PSR"
|
||||
}
|
||||
},
|
||||
@@ -494,8 +494,8 @@
|
||||
"lat": 31.7587,
|
||||
"lon": -106.4869,
|
||||
"point": {
|
||||
"x": 99,
|
||||
"y": 55,
|
||||
"x": 100,
|
||||
"y": 56,
|
||||
"wfo": "EPZ"
|
||||
}
|
||||
},
|
||||
@@ -504,8 +504,8 @@
|
||||
"lat": 44.0521,
|
||||
"lon": -123.0867,
|
||||
"point": {
|
||||
"x": 84,
|
||||
"y": 38,
|
||||
"x": 85,
|
||||
"y": 39,
|
||||
"wfo": "PQR"
|
||||
}
|
||||
},
|
||||
@@ -514,8 +514,8 @@
|
||||
"lat": 46.8772,
|
||||
"lon": -96.7898,
|
||||
"point": {
|
||||
"x": 99,
|
||||
"y": 56,
|
||||
"x": 100,
|
||||
"y": 57,
|
||||
"wfo": "FGF"
|
||||
}
|
||||
},
|
||||
@@ -524,8 +524,8 @@
|
||||
"lat": 35.1981,
|
||||
"lon": -111.6513,
|
||||
"point": {
|
||||
"x": 73,
|
||||
"y": 88,
|
||||
"x": 74,
|
||||
"y": 89,
|
||||
"wfo": "FGZ"
|
||||
}
|
||||
},
|
||||
@@ -544,8 +544,8 @@
|
||||
"lat": 39.0639,
|
||||
"lon": -108.5506,
|
||||
"point": {
|
||||
"x": 94,
|
||||
"y": 101,
|
||||
"x": 95,
|
||||
"y": 102,
|
||||
"wfo": "GJT"
|
||||
}
|
||||
},
|
||||
@@ -554,8 +554,8 @@
|
||||
"lat": 42.9634,
|
||||
"lon": -85.6681,
|
||||
"point": {
|
||||
"x": 40,
|
||||
"y": 46,
|
||||
"x": 41,
|
||||
"y": 47,
|
||||
"wfo": "GRR"
|
||||
}
|
||||
},
|
||||
@@ -564,8 +564,8 @@
|
||||
"lat": 48.55,
|
||||
"lon": -109.6841,
|
||||
"point": {
|
||||
"x": 154,
|
||||
"y": 187,
|
||||
"x": 155,
|
||||
"y": 188,
|
||||
"wfo": "TFX"
|
||||
}
|
||||
},
|
||||
@@ -574,8 +574,8 @@
|
||||
"lat": 46.5927,
|
||||
"lon": -112.0361,
|
||||
"point": {
|
||||
"x": 68,
|
||||
"y": 103,
|
||||
"x": 69,
|
||||
"y": 104,
|
||||
"wfo": "TFX"
|
||||
}
|
||||
},
|
||||
@@ -584,8 +584,8 @@
|
||||
"lat": 21.3069,
|
||||
"lon": -157.8583,
|
||||
"point": {
|
||||
"x": 153,
|
||||
"y": 144,
|
||||
"x": 154,
|
||||
"y": 145,
|
||||
"wfo": "HFO"
|
||||
}
|
||||
},
|
||||
@@ -594,8 +594,8 @@
|
||||
"lat": 34.5037,
|
||||
"lon": -93.0552,
|
||||
"point": {
|
||||
"x": 53,
|
||||
"y": 60,
|
||||
"x": 54,
|
||||
"y": 61,
|
||||
"wfo": "LZK"
|
||||
}
|
||||
},
|
||||
@@ -604,8 +604,8 @@
|
||||
"lat": 43.4666,
|
||||
"lon": -112.0341,
|
||||
"point": {
|
||||
"x": 115,
|
||||
"y": 72,
|
||||
"x": 124,
|
||||
"y": 73,
|
||||
"wfo": "PIH"
|
||||
}
|
||||
},
|
||||
@@ -614,8 +614,8 @@
|
||||
"lat": 32.2988,
|
||||
"lon": -90.1848,
|
||||
"point": {
|
||||
"x": 75,
|
||||
"y": 62,
|
||||
"x": 76,
|
||||
"y": 63,
|
||||
"wfo": "JAN"
|
||||
}
|
||||
},
|
||||
@@ -624,8 +624,8 @@
|
||||
"lat": 30.3322,
|
||||
"lon": -81.6556,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 64,
|
||||
"x": 66,
|
||||
"y": 65,
|
||||
"wfo": "JAX"
|
||||
}
|
||||
},
|
||||
@@ -644,8 +644,8 @@
|
||||
"lat": 39.1142,
|
||||
"lon": -94.6275,
|
||||
"point": {
|
||||
"x": 41,
|
||||
"y": 50,
|
||||
"x": 42,
|
||||
"y": 51,
|
||||
"wfo": "EAX"
|
||||
}
|
||||
},
|
||||
@@ -654,8 +654,8 @@
|
||||
"lat": 24.5557,
|
||||
"lon": -81.7826,
|
||||
"point": {
|
||||
"x": 61,
|
||||
"y": 47,
|
||||
"x": 62,
|
||||
"y": 48,
|
||||
"wfo": "KEY"
|
||||
}
|
||||
},
|
||||
@@ -684,8 +684,8 @@
|
||||
"lat": 36.175,
|
||||
"lon": -115.1372,
|
||||
"point": {
|
||||
"x": 122,
|
||||
"y": 97,
|
||||
"x": 123,
|
||||
"y": 98,
|
||||
"wfo": "VEF"
|
||||
}
|
||||
},
|
||||
@@ -704,8 +704,8 @@
|
||||
"lat": 40.8,
|
||||
"lon": -96.667,
|
||||
"point": {
|
||||
"x": 56,
|
||||
"y": 38,
|
||||
"x": 57,
|
||||
"y": 39,
|
||||
"wfo": "OAX"
|
||||
}
|
||||
},
|
||||
@@ -714,8 +714,8 @@
|
||||
"lat": 33.767,
|
||||
"lon": -118.1892,
|
||||
"point": {
|
||||
"x": 154,
|
||||
"y": 31,
|
||||
"x": 155,
|
||||
"y": 32,
|
||||
"wfo": "LOX"
|
||||
}
|
||||
},
|
||||
@@ -724,8 +724,8 @@
|
||||
"lat": 38.2542,
|
||||
"lon": -85.7594,
|
||||
"point": {
|
||||
"x": 49,
|
||||
"y": 77,
|
||||
"x": 50,
|
||||
"y": 78,
|
||||
"wfo": "LMK"
|
||||
}
|
||||
},
|
||||
@@ -734,8 +734,8 @@
|
||||
"lat": 42.9956,
|
||||
"lon": -71.4548,
|
||||
"point": {
|
||||
"x": 41,
|
||||
"y": 20,
|
||||
"x": 42,
|
||||
"y": 21,
|
||||
"wfo": "GYX"
|
||||
}
|
||||
},
|
||||
@@ -744,8 +744,8 @@
|
||||
"lat": 35.1495,
|
||||
"lon": -90.049,
|
||||
"point": {
|
||||
"x": 41,
|
||||
"y": 66,
|
||||
"x": 42,
|
||||
"y": 67,
|
||||
"wfo": "MEG"
|
||||
}
|
||||
},
|
||||
@@ -754,8 +754,8 @@
|
||||
"lat": 43.0389,
|
||||
"lon": -87.9065,
|
||||
"point": {
|
||||
"x": 87,
|
||||
"y": 64,
|
||||
"x": 88,
|
||||
"y": 65,
|
||||
"wfo": "MKX"
|
||||
}
|
||||
},
|
||||
@@ -764,8 +764,8 @@
|
||||
"lat": 30.6944,
|
||||
"lon": -88.043,
|
||||
"point": {
|
||||
"x": 51,
|
||||
"y": 66,
|
||||
"x": 52,
|
||||
"y": 67,
|
||||
"wfo": "MOB"
|
||||
}
|
||||
},
|
||||
@@ -774,8 +774,8 @@
|
||||
"lat": 32.3668,
|
||||
"lon": -86.3,
|
||||
"point": {
|
||||
"x": 80,
|
||||
"y": 34,
|
||||
"x": 81,
|
||||
"y": 35,
|
||||
"wfo": "BMX"
|
||||
}
|
||||
},
|
||||
@@ -784,8 +784,8 @@
|
||||
"lat": 44.2601,
|
||||
"lon": -72.5754,
|
||||
"point": {
|
||||
"x": 110,
|
||||
"y": 49,
|
||||
"x": 111,
|
||||
"y": 50,
|
||||
"wfo": "BTV"
|
||||
}
|
||||
},
|
||||
@@ -794,8 +794,8 @@
|
||||
"lat": 36.1659,
|
||||
"lon": -86.7844,
|
||||
"point": {
|
||||
"x": 49,
|
||||
"y": 56,
|
||||
"x": 50,
|
||||
"y": 57,
|
||||
"wfo": "OHX"
|
||||
}
|
||||
},
|
||||
@@ -804,8 +804,8 @@
|
||||
"lat": 40.7357,
|
||||
"lon": -74.1724,
|
||||
"point": {
|
||||
"x": 26,
|
||||
"y": 34,
|
||||
"x": 27,
|
||||
"y": 35,
|
||||
"wfo": "OKX"
|
||||
}
|
||||
},
|
||||
@@ -814,8 +814,8 @@
|
||||
"lat": 41.3081,
|
||||
"lon": -72.9282,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 67,
|
||||
"x": 66,
|
||||
"y": 68,
|
||||
"wfo": "OKX"
|
||||
}
|
||||
},
|
||||
@@ -845,7 +845,7 @@
|
||||
"lon": -97.5164,
|
||||
"point": {
|
||||
"x": 97,
|
||||
"y": 93,
|
||||
"y": 94,
|
||||
"wfo": "OUN"
|
||||
}
|
||||
},
|
||||
@@ -854,8 +854,8 @@
|
||||
"lat": 41.2586,
|
||||
"lon": -95.9378,
|
||||
"point": {
|
||||
"x": 82,
|
||||
"y": 59,
|
||||
"x": 83,
|
||||
"y": 60,
|
||||
"wfo": "OAX"
|
||||
}
|
||||
},
|
||||
@@ -864,8 +864,8 @@
|
||||
"lat": 33.4484,
|
||||
"lon": -112.074,
|
||||
"point": {
|
||||
"x": 158,
|
||||
"y": 57,
|
||||
"x": 159,
|
||||
"y": 58,
|
||||
"wfo": "PSR"
|
||||
}
|
||||
},
|
||||
@@ -874,8 +874,8 @@
|
||||
"lat": 44.3683,
|
||||
"lon": -100.351,
|
||||
"point": {
|
||||
"x": 54,
|
||||
"y": 43,
|
||||
"x": 55,
|
||||
"y": 44,
|
||||
"wfo": "ABR"
|
||||
}
|
||||
},
|
||||
@@ -884,8 +884,8 @@
|
||||
"lat": 43.6615,
|
||||
"lon": -70.2553,
|
||||
"point": {
|
||||
"x": 75,
|
||||
"y": 58,
|
||||
"x": 76,
|
||||
"y": 59,
|
||||
"wfo": "GYX"
|
||||
}
|
||||
},
|
||||
@@ -894,8 +894,8 @@
|
||||
"lat": 45.5234,
|
||||
"lon": -122.6762,
|
||||
"point": {
|
||||
"x": 112,
|
||||
"y": 103,
|
||||
"x": 113,
|
||||
"y": 104,
|
||||
"wfo": "PQR"
|
||||
}
|
||||
},
|
||||
@@ -914,8 +914,8 @@
|
||||
"lat": 35.7721,
|
||||
"lon": -78.6386,
|
||||
"point": {
|
||||
"x": 74,
|
||||
"y": 56,
|
||||
"x": 75,
|
||||
"y": 57,
|
||||
"wfo": "RAH"
|
||||
}
|
||||
},
|
||||
@@ -924,8 +924,8 @@
|
||||
"lat": 39.4986,
|
||||
"lon": -119.7681,
|
||||
"point": {
|
||||
"x": 45,
|
||||
"y": 104,
|
||||
"x": 46,
|
||||
"y": 105,
|
||||
"wfo": "REV"
|
||||
}
|
||||
},
|
||||
@@ -934,8 +934,8 @@
|
||||
"lat": 38.7725,
|
||||
"lon": -112.0841,
|
||||
"point": {
|
||||
"x": 81,
|
||||
"y": 86,
|
||||
"x": 82,
|
||||
"y": 87,
|
||||
"wfo": "SLC"
|
||||
}
|
||||
},
|
||||
@@ -944,8 +944,8 @@
|
||||
"lat": 37.5538,
|
||||
"lon": -77.4603,
|
||||
"point": {
|
||||
"x": 44,
|
||||
"y": 76,
|
||||
"x": 45,
|
||||
"y": 77,
|
||||
"wfo": "AKQ"
|
||||
}
|
||||
},
|
||||
@@ -954,8 +954,8 @@
|
||||
"lat": 37.271,
|
||||
"lon": -79.9414,
|
||||
"point": {
|
||||
"x": 73,
|
||||
"y": 68,
|
||||
"x": 74,
|
||||
"y": 69,
|
||||
"wfo": "RNK"
|
||||
}
|
||||
},
|
||||
@@ -964,8 +964,8 @@
|
||||
"lat": 38.5816,
|
||||
"lon": -121.4944,
|
||||
"point": {
|
||||
"x": 40,
|
||||
"y": 67,
|
||||
"x": 41,
|
||||
"y": 68,
|
||||
"wfo": "STO"
|
||||
}
|
||||
},
|
||||
@@ -974,8 +974,8 @@
|
||||
"lat": 40.7608,
|
||||
"lon": -111.891,
|
||||
"point": {
|
||||
"x": 99,
|
||||
"y": 174,
|
||||
"x": 100,
|
||||
"y": 175,
|
||||
"wfo": "SLC"
|
||||
}
|
||||
},
|
||||
@@ -984,8 +984,8 @@
|
||||
"lat": 29.4241,
|
||||
"lon": -98.4936,
|
||||
"point": {
|
||||
"x": 125,
|
||||
"y": 53,
|
||||
"x": 126,
|
||||
"y": 54,
|
||||
"wfo": "EWX"
|
||||
}
|
||||
},
|
||||
@@ -994,8 +994,8 @@
|
||||
"lat": 32.7153,
|
||||
"lon": -117.1573,
|
||||
"point": {
|
||||
"x": 56,
|
||||
"y": 13,
|
||||
"x": 57,
|
||||
"y": 14,
|
||||
"wfo": "SGX"
|
||||
}
|
||||
},
|
||||
@@ -1014,8 +1014,8 @@
|
||||
"lat": 35.687,
|
||||
"lon": -105.9378,
|
||||
"point": {
|
||||
"x": 125,
|
||||
"y": 143,
|
||||
"x": 126,
|
||||
"y": 146,
|
||||
"wfo": "ABQ"
|
||||
}
|
||||
},
|
||||
@@ -1024,8 +1024,8 @@
|
||||
"lat": 32.0835,
|
||||
"lon": -81.0998,
|
||||
"point": {
|
||||
"x": 46,
|
||||
"y": 40,
|
||||
"x": 47,
|
||||
"y": 41,
|
||||
"wfo": "CHS"
|
||||
}
|
||||
},
|
||||
@@ -1034,7 +1034,7 @@
|
||||
"lat": 32.5251,
|
||||
"lon": -93.7502,
|
||||
"point": {
|
||||
"x": 76,
|
||||
"x": 74,
|
||||
"y": 69,
|
||||
"wfo": "SHV"
|
||||
}
|
||||
@@ -1074,8 +1074,8 @@
|
||||
"lat": 39.8017,
|
||||
"lon": -89.6437,
|
||||
"point": {
|
||||
"x": 47,
|
||||
"y": 55,
|
||||
"x": 48,
|
||||
"y": 56,
|
||||
"wfo": "ILX"
|
||||
}
|
||||
},
|
||||
@@ -1094,8 +1094,8 @@
|
||||
"lat": 37.2153,
|
||||
"lon": -93.2982,
|
||||
"point": {
|
||||
"x": 66,
|
||||
"y": 34,
|
||||
"x": 67,
|
||||
"y": 35,
|
||||
"wfo": "SGF"
|
||||
}
|
||||
},
|
||||
@@ -1104,8 +1104,8 @@
|
||||
"lat": 41.6639,
|
||||
"lon": -83.5552,
|
||||
"point": {
|
||||
"x": 18,
|
||||
"y": 66,
|
||||
"x": 19,
|
||||
"y": 67,
|
||||
"wfo": "CLE"
|
||||
}
|
||||
},
|
||||
@@ -1114,8 +1114,8 @@
|
||||
"lat": 36.154,
|
||||
"lon": -95.9928,
|
||||
"point": {
|
||||
"x": 40,
|
||||
"y": 104,
|
||||
"x": 41,
|
||||
"y": 105,
|
||||
"wfo": "TSA"
|
||||
}
|
||||
},
|
||||
@@ -1124,8 +1124,8 @@
|
||||
"lat": 36.8529,
|
||||
"lon": -75.978,
|
||||
"point": {
|
||||
"x": 100,
|
||||
"y": 52,
|
||||
"x": 101,
|
||||
"y": 53,
|
||||
"wfo": "AKQ"
|
||||
}
|
||||
},
|
||||
@@ -1134,8 +1134,8 @@
|
||||
"lat": 37.6922,
|
||||
"lon": -97.3375,
|
||||
"point": {
|
||||
"x": 61,
|
||||
"y": 33,
|
||||
"x": 62,
|
||||
"y": 34,
|
||||
"wfo": "ICT"
|
||||
}
|
||||
},
|
||||
@@ -1144,18 +1144,18 @@
|
||||
"lat": 34.2257,
|
||||
"lon": -77.9447,
|
||||
"point": {
|
||||
"x": 88,
|
||||
"y": 67,
|
||||
"x": 89,
|
||||
"y": 68,
|
||||
"wfo": "ILM"
|
||||
}
|
||||
},
|
||||
{
|
||||
"city": "Tuscan",
|
||||
"city": "Tucsan",
|
||||
"lat": 32.2216,
|
||||
"lon": -110.9698,
|
||||
"point": {
|
||||
"x": 90,
|
||||
"y": 48,
|
||||
"x": 91,
|
||||
"y": 49,
|
||||
"wfo": "TWC"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@
|
||||
"Latitude": 33.749,
|
||||
"Longitude": -84.388,
|
||||
"point": {
|
||||
"x": 50,
|
||||
"y": 86,
|
||||
"x": 51,
|
||||
"y": 87,
|
||||
"wfo": "FFC"
|
||||
}
|
||||
},
|
||||
@@ -24,8 +24,8 @@
|
||||
"Latitude": 41.9796,
|
||||
"Longitude": -87.9045,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 76,
|
||||
"x": 66,
|
||||
"y": 77,
|
||||
"wfo": "LOT"
|
||||
}
|
||||
},
|
||||
@@ -34,8 +34,8 @@
|
||||
"Latitude": 41.4995,
|
||||
"Longitude": -81.6954,
|
||||
"point": {
|
||||
"x": 82,
|
||||
"y": 64,
|
||||
"x": 83,
|
||||
"y": 65,
|
||||
"wfo": "CLE"
|
||||
}
|
||||
},
|
||||
@@ -44,8 +44,8 @@
|
||||
"Latitude": 32.8959,
|
||||
"Longitude": -97.0372,
|
||||
"point": {
|
||||
"x": 79,
|
||||
"y": 108,
|
||||
"x": 80,
|
||||
"y": 109,
|
||||
"wfo": "FWD"
|
||||
}
|
||||
},
|
||||
@@ -54,8 +54,8 @@
|
||||
"Latitude": 39.7391,
|
||||
"Longitude": -104.9847,
|
||||
"point": {
|
||||
"x": 62,
|
||||
"y": 60,
|
||||
"x": 63,
|
||||
"y": 61,
|
||||
"wfo": "BOU"
|
||||
}
|
||||
},
|
||||
@@ -64,8 +64,8 @@
|
||||
"Latitude": 42.3314,
|
||||
"Longitude": -83.0457,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 33,
|
||||
"x": 66,
|
||||
"y": 34,
|
||||
"wfo": "DTX"
|
||||
}
|
||||
},
|
||||
@@ -94,8 +94,8 @@
|
||||
"Latitude": 39.7684,
|
||||
"Longitude": -86.158,
|
||||
"point": {
|
||||
"x": 57,
|
||||
"y": 68,
|
||||
"x": 58,
|
||||
"y": 69,
|
||||
"wfo": "IND"
|
||||
}
|
||||
},
|
||||
@@ -104,8 +104,8 @@
|
||||
"Latitude": 34.0522,
|
||||
"Longitude": -118.2437,
|
||||
"point": {
|
||||
"x": 154,
|
||||
"y": 44,
|
||||
"x": 155,
|
||||
"y": 45,
|
||||
"wfo": "LOX"
|
||||
}
|
||||
},
|
||||
@@ -114,8 +114,8 @@
|
||||
"Latitude": 25.7743,
|
||||
"Longitude": -80.1937,
|
||||
"point": {
|
||||
"x": 109,
|
||||
"y": 50,
|
||||
"x": 110,
|
||||
"y": 51,
|
||||
"wfo": "MFL"
|
||||
}
|
||||
},
|
||||
@@ -124,8 +124,8 @@
|
||||
"Latitude": 44.98,
|
||||
"Longitude": -93.2638,
|
||||
"point": {
|
||||
"x": 107,
|
||||
"y": 71,
|
||||
"x": 108,
|
||||
"y": 72,
|
||||
"wfo": "MPX"
|
||||
}
|
||||
},
|
||||
@@ -134,8 +134,8 @@
|
||||
"Latitude": 40.7142,
|
||||
"Longitude": -74.0059,
|
||||
"point": {
|
||||
"x": 32,
|
||||
"y": 34,
|
||||
"x": 33,
|
||||
"y": 35,
|
||||
"wfo": "OKX"
|
||||
}
|
||||
},
|
||||
@@ -144,8 +144,8 @@
|
||||
"Latitude": 36.8468,
|
||||
"Longitude": -76.2852,
|
||||
"point": {
|
||||
"x": 89,
|
||||
"y": 51,
|
||||
"x": 90,
|
||||
"y": 52,
|
||||
"wfo": "AKQ"
|
||||
}
|
||||
},
|
||||
@@ -164,8 +164,8 @@
|
||||
"Latitude": 39.9523,
|
||||
"Longitude": -75.1638,
|
||||
"point": {
|
||||
"x": 49,
|
||||
"y": 75,
|
||||
"x": 50,
|
||||
"y": 76,
|
||||
"wfo": "PHI"
|
||||
}
|
||||
},
|
||||
@@ -174,8 +174,8 @@
|
||||
"Latitude": 40.4406,
|
||||
"Longitude": -79.9959,
|
||||
"point": {
|
||||
"x": 77,
|
||||
"y": 65,
|
||||
"x": 78,
|
||||
"y": 66,
|
||||
"wfo": "PBZ"
|
||||
}
|
||||
},
|
||||
@@ -184,8 +184,8 @@
|
||||
"Latitude": 38.6273,
|
||||
"Longitude": -90.1979,
|
||||
"point": {
|
||||
"x": 94,
|
||||
"y": 73,
|
||||
"x": 95,
|
||||
"y": 74,
|
||||
"wfo": "LSX"
|
||||
}
|
||||
},
|
||||
@@ -204,8 +204,8 @@
|
||||
"Latitude": 47.6062,
|
||||
"Longitude": -122.3321,
|
||||
"point": {
|
||||
"x": 124,
|
||||
"y": 67,
|
||||
"x": 125,
|
||||
"y": 68,
|
||||
"wfo": "SEW"
|
||||
}
|
||||
},
|
||||
@@ -214,8 +214,8 @@
|
||||
"Latitude": 43.0481,
|
||||
"Longitude": -76.1474,
|
||||
"point": {
|
||||
"x": 51,
|
||||
"y": 98,
|
||||
"x": 52,
|
||||
"y": 99,
|
||||
"wfo": "BGM"
|
||||
}
|
||||
},
|
||||
@@ -224,8 +224,8 @@
|
||||
"Latitude": 27.9475,
|
||||
"Longitude": -82.4584,
|
||||
"point": {
|
||||
"x": 70,
|
||||
"y": 96,
|
||||
"x": 71,
|
||||
"y": 97,
|
||||
"wfo": "TBW"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
"lon": -77.9447
|
||||
},
|
||||
{
|
||||
"city": "Tuscan",
|
||||
"city": "Tucsan",
|
||||
"lat": 32.2216,
|
||||
"lon": -110.9698
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// look up points for each regional city
|
||||
const fs = require('fs/promises');
|
||||
const chunk = require('./chunk');
|
||||
const https = require('./https');
|
||||
|
||||
(async () => {
|
||||
// source data
|
||||
const regionalCities = JSON.parse(await fs.readFile('./datagenerators/regionalcities-raw.json'));
|
||||
|
||||
const result = [];
|
||||
const dataChunks = chunk(regionalCities, 5);
|
||||
|
||||
// for loop intentional for use of await
|
||||
// this keeps the api from getting overwhelmed
|
||||
for (let i = 0; i < dataChunks.length; i += 1) {
|
||||
const cityChunk = dataChunks[i];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const chunkResult = await Promise.all(cityChunk.map(async (city) => {
|
||||
try {
|
||||
const data = await https(`https://api.weather.gov/points/${city.lat},${city.lon}`);
|
||||
const point = JSON.parse(data);
|
||||
return {
|
||||
...city,
|
||||
point: {
|
||||
x: point.properties.gridX,
|
||||
y: point.properties.gridY,
|
||||
wfo: point.properties.gridId,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return city;
|
||||
}
|
||||
}));
|
||||
|
||||
result.push(...chunkResult);
|
||||
}
|
||||
|
||||
await fs.writeFile('./datagenerators/output/regionalcities.json', JSON.stringify(result, null, ' '));
|
||||
})();
|
||||
40
datagenerators/regionalcities.mjs
Normal file
40
datagenerators/regionalcities.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
// look up points for each regional city
|
||||
import fs from 'fs/promises';
|
||||
|
||||
import chunk from './chunk.mjs';
|
||||
import https from './https.mjs';
|
||||
|
||||
// source data
|
||||
const regionalCities = JSON.parse(await fs.readFile('./datagenerators/regionalcities-raw.json'));
|
||||
|
||||
const result = [];
|
||||
const dataChunks = chunk(regionalCities, 5);
|
||||
|
||||
// for loop intentional for use of await
|
||||
// this keeps the api from getting overwhelmed
|
||||
for (let i = 0; i < dataChunks.length; i += 1) {
|
||||
const cityChunk = dataChunks[i];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const chunkResult = await Promise.all(cityChunk.map(async (city) => {
|
||||
try {
|
||||
const data = await https(`https://api.weather.gov/points/${city.lat},${city.lon}`);
|
||||
const point = JSON.parse(data);
|
||||
return {
|
||||
...city,
|
||||
point: {
|
||||
x: point.properties.gridX,
|
||||
y: point.properties.gridY,
|
||||
wfo: point.properties.gridId,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return city;
|
||||
}
|
||||
}));
|
||||
|
||||
result.push(...chunkResult);
|
||||
}
|
||||
|
||||
await fs.writeFile('./datagenerators/output/regionalcities.json', JSON.stringify(result, null, ' '));
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = [
|
||||
export default [
|
||||
'AZ',
|
||||
'AL',
|
||||
'AK',
|
||||
@@ -50,4 +50,4 @@ module.exports = [
|
||||
'WI',
|
||||
'WY',
|
||||
'PR',
|
||||
];
|
||||
];
|
||||
@@ -1,11 +1,10 @@
|
||||
// list all stations in a single file
|
||||
// only find stations with 4 letter codes
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('./https');
|
||||
const states = require('./stations-states');
|
||||
const chunk = require('./chunk');
|
||||
import { writeFileSync } from 'fs';
|
||||
import https from './https.mjs';
|
||||
import states from './stations-states.mjs';
|
||||
import chunk from './chunk.mjs';
|
||||
|
||||
// skip stations starting with these letters
|
||||
const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J'];
|
||||
@@ -28,7 +27,7 @@ const start = async () => {
|
||||
let stations;
|
||||
let next = `https://api.weather.gov/stations?state=${state}`;
|
||||
do {
|
||||
// get list and parse the JSON
|
||||
// get list and parse the JSON
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const stationsRaw = await https(next);
|
||||
stations = JSON.parse(stationsRaw);
|
||||
@@ -53,8 +52,7 @@ const start = async () => {
|
||||
});
|
||||
next = stations?.pagination?.next;
|
||||
// write the output
|
||||
// write the output
|
||||
fs.writeFileSync(path.join(__dirname, 'output/stations.json'), JSON.stringify(output, null, 2));
|
||||
writeFileSync('./datagenerators/output/stations.json', JSON.stringify(output, null, 2));
|
||||
}
|
||||
while (next && stations.features.length > 0);
|
||||
console.log(`Complete: ${state}`);
|
||||
@@ -68,6 +66,4 @@ const start = async () => {
|
||||
};
|
||||
|
||||
// immediately invoked function allows access to async
|
||||
(async () => {
|
||||
await start();
|
||||
})();
|
||||
await start();
|
||||
@@ -1,41 +0,0 @@
|
||||
// look up points for each travel city
|
||||
const fs = require('fs/promises');
|
||||
const chunk = require('./chunk');
|
||||
const https = require('./https');
|
||||
|
||||
(async () => {
|
||||
// source data
|
||||
const travelCities = JSON.parse(await fs.readFile('./datagenerators/travelcities-raw.json'));
|
||||
|
||||
const result = [];
|
||||
const dataChunks = chunk(travelCities, 5);
|
||||
|
||||
// for loop intentional for use of await
|
||||
// this keeps the api from getting overwhelmed
|
||||
for (let i = 0; i < dataChunks.length; i += 1) {
|
||||
const cityChunk = dataChunks[i];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const chunkResult = await Promise.all(cityChunk.map(async (city) => {
|
||||
try {
|
||||
const data = await https(`https://api.weather.gov/points/${city.Latitude},${city.Longitude}`);
|
||||
const point = JSON.parse(data);
|
||||
return {
|
||||
...city,
|
||||
point: {
|
||||
x: point.properties.gridX,
|
||||
y: point.properties.gridY,
|
||||
wfo: point.properties.gridId,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return city;
|
||||
}
|
||||
}));
|
||||
|
||||
result.push(...chunkResult);
|
||||
}
|
||||
|
||||
await fs.writeFile('./datagenerators/output/travelcities.json', JSON.stringify(result, null, ' '));
|
||||
})();
|
||||
39
datagenerators/travelcities.mjs
Normal file
39
datagenerators/travelcities.mjs
Normal file
@@ -0,0 +1,39 @@
|
||||
// look up points for each travel city
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import chunk from './chunk.mjs';
|
||||
import https from './https.mjs';
|
||||
|
||||
// source data
|
||||
const travelCities = JSON.parse(await readFile('./datagenerators/travelcities-raw.json'));
|
||||
|
||||
const result = [];
|
||||
const dataChunks = chunk(travelCities, 5);
|
||||
|
||||
// for loop intentional for use of await
|
||||
// this keeps the api from getting overwhelmed
|
||||
for (let i = 0; i < dataChunks.length; i += 1) {
|
||||
const cityChunk = dataChunks[i];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const chunkResult = await Promise.all(cityChunk.map(async (city) => {
|
||||
try {
|
||||
const data = await https(`https://api.weather.gov/points/${city.Latitude},${city.Longitude}`);
|
||||
const point = JSON.parse(data);
|
||||
return {
|
||||
...city,
|
||||
point: {
|
||||
x: point.properties.gridX,
|
||||
y: point.properties.gridY,
|
||||
wfo: point.properties.gridId,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return city;
|
||||
}
|
||||
}));
|
||||
|
||||
result.push(...chunkResult);
|
||||
}
|
||||
|
||||
await writeFile('./datagenerators/output/travelcities.json', JSON.stringify(result, null, ' '));
|
||||
@@ -12,9 +12,9 @@ const port = process.env.WS4KP_PORT ?? 8080;
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// cors pass-thru to api.weather.gov
|
||||
app.get('/stations/*', corsPassThru);
|
||||
app.get('/Conus/*', radarPassThru);
|
||||
app.get('/products/*', outlookPassThru);
|
||||
app.get('/stations/*station', corsPassThru);
|
||||
app.get('/Conus/*radar', radarPassThru);
|
||||
app.get('/products/*product', outlookPassThru);
|
||||
app.get('/playlist.json', playlist);
|
||||
|
||||
// version
|
||||
@@ -38,7 +38,7 @@ if (process.env?.DIST === '1') {
|
||||
// debugging
|
||||
app.get('/index.html', index);
|
||||
app.get('/', index);
|
||||
app.get('*', express.static('./server'));
|
||||
app.get('*name', express.static('./server'));
|
||||
}
|
||||
|
||||
const server = app.listen(port, () => {
|
||||
|
||||
1770
package-lock.json
generated
1770
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ws4kp",
|
||||
"version": "5.15.1",
|
||||
"version": "5.16.3",
|
||||
"description": "Welcome to the WeatherStar 4000+ project page!",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -49,6 +49,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ejs": "^3.1.5",
|
||||
"express": "^4.17.1"
|
||||
"express": "^5.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,243 +1,243 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const TravelCities = [
|
||||
{
|
||||
Name: 'Atlanta',
|
||||
Latitude: 33.749,
|
||||
Longitude: -84.388,
|
||||
point: {
|
||||
x: 50,
|
||||
y: 86,
|
||||
wfo: 'FFC',
|
||||
},
|
||||
"Name": "Atlanta",
|
||||
"Latitude": 33.749,
|
||||
"Longitude": -84.388,
|
||||
"point": {
|
||||
"x": 51,
|
||||
"y": 87,
|
||||
"wfo": "FFC"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Boston',
|
||||
Latitude: 42.3584,
|
||||
Longitude: -71.0598,
|
||||
point: {
|
||||
x: 71,
|
||||
y: 90,
|
||||
wfo: 'BOX',
|
||||
},
|
||||
"Name": "Boston",
|
||||
"Latitude": 42.3584,
|
||||
"Longitude": -71.0598,
|
||||
"point": {
|
||||
"x": 71,
|
||||
"y": 90,
|
||||
"wfo": "BOX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Chicago',
|
||||
Latitude: 41.9796,
|
||||
Longitude: -87.9045,
|
||||
point: {
|
||||
x: 65,
|
||||
y: 76,
|
||||
wfo: 'LOT',
|
||||
},
|
||||
"Name": "Chicago",
|
||||
"Latitude": 41.9796,
|
||||
"Longitude": -87.9045,
|
||||
"point": {
|
||||
"x": 66,
|
||||
"y": 77,
|
||||
"wfo": "LOT"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Cleveland',
|
||||
Latitude: 41.4995,
|
||||
Longitude: -81.6954,
|
||||
point: {
|
||||
x: 82,
|
||||
y: 64,
|
||||
wfo: 'CLE',
|
||||
},
|
||||
"Name": "Cleveland",
|
||||
"Latitude": 41.4995,
|
||||
"Longitude": -81.6954,
|
||||
"point": {
|
||||
"x": 83,
|
||||
"y": 65,
|
||||
"wfo": "CLE"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Dallas',
|
||||
Latitude: 32.8959,
|
||||
Longitude: -97.0372,
|
||||
point: {
|
||||
x: 79,
|
||||
y: 108,
|
||||
wfo: 'FWD',
|
||||
},
|
||||
"Name": "Dallas",
|
||||
"Latitude": 32.8959,
|
||||
"Longitude": -97.0372,
|
||||
"point": {
|
||||
"x": 80,
|
||||
"y": 109,
|
||||
"wfo": "FWD"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Denver',
|
||||
Latitude: 39.7391,
|
||||
Longitude: -104.9847,
|
||||
point: {
|
||||
x: 62,
|
||||
y: 60,
|
||||
wfo: 'BOU',
|
||||
},
|
||||
"Name": "Denver",
|
||||
"Latitude": 39.7391,
|
||||
"Longitude": -104.9847,
|
||||
"point": {
|
||||
"x": 63,
|
||||
"y": 61,
|
||||
"wfo": "BOU"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Detroit',
|
||||
Latitude: 42.3314,
|
||||
Longitude: -83.0457,
|
||||
point: {
|
||||
x: 65,
|
||||
y: 33,
|
||||
wfo: 'DTX',
|
||||
},
|
||||
"Name": "Detroit",
|
||||
"Latitude": 42.3314,
|
||||
"Longitude": -83.0457,
|
||||
"point": {
|
||||
"x": 66,
|
||||
"y": 34,
|
||||
"wfo": "DTX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Hartford',
|
||||
Latitude: 41.7637,
|
||||
Longitude: -72.6851,
|
||||
point: {
|
||||
x: 21,
|
||||
y: 54,
|
||||
wfo: 'BOX',
|
||||
},
|
||||
"Name": "Hartford",
|
||||
"Latitude": 41.7637,
|
||||
"Longitude": -72.6851,
|
||||
"point": {
|
||||
"x": 21,
|
||||
"y": 54,
|
||||
"wfo": "BOX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Houston',
|
||||
Latitude: 29.7633,
|
||||
Longitude: -95.3633,
|
||||
point: {
|
||||
x: 65,
|
||||
y: 97,
|
||||
wfo: 'HGX',
|
||||
},
|
||||
"Name": "Houston",
|
||||
"Latitude": 29.7633,
|
||||
"Longitude": -95.3633,
|
||||
"point": {
|
||||
"x": 65,
|
||||
"y": 97,
|
||||
"wfo": "HGX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Indianapolis',
|
||||
Latitude: 39.7684,
|
||||
Longitude: -86.158,
|
||||
point: {
|
||||
x: 57,
|
||||
y: 68,
|
||||
wfo: 'IND',
|
||||
},
|
||||
"Name": "Indianapolis",
|
||||
"Latitude": 39.7684,
|
||||
"Longitude": -86.158,
|
||||
"point": {
|
||||
"x": 58,
|
||||
"y": 69,
|
||||
"wfo": "IND"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Los Angeles',
|
||||
Latitude: 34.0522,
|
||||
Longitude: -118.2437,
|
||||
point: {
|
||||
x: 154,
|
||||
y: 44,
|
||||
wfo: 'LOX',
|
||||
},
|
||||
"Name": "Los Angeles",
|
||||
"Latitude": 34.0522,
|
||||
"Longitude": -118.2437,
|
||||
"point": {
|
||||
"x": 155,
|
||||
"y": 45,
|
||||
"wfo": "LOX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Miami',
|
||||
Latitude: 25.7743,
|
||||
Longitude: -80.1937,
|
||||
point: {
|
||||
x: 109,
|
||||
y: 50,
|
||||
wfo: 'MFL',
|
||||
},
|
||||
"Name": "Miami",
|
||||
"Latitude": 25.7743,
|
||||
"Longitude": -80.1937,
|
||||
"point": {
|
||||
"x": 110,
|
||||
"y": 51,
|
||||
"wfo": "MFL"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Minneapolis',
|
||||
Latitude: 44.98,
|
||||
Longitude: -93.2638,
|
||||
point: {
|
||||
x: 107,
|
||||
y: 71,
|
||||
wfo: 'MPX',
|
||||
},
|
||||
"Name": "Minneapolis",
|
||||
"Latitude": 44.98,
|
||||
"Longitude": -93.2638,
|
||||
"point": {
|
||||
"x": 108,
|
||||
"y": 72,
|
||||
"wfo": "MPX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'New York',
|
||||
Latitude: 40.7142,
|
||||
Longitude: -74.0059,
|
||||
point: {
|
||||
x: 32,
|
||||
y: 34,
|
||||
wfo: 'OKX',
|
||||
},
|
||||
"Name": "New York",
|
||||
"Latitude": 40.7142,
|
||||
"Longitude": -74.0059,
|
||||
"point": {
|
||||
"x": 33,
|
||||
"y": 35,
|
||||
"wfo": "OKX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Norfolk',
|
||||
Latitude: 36.8468,
|
||||
Longitude: -76.2852,
|
||||
point: {
|
||||
x: 89,
|
||||
y: 51,
|
||||
wfo: 'AKQ',
|
||||
},
|
||||
"Name": "Norfolk",
|
||||
"Latitude": 36.8468,
|
||||
"Longitude": -76.2852,
|
||||
"point": {
|
||||
"x": 90,
|
||||
"y": 52,
|
||||
"wfo": "AKQ"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Orlando',
|
||||
Latitude: 28.5383,
|
||||
Longitude: -81.3792,
|
||||
point: {
|
||||
x: 26,
|
||||
y: 68,
|
||||
wfo: 'MLB',
|
||||
},
|
||||
"Name": "Orlando",
|
||||
"Latitude": 28.5383,
|
||||
"Longitude": -81.3792,
|
||||
"point": {
|
||||
"x": 26,
|
||||
"y": 68,
|
||||
"wfo": "MLB"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Philadelphia',
|
||||
Latitude: 39.9523,
|
||||
Longitude: -75.1638,
|
||||
point: {
|
||||
x: 49,
|
||||
y: 75,
|
||||
wfo: 'PHI',
|
||||
},
|
||||
"Name": "Philadelphia",
|
||||
"Latitude": 39.9523,
|
||||
"Longitude": -75.1638,
|
||||
"point": {
|
||||
"x": 50,
|
||||
"y": 76,
|
||||
"wfo": "PHI"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Pittsburgh',
|
||||
Latitude: 40.4406,
|
||||
Longitude: -79.9959,
|
||||
point: {
|
||||
x: 77,
|
||||
y: 65,
|
||||
wfo: 'PBZ',
|
||||
},
|
||||
"Name": "Pittsburgh",
|
||||
"Latitude": 40.4406,
|
||||
"Longitude": -79.9959,
|
||||
"point": {
|
||||
"x": 78,
|
||||
"y": 66,
|
||||
"wfo": "PBZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'St. Louis',
|
||||
Latitude: 38.6273,
|
||||
Longitude: -90.1979,
|
||||
point: {
|
||||
x: 94,
|
||||
y: 73,
|
||||
wfo: 'LSX',
|
||||
},
|
||||
"Name": "St. Louis",
|
||||
"Latitude": 38.6273,
|
||||
"Longitude": -90.1979,
|
||||
"point": {
|
||||
"x": 95,
|
||||
"y": 74,
|
||||
"wfo": "LSX"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'San Francisco',
|
||||
Latitude: 37.7749,
|
||||
Longitude: -122.4194,
|
||||
point: {
|
||||
x: 85,
|
||||
y: 105,
|
||||
wfo: 'MTR',
|
||||
},
|
||||
"Name": "San Francisco",
|
||||
"Latitude": 37.7749,
|
||||
"Longitude": -122.4194,
|
||||
"point": {
|
||||
"x": 85,
|
||||
"y": 105,
|
||||
"wfo": "MTR"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Seattle',
|
||||
Latitude: 47.6062,
|
||||
Longitude: -122.3321,
|
||||
point: {
|
||||
x: 124,
|
||||
y: 67,
|
||||
wfo: 'SEW',
|
||||
},
|
||||
"Name": "Seattle",
|
||||
"Latitude": 47.6062,
|
||||
"Longitude": -122.3321,
|
||||
"point": {
|
||||
"x": 125,
|
||||
"y": 68,
|
||||
"wfo": "SEW"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Syracuse',
|
||||
Latitude: 43.0481,
|
||||
Longitude: -76.1474,
|
||||
point: {
|
||||
x: 51,
|
||||
y: 98,
|
||||
wfo: 'BGM',
|
||||
},
|
||||
"Name": "Syracuse",
|
||||
"Latitude": 43.0481,
|
||||
"Longitude": -76.1474,
|
||||
"point": {
|
||||
"x": 52,
|
||||
"y": 99,
|
||||
"wfo": "BGM"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Tampa',
|
||||
Latitude: 27.9475,
|
||||
Longitude: -82.4584,
|
||||
point: {
|
||||
x: 70,
|
||||
y: 96,
|
||||
wfo: 'TBW',
|
||||
},
|
||||
"Name": "Tampa",
|
||||
"Latitude": 27.9475,
|
||||
"Longitude": -82.4584,
|
||||
"point": {
|
||||
"x": 71,
|
||||
"y": 97,
|
||||
"wfo": "TBW"
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'Washington DC',
|
||||
Latitude: 38.8951,
|
||||
Longitude: -77.0364,
|
||||
point: {
|
||||
x: 97,
|
||||
y: 71,
|
||||
wfo: 'LWX',
|
||||
},
|
||||
},
|
||||
];
|
||||
"Name": "Washington DC",
|
||||
"Latitude": 38.8951,
|
||||
"Longitude": -77.0364,
|
||||
"point": {
|
||||
"x": 97,
|
||||
"y": 71,
|
||||
"wfo": "LWX"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,7 +1,7 @@
|
||||
import { json } from './modules/utils/fetch.mjs';
|
||||
import noSleep from './modules/utils/nosleep.mjs';
|
||||
import {
|
||||
message as navMessage, isPlaying, resize, resetStatuses, latLonReceived, stopAutoRefreshTimer, registerRefreshData,
|
||||
message as navMessage, isPlaying, resize, resetStatuses, latLonReceived,
|
||||
} from './modules/navigation.mjs';
|
||||
import { round2 } from './modules/utils/units.mjs';
|
||||
import { parseQueryString } from './modules/share.mjs';
|
||||
@@ -32,8 +32,6 @@ const init = () => {
|
||||
e.target.select();
|
||||
});
|
||||
|
||||
registerRefreshData(loadData);
|
||||
|
||||
document.querySelector('#NavigateMenu').addEventListener('click', btnNavigateMenuClick);
|
||||
document.querySelector('#NavigateRefresh').addEventListener('click', btnNavigateRefreshClick);
|
||||
document.querySelector('#NavigateNext').addEventListener('click', btnNavigateNextClick);
|
||||
@@ -249,7 +247,6 @@ const loadData = (_latLon, haveDataCallback) => {
|
||||
if (!latLon) return;
|
||||
|
||||
document.querySelector(TXT_ADDRESS_SELECTOR).blur();
|
||||
stopAutoRefreshTimer();
|
||||
latLonReceived(latLon, haveDataCallback);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ class Almanac extends WeatherDisplay {
|
||||
this.timing.totalScreens = 1;
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
const superResponse = super.getData(_weatherParameters);
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
const superResponse = super.getData(weatherParameters, refresh);
|
||||
|
||||
// get sun/moon data
|
||||
const { sun, moon } = this.calcSunMoonData(weatherParameters);
|
||||
const { sun, moon } = this.calcSunMoonData(this.weatherParameters);
|
||||
|
||||
// store the data
|
||||
this.data = {
|
||||
|
||||
@@ -21,13 +21,14 @@ class CurrentWeather extends WeatherDisplay {
|
||||
this.backgroundImage = loadImg('images/BackGround1_1.png');
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
async getData(weatherParameters, refresh) {
|
||||
// always load the data for use in the lower scroll
|
||||
const superResult = super.getData(_weatherParameters);
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
const superResult = super.getData(weatherParameters, refresh);
|
||||
// note: current weather does not use old data on a silent refresh
|
||||
// this is deliberate because it can pull data from more than one station in sequence
|
||||
|
||||
// filter for 4-letter observation stations, only those contain sky conditions and thus an icon
|
||||
const filteredStations = weatherParameters.stations.filter((station) => station?.properties?.stationIdentifier?.length === 4 && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
|
||||
const filteredStations = this.weatherParameters.stations.filter((station) => station?.properties?.stationIdentifier?.length === 4 && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
|
||||
|
||||
// Load the observations
|
||||
let observations;
|
||||
|
||||
@@ -18,14 +18,12 @@ class ExtendedForecast extends WeatherDisplay {
|
||||
this.timing.totalScreens = 2;
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
if (!super.getData(_weatherParameters)) return;
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
|
||||
// request us or si units
|
||||
let forecast;
|
||||
try {
|
||||
forecast = await json(weatherParameters.forecast, {
|
||||
this.data = await json(this.weatherParameters.forecast, {
|
||||
data: {
|
||||
units: settings.units.value,
|
||||
},
|
||||
@@ -35,11 +33,13 @@ class ExtendedForecast extends WeatherDisplay {
|
||||
} catch (error) {
|
||||
console.error('Unable to get extended forecast');
|
||||
console.error(error.status, error.responseJSON);
|
||||
this.setStatus(STATUS.failed);
|
||||
return;
|
||||
// if there's no previous data, fail
|
||||
if (!this.data) {
|
||||
this.setStatus(STATUS.failed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// we only get here if there was no error above
|
||||
this.data = parse(forecast.properties.periods);
|
||||
this.screenIndex = 0;
|
||||
this.setStatus(STATUS.loaded);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class ExtendedForecast extends WeatherDisplay {
|
||||
|
||||
// determine bounds
|
||||
// grab the first three or second set of three array elements
|
||||
const forecast = this.data.slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
|
||||
const forecast = parse(this.data.properties.periods).slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
|
||||
|
||||
// create each day template
|
||||
const days = forecast.map((Day) => {
|
||||
|
||||
@@ -26,9 +26,11 @@ class Hazards extends WeatherDisplay {
|
||||
this.timing.totalScreens = 0;
|
||||
}
|
||||
|
||||
async getData(weatherParameters) {
|
||||
async getData(weatherParameters, refresh) {
|
||||
// super checks for enabled
|
||||
const superResult = super.getData(weatherParameters);
|
||||
const superResult = super.getData(weatherParameters, refresh);
|
||||
// hazards performs a silent refresh, but does not fall back to a previous fetch if no data is available
|
||||
// this is intentional to ensure the latest alerts only are displayed.
|
||||
|
||||
const alert = this.checkbox.querySelector('.alert');
|
||||
alert.classList.remove('show');
|
||||
|
||||
@@ -23,8 +23,8 @@ class HourlyGraph extends WeatherDisplay {
|
||||
this.elem.querySelector('.header .right').append(header);
|
||||
}
|
||||
|
||||
async getData() {
|
||||
if (!super.getData()) return;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(undefined, refresh)) return;
|
||||
|
||||
const data = await getHourlyData(() => this.stillWaiting());
|
||||
if (data === undefined) {
|
||||
|
||||
@@ -27,23 +27,30 @@ class Hourly extends WeatherDisplay {
|
||||
this.timing.delay.push(150);
|
||||
}
|
||||
|
||||
async getData(weatherParameters) {
|
||||
async getData(weatherParameters, refresh) {
|
||||
// super checks for enabled
|
||||
const superResponse = super.getData(weatherParameters);
|
||||
const superResponse = super.getData(weatherParameters, refresh);
|
||||
let forecast;
|
||||
try {
|
||||
// get the forecast
|
||||
forecast = await json(weatherParameters.forecastGridData, { retryCount: 3, stillWaiting: () => this.stillWaiting() });
|
||||
forecast = await json(this.weatherParameters.forecastGridData, { retryCount: 3, stillWaiting: () => this.stillWaiting() });
|
||||
// parse the forecast
|
||||
this.data = await parseForecast(forecast.properties);
|
||||
} catch (error) {
|
||||
console.error('Get hourly forecast failed');
|
||||
console.error(error.status, error.responseJSON);
|
||||
if (this.isEnabled) this.setStatus(STATUS.failed);
|
||||
// return undefined to other subscribers
|
||||
this.getDataCallback(undefined);
|
||||
return;
|
||||
// use old data if available
|
||||
if (this.data) {
|
||||
console.log('Using previous hourly forecast');
|
||||
// don't return, this.data is usable from the previous update
|
||||
} else {
|
||||
if (this.isEnabled) this.setStatus(STATUS.failed);
|
||||
// return undefined to other subscribers
|
||||
this.getDataCallback(undefined);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.data = await parseForecast(forecast.properties);
|
||||
this.getDataCallback();
|
||||
if (!superResponse) return;
|
||||
|
||||
|
||||
@@ -16,14 +16,15 @@ class LatestObservations extends WeatherDisplay {
|
||||
this.MaximumRegionalStations = 7;
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
if (!super.getData(_weatherParameters)) return;
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
// latest observations does a silent refresh but will not fall back to previously fetched data
|
||||
// this is intentional because up to 30 stations are available to pull data from
|
||||
|
||||
// calculate distance to each station
|
||||
const stationsByDistance = Object.keys(StationInfo).map((key) => {
|
||||
const station = StationInfo[key];
|
||||
const distance = calcDistance(station.lat, station.lon, weatherParameters.latitude, weatherParameters.longitude);
|
||||
const distance = calcDistance(station.lat, station.lon, this.weatherParameters.latitude, this.weatherParameters.longitude);
|
||||
return { ...station, distance };
|
||||
});
|
||||
|
||||
|
||||
@@ -14,19 +14,21 @@ class LocalForecast extends WeatherDisplay {
|
||||
this.timing.baseDelay = 5000;
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
if (!super.getData(_weatherParameters)) return;
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
|
||||
// get raw data
|
||||
const rawData = await this.getRawData(weatherParameters);
|
||||
// check for data
|
||||
if (!rawData) {
|
||||
const rawData = await this.getRawData(this.weatherParameters);
|
||||
// check for data, or if there's old data available
|
||||
if (!rawData && !this.data) {
|
||||
// fail for no old or new data
|
||||
this.setStatus(STATUS.failed);
|
||||
return;
|
||||
}
|
||||
// store the data
|
||||
this.data = rawData || this.data;
|
||||
// parse raw data
|
||||
const conditions = parse(rawData);
|
||||
const conditions = parse(this.data);
|
||||
|
||||
// read each text
|
||||
this.screenTexts = conditions.map((condition) => {
|
||||
@@ -70,7 +72,6 @@ class LocalForecast extends WeatherDisplay {
|
||||
} catch (error) {
|
||||
console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`);
|
||||
console.error(error.status, error.responseJSON);
|
||||
this.setStatus(STATUS.failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ const playerEnded = () => {
|
||||
// next track
|
||||
currentTrack += 1;
|
||||
// roll over and re-randomize the tracks
|
||||
if (currentTrack >= playlist.availableFiles) {
|
||||
if (currentTrack >= playlist.availableFiles.length) {
|
||||
randomizePlaylist();
|
||||
currentTrack = 0;
|
||||
}
|
||||
|
||||
@@ -15,26 +15,11 @@ let playing = false;
|
||||
let progress;
|
||||
const weatherParameters = {};
|
||||
|
||||
// auto refresh
|
||||
const AUTO_REFRESH_INTERVAL_MS = 500;
|
||||
const AUTO_REFRESH_TIME_MS = 600_000; // 10 min.
|
||||
const CHK_AUTO_REFRESH_SELECTOR = '#chkAutoRefresh';
|
||||
let AutoRefreshIntervalId = null;
|
||||
let AutoRefreshCountMs = 0;
|
||||
|
||||
const init = async () => {
|
||||
// set up resize handler
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
|
||||
// auto refresh
|
||||
const autoRefresh = localStorage.getItem('autoRefresh');
|
||||
if (!autoRefresh || autoRefresh === 'true') {
|
||||
document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = true;
|
||||
} else {
|
||||
document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = false;
|
||||
}
|
||||
document.querySelector(CHK_AUTO_REFRESH_SELECTOR).addEventListener('change', autoRefreshChange);
|
||||
generateCheckboxes();
|
||||
};
|
||||
|
||||
@@ -123,12 +108,6 @@ const updateStatus = (value) => {
|
||||
if (isPlaying() && value.id === firstDisplayIndex && value.status === STATUS.loaded) {
|
||||
navTo(msg.command.firstFrame);
|
||||
}
|
||||
|
||||
// send loaded messaged to parent
|
||||
if (countLoadedDisplays() < displays.length) return;
|
||||
|
||||
// everything loaded, set timestamps
|
||||
AssignLastUpdate(new Date());
|
||||
};
|
||||
|
||||
// note: a display that is "still waiting"/"retrying" is considered loaded intentionally
|
||||
@@ -202,8 +181,6 @@ const loadDisplay = (direction) => {
|
||||
idx = wrap(curIdx + (i + 1) * direction, totalDisplays);
|
||||
if (displays[idx].status === STATUS.loaded && displays[idx].timing.totalScreens > 0) break;
|
||||
}
|
||||
// if new display index is less than current display a wrap occurred, test for reload timeout
|
||||
if (idx <= curIdx && refreshCheck()) return;
|
||||
const newDisplay = displays[idx];
|
||||
// hide all displays
|
||||
hideAllCanvases();
|
||||
@@ -320,83 +297,8 @@ const populateWeatherParameters = (params) => {
|
||||
document.querySelector('#spanZoneId').innerHTML = params.zoneId;
|
||||
};
|
||||
|
||||
const autoRefreshChange = (e) => {
|
||||
const { checked } = e.target;
|
||||
|
||||
if (checked) {
|
||||
startAutoRefreshTimer();
|
||||
} else {
|
||||
stopAutoRefreshTimer();
|
||||
}
|
||||
|
||||
localStorage.setItem('autoRefresh', checked);
|
||||
};
|
||||
|
||||
const AssignLastUpdate = (date) => {
|
||||
if (date) {
|
||||
document.querySelector('#spanLastRefresh').innerHTML = date.toLocaleString('en-US', {
|
||||
weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short',
|
||||
});
|
||||
if (document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked) startAutoRefreshTimer();
|
||||
} else {
|
||||
document.querySelector('#spanLastRefresh').innerHTML = '(none)';
|
||||
}
|
||||
};
|
||||
|
||||
const latLonReceived = (data, haveDataCallback) => {
|
||||
getWeather(data, haveDataCallback);
|
||||
AssignLastUpdate(null);
|
||||
};
|
||||
|
||||
const startAutoRefreshTimer = () => {
|
||||
// Ensure that any previous timer has already stopped.
|
||||
// check if timer is running
|
||||
if (AutoRefreshIntervalId) return;
|
||||
|
||||
// Reset the time elapsed.
|
||||
AutoRefreshCountMs = 0;
|
||||
|
||||
const AutoRefreshTimer = () => {
|
||||
// Increment the total time elapsed.
|
||||
AutoRefreshCountMs += AUTO_REFRESH_INTERVAL_MS;
|
||||
|
||||
// Display the count down.
|
||||
let RemainingMs = (AUTO_REFRESH_TIME_MS - AutoRefreshCountMs);
|
||||
if (RemainingMs < 0) {
|
||||
RemainingMs = 0;
|
||||
}
|
||||
const dt = new Date(RemainingMs);
|
||||
document.querySelector('#spanRefreshCountDown').innerHTML = `${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}`;
|
||||
|
||||
// Time has elapsed.
|
||||
if (AutoRefreshCountMs >= AUTO_REFRESH_TIME_MS && !isPlaying()) loadTwcData();
|
||||
};
|
||||
AutoRefreshIntervalId = window.setInterval(AutoRefreshTimer, AUTO_REFRESH_INTERVAL_MS);
|
||||
AutoRefreshTimer();
|
||||
};
|
||||
const stopAutoRefreshTimer = () => {
|
||||
if (AutoRefreshIntervalId) {
|
||||
window.clearInterval(AutoRefreshIntervalId);
|
||||
document.querySelector('#spanRefreshCountDown').innerHTML = '--:--';
|
||||
AutoRefreshIntervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshCheck = () => {
|
||||
// Time has elapsed.
|
||||
if (AutoRefreshCountMs >= AUTO_REFRESH_TIME_MS && isPlaying()) {
|
||||
loadTwcData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const loadTwcData = () => {
|
||||
if (loadTwcData.callback) loadTwcData.callback();
|
||||
};
|
||||
|
||||
const registerRefreshData = (callback) => {
|
||||
loadTwcData.callback = callback;
|
||||
};
|
||||
|
||||
const timeZone = () => weatherParameters.timeZone;
|
||||
@@ -414,7 +316,5 @@ export {
|
||||
msg,
|
||||
message,
|
||||
latLonReceived,
|
||||
stopAutoRefreshTimer,
|
||||
registerRefreshData,
|
||||
timeZone,
|
||||
};
|
||||
|
||||
@@ -42,19 +42,17 @@ class Radar extends WeatherDisplay {
|
||||
];
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
if (!super.getData(_weatherParameters)) return;
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
|
||||
// ALASKA AND HAWAII AREN'T SUPPORTED!
|
||||
if (weatherParameters.state === 'AK' || weatherParameters.state === 'HI') {
|
||||
if (this.weatherParameters.state === 'AK' || this.weatherParameters.state === 'HI') {
|
||||
this.setStatus(STATUS.noData);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the base map
|
||||
let src = 'images/4000RadarMap2.jpg';
|
||||
if (weatherParameters.State === 'HI') src = 'images/HawaiiRadarMap2.png';
|
||||
const src = 'images/4000RadarMap2.jpg';
|
||||
this.baseMap = await loadImg(src);
|
||||
|
||||
const baseUrl = 'https://mesonet.agron.iastate.edu/archive/data/';
|
||||
@@ -70,7 +68,7 @@ class Radar extends WeatherDisplay {
|
||||
|
||||
const lists = (await Promise.all(baseUrls.map(async (url) => {
|
||||
try {
|
||||
// get a list of available radars
|
||||
// get a list of available radars
|
||||
return text(url, { cors: true });
|
||||
} catch (error) {
|
||||
console.log('Unable to get list of radars');
|
||||
@@ -91,7 +89,7 @@ class Radar extends WeatherDisplay {
|
||||
const anchors = xmlDoc.querySelectorAll('a');
|
||||
const urls = [];
|
||||
Array.from(anchors).forEach((elem) => {
|
||||
if (elem.innerHTML?.match(/n0r_\d{12}\.png/)) {
|
||||
if (elem.innerHTML?.match(/n0r_\d{12}\.png/)) {
|
||||
urls.push(elem.href);
|
||||
}
|
||||
});
|
||||
@@ -110,19 +108,12 @@ class Radar extends WeatherDisplay {
|
||||
const height = 1600;
|
||||
offsetX *= 2;
|
||||
offsetY *= 2;
|
||||
const sourceXY = utils.getXYFromLatitudeLongitudeMap(weatherParameters, offsetX, offsetY);
|
||||
|
||||
// create working context for manipulation
|
||||
const workingCanvas = document.createElement('canvas');
|
||||
workingCanvas.width = width;
|
||||
workingCanvas.height = height;
|
||||
const workingContext = workingCanvas.getContext('2d');
|
||||
workingContext.imageSmoothingEnabled = false;
|
||||
const sourceXY = utils.getXYFromLatitudeLongitudeMap(this.weatherParameters, offsetX, offsetY);
|
||||
|
||||
// calculate radar offsets
|
||||
const radarOffsetX = 120;
|
||||
const radarOffsetY = 70;
|
||||
const radarSourceXY = utils.getXYFromLatitudeLongitudeDoppler(weatherParameters, offsetX, offsetY);
|
||||
const radarSourceXY = utils.getXYFromLatitudeLongitudeDoppler(this.weatherParameters, offsetX, offsetY);
|
||||
const radarSourceX = radarSourceXY.x / 2;
|
||||
const radarSourceY = radarSourceXY.y / 2;
|
||||
|
||||
@@ -135,6 +126,13 @@ class Radar extends WeatherDisplay {
|
||||
const context = canvas.getContext('2d');
|
||||
context.imageSmoothingEnabled = false;
|
||||
|
||||
// create working context for manipulation
|
||||
const workingCanvas = document.createElement('canvas');
|
||||
workingCanvas.width = width;
|
||||
workingCanvas.height = height;
|
||||
const workingContext = workingCanvas.getContext('2d');
|
||||
workingContext.imageSmoothingEnabled = false;
|
||||
|
||||
// get the image
|
||||
const response = await fetch(rewriteUrl(url));
|
||||
|
||||
@@ -170,7 +168,7 @@ class Radar extends WeatherDisplay {
|
||||
workingContext.drawImage(imgBlob, 0, 0, width, 1600);
|
||||
|
||||
// get the base map
|
||||
context.drawImage(await this.baseMap, sourceXY.x, sourceXY.y, offsetX * 2, offsetY * 2, 0, 0, 640, 367);
|
||||
context.drawImage(this.baseMap, sourceXY.x, sourceXY.y, offsetX * 2, offsetY * 2, 0, 0, 640, 367);
|
||||
|
||||
// crop the radar image
|
||||
const cropCanvas = document.createElement('canvas');
|
||||
|
||||
@@ -21,9 +21,11 @@ class RegionalForecast extends WeatherDisplay {
|
||||
this.timing.totalScreens = 3;
|
||||
}
|
||||
|
||||
async getData(_weatherParameters) {
|
||||
if (!super.getData(_weatherParameters)) return;
|
||||
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
// regional forecast implements a silent reload
|
||||
// but it will not fall back to previously loaded data if data can not be loaded
|
||||
// there are enough other cities available to populate the map sufficiently even if some do not load
|
||||
|
||||
// pre-load the base map
|
||||
let baseMap = 'images/Basemap2.png';
|
||||
@@ -40,14 +42,14 @@ class RegionalForecast extends WeatherDisplay {
|
||||
y: 117,
|
||||
};
|
||||
// get user's location in x/y
|
||||
const sourceXY = utils.getXYFromLatitudeLongitude(weatherParameters.latitude, weatherParameters.longitude, offsetXY.x, offsetXY.y, weatherParameters.state);
|
||||
const sourceXY = utils.getXYFromLatitudeLongitude(this.weatherParameters.latitude, this.weatherParameters.longitude, offsetXY.x, offsetXY.y, weatherParameters.state);
|
||||
|
||||
// get latitude and longitude limits
|
||||
const minMaxLatLon = utils.getMinMaxLatitudeLongitude(sourceXY.x, sourceXY.y, offsetXY.x, offsetXY.y, weatherParameters.state);
|
||||
const minMaxLatLon = utils.getMinMaxLatitudeLongitude(sourceXY.x, sourceXY.y, offsetXY.x, offsetXY.y, this.weatherParameters.state);
|
||||
|
||||
// get a target distance
|
||||
let targetDistance = 2.5;
|
||||
if (weatherParameters.state === 'HI') targetDistance = 1;
|
||||
if (this.weatherParameters.state === 'HI') targetDistance = 1;
|
||||
|
||||
// make station info into an array
|
||||
const stationInfoArray = Object.values(StationInfo).map((value) => ({ ...value, targetDistance }));
|
||||
@@ -86,7 +88,7 @@ class RegionalForecast extends WeatherDisplay {
|
||||
const forecast = await json(`https://api.weather.gov/gridpoints/${point.wfo}/${point.x},${point.y}/forecast`);
|
||||
|
||||
// get XY on map for city
|
||||
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, weatherParameters.state);
|
||||
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, this.weatherParameters.state);
|
||||
|
||||
// wait for the regional observation if it's not done yet
|
||||
const observation = await observationPromise;
|
||||
|
||||
@@ -22,6 +22,13 @@ const init = () => {
|
||||
['us', 'US'],
|
||||
['si', 'Metric'],
|
||||
]);
|
||||
settings.refreshTime = new Setting('refreshTime', 'Refresh Time', 'select', 600_000, null, false, [
|
||||
[30_000, 'TESTING'],
|
||||
[300_000, '5 minutes'],
|
||||
[600_000, '10 minutes'],
|
||||
[900_000, '15 minutes'],
|
||||
[1_800_000, '30 minutes'],
|
||||
]);
|
||||
|
||||
// generate html objects
|
||||
const settingHtml = Object.values(settings).map((d) => d.generate());
|
||||
|
||||
@@ -26,20 +26,42 @@ class TravelForecast extends WeatherDisplay {
|
||||
if (extra !== 0) this.timing.delay.push(Math.round(this.extra * this.cityHeight));
|
||||
// add the final 3 second delay
|
||||
this.timing.delay.push(150);
|
||||
|
||||
// add previous data cache
|
||||
this.previousData = [];
|
||||
}
|
||||
|
||||
async getData() {
|
||||
async getData(weatherParameters, refresh) {
|
||||
// super checks for enabled
|
||||
if (!super.getData()) return;
|
||||
const forecastPromises = TravelCities.map(async (city) => {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
|
||||
// clear stored data if not refresh
|
||||
if (!refresh) {
|
||||
this.previousData = [];
|
||||
}
|
||||
|
||||
const forecastPromises = TravelCities.map(async (city, index) => {
|
||||
try {
|
||||
// get point then forecast
|
||||
if (!city.point) throw new Error('No pre-loaded point');
|
||||
const forecast = await json(`https://api.weather.gov/gridpoints/${city.point.wfo}/${city.point.x},${city.point.y}/forecast`, {
|
||||
data: {
|
||||
units: settings.units.value,
|
||||
},
|
||||
});
|
||||
let forecast;
|
||||
try {
|
||||
forecast = await json(`https://api.weather.gov/gridpoints/${city.point.wfo}/${city.point.x},${city.point.y}/forecast`, {
|
||||
data: {
|
||||
units: settings.units.value,
|
||||
},
|
||||
});
|
||||
// store for the next run
|
||||
this.previousData[index] = forecast;
|
||||
} catch (e) {
|
||||
// if there's previous data use it
|
||||
if (this.previousData?.[index]) {
|
||||
forecast = this.previousData?.[index];
|
||||
} else {
|
||||
// otherwise re-throw for the standard error handling
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
// determine today or tomorrow (shift periods by 1 if tomorrow)
|
||||
const todayShift = forecast.properties.periods[0].isDaytime ? 0 : 1;
|
||||
// return a pared-down forecast
|
||||
|
||||
@@ -73,7 +73,7 @@ const doFetch = (url, params) => new Promise((resolve, reject) => {
|
||||
// out of retries
|
||||
return resolve(response);
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
.catch(reject);
|
||||
});
|
||||
|
||||
const delay = (time, func, ...args) => new Promise((resolve) => {
|
||||
|
||||
@@ -181,7 +181,7 @@ class Setting {
|
||||
|
||||
selectHighlight(newValue) {
|
||||
// set the dropdown to the provided value
|
||||
this.element.querySelectorAll('option').forEach((elem) => {
|
||||
this?.element?.querySelectorAll('option')?.forEach?.((elem) => {
|
||||
elem.selected = (newValue?.toFixed?.(2) === elem.value) || (newValue === elem.value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class WeatherDisplay {
|
||||
this.okToDrawCurrentConditions = true;
|
||||
this.okToDrawCurrentDateTime = true;
|
||||
this.showOnProgress = true;
|
||||
this.autoRefreshHandle = null;
|
||||
|
||||
// default navigation timing
|
||||
this.timing = {
|
||||
@@ -129,9 +130,14 @@ class WeatherDisplay {
|
||||
}
|
||||
|
||||
// get necessary data for this display
|
||||
getData(weatherParameters) {
|
||||
// clear current data
|
||||
this.data = undefined;
|
||||
getData(weatherParameters, refresh) {
|
||||
// refresh doesn't delete existing data, and is reused if the silent refresh fails
|
||||
if (!refresh) {
|
||||
this.data = undefined;
|
||||
}
|
||||
// clear any refresh timers
|
||||
clearTimeout(this.autoRefreshHandle);
|
||||
this.autoRefreshHandle = null;
|
||||
|
||||
// store weatherParameters locally in case we need them later
|
||||
if (weatherParameters) this.weatherParameters = weatherParameters;
|
||||
@@ -144,6 +150,9 @@ class WeatherDisplay {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set up auto reload
|
||||
this.autoRefreshHandle = setTimeout(() => this.getData(false, true), settings.refreshTime.value);
|
||||
|
||||
// recalculate navigation timing (in case it was modified in the constructor)
|
||||
this.calcNavTiming();
|
||||
return true;
|
||||
|
||||
2
server/scripts/vendor/auto/luxon.js.map
vendored
2
server/scripts/vendor/auto/luxon.js.map
vendored
File diff suppressed because one or more lines are too long
158
server/scripts/vendor/auto/luxon.mjs
vendored
158
server/scripts/vendor/auto/luxon.mjs
vendored
@@ -392,12 +392,13 @@ class SystemZone extends Zone {
|
||||
}
|
||||
}
|
||||
|
||||
let dtfCache = {};
|
||||
function makeDTF(zone) {
|
||||
if (!dtfCache[zone]) {
|
||||
dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
|
||||
const dtfCache = new Map();
|
||||
function makeDTF(zoneName) {
|
||||
let dtf = dtfCache.get(zoneName);
|
||||
if (dtf === undefined) {
|
||||
dtf = new Intl.DateTimeFormat("en-US", {
|
||||
hour12: false,
|
||||
timeZone: zone,
|
||||
timeZone: zoneName,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
@@ -406,8 +407,9 @@ function makeDTF(zone) {
|
||||
second: "2-digit",
|
||||
era: "short",
|
||||
});
|
||||
dtfCache.set(zoneName, dtf);
|
||||
}
|
||||
return dtfCache[zone];
|
||||
return dtf;
|
||||
}
|
||||
|
||||
const typeToPos = {
|
||||
@@ -443,7 +445,7 @@ function partsOffset(dtf, date) {
|
||||
return filled;
|
||||
}
|
||||
|
||||
let ianaZoneCache = {};
|
||||
const ianaZoneCache = new Map();
|
||||
/**
|
||||
* A zone identified by an IANA identifier, like America/New_York
|
||||
* @implements {Zone}
|
||||
@@ -454,10 +456,11 @@ class IANAZone extends Zone {
|
||||
* @return {IANAZone}
|
||||
*/
|
||||
static create(name) {
|
||||
if (!ianaZoneCache[name]) {
|
||||
ianaZoneCache[name] = new IANAZone(name);
|
||||
let zone = ianaZoneCache.get(name);
|
||||
if (zone === undefined) {
|
||||
ianaZoneCache.set(name, (zone = new IANAZone(name)));
|
||||
}
|
||||
return ianaZoneCache[name];
|
||||
return zone;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,8 +468,8 @@ class IANAZone extends Zone {
|
||||
* @return {void}
|
||||
*/
|
||||
static resetCache() {
|
||||
ianaZoneCache = {};
|
||||
dtfCache = {};
|
||||
ianaZoneCache.clear();
|
||||
dtfCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,6 +572,7 @@ class IANAZone extends Zone {
|
||||
* @return {number}
|
||||
*/
|
||||
offset(ts) {
|
||||
if (!this.valid) return NaN;
|
||||
const date = new Date(ts);
|
||||
|
||||
if (isNaN(date)) return NaN;
|
||||
@@ -634,36 +638,36 @@ function getCachedLF(locString, opts = {}) {
|
||||
return dtf;
|
||||
}
|
||||
|
||||
let intlDTCache = {};
|
||||
const intlDTCache = new Map();
|
||||
function getCachedDTF(locString, opts = {}) {
|
||||
const key = JSON.stringify([locString, opts]);
|
||||
let dtf = intlDTCache[key];
|
||||
if (!dtf) {
|
||||
let dtf = intlDTCache.get(key);
|
||||
if (dtf === undefined) {
|
||||
dtf = new Intl.DateTimeFormat(locString, opts);
|
||||
intlDTCache[key] = dtf;
|
||||
intlDTCache.set(key, dtf);
|
||||
}
|
||||
return dtf;
|
||||
}
|
||||
|
||||
let intlNumCache = {};
|
||||
const intlNumCache = new Map();
|
||||
function getCachedINF(locString, opts = {}) {
|
||||
const key = JSON.stringify([locString, opts]);
|
||||
let inf = intlNumCache[key];
|
||||
if (!inf) {
|
||||
let inf = intlNumCache.get(key);
|
||||
if (inf === undefined) {
|
||||
inf = new Intl.NumberFormat(locString, opts);
|
||||
intlNumCache[key] = inf;
|
||||
intlNumCache.set(key, inf);
|
||||
}
|
||||
return inf;
|
||||
}
|
||||
|
||||
let intlRelCache = {};
|
||||
const intlRelCache = new Map();
|
||||
function getCachedRTF(locString, opts = {}) {
|
||||
const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
|
||||
const key = JSON.stringify([locString, cacheKeyOpts]);
|
||||
let inf = intlRelCache[key];
|
||||
if (!inf) {
|
||||
let inf = intlRelCache.get(key);
|
||||
if (inf === undefined) {
|
||||
inf = new Intl.RelativeTimeFormat(locString, opts);
|
||||
intlRelCache[key] = inf;
|
||||
intlRelCache.set(key, inf);
|
||||
}
|
||||
return inf;
|
||||
}
|
||||
@@ -678,14 +682,28 @@ function systemLocale() {
|
||||
}
|
||||
}
|
||||
|
||||
let weekInfoCache = {};
|
||||
const intlResolvedOptionsCache = new Map();
|
||||
function getCachedIntResolvedOptions(locString) {
|
||||
let opts = intlResolvedOptionsCache.get(locString);
|
||||
if (opts === undefined) {
|
||||
opts = new Intl.DateTimeFormat(locString).resolvedOptions();
|
||||
intlResolvedOptionsCache.set(locString, opts);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
const weekInfoCache = new Map();
|
||||
function getCachedWeekInfo(locString) {
|
||||
let data = weekInfoCache[locString];
|
||||
let data = weekInfoCache.get(locString);
|
||||
if (!data) {
|
||||
const locale = new Intl.Locale(locString);
|
||||
// browsers currently implement this as a property, but spec says it should be a getter function
|
||||
data = "getWeekInfo" in locale ? locale.getWeekInfo() : locale.weekInfo;
|
||||
weekInfoCache[locString] = data;
|
||||
// minimalDays was removed from WeekInfo: https://github.com/tc39/proposal-intl-locale-info/issues/86
|
||||
if (!("minimalDays" in data)) {
|
||||
data = { ...fallbackWeekSettings, ...data };
|
||||
}
|
||||
weekInfoCache.set(locString, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -784,7 +802,7 @@ function supportsFastNumbers(loc) {
|
||||
loc.numberingSystem === "latn" ||
|
||||
!loc.locale ||
|
||||
loc.locale.startsWith("en") ||
|
||||
new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn"
|
||||
getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -943,7 +961,6 @@ const fallbackWeekSettings = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
class Locale {
|
||||
static fromOpts(opts) {
|
||||
return Locale.create(
|
||||
@@ -967,9 +984,11 @@ class Locale {
|
||||
|
||||
static resetCache() {
|
||||
sysLocaleCache = null;
|
||||
intlDTCache = {};
|
||||
intlNumCache = {};
|
||||
intlRelCache = {};
|
||||
intlDTCache.clear();
|
||||
intlNumCache.clear();
|
||||
intlRelCache.clear();
|
||||
intlResolvedOptionsCache.clear();
|
||||
weekInfoCache.clear();
|
||||
}
|
||||
|
||||
static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {
|
||||
@@ -1123,7 +1142,7 @@ class Locale {
|
||||
return (
|
||||
this.locale === "en" ||
|
||||
this.locale.toLowerCase() === "en-us" ||
|
||||
new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us")
|
||||
getCachedIntResolvedOptions(this.intl).locale.startsWith("en-us")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1461,22 +1480,26 @@ function parseDigits(str) {
|
||||
}
|
||||
|
||||
// cache of {numberingSystem: {append: regex}}
|
||||
let digitRegexCache = {};
|
||||
const digitRegexCache = new Map();
|
||||
function resetDigitRegexCache() {
|
||||
digitRegexCache = {};
|
||||
digitRegexCache.clear();
|
||||
}
|
||||
|
||||
function digitRegex({ numberingSystem }, append = "") {
|
||||
const ns = numberingSystem || "latn";
|
||||
|
||||
if (!digitRegexCache[ns]) {
|
||||
digitRegexCache[ns] = {};
|
||||
let appendCache = digitRegexCache.get(ns);
|
||||
if (appendCache === undefined) {
|
||||
appendCache = new Map();
|
||||
digitRegexCache.set(ns, appendCache);
|
||||
}
|
||||
if (!digitRegexCache[ns][append]) {
|
||||
digitRegexCache[ns][append] = new RegExp(`${numberingSystems[ns]}${append}`);
|
||||
let regex = appendCache.get(append);
|
||||
if (regex === undefined) {
|
||||
regex = new RegExp(`${numberingSystems[ns]}${append}`);
|
||||
appendCache.set(append, regex);
|
||||
}
|
||||
|
||||
return digitRegexCache[ns][append];
|
||||
return regex;
|
||||
}
|
||||
|
||||
let now = () => Date.now(),
|
||||
@@ -4227,6 +4250,14 @@ class Interval {
|
||||
return this.isValid ? this.e : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last DateTime included in the interval (since end is not part of the interval)
|
||||
* @type {DateTime}
|
||||
*/
|
||||
get lastDateTime() {
|
||||
return this.isValid ? (this.e ? this.e.minus(1) : null) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
|
||||
* @type {boolean}
|
||||
@@ -4491,8 +4522,11 @@ class Interval {
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge an array of Intervals into a equivalent minimal set of Intervals.
|
||||
* Merge an array of Intervals into an equivalent minimal set of Intervals.
|
||||
* Combines overlapping and adjacent Intervals.
|
||||
* The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval
|
||||
* and ending with the latest.
|
||||
*
|
||||
* @param {Array} intervals
|
||||
* @return {Array}
|
||||
*/
|
||||
@@ -5815,15 +5849,27 @@ function normalizeUnitWithLocalWeeks(unit) {
|
||||
// This is safe for quickDT (used by local() and utc()) because we don't fill in
|
||||
// higher-order units from tsNow (as we do in fromObject, this requires that
|
||||
// offset is calculated from tsNow).
|
||||
/**
|
||||
* @param {Zone} zone
|
||||
* @return {number}
|
||||
*/
|
||||
function guessOffsetForZone(zone) {
|
||||
if (!zoneOffsetGuessCache[zone]) {
|
||||
if (zoneOffsetTs === undefined) {
|
||||
zoneOffsetTs = Settings.now();
|
||||
}
|
||||
|
||||
zoneOffsetGuessCache[zone] = zone.offset(zoneOffsetTs);
|
||||
if (zoneOffsetTs === undefined) {
|
||||
zoneOffsetTs = Settings.now();
|
||||
}
|
||||
return zoneOffsetGuessCache[zone];
|
||||
|
||||
// Do not cache anything but IANA zones, because it is not safe to do so.
|
||||
// Guessing an offset which is not present in the zone can cause wrong results from fixOffset
|
||||
if (zone.type !== "iana") {
|
||||
return zone.offset(zoneOffsetTs);
|
||||
}
|
||||
const zoneName = zone.name;
|
||||
let offsetGuess = zoneOffsetGuessCache.get(zoneName);
|
||||
if (offsetGuess === undefined) {
|
||||
offsetGuess = zone.offset(zoneOffsetTs);
|
||||
zoneOffsetGuessCache.set(zoneName, offsetGuess);
|
||||
}
|
||||
return offsetGuess;
|
||||
}
|
||||
|
||||
// this is a dumbed down version of fromObject() that runs about 60% faster
|
||||
@@ -5913,7 +5959,7 @@ let zoneOffsetTs;
|
||||
* This optimizes quickDT via guessOffsetForZone to avoid repeated calls of
|
||||
* zone.offset().
|
||||
*/
|
||||
let zoneOffsetGuessCache = {};
|
||||
const zoneOffsetGuessCache = new Map();
|
||||
|
||||
/**
|
||||
* A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
|
||||
@@ -6478,7 +6524,7 @@ class DateTime {
|
||||
|
||||
static resetCache() {
|
||||
zoneOffsetTs = undefined;
|
||||
zoneOffsetGuessCache = {};
|
||||
zoneOffsetGuessCache.clear();
|
||||
}
|
||||
|
||||
// INFO
|
||||
@@ -7247,7 +7293,7 @@ class DateTime {
|
||||
* @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
|
||||
* @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
|
||||
* @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
|
||||
* @return {string}
|
||||
* @return {string|null}
|
||||
*/
|
||||
toISO({
|
||||
format = "extended",
|
||||
@@ -7274,7 +7320,7 @@ class DateTime {
|
||||
* @param {string} [opts.format='extended'] - choose between the basic and extended format
|
||||
* @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
|
||||
* @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
|
||||
* @return {string}
|
||||
* @return {string|null}
|
||||
*/
|
||||
toISODate({ format = "extended" } = {}) {
|
||||
if (!this.isValid) {
|
||||
@@ -7359,7 +7405,7 @@ class DateTime {
|
||||
/**
|
||||
* Returns a string representation of this DateTime appropriate for use in SQL Date
|
||||
* @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
|
||||
* @return {string}
|
||||
* @return {string|null}
|
||||
*/
|
||||
toSQLDate() {
|
||||
if (!this.isValid) {
|
||||
@@ -7454,7 +7500,7 @@ class DateTime {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the epoch seconds of this DateTime.
|
||||
* Returns the epoch seconds (including milliseconds in the fractional part) of this DateTime.
|
||||
* @return {number}
|
||||
*/
|
||||
toSeconds() {
|
||||
@@ -7561,7 +7607,7 @@ class DateTime {
|
||||
/**
|
||||
* Return an Interval spanning between this DateTime and another DateTime
|
||||
* @param {DateTime} otherDateTime - the other end point of the Interval
|
||||
* @return {Interval}
|
||||
* @return {Interval|DateTime}
|
||||
*/
|
||||
until(otherDateTime) {
|
||||
return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
|
||||
@@ -7979,7 +8025,7 @@ function friendlyDateTime(dateTimeish) {
|
||||
}
|
||||
}
|
||||
|
||||
const VERSION = "3.5.0";
|
||||
const VERSION = "3.6.1";
|
||||
|
||||
export { DateTime, Duration, FixedOffsetZone, IANAZone, Info, Interval, InvalidZone, Settings, SystemZone, VERSION, Zone };
|
||||
//# sourceMappingURL=luxon.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -745,8 +745,7 @@ body {
|
||||
>.heading,
|
||||
#enabledDisplays,
|
||||
#settings,
|
||||
#divInfo,
|
||||
#divRefresh {
|
||||
#divInfo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="icon" href="images/Logo192.png" />
|
||||
<link rel="preload" href="playlist.json" as="fetch"/>
|
||||
<link rel="preload" href="playlist.json" as="fetch" crossorigin="anonymous"/>
|
||||
<meta property="og:image" content="https://weatherstar.netbymatt.com/images/social/1200x600.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="627">
|
||||
@@ -133,7 +133,7 @@
|
||||
<div id="divTwcBottomRight">
|
||||
<div id="ToggleMedia">
|
||||
<img class="navButton off" src="images/nav/ic_volume_off_white_24dp_2x.png" title="Unmute" />
|
||||
<img class="navButton on" src="images/nav/ic_volume_on_white_24dp_2x.png" title="Unmute" />
|
||||
<img class="navButton on" src="images/nav/ic_volume_on_white_24dp_2x.png" title="Mute" />
|
||||
</div>
|
||||
<img id="ToggleFullScreen" class="navButton" src="images/nav/ic_fullscreen_white_24dp_2x.png" title="Enter Fullscreen" />
|
||||
</div>
|
||||
@@ -173,11 +173,6 @@
|
||||
Zone Id: <span id="spanZoneId"></span><br />
|
||||
</div>
|
||||
|
||||
<div id="divRefresh">
|
||||
Last Update: <span id="spanLastRefresh">(None)</span><br />
|
||||
<input id="chkAutoRefresh" name="chkAutoRefresh" type="checkbox" /><label id="lblRefreshCountDown" for="chkAutoRefresh">Auto Refresh: <span id="spanRefreshCountDown">--:--</span></label>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -52,11 +52,7 @@
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "j69.ejs-beautify"
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/debug.log": true,
|
||||
"server/scripts/custom.js": true
|
||||
},
|
||||
"files.exclude": {},
|
||||
"files.eol": "\n",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
|
||||
Reference in New Issue
Block a user