Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74a15c40c | ||
|
|
5419425834 | ||
|
|
f3a386079b | ||
|
|
aa43713943 | ||
|
|
1dece10679 | ||
|
|
c4f16d786a | ||
|
|
36b8adc019 | ||
|
|
bfe0b4757d | ||
|
|
2b61e55783 | ||
|
|
36c4f451b3 | ||
|
|
268d4ae7fa | ||
|
|
1b49e02cd8 | ||
|
|
9a55a6ec39 | ||
|
|
faaf6f770f | ||
|
|
79e4ed6e8b | ||
|
|
f956df1272 | ||
|
|
089ef56b10 | ||
|
|
c4e8721a2b | ||
|
|
a2efc2f767 | ||
|
|
c0e1c55453 | ||
|
|
860ca52e2d | ||
|
|
b891a1e3c0 | ||
|
|
70fb3b5dbe | ||
|
|
5bcc744867 | ||
|
|
da75226a63 | ||
|
|
cab4219740 | ||
|
|
9252275436 | ||
|
|
9d1c21d8ef | ||
|
|
6473f167a8 | ||
|
|
d280a5b3a9 | ||
|
|
b195ce042b | ||
|
|
39e8879697 | ||
|
|
5e3b917023 | ||
|
|
a813ee19a7 | ||
|
|
e01edc6972 | ||
|
|
ab0249e6eb | ||
|
|
c4c85b3b7b | ||
|
|
e954033979 | ||
|
|
ba39af9126 | ||
|
|
a814fde5b5 | ||
|
|
3f5f78eddf | ||
|
|
974a061b44 | ||
|
|
dc64e4bd7f | ||
|
|
776148fa6b | ||
|
|
7c50f5f1d7 | ||
|
|
4bf3f4d1e0 | ||
|
|
46da573715 | ||
|
|
69c050eb8f | ||
|
|
a3e142dade | ||
|
|
28917489bb | ||
|
|
2365a4c0f7 | ||
|
|
8afef77ea5 | ||
|
|
8f70ee87c5 | ||
|
|
4e7429bfba | ||
|
|
c5ffe1542a | ||
|
|
5364855c58 | ||
|
|
18efd810bd | ||
|
|
68a6bae3a7 | ||
|
|
5f0f0d9000 | ||
|
|
9d9cf4b0f3 | ||
|
|
9e500143c0 | ||
|
|
71da682660 | ||
|
|
1b9a1dcb22 | ||
|
|
095761ee81 | ||
|
|
21e528aaa3 | ||
|
|
a92c632937 | ||
|
|
6073fd1733 | ||
|
|
5da8185633 | ||
|
|
cf5c818ee3 | ||
|
|
97cec114f6 | ||
|
|
7efd2e8db7 | ||
|
|
8c28f41d54 | ||
|
|
e9d603fbfc | ||
|
|
32aa43c5b1 | ||
|
|
dbc56f014a | ||
|
|
3161a03797 | ||
|
|
205fa77f51 | ||
|
|
28bb8f2e2a | ||
|
|
cf9a99a6ca | ||
|
|
a83afa71cd | ||
|
|
74f1abd6f8 | ||
|
|
1bd45bdeeb | ||
|
|
232061b4d8 | ||
|
|
10d10ffbfb | ||
|
|
25ac2059a6 | ||
|
|
25626a98c9 | ||
|
|
002e037bbd | ||
|
|
8d20f7672c | ||
|
|
5567fe37a6 | ||
|
|
2dcc33f210 | ||
|
|
8f86f80eb5 | ||
|
|
1609ab3d38 | ||
|
|
0be23ee988 | ||
|
|
a3ea2c3708 | ||
|
|
09fb698350 | ||
|
|
6f6efe801c | ||
|
|
bc77a1891c | ||
|
|
4666878250 | ||
|
|
5813dd9a92 | ||
|
|
8cb8873760 | ||
|
|
323c175936 | ||
|
|
85e2553cb2 | ||
|
|
101d0ac9ea | ||
|
|
834d68f9e3 | ||
|
|
0ee7fdc9f8 | ||
|
|
d75121e894 | ||
|
|
4cdced3659 | ||
|
|
1a5548d135 | ||
|
|
11c826a2af | ||
|
|
7a129c1cd3 | ||
|
|
867657a965 | ||
|
|
e89dc52541 | ||
|
|
317883fc04 | ||
|
|
a4a601a387 | ||
|
|
375812c024 | ||
|
|
6af8b58f14 | ||
|
|
6287db7483 | ||
|
|
46a8fa470c | ||
|
|
8489b7e2be | ||
|
|
7a196ac64a | ||
|
|
5946ee495a | ||
|
|
93ac03acd4 |
@@ -12,7 +12,8 @@
|
||||
"RegionalCities": "readonly",
|
||||
"StationInfo": "readonly",
|
||||
"SunCalc": "readonly",
|
||||
"NoSleep": "readonly"
|
||||
"NoSleep": "readonly",
|
||||
"OVERRIDES": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
|
||||
23
.github/workflows/build-docker.yaml
vendored
@@ -1,5 +1,12 @@
|
||||
name: build-docker
|
||||
on: push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- 'v*.*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -13,7 +20,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/netbymatt/ws4kp
|
||||
@@ -27,23 +34,23 @@ jobs:
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and Push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
pull: true
|
||||
push: ${{ github.ref == 'refs/heads/main' }}
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
push: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18-alpine
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json .
|
||||
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2024 Matt Walsh
|
||||
Copyright (c) 2020-2025 Matt Walsh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
151
README.md
@@ -6,15 +6,31 @@ A live version of this project is available at https://weatherstar.netbymatt.com
|
||||
|
||||
This project aims to bring back the feel of the 90's with a weather forecast that has the look and feel of The Weather Channel at that time but available in a modern way. This is by no means intended to be a perfect emulation of the WeatherStar 4000, the hardware that produced those wonderful blue and orange graphics you saw during the local forecast on The Weather Channel. If you would like a much more accurate project please see the [WS4000 Simulator](http://www.taiganet.com/). Instead, this project intends to create a simple to use interface with minimal configuration fuss. Some changes have been made to the screens available because either more or less forecast information is available today than was in the 90's. Most of these changes are captured in sections below.
|
||||
|
||||
## Acknowledgements
|
||||
## What's your motivation
|
||||
|
||||
This project is based on the work of [Mike Battaglia](https://github.com/vbguyny/ws4kp). It was forked from his work in August 2020.
|
||||
Nostalgia. And I enjoy following the weather, especially severe storms.
|
||||
|
||||
* Mike Battaglia for the original project and all of the code which draws the weather displays. This code remains largely intact and was a huge amount of work to get exactly right. He's also responsible for all of the background graphics including the maps used in the application.
|
||||
* The team at [TWCClassics](https://twcclassics.com/) for several resources.
|
||||
* A [font](https://twcclassics.com/downloads.html) set used on the original WeatherStar 4000
|
||||
* [Icon](https://twcclassics.com/downloads.html) sets
|
||||
* Countless photos and videos of WeatherStar 4000 forecasts used as references.
|
||||
It's also a creative outlet for me and keeps my programming skills honed for when I need them for my day job.
|
||||
|
||||
### Included technology
|
||||
I've kept this open source, well commented, and made it as library-free as possible to help others interested in programming be able to jump right in and start working with the code.
|
||||
|
||||
From a learning standpoint, this codebase make use of a lot of different methods and technologies common on the internet including:
|
||||
|
||||
* The https://api.weather.gov REST API. ([documentation](https://www.weather.gov/documentation/services-web-api)).
|
||||
* ES 6 functionality
|
||||
* Arrow functions
|
||||
* Promises
|
||||
* Async/await and parallel loading of all forecast resources
|
||||
* Classes and extensions
|
||||
* Javascript modules
|
||||
* Separation between API code and user interface code
|
||||
* Use of a modern date parsing library [luxon](https://moment.github.io/luxon/)
|
||||
* Practical API rates and static asset caching
|
||||
* Very straight-forward hand written HTML
|
||||
* Build system integration (Gulp, Webpack) to reduce the number of scripts that need to be loaded
|
||||
* Hand written CSS made easier to mange with SASS
|
||||
* A linting library to keep code style consistent
|
||||
|
||||
## Does WeatherStar 4000+ work outside of the USA?
|
||||
|
||||
@@ -24,14 +40,12 @@ If you would like to display weather information for international locations (ou
|
||||
- [`ws4kp-international`](https://github.com/mwood77/ws4kp-international)
|
||||
|
||||
## Run Your WeatherStar
|
||||
There are a lot of CORS considerations and issues with api.weather.gov that are easiest to deal with by running a local server to see this in action (or use the live link above). You'll need Node.js >12.0 to run the local server.
|
||||
|
||||
To run via Node locally:
|
||||
```
|
||||
git clone https://github.com/netbymatt/ws4kp.git
|
||||
cd ws4kp
|
||||
npm i
|
||||
node index.js
|
||||
node index.mjs
|
||||
```
|
||||
|
||||
To run via Docker:
|
||||
@@ -40,35 +54,31 @@ docker run -p 8080:8080 ghcr.io/netbymatt/ws4kp
|
||||
```
|
||||
Open your web browser: http://localhost:8080/
|
||||
|
||||
## Updates in 5.0
|
||||
The change to 5.0 changes from drawing the weather graphics on canvas elements and instead uses HTML and CSS to style all of the weather graphics. A lot of other changes and fixes were implemented at the same time.
|
||||
To run via Docker Compose (docker-compose.yaml):
|
||||
```
|
||||
---
|
||||
services:
|
||||
ws4kp:
|
||||
image: ghcr.io/netbymatt/ws4kp
|
||||
container_name: ws4kp
|
||||
environment:
|
||||
# Each argument in the permalink URL can become an environment variable on the Docker host by adding WSQS_
|
||||
# Following the "Sharing a Permalink" example below, here are a few environment variables defined. Visit that section for a
|
||||
# more complete list of configuration options.
|
||||
- WSQS_latLonQuery="Orlando International Airport Orlando FL USA"
|
||||
- WSQS_hazards_checkbox=false
|
||||
- WSQS_current_weather_checkbox=true
|
||||
ports:
|
||||
- 8080:8080 # change the first 8080 to meet your local network needs
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
* Replace all canvas elements with HTML and CSS
|
||||
* City and airport names are better parsed to fit the available space
|
||||
* Remove the dependency on libgif-js
|
||||
* Use browser for text wrapping where necessary
|
||||
* Some new weather icons
|
||||
* Refresh only on slideshow repeat
|
||||
* Removed Almanac 30-day outlook
|
||||
* Fixed startup issue when current conditions are unavailable
|
||||
|
||||
## Why the fork?
|
||||
|
||||
The fork is a result of wanting a more manageable, modern code base to work with. Part of it is an exercise in my education in JavaScript. There are several technical changes that were made behind the scenes.
|
||||
|
||||
* Make use of the new API available at https://api.weather.gov ([documentation](https://www.weather.gov/documentation/services-web-api)). This caused the removal of some of the original WeatherStar 4000 displays, and allowed for new displays to be created.
|
||||
* Changed code to make extensive use of ES6 functionality including:
|
||||
* Arrow functions
|
||||
* Promises
|
||||
* Async/await and parallel loading of all forecast resources
|
||||
* Classes
|
||||
* Common code base for each display through use of classes
|
||||
* Separation between weather display code and user interface
|
||||
* Use of a modern date parsing library [luxon](https://moment.github.io/luxon/)
|
||||
* Attempt to remove the need for a local server to bypass [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) issues with the various APIs used. This is almost workable but there are still some minor CORS issues with https://api.weather.gov.
|
||||
* The necessary CORS pass through URLs have been rewritten so they can be deployed on Node.js using the included server or through S3/Cloudfront in a serverless environment.
|
||||
* Proper settings for static resource caching
|
||||
* Build system integration to reduce the number of scripts that need to be loaded
|
||||
### 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:
|
||||
```
|
||||
npm run buildDist
|
||||
```
|
||||
The resulting files will be in the /dist folder in the root of the project. These can then be uploaded to a web server for hosting, no server-side scripting is required.
|
||||
|
||||
## What's different
|
||||
|
||||
@@ -77,39 +87,44 @@ I've made several changes to this Weather Star 4000 simulation compared to the o
|
||||
* Radar displays the timestamp of the image.
|
||||
* A new hour-by-hour graph of the temperature, cloud cover and precipitation chances for the next 24 hours.
|
||||
* A new hourly forecast display for the next 24 hours is available, and is shown in the style of the travel cities forecast. (off by default because it duplicates the hourly graph)
|
||||
* The SPC Outlook is shown in the style of the old air quality screen. This shows the probability of severe weather over the next 3 days at your location.
|
||||
* The "Local Forecast" and "Extended Forecast" provide several additional days of information compared to the original format in the 90's.
|
||||
* Narration was removed. In the original code narration made use of the computer's local text-to-speech engine which didn't sound great.
|
||||
* Music was removed. I don't want to deal with copyright issues and hosting MP3s. If you're looking for the music that played during forecasts please visit [TWCClassics](https://twcclassics.com/audio/).
|
||||
* Marine forecast (tides) is not available as it is not part of the new API.
|
||||
* The nearby cities displayed on screens such as "Latest Observations" and "Regional Forecast" are likely not the same as they were in the 90's. The weather monitoring equipment at these stations move over time for one reason or another, and coming up with a simple formulaic way of finding nearby stations is sufficient to give the same look-and-feel as the original.
|
||||
* The original music has been replaced. More info in [Music](#music).
|
||||
* Marine forecast (tides) is not available as it is not reliably part of the new API.
|
||||
* "Flavors" are not present in this simulation. Flavors refer to the order of the weather information that was shown on the original units. Instead, the order of the displays has been fixed and a checkboxes can be used to turn on and off individual displays. The travel forecast has been defaulted to off so only local information shows for new users.
|
||||
|
||||
## Sharing a permalink (bookmarking)
|
||||
Selected displays, the forecast city and widescreen setting are sticky from one session to the next. However if you would like to share your exact configuration or bookmark it click the "Copy Permalink" (or get "Get Parmalink") near the bottom of the page. A URL will be copied to your clipboard with all of you selected displays and location (or copy it from the page if your browser doesn't support clipboard transfers directly). You can then share this link or add it to your bookmarks.
|
||||
Selected displays, the forecast city and widescreen setting are sticky from one session to the next. However if you would like to share your exact configuration or bookmark it, click the "Copy Permalink" (or get "Get Parmalink") near the bottom of the page. A URL will be copied to your clipboard with all of you selected displays and location (or copy it from the page if your browser doesn't support clipboard transfers directly). You can then share this link or add it to your bookmarks.
|
||||
|
||||
## Kiosk mode
|
||||
Kiosk mode can be activated by a checkbox on the page. Note that there is no way out of kiosk mode (except refresh or closing the browser), and the play/pause and other controls will not be available. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified.
|
||||
Your permalink will be very long. Here is an example for the Orlando International Airport:
|
||||
```
|
||||
https://weatherstar.netbymatt.com/?hazards-checkbox=false¤t-weather-checkbox=true&latest-observations-checkbox=true&hourly-checkbox=false&hourly-graph-checkbox=true&travel-checkbox=false®ional-forecast-checkbox=true&local-forecast-checkbox=true&extended-forecast-checkbox=true&almanac-checkbox=false&spc-outlook-checkbox=true&radar-checkbox=true&settings-wide-checkbox=false&settings-kiosk-checkbox=false&settings-scanLines-checkbox=false&settings-speed-select=1.00&settings-units-select=us&latLonQuery=Orlando+International+Airport%2C+Orlando%2C+FL%2C+USA&latLon=%7B%22lat%22%3A28.431%2C%22lon%22%3A-81.3076%7D
|
||||
```
|
||||
You can also build your own permalink. Any omitted settings will be filled with defaults. Here are a few examples:
|
||||
```
|
||||
https://weatherstar.netbymatt.com/?latLonQuery=Orlando+International+Airport
|
||||
https://weatherstar.netbymatt.com/?kiosk=true
|
||||
https://weatherstar.netbymatt.com/?settings-units-select=metric
|
||||
```
|
||||
|
||||
### Kiosk mode
|
||||
Kiosk mode can be activated by a checkbox on the page. Note that there is no way out of kiosk mode (except refresh or closing the browser), and the play/pause and other controls will not be available. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified. A separate full-screen icon is available in the tool bar to go full-screen on a laptop or mobile browser.
|
||||
|
||||
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.
|
||||
|
||||
## Default query string paramaters (environment variables)
|
||||
### 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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
```
|
||||
npm run buildDist
|
||||
```
|
||||
The resulting files will be in the /dist folder in the root of the project. These can then be uploaded to a web server for hosting, no server-side scripting is required.
|
||||
|
||||
## Music
|
||||
The WeatherStar had wonderful background music from the smooth jazz and new age genres by artists of the time. Lists of the music that played are available by searching online, but it's all copyrighted music and would be difficult to provide as part of this repository.
|
||||
|
||||
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).
|
||||
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 are in a companion repository [ws4kp-music](https://github.com/netbymatt/ws4kp-music).
|
||||
|
||||
If you're looking for the original music that played during forecasts [TWCClassics](https://twcclassics.com/audio/) has thurough documentation of playlists.
|
||||
|
||||
### Customizing the 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`.
|
||||
@@ -124,6 +139,11 @@ Ws4kp is muted by default, but if it was unmuted on the last visit it is coded t
|
||||
|
||||
Chrome seems to be more lenient on auto play and will eventually let a site auto-play music if you're visited it enough recently and manually clicked to start playing music on each visit. It also has a flag you can add to the command line when launching Chrome: `chrome.exe --autoplay-policy=no-user-gesture-required`. This is the best solution when using Kiosk-style setup.
|
||||
|
||||
If you're unable to pre-set the play state before entering kiosk mode (such as with a home dashboard implemenation) you can add the query string value below to the url. The browser will still follow the auto play rules outlined above.
|
||||
```
|
||||
?settings-mediaPlaying-boolean=true
|
||||
```
|
||||
|
||||
## Community Notes
|
||||
|
||||
Thanks to the WeatherStar community for providing these discussions to further extend your retro forecasts!
|
||||
@@ -137,10 +157,31 @@ A hook is provided as `/server/scripts/custom.js` to allow customizations to you
|
||||
|
||||
## Issue reporting and feature requests
|
||||
|
||||
Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. Before reporting an issue or requesting a feature please consider that this is not intended to be a perfect recreation of the WeatherStar 4000, it's a best effort that fits within what's available from the API and within a web browser.
|
||||
Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. I've also observed that the API can go down on a regional basis (based on NWS office locations). This means that you may have problems getting data for, say, Chicago right now, but Dallas and others are working just fine.
|
||||
|
||||
Before reporting an issue or requesting a feature please consider that this is not intended to be a perfect recreation of the WeatherStar 4000, it's a best effort that fits within what's available from the API and within a web browser.
|
||||
|
||||
Note: not all units are converted to metric, if selected. Some text-based products such as warnings are simple text strings provided from the national weather service and thus have baked-in units such as "gusts up to 60 mph." These values will not be converted.
|
||||
|
||||
## Related Projects
|
||||
|
||||
Not retro enough? Try the [Weatherstar 3000+](https://github.com/netbymatt/ws3kp)
|
||||
|
||||
## Use
|
||||
|
||||
Linking directly to the live web site at https://weatherstar.netbymatt.com is encouraged. As is using the live site for digital signage, home dashboards, streaming and public display. Please note the disclaimer below.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
This project is based on the work of [Mike Battaglia](https://github.com/vbguyny/ws4kp). It was forked from his work in August 2020.
|
||||
|
||||
* Mike Battaglia for the original project and all of the code which draws the weather displays. This code remains largely intact and was a huge amount of work to get exactly right. He's also responsible for all of the background graphics including the maps used in the application.
|
||||
* The team at [TWCClassics](https://twcclassics.com/) for several resources.
|
||||
* A [font](https://twcclassics.com/downloads.html) set used on the original WeatherStar 4000
|
||||
* [Icon](https://twcclassics.com/downloads.html) sets
|
||||
* Countless photos and videos of WeatherStar 4000 forecasts used as references.
|
||||
* The growing list of contributors to this repository
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This web site should NOT be used in life threatening weather situations, or be relied on to inform the public of such situations. The Internet is an unreliable network subject to server and network outages and by nature is not suitable for such mission critical use. If you require such access to NWS data, please consider one of their subscription services. The authors of this web site shall not be held liable in the event of injury, death or property damage that occur as a result of disregarding this warning.
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// pass through api requests
|
||||
|
||||
// http(s) modules
|
||||
import https from 'https';
|
||||
|
||||
// url parsing
|
||||
import queryString from 'querystring';
|
||||
|
||||
// return an express router
|
||||
const cors = (req, res) => {
|
||||
// add out-going headers
|
||||
const headers = {};
|
||||
headers['user-agent'] = '(WeatherStar 4000+, ws4000@netbymatt.com)';
|
||||
headers.accept = req.headers.accept;
|
||||
|
||||
// get query paramaters if the exist
|
||||
const queryParams = Object.keys(req.query).reduce((acc, key) => {
|
||||
// skip the paramater 'u'
|
||||
if (key === 'u') return acc;
|
||||
// add the paramter to the resulting object
|
||||
acc[key] = req.query[key];
|
||||
return acc;
|
||||
}, {});
|
||||
let query = queryString.encode(queryParams);
|
||||
if (query.length > 0) query = `?${query}`;
|
||||
|
||||
// get the page
|
||||
https.get(`https://api.weather.gov${req.path}${query}`, {
|
||||
headers,
|
||||
}, (getRes) => {
|
||||
// pull some info
|
||||
const { statusCode } = getRes;
|
||||
// pass the status code through
|
||||
res.status(statusCode);
|
||||
|
||||
// set headers
|
||||
res.header('content-type', getRes.headers['content-type']);
|
||||
// pipe to response
|
||||
getRes.pipe(res);
|
||||
}).on('error', (e) => {
|
||||
console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
export default cors;
|
||||
@@ -1,46 +0,0 @@
|
||||
// pass through api requests
|
||||
|
||||
// http(s) modules
|
||||
import https from 'https';
|
||||
|
||||
// url parsing
|
||||
import queryString from 'querystring';
|
||||
|
||||
// return an express router
|
||||
const outlook = (req, res) => {
|
||||
// add out-going headers
|
||||
const headers = {};
|
||||
headers['user-agent'] = '(WeatherStar 4000+, ws4000@netbymatt.com)';
|
||||
headers.accept = req.headers.accept;
|
||||
|
||||
// get query paramaters if the exist
|
||||
const queryParams = Object.keys(req.query).reduce((acc, key) => {
|
||||
// skip the paramater 'u'
|
||||
if (key === 'u') return acc;
|
||||
// add the paramter to the resulting object
|
||||
acc[key] = req.query[key];
|
||||
return acc;
|
||||
}, {});
|
||||
let query = queryString.encode(queryParams);
|
||||
if (query.length > 0) query = `?${query}`;
|
||||
|
||||
// get the page
|
||||
https.get(`https://www.cpc.ncep.noaa.gov/${req.path}${query}`, {
|
||||
headers,
|
||||
}, (getRes) => {
|
||||
// pull some info
|
||||
const { statusCode } = getRes;
|
||||
// pass the status code through
|
||||
res.status(statusCode);
|
||||
|
||||
// set headers
|
||||
res.header('content-type', getRes.headers['content-type']);
|
||||
res.header('last-modified', getRes.headers['last-modified']);
|
||||
// pipe to response
|
||||
getRes.pipe(res);
|
||||
}).on('error', (e) => {
|
||||
console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
export default outlook;
|
||||
@@ -1,46 +0,0 @@
|
||||
// pass through api requests
|
||||
|
||||
// http(s) modules
|
||||
import https from 'https';
|
||||
|
||||
// url parsing
|
||||
import queryString from 'querystring';
|
||||
|
||||
// return an express router
|
||||
const radar = (req, res) => {
|
||||
// add out-going headers
|
||||
const headers = {};
|
||||
headers['user-agent'] = '(WeatherStar 4000+, ws4000@netbymatt.com)';
|
||||
headers.accept = req.headers.accept;
|
||||
|
||||
// get query paramaters if the exist
|
||||
const queryParams = Object.keys(req.query).reduce((acc, key) => {
|
||||
// skip the paramater 'u'
|
||||
if (key === 'u') return acc;
|
||||
// add the paramter to the resulting object
|
||||
acc[key] = req.query[key];
|
||||
return acc;
|
||||
}, {});
|
||||
let query = queryString.encode(queryParams);
|
||||
if (query.length > 0) query = `?${query}`;
|
||||
|
||||
// get the page
|
||||
https.get(`https://radar.weather.gov${req.path}${query}`, {
|
||||
headers,
|
||||
}, (getRes) => {
|
||||
// pull some info
|
||||
const { statusCode } = getRes;
|
||||
// pass the status code through
|
||||
res.status(statusCode);
|
||||
|
||||
// set headers
|
||||
res.header('content-type', getRes.headers['content-type']);
|
||||
res.header('last-modified', getRes.headers['last-modified']);
|
||||
// pipe to response
|
||||
getRes.pipe(res);
|
||||
}).on('error', (e) => {
|
||||
console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
export default radar;
|
||||
@@ -14,9 +14,10 @@ import webpack from 'webpack-stream';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import { readFile } from 'fs/promises';
|
||||
import file from 'gulp-file';
|
||||
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
|
||||
import OVERRIDES from '../src/overrides.mjs';
|
||||
|
||||
// get cloudfront
|
||||
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
|
||||
import reader from '../src/playlist-reader.mjs';
|
||||
|
||||
const clean = () => deleteAsync(['./dist/**/*', '!./dist/readme.txt']);
|
||||
@@ -76,6 +77,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',
|
||||
@@ -94,6 +96,19 @@ const buildJs = () => src(mjsSources)
|
||||
.pipe(webpack(webpackOptions))
|
||||
.pipe(dest(RESOURCES_PATH));
|
||||
|
||||
const workerSources = [
|
||||
'server/scripts/modules/radar-worker.mjs',
|
||||
];
|
||||
|
||||
const buildWorkers = () => {
|
||||
// update the file name in the webpack options
|
||||
const output = { filename: 'radar-worker.mjs' };
|
||||
const workerWebpackOptions = { ...webpackOptions, output };
|
||||
return src(workerSources)
|
||||
.pipe(webpack(workerWebpackOptions))
|
||||
.pipe(dest(RESOURCES_PATH));
|
||||
};
|
||||
|
||||
const cssSources = [
|
||||
'server/styles/main.css',
|
||||
];
|
||||
@@ -112,6 +127,7 @@ const compressHtml = async () => {
|
||||
.pipe(ejs({
|
||||
production: version,
|
||||
version,
|
||||
OVERRIDES,
|
||||
}))
|
||||
.pipe(rename({ extname: '.html' }))
|
||||
.pipe(htmlmin({ collapseWhitespace: true }))
|
||||
@@ -134,6 +150,8 @@ const s3 = s3Upload({
|
||||
const uploadSources = [
|
||||
'dist/**',
|
||||
'!dist/**/*.map',
|
||||
'!dist/images/**/*',
|
||||
'!dist/fonts/**/*',
|
||||
];
|
||||
const upload = () => src(uploadSources, { base: './dist', encoding: false })
|
||||
.pipe(s3({
|
||||
@@ -151,15 +169,22 @@ 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: process.env.BUCKET,
|
||||
StorageClass: 'STANDARD',
|
||||
maps: {
|
||||
CacheControl: () => 'max-age=31536000',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const copyImageSources = () => src(imageSources, { base: './server', encoding: false })
|
||||
.pipe(dest('./dist'));
|
||||
|
||||
const invalidate = () => cloudfront.send(new CreateInvalidationCommand({
|
||||
DistributionId: process.env.DISTRIBUTION_ID,
|
||||
InvalidationBatch: {
|
||||
@@ -177,7 +202,7 @@ const buildPlaylist = async () => {
|
||||
return file('playlist.json', JSON.stringify(playlist)).pipe(dest('./dist'));
|
||||
};
|
||||
|
||||
const buildDist = series(clean, parallel(buildJs, compressJsData, compressJsVendor, copyCss, compressHtml, copyOtherFiles, buildPlaylist));
|
||||
const buildDist = series(clean, parallel(buildJs, buildWorkers, compressJsData, compressJsVendor, copyCss, compressHtml, copyOtherFiles, copyImageSources, buildPlaylist));
|
||||
|
||||
// upload_images could be in parallel with upload, but _images logs a lot and has little changes
|
||||
// by running upload last the majority of the changes will be at the bottom of the log for easy viewing
|
||||
@@ -187,4 +212,5 @@ export default publishFrontend;
|
||||
|
||||
export {
|
||||
buildDist,
|
||||
invalidate,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import updateVendor from './gulp/update-vendor.mjs';
|
||||
import publishFrontend, { buildDist } from './gulp/publish-frontend.mjs'
|
||||
import publishFrontend, { buildDist, invalidate } from './gulp/publish-frontend.mjs';
|
||||
|
||||
export {
|
||||
updateVendor,
|
||||
publishFrontend,
|
||||
buildDist,
|
||||
invalidate,
|
||||
};
|
||||
|
||||
41
index.mjs
@@ -1,10 +1,8 @@
|
||||
import 'dotenv/config';
|
||||
import express from 'express';
|
||||
import fs from 'fs';
|
||||
import corsPassThru from './cors/index.mjs';
|
||||
import radarPassThru from './cors/radar.mjs';
|
||||
import outlookPassThru from './cors/outlook.mjs';
|
||||
import playlist from './src/playlist.mjs';
|
||||
import OVERRIDES from './src/overrides.mjs';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.WS4KP_PORT ?? 8080;
|
||||
@@ -12,12 +10,6 @@ const port = process.env.WS4KP_PORT ?? 8080;
|
||||
// template engine
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// cors pass-thru to api.weather.gov
|
||||
app.get('/stations/*station', corsPassThru);
|
||||
app.get('/Conus/*radar', radarPassThru);
|
||||
app.get('/products/*product', outlookPassThru);
|
||||
app.get('/playlist.json', playlist);
|
||||
|
||||
// version
|
||||
const { version } = JSON.parse(fs.readFileSync('package.json'));
|
||||
|
||||
@@ -57,21 +49,41 @@ const index = (req, res) => {
|
||||
res.render('index', {
|
||||
production: false,
|
||||
version,
|
||||
OVERRIDES,
|
||||
});
|
||||
};
|
||||
|
||||
const geoip = (req, res) => {
|
||||
res.set({
|
||||
'x-geoip-city': 'Orlando',
|
||||
'x-geoip-country': 'US',
|
||||
'x-geoip-country-name': 'United States',
|
||||
'x-geoip-country-region': 'FL',
|
||||
'x-geoip-country-region-name': 'Florida',
|
||||
'x-geoip-latitude': '28.52135',
|
||||
'x-geoip-longitude': '-81.41079',
|
||||
'x-geoip-postal-code': '32789',
|
||||
'x-geoip-time-zone': 'America/New_York',
|
||||
'content-type': 'application/json',
|
||||
});
|
||||
res.json({});
|
||||
};
|
||||
|
||||
// debugging
|
||||
if (process.env?.DIST === '1') {
|
||||
// distribution
|
||||
app.use('/images', express.static('./server/images'));
|
||||
app.use('/fonts', express.static('./server/fonts'));
|
||||
app.use('/scripts', express.static('./server/scripts'));
|
||||
app.use('/geoip', geoip);
|
||||
app.use('/', express.static('./dist'));
|
||||
} else {
|
||||
// debugging
|
||||
app.get('/index.html', index);
|
||||
app.use('/geoip', geoip);
|
||||
app.use('/resources', express.static('./server/scripts/modules'));
|
||||
app.get('/', index);
|
||||
app.get('*name', express.static('./server'));
|
||||
// cors pass-thru to api.weather.gov
|
||||
app.get('/playlist.json', playlist);
|
||||
}
|
||||
|
||||
const server = app.listen(port, () => {
|
||||
@@ -79,8 +91,11 @@ const server = app.listen(port, () => {
|
||||
});
|
||||
|
||||
// graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
const gracefulShutdown = () => {
|
||||
server.close(() => {
|
||||
console.log('Server closed');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
process.on('SIGINT', gracefulShutdown);
|
||||
process.on('SIGTERM', gracefulShutdown);
|
||||
|
||||
1542
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ws4kp",
|
||||
"version": "5.18.0",
|
||||
"version": "5.23.6",
|
||||
"description": "Welcome to the WeatherStar 4000+ project page!",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -42,12 +42,12 @@
|
||||
"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",
|
||||
"gulp-html-minifier-terser": "^7.1.0"
|
||||
"express": "^5.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
--Star 3000--
|
||||
|
||||
Star3000.ttf - Standard text style for most screens (and Travel Cities title header)
|
||||
Star3000 Small.ttf - Time/Date and some page headers
|
||||
Star3000 Large.ttf - Travel Cities Forecast (Forecast portion only)
|
||||
Star3000 Extra Large.ttf - Only used on some advertiser text
|
||||
Star3000 Extended.ttf - Only used on some advertiser text
|
||||
"Heavy" style is an emboldened version of the standard font (used on some STARs)
|
||||
|
||||
Star3000 Outline.ttf - A contrast border (stroke) that surrounds the Star3000.ttf base font. When used, must be as a text layer undeneath the base font (and is usually black in color).
|
||||
Star3000 Small Outline.ttf - A contrast border (stroke) that surrounds the Star3000 Small.ttf base font. When used, must be as a text layer undeneath the base font (and is usually black in color).
|
||||
Star3000 Large Outline.ttf - A contrast border (stroke) that surrounds the Star3000 Large.ttf base font. When used, must be as a text layer undeneath the base font (and is usually black in color).
|
||||
|
||||
***Outlines for other font styles are not currently available.
|
||||
|
||||
--Star 4000--
|
||||
|
||||
Star4000.ttf - Standard text style for zone forecast, observation tables, regional map cities, almanac, extended forecast day/weather/temperature headers, Current Conditions right half data and most page header titles (also Travel Cities title header before Nov. 1992)
|
||||
Star4000 Small.ttf - Time/Date, NWS Local Update page header, temperature header for Travel Cities Forecast (after Nov. 1992)
|
||||
Star4000 Large.ttf - City names and temperature data on Travel Cities Forecast (after Nov. 1992), Extended forecast temperature values (after Feb. 1991), Current Conditions temperature value (after Mar. 1991)
|
||||
Star4000 Large Compressed - Travel Cities Forecast (before Nov. 1992), regional map temperatures
|
||||
Star4000 Large Compressed Numbers - Temperature values on regional forecast/observation maps
|
||||
Star4000 Extended - A proportional width font used for the Current Conditions present weather description and wind data
|
||||
Star 4 Radar.ttf - Radar airport I.D.
|
||||
|
||||
--Star Jr.--
|
||||
|
||||
StarJr.ttf - Standard text style for most screens (and Travel Cities title header)
|
||||
StarJr Small.ttf - Time/Date and some page headers
|
||||
StarJr Compressed.ttf - Travel Cities Forecast
|
||||
BIN
server/images/backgrounds/6.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
server/images/gimp/Background 6.xcf
Normal file
BIN
server/images/gimp/Radar Basemap5.xcf
Normal file
BIN
server/images/icons/current-conditions/Smoke.gif
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 234 KiB |
BIN
server/images/maps/basemap.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
server/images/maps/radar-tiles/00-00.webp
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
server/images/maps/radar-tiles/00-01.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
server/images/maps/radar-tiles/00-02.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
server/images/maps/radar-tiles/00-03.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
server/images/maps/radar-tiles/00-04.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
server/images/maps/radar-tiles/00-05.webp
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
server/images/maps/radar-tiles/00-06.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/00-07.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/00-08.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/00-09.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/01-00.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
server/images/maps/radar-tiles/01-01.webp
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
server/images/maps/radar-tiles/01-02.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
server/images/maps/radar-tiles/01-03.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
server/images/maps/radar-tiles/01-04.webp
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
server/images/maps/radar-tiles/01-05.webp
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
server/images/maps/radar-tiles/01-06.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
server/images/maps/radar-tiles/01-07.webp
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
server/images/maps/radar-tiles/01-08.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/01-09.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/02-00.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
server/images/maps/radar-tiles/02-01.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
server/images/maps/radar-tiles/02-02.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
server/images/maps/radar-tiles/02-03.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
server/images/maps/radar-tiles/02-04.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
server/images/maps/radar-tiles/02-05.webp
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
server/images/maps/radar-tiles/02-06.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
server/images/maps/radar-tiles/02-07.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
server/images/maps/radar-tiles/02-08.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
server/images/maps/radar-tiles/02-09.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
server/images/maps/radar-tiles/03-00.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
server/images/maps/radar-tiles/03-01.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
server/images/maps/radar-tiles/03-02.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
server/images/maps/radar-tiles/03-03.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
server/images/maps/radar-tiles/03-04.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
server/images/maps/radar-tiles/03-05.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
server/images/maps/radar-tiles/03-06.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
server/images/maps/radar-tiles/03-07.webp
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
server/images/maps/radar-tiles/03-08.webp
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
server/images/maps/radar-tiles/03-09.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
server/images/maps/radar-tiles/04-00.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
server/images/maps/radar-tiles/04-01.webp
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
server/images/maps/radar-tiles/04-02.webp
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
server/images/maps/radar-tiles/04-03.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
server/images/maps/radar-tiles/04-04.webp
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
server/images/maps/radar-tiles/04-05.webp
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
server/images/maps/radar-tiles/04-06.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
server/images/maps/radar-tiles/04-07.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
server/images/maps/radar-tiles/04-08.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
server/images/maps/radar-tiles/04-09.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
server/images/maps/radar-tiles/05-00.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
server/images/maps/radar-tiles/05-01.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
server/images/maps/radar-tiles/05-02.webp
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
server/images/maps/radar-tiles/05-03.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
server/images/maps/radar-tiles/05-04.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
server/images/maps/radar-tiles/05-05.webp
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
server/images/maps/radar-tiles/05-06.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
server/images/maps/radar-tiles/05-07.webp
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
server/images/maps/radar-tiles/05-08.webp
Normal file
|
After Width: | Height: | Size: 462 B |
BIN
server/images/maps/radar-tiles/05-09.webp
Normal file
|
After Width: | Height: | Size: 50 B |
BIN
server/images/maps/radar-tiles/06-00.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
server/images/maps/radar-tiles/06-01.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
server/images/maps/radar-tiles/06-02.webp
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
server/images/maps/radar-tiles/06-03.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
server/images/maps/radar-tiles/06-04.webp
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
server/images/maps/radar-tiles/06-05.webp
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
server/images/maps/radar-tiles/06-06.webp
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
server/images/maps/radar-tiles/06-07.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |