Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4a601a387 | ||
|
|
375812c024 | ||
|
|
6af8b58f14 | ||
|
|
6287db7483 | ||
|
|
46a8fa470c | ||
|
|
8489b7e2be | ||
|
|
7a196ac64a | ||
|
|
5946ee495a | ||
|
|
93ac03acd4 | ||
|
|
23a0dd6870 | ||
|
|
384693688c | ||
|
|
e2877aad77 | ||
|
|
7fae649357 | ||
|
|
468e057c51 | ||
|
|
4c65f5bc4d | ||
|
|
99308155d6 | ||
|
|
cde7ceda6e | ||
|
|
72938671ac | ||
|
|
9e1ea31262 | ||
|
|
e952d860dc | ||
|
|
262ea15468 | ||
|
|
5d2e5a6d9c | ||
|
|
992258d3ce | ||
|
|
b031934022 | ||
|
|
4cc2312ffd | ||
|
|
ce99fc16e7 | ||
|
|
97f96d4091 | ||
|
|
13f08b62cc | ||
|
|
eacd82b4f4 | ||
|
|
91f669e828 | ||
|
|
cdbe3d968f | ||
|
|
0a388cdb83 | ||
|
|
8678d9f053 | ||
|
|
a592668d0d | ||
|
|
b3faf95e39 | ||
|
|
3a304d7c08 | ||
|
|
c6918f0c6c | ||
|
|
fb93ade4d2 | ||
|
|
6f260a6ac7 | ||
|
|
50e526639c | ||
|
|
38db5cb6a6 | ||
|
|
c7eb56f60c |
@@ -2,8 +2,7 @@
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jquery": true
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb-base"
|
||||
@@ -12,10 +11,11 @@
|
||||
"TravelCities": "readonly",
|
||||
"RegionalCities": "readonly",
|
||||
"StationInfo": "readonly",
|
||||
"SunCalc": "readonly"
|
||||
"SunCalc": "readonly",
|
||||
"NoSleep": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2023,
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [],
|
||||
|
||||
5
.gitignore
vendored
@@ -11,4 +11,7 @@ server/music/*
|
||||
|
||||
#dist folder
|
||||
dist/*
|
||||
!dist/readme.txt
|
||||
!dist/readme.txt
|
||||
|
||||
#environment variables
|
||||
.env
|
||||
13
.vscode/launch.json
vendored
@@ -15,10 +15,13 @@
|
||||
"**/*.min.js",
|
||||
"**/vendor/**"
|
||||
],
|
||||
"runtimeArgs": [
|
||||
"--autoplay-policy=no-user-gesture-required"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Data:stations",
|
||||
"program": "${workspaceFolder}/datagenerators/stations.js",
|
||||
"program": "${workspaceFolder}/datagenerators/stations.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
@@ -27,7 +30,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Data:regionalcities",
|
||||
"program": "${workspaceFolder}/datagenerators/regionalcities.js",
|
||||
"program": "${workspaceFolder}/datagenerators/regionalcities.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
@@ -36,7 +39,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Data:travelcities",
|
||||
"program": "${workspaceFolder}/datagenerators/travelcities.js",
|
||||
"program": "${workspaceFolder}/datagenerators/travelcities.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
@@ -66,13 +69,13 @@
|
||||
},
|
||||
{
|
||||
"name": "Test",
|
||||
"program": "${workspaceFolder}/tests/index.js",
|
||||
"program": "${workspaceFolder}/tests/index.mjs",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"outputCapture": "std"
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
],
|
||||
"compounds": [
|
||||
|
||||
5
.vscode/settings.json
vendored
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"cSpell.enableFiletypes": [
|
||||
"javascript"
|
||||
],
|
||||
"liveSassCompile.settings.formats": [
|
||||
{
|
||||
"format": "compressed",
|
||||
@@ -22,5 +19,5 @@
|
||||
},
|
||||
"eslint.validate": [
|
||||
"javascript"
|
||||
]
|
||||
],
|
||||
}
|
||||
10
README.md
@@ -18,7 +18,7 @@ This project is based on the work of [Mike Battaglia](https://github.com/vbguyny
|
||||
|
||||
## Does WeatherStar 4000+ work outside of the USA?
|
||||
|
||||
This project is tightly coupled to [NOAA's Weather API](https://www.weather.gov/documentation/services-web-api), which is exclsuive to the United States. Using NOAA's Weather API is a crucial requirement to provide an authentic WeatherStar 4000+ experience.
|
||||
This project is tightly coupled to [NOAA's Weather API](https://www.weather.gov/documentation/services-web-api), which is exclusive to the United States. Using NOAA's Weather API is a crucial requirement to provide an authentic WeatherStar 4000+ experience.
|
||||
|
||||
If you would like to display weather information for international locations (outside of the USA), please checkout a fork of this project created by [@mwood77](https://github.com/mwood77):
|
||||
- [`ws4kp-international`](https://github.com/mwood77/ws4kp-international)
|
||||
@@ -92,11 +92,12 @@ Kiosk mode can be activated by a checkbox on the page. Note that there is no way
|
||||
|
||||
It's also possible to enter kiosk mode using a permalink. First generate a [Permalink](#sharing-a-permalink-bookmarking), then to the end of it add `&kiosk=true`. Opening this link will load all of the selected displays included in the Permalink, enter kiosk mode immediately upon loading and start playing the forecast.
|
||||
|
||||
## Wish list
|
||||
## Default query string paramaters (environment variables)
|
||||
When serving this via the built-in Express server, it's possible to define environment variables that direct the user to a default set of paramaters (like the [Permalink](#sharing-a-permalink-bookmarking) above). If a user requests the root page at `http://localhost:8080/` the query string provided by environment variables will be appended to the url thus providing a default configuration.
|
||||
|
||||
As time allows I will be working on the following enhancements.
|
||||
Environment variables can be added to the command line as usual, or via a .env file which is parsed with [dotenv](https://github.com/motdotla/dotenv). Both methods have the same effect.
|
||||
|
||||
* Better error reporting when api.weather.gov is down (happens more often than you would think)
|
||||
Environment variables that are to be added to the default query string are prefixed with `WSQS_` and then use the same key/value pairs generated by the [Permalink](#sharing-a-permalink-bookmarking) above, with the `- (dash)` character replaced by an `_ (underscore)`. For example, if you wanted to turn the travel forecast on, you would find `travel-checkbox=true` in the permalink, it's matching environment variable becomes `WSQS_travel_checkbox=true`.
|
||||
|
||||
## Serving static files
|
||||
The app can be served as a static set of files on any web server. Run the provided gulp task to create a set of static distribution files:
|
||||
@@ -129,6 +130,7 @@ Thanks to the WeatherStar community for providing these discussions to further e
|
||||
|
||||
* [Stream as FFMPEG](https://github.com/netbymatt/ws4kp/issues/37#issuecomment-2008491948)
|
||||
* [Weather like it's 1999](https://blog.scottlabs.io/2024/02/weather-like-its-1999/) Raspberry pi, streaming, music and CRT all combined into a complete solution.
|
||||
* [ws4channels](https://github.com/rice9797/ws4channels) A Dockerized Node.js application to stream WeatherStar 4000 data into Channels DVR using Puppeteer and FFmpeg.
|
||||
|
||||
## Customization
|
||||
A hook is provided as `/server/scripts/custom.js` to allow customizations to your own fork of this project, without accidentally pushing your customizations back upstream to the git repository. An sample file is provided at `/server/scripts/custom.sample.js` and should be renamed to `custom.js` activate it.
|
||||
|
||||
@@ -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": "Tucson",
|
||||
"lat": 32.2216,
|
||||
"lon": -110.9698,
|
||||
"point": {
|
||||
"x": 90,
|
||||
"y": 48,
|
||||
"x": 91,
|
||||
"y": 49,
|
||||
"wfo": "TWC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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": "Tucson",
|
||||
"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
@@ -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,73 +0,0 @@
|
||||
// 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');
|
||||
|
||||
// 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'];
|
||||
|
||||
// immediately invoked function so we can access async/await
|
||||
const start = async () => {
|
||||
// chunk the list of states
|
||||
const chunkStates = chunk(states, 5);
|
||||
|
||||
// store output
|
||||
const output = {};
|
||||
|
||||
// process all chunks
|
||||
for (let i = 0; i < chunkStates.length; i += 1) {
|
||||
const stateChunk = chunkStates[i];
|
||||
// loop through states
|
||||
|
||||
stateChunk.forEach(async (state) => {
|
||||
try {
|
||||
let stations;
|
||||
let next = `https://api.weather.gov/stations?state=${state}`;
|
||||
do {
|
||||
// get list and parse the JSON
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const stationsRaw = await https(next);
|
||||
stations = JSON.parse(stationsRaw);
|
||||
// filter stations for 4 letter identifiers
|
||||
const stationsFiltered4 = stations.features.filter((station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/));
|
||||
// filter against starting letter
|
||||
const stationsFiltered = stationsFiltered4.filter((station) => !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
|
||||
// add each resulting station to the output
|
||||
stationsFiltered.forEach((station) => {
|
||||
const id = station.properties.stationIdentifier;
|
||||
if (output[id]) {
|
||||
console.log(`Duplicate station: ${state}-${id}`);
|
||||
return;
|
||||
}
|
||||
output[id] = {
|
||||
id,
|
||||
city: station.properties.name,
|
||||
state,
|
||||
lat: station.geometry.coordinates[1],
|
||||
lon: station.geometry.coordinates[0],
|
||||
};
|
||||
});
|
||||
next = stations?.pagination?.next;
|
||||
// write the output
|
||||
// write the output
|
||||
fs.writeFileSync(path.join(__dirname, 'output/stations.json'), JSON.stringify(output, null, 2));
|
||||
}
|
||||
while (next && stations.features.length > 0);
|
||||
console.log(`Complete: ${state}`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(`Unable to get state: ${state}`);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// immediately invoked function allows access to async
|
||||
(async () => {
|
||||
await start();
|
||||
})();
|
||||
67
datagenerators/stations.mjs
Normal file
@@ -0,0 +1,67 @@
|
||||
// list all stations in a single file
|
||||
// only find stations with 4 letter codes
|
||||
|
||||
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'];
|
||||
|
||||
// chunk the list of states
|
||||
const chunkStates = chunk(states, 1);
|
||||
|
||||
// store output
|
||||
const output = {};
|
||||
|
||||
// process all chunks
|
||||
for (let i = 0; i < chunkStates.length; i += 1) {
|
||||
const stateChunk = chunkStates[i];
|
||||
// loop through states
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.allSettled(stateChunk.map(async (state) => {
|
||||
try {
|
||||
let stations;
|
||||
let next = `https://api.weather.gov/stations?state=${state}`;
|
||||
let round = 0;
|
||||
do {
|
||||
console.log(`Getting: ${state}-${round}`);
|
||||
// get list and parse the JSON
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const stationsRaw = await https(next);
|
||||
stations = JSON.parse(stationsRaw);
|
||||
// filter stations for 4 letter identifiers
|
||||
const stationsFiltered4 = stations.features.filter((station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/));
|
||||
// filter against starting letter
|
||||
const stationsFiltered = stationsFiltered4.filter((station) => !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
|
||||
// add each resulting station to the output
|
||||
stationsFiltered.forEach((station) => {
|
||||
const id = station.properties.stationIdentifier;
|
||||
if (output[id]) {
|
||||
console.log(`Duplicate station: ${state}-${id}`);
|
||||
return;
|
||||
}
|
||||
output[id] = {
|
||||
id,
|
||||
city: station.properties.name,
|
||||
state,
|
||||
lat: station.geometry.coordinates[1],
|
||||
lon: station.geometry.coordinates[0],
|
||||
};
|
||||
});
|
||||
next = stations?.pagination?.next;
|
||||
round += 1;
|
||||
// write the output
|
||||
writeFileSync('./datagenerators/output/stations.json', JSON.stringify(output, null, 2));
|
||||
}
|
||||
while (next && stations.features.length > 0);
|
||||
console.log(`Complete: ${state}`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(`Unable to get state: ${state}`);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -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
@@ -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, ' '));
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import 'dotenv/config';
|
||||
import {
|
||||
src, dest, series, parallel,
|
||||
} from 'gulp';
|
||||
@@ -6,9 +7,9 @@ import concat from 'gulp-concat';
|
||||
import terser from 'gulp-terser';
|
||||
import ejs from 'gulp-ejs';
|
||||
import rename from 'gulp-rename';
|
||||
import htmlmin from 'gulp-htmlmin';
|
||||
import htmlmin from 'gulp-html-minifier-terser';
|
||||
import { deleteAsync } from 'del';
|
||||
import s3Upload from 'gulp-s3-upload';
|
||||
import s3Upload from 'gulp-s3-uploader';
|
||||
import webpack from 'webpack-stream';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import { readFile } from 'fs/promises';
|
||||
@@ -32,8 +33,6 @@ const jsSourcesData = [
|
||||
|
||||
const webpackOptions = {
|
||||
mode: 'production',
|
||||
// mode: 'development',
|
||||
// devtool: 'source-map',
|
||||
output: {
|
||||
filename: 'ws.min.js',
|
||||
},
|
||||
@@ -62,8 +61,6 @@ const compressJsData = () => src(jsSourcesData)
|
||||
.pipe(dest(RESOURCES_PATH));
|
||||
|
||||
const jsVendorSources = [
|
||||
'server/scripts/vendor/auto/jquery.js',
|
||||
'server/scripts/vendor/jquery.autocomplete.min.js',
|
||||
'server/scripts/vendor/auto/nosleep.js',
|
||||
'server/scripts/vendor/auto/swiped-events.js',
|
||||
'server/scripts/vendor/auto/suncalc.js',
|
||||
@@ -79,6 +76,7 @@ const mjsSources = [
|
||||
'server/scripts/modules/hazards.mjs',
|
||||
'server/scripts/modules/currentweather.mjs',
|
||||
'server/scripts/modules/almanac.mjs',
|
||||
'server/scripts/modules/spc-outlook.mjs',
|
||||
'server/scripts/modules/icons.mjs',
|
||||
'server/scripts/modules/extendedforecast.mjs',
|
||||
'server/scripts/modules/hourly.mjs',
|
||||
@@ -140,7 +138,7 @@ const uploadSources = [
|
||||
];
|
||||
const upload = () => src(uploadSources, { base: './dist', encoding: false })
|
||||
.pipe(s3({
|
||||
Bucket: 'weatherstar',
|
||||
Bucket: process.env.BUCKET,
|
||||
StorageClass: 'STANDARD',
|
||||
maps: {
|
||||
CacheControl: (keyname) => {
|
||||
@@ -154,17 +152,18 @@ const upload = () => src(uploadSources, { base: './dist', encoding: false })
|
||||
const imageSources = [
|
||||
'server/fonts/**',
|
||||
'server/images/**',
|
||||
'!server/images/gimp/**',
|
||||
];
|
||||
const uploadImages = () => src(imageSources, { base: './server', encoding: false })
|
||||
.pipe(
|
||||
s3({
|
||||
Bucket: 'weatherstar',
|
||||
Bucket: process.env.BUCKET,
|
||||
StorageClass: 'STANDARD',
|
||||
}),
|
||||
);
|
||||
|
||||
const invalidate = () => cloudfront.send(new CreateInvalidationCommand({
|
||||
DistributionId: 'E9171A4KV8KCW',
|
||||
DistributionId: process.env.DISTRIBUTION_ID,
|
||||
InvalidationBatch: {
|
||||
CallerReference: (new Date()).toLocaleString(),
|
||||
Paths: {
|
||||
|
||||
@@ -9,7 +9,6 @@ const vendorFiles = [
|
||||
'./node_modules/luxon/build/es6/luxon.js',
|
||||
'./node_modules/luxon/build/es6/luxon.js.map',
|
||||
'./node_modules/nosleep.js/dist/NoSleep.js',
|
||||
'./node_modules/jquery/dist/jquery.js',
|
||||
'./node_modules/suncalc/suncalc.js',
|
||||
'./node_modules/swiped-events/src/swiped-events.js',
|
||||
];
|
||||
|
||||
33
index.mjs
@@ -1,3 +1,4 @@
|
||||
import 'dotenv/config';
|
||||
import express from 'express';
|
||||
import fs from 'fs';
|
||||
import corsPassThru from './cors/index.mjs';
|
||||
@@ -20,7 +21,39 @@ app.get('/playlist.json', playlist);
|
||||
// version
|
||||
const { version } = JSON.parse(fs.readFileSync('package.json'));
|
||||
|
||||
// read and parse environment variables to append to the query string
|
||||
// use the permalink (share) button on the web app to generate a starting point for your configuration
|
||||
// then take each key/value in the querystring and append WSQS_ to the beginning, and then replace any
|
||||
// hyphens with underscores in the key name
|
||||
// environment variables are read from the command line and .env file via the dotenv package
|
||||
|
||||
const qsVars = {};
|
||||
|
||||
Object.entries(process.env).forEach(([key, value]) => {
|
||||
// test for key matching pattern described above
|
||||
if (key.match(/^WSQS_[A-Za-z0-9_]+$/)) {
|
||||
// convert the key to a querystring formatted key
|
||||
const formattedKey = key.replace(/^WSQS_/, '').replaceAll('_', '-');
|
||||
qsVars[formattedKey] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// single flag to determine if environment variables are present
|
||||
const hasQsVars = Object.entries(qsVars).length > 0;
|
||||
|
||||
// turn the environment query string into search params
|
||||
const defaultSearchParams = (new URLSearchParams(qsVars)).toString();
|
||||
|
||||
const index = (req, res) => {
|
||||
// test for no query string in request and if environment query string values were provided
|
||||
if (hasQsVars && Object.keys(req.query).length === 0) {
|
||||
// redirect the user to the query-string appended url
|
||||
const url = new URL(`${req.protocol}://${req.host}${req.url}`);
|
||||
url.search = defaultSearchParams;
|
||||
res.redirect(307, url.toString());
|
||||
return;
|
||||
}
|
||||
// return the standard page
|
||||
res.render('index', {
|
||||
production: false,
|
||||
version,
|
||||
|
||||
1885
package-lock.json
generated
11
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ws4kp",
|
||||
"version": "5.16.0",
|
||||
"version": "5.19.1",
|
||||
"description": "Welcome to the WeatherStar 4000+ project page!",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -32,22 +32,21 @@
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-ejs": "^5.1.0",
|
||||
"gulp-file": "^0.4.0",
|
||||
"gulp-htmlmin": "^5.0.1",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-s3-upload": "^1.7.3",
|
||||
"gulp-s3-uploader": "^1.0.6",
|
||||
"gulp-sass": "^6.0.0",
|
||||
"gulp-terser": "^2.0.0",
|
||||
"jquery": "^3.6.0",
|
||||
"jquery-touchswipe": "^1.6.19",
|
||||
"luxon": "^3.0.0",
|
||||
"nosleep.js": "^0.12.0",
|
||||
"sass": "^1.54.0",
|
||||
"suncalc": "^1.8.0",
|
||||
"swiped-events": "^1.1.4",
|
||||
"terser-webpack-plugin": "^5.3.6",
|
||||
"webpack-stream": "^7.0.0"
|
||||
"webpack-stream": "^7.0.0",
|
||||
"gulp-html-minifier-terser": "^7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.5.0",
|
||||
"ejs": "^3.1.5",
|
||||
"express": "^5.1.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[Dolphin]
|
||||
PreviewsShown=true
|
||||
Timestamp=2020,10,1,21,36,7
|
||||
Version=4
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 582 B |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 890 B |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 925 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 273 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 925 B |
|
Before Width: | Height: | Size: 467 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 358 B |
|
Before Width: | Height: | Size: 232 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 729 B |
|
Before Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |