mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-16 08:39:29 -07:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3faf95e39 | ||
|
|
3a304d7c08 | ||
|
|
c6918f0c6c | ||
|
|
fb93ade4d2 | ||
|
|
6f260a6ac7 | ||
|
|
50e526639c | ||
|
|
38db5cb6a6 | ||
|
|
8c278928b9 | ||
|
|
130a1bfad3 | ||
|
|
bd6c5430c4 | ||
|
|
cc896bf18d | ||
|
|
f7a15a93c6 | ||
|
|
0baa31a92c | ||
|
|
8fa00b34b4 | ||
|
|
23cc1a1f7a | ||
|
|
b272aa298a | ||
|
|
d810b429c5 | ||
|
|
560b51ccee | ||
|
|
e8f69ce28b | ||
|
|
46fb40058f | ||
|
|
b40fe08465 | ||
|
|
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"
|
||||
]
|
||||
}
|
||||
@@ -7,4 +7,4 @@ COPY package-lock.json .
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
CMD ["node", "index.js"]
|
||||
CMD ["node", "index.mjs"]
|
||||
|
||||
@@ -111,7 +111,12 @@ The WeatherStar had wonderful background music from the smooth jazz and new age
|
||||
I've used AI tools to create WeatherStar-inspired music tracks that are unencumbered by copyright and are included in this repo. Too keep the size down, I've only included 4 tracks. Additional tracks will be posted in a companion repository [ws4kp-music](https://github.com/netbymatt/ws4kp-music).
|
||||
|
||||
### Customizing the music
|
||||
Placing .mp3 files in the `/server/music` folder will override the default music included in the repo. When weatherstar loads in the browser it will load a list if available files and randomize the order when it starts playing. On each loop through the available tracks the order will again be shuffled. If you're using the static files method to host your WeatherStar music is located in `/music`.
|
||||
Placing .mp3 files in the `/server/music` folder will override the default music included in the repo. Subdirectories will not be scanned. When weatherstar loads in the browser it will load a list if available files and randomize the order when it starts playing. On each loop through the available tracks the order will again be shuffled. If you're using the static files method to host your WeatherStar music is located in `/music`.
|
||||
|
||||
If using docker, you must pass a local accessible folder to the container in the `/app/server/music` directory.
|
||||
```
|
||||
docker run -p 8080:8080 -v /path/to/local/music:/app/server/music ghcr.io/netbymatt/ws4kp
|
||||
```
|
||||
|
||||
### Music doesn't auto play
|
||||
Ws4kp is muted by default, but if it was unmuted on the last visit it is coded to try and auto play music on subsequent visits. But, it's considered bad form to have a web site play music automatically on load, and I fully agree with this. [Chrome](https://developer.chrome.com/blog/autoplay/#media_engagement_index) and [Firefox](https://hacks.mozilla.org/2019/02/firefox-66-to-block-automatically-playing-audible-video-and-audio/) have extensive details on how and when auto play is allowed.
|
||||
|
||||
@@ -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,11 +12,11 @@ import s3Upload from 'gulp-s3-upload';
|
||||
import webpack from 'webpack-stream';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import { readFile } from 'fs/promises';
|
||||
import reader from '../src/playlist-reader.mjs';
|
||||
import file from "gulp-file";
|
||||
import file from 'gulp-file';
|
||||
|
||||
// get cloudfront
|
||||
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
|
||||
import reader from '../src/playlist-reader.mjs';
|
||||
|
||||
const clean = () => deleteAsync(['./dist/**/*', '!./dist/readme.txt']);
|
||||
|
||||
@@ -124,7 +124,7 @@ const compressHtml = async () => {
|
||||
const otherFiles = [
|
||||
'server/robots.txt',
|
||||
'server/manifest.json',
|
||||
'server/music/**/*.mp3'
|
||||
'server/music/**/*.mp3',
|
||||
];
|
||||
const copyOtherFiles = () => src(otherFiles, { base: 'server/', encoding: false })
|
||||
.pipe(dest('./dist'));
|
||||
@@ -138,13 +138,14 @@ const uploadSources = [
|
||||
'dist/**',
|
||||
'!dist/**/*.map',
|
||||
];
|
||||
const upload = () => src(uploadSources, { base: './dist' })
|
||||
const upload = () => src(uploadSources, { base: './dist', encoding: false })
|
||||
.pipe(s3({
|
||||
Bucket: 'weatherstar',
|
||||
StorageClass: 'STANDARD',
|
||||
maps: {
|
||||
CacheControl: (keyname) => {
|
||||
if (keyname.indexOf('index.html') > -1) return 'max-age=300'; // 10 minutes
|
||||
if (keyname.indexOf('.mp3') > -1) return 'max-age=31536000'; // 1 year for mp3 files
|
||||
return 'max-age=2592000'; // 1 month
|
||||
},
|
||||
},
|
||||
@@ -176,8 +177,8 @@ const invalidate = () => cloudfront.send(new CreateInvalidationCommand({
|
||||
const buildPlaylist = async () => {
|
||||
const availableFiles = await reader();
|
||||
const playlist = { availableFiles };
|
||||
return file('playlist.json', JSON.stringify(playlist)).pipe(dest('./dist'))
|
||||
}
|
||||
return file('playlist.json', JSON.stringify(playlist)).pipe(dest('./dist'));
|
||||
};
|
||||
|
||||
const buildDist = series(clean, parallel(buildJs, compressJsData, compressJsVendor, copyCss, compressHtml, copyOtherFiles, buildPlaylist));
|
||||
|
||||
@@ -189,4 +190,4 @@ export default publishFrontend;
|
||||
|
||||
export {
|
||||
buildDist,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.0",
|
||||
"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" />
|
||||
<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