From 1f8b69e846a9195d902b556b31259372bf08a6ea Mon Sep 17 00:00:00 2001 From: root Date: Tue, 20 Jan 2026 12:03:37 -0800 Subject: [PATCH] Add theme system for customizable backgrounds - Move backgrounds to themed folders (backgrounds/desert/, backgrounds/custom/) - Add theme config option in config.json (default: "desert") - Add /api/config endpoint to serve theme and location - Update pixel-view.js to load backgrounds from theme folder - Add config.json.example for reference - Update CONFIG.md documentation Users can now set "theme": "custom" and place their own backgrounds in backgrounds/custom/ to customize the view for their location. Co-Authored-By: Claude Opus 4.5 --- CONFIG.md | 40 ++++++++++++- backgrounds/custom/README.md | 35 ++++++++++++ east.png => backgrounds/desert/east.png | Bin north.png => backgrounds/desert/north.png | Bin south.png => backgrounds/desert/south.png | Bin west.png => backgrounds/desert/west.png | Bin config.json | 3 +- config.json.example | 11 ++++ pixel-view.js | 65 +++++++++++++++------- server.py | 12 +++- 10 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 backgrounds/custom/README.md rename east.png => backgrounds/desert/east.png (100%) rename north.png => backgrounds/desert/north.png (100%) rename south.png => backgrounds/desert/south.png (100%) rename west.png => backgrounds/desert/west.png (100%) create mode 100644 config.json.example diff --git a/CONFIG.md b/CONFIG.md index 3d021fb..21e1099 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -15,7 +15,8 @@ Edit `config.json` to customize your installation: "lat": 0.0, "lon": 0.0 }, - "web_port": 2001 + "web_port": 2001, + "theme": "desert" } ``` @@ -31,6 +32,7 @@ Edit `config.json` to customize your installation: | `location.lat` | number | Latitude of your receiver location | | `location.lon` | number | Longitude of your receiver location | | `web_port` | number | Port for the web interface (default: 2001) | +| `theme` | string | Background theme: `"desert"` (default) or `"custom"` | ### Receiver Configuration Examples @@ -51,12 +53,44 @@ Edit `config.json` to customize your installation: --- -## Background Images +## Background Themes -Pixel View uses directional background images to show the horizon view from your receiver location. You should customize these to match your actual surroundings. +Pixel View uses directional background images to show the horizon view from your receiver location. Backgrounds are organized into themes stored in the `backgrounds/` folder. + +### Available Themes + +| Theme | Description | +|-------|-------------| +| `desert` | Las Vegas desert landscape (default) | +| `custom` | Your own custom backgrounds | + +### Theme Folder Structure + +``` +backgrounds/ +├── desert/ # Default desert theme +│ ├── north.png +│ ├── east.png +│ ├── south.png +│ └── west.png +└── custom/ # Your custom backgrounds + ├── README.md # Instructions for creating custom backgrounds + ├── north.png + ├── east.png + ├── south.png + └── west.png +``` + +### Using Custom Backgrounds + +1. Add your background images to `backgrounds/custom/` +2. Set `"theme": "custom"` in your `config.json` +3. Restart the server ### Image Files +Each theme folder needs these 4 directional images: + | File | Direction | Description | |------|-----------|-------------| | `north.png` | North (0°) | View looking north from your location | diff --git a/backgrounds/custom/README.md b/backgrounds/custom/README.md new file mode 100644 index 0000000..7ad810f --- /dev/null +++ b/backgrounds/custom/README.md @@ -0,0 +1,35 @@ +# Custom Backgrounds + +Place your custom background images in this folder. + +## Required Files + +You need to provide 4 directional background images: +- `north.png` - View looking north +- `east.png` - View looking east +- `south.png` - View looking south +- `west.png` - View looking west + +## Image Specifications + +- **Dimensions:** 1536 x 1024 pixels +- **Format:** PNG with transparency support +- **Orientation:** Each image should show the horizon/landscape as seen when facing that cardinal direction from your location + +## Tips + +- Include a horizon line in each image - the sun and moon will set behind it +- Keep the upper portion (sky area) relatively simple for aircraft visibility +- The bottom portion can have more detail (terrain, buildings, etc.) +- Consider your local landmarks and terrain for each direction + +## Enabling Custom Theme + +Set the theme in `config.json`: +```json +{ + "theme": "custom" +} +``` + +Then restart the server. diff --git a/east.png b/backgrounds/desert/east.png similarity index 100% rename from east.png rename to backgrounds/desert/east.png diff --git a/north.png b/backgrounds/desert/north.png similarity index 100% rename from north.png rename to backgrounds/desert/north.png diff --git a/south.png b/backgrounds/desert/south.png similarity index 100% rename from south.png rename to backgrounds/desert/south.png diff --git a/west.png b/backgrounds/desert/west.png similarity index 100% rename from west.png rename to backgrounds/desert/west.png diff --git a/config.json b/config.json index 98020f3..16a6bd2 100644 --- a/config.json +++ b/config.json @@ -6,5 +6,6 @@ "lat": 0.0, "lon": 0.0 }, - "web_port": 2001 + "web_port": 2001, + "theme": "desert" } diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..16a6bd2 --- /dev/null +++ b/config.json.example @@ -0,0 +1,11 @@ +{ + "receivers": "AUTO", + "receiver_port": 30003, + "location": { + "name": "My Location", + "lat": 0.0, + "lon": 0.0 + }, + "web_port": 2001, + "theme": "desert" +} diff --git a/pixel-view.js b/pixel-view.js index 31dbe63..243f5b3 100644 --- a/pixel-view.js +++ b/pixel-view.js @@ -65,6 +65,9 @@ class PixelADSB { this.viewDirectionNames = { 0: 'N', 90: 'E', 180: 'S', 270: 'W' }; this.fieldOfView = 90; // 90 degree field of view + // Theme for background images (loaded from config) + this.theme = 'desert'; + // Hover and selection tracking this.mouseX = -1; this.mouseY = -1; @@ -109,7 +112,7 @@ class PixelADSB { }); // Load environment images (directional backgrounds, base, sun, clouds) - // Directional background images + // Directional background images (loaded after fetching config) this.backgroundImages = { 0: new Image(), // North 90: new Image(), // East @@ -118,24 +121,6 @@ class PixelADSB { }; this.backgroundImagesLoaded = { 0: false, 90: false, 180: false, 270: false }; - // Load directional backgrounds (fallback to desert.png if not available) - const directions = [ - { deg: 0, name: 'north' }, - { deg: 90, name: 'east' }, - { deg: 180, name: 'south' }, - { deg: 270, name: 'west' } - ]; - directions.forEach(dir => { - this.backgroundImages[dir.deg].onload = () => { - this.backgroundImagesLoaded[dir.deg] = true; - console.log(`${dir.name}.png loaded`); - }; - this.backgroundImages[dir.deg].onerror = () => { - console.warn(`Failed to load ${dir.name}.png, using fallback`); - }; - this.backgroundImages[dir.deg].src = `${dir.name}.png?v=1`; - }); - this.sunImage = new Image(); this.sunImage.onload = () => { this.sunImageLoaded = true; @@ -410,8 +395,11 @@ class PixelADSB { } async init() { - // Fetch receiver location - await this.fetchReceiverLocation(); + // Fetch config (includes theme and location) + await this.fetchConfig(); + + // Load background images based on theme + this.loadBackgroundImages(); // Fetch weather await this.fetchWeather(); @@ -547,6 +535,41 @@ class PixelADSB { return padding + (normalizedAngle * usableWidth); } + async fetchConfig() { + try { + const response = await fetch('/api/config'); + const data = await response.json(); + this.theme = data.theme || 'desert'; + this.receiverLat = data.location?.lat || 0; + this.receiverLon = data.location?.lon || 0; + this.locationName = data.location?.name || 'My Location'; + console.log(`Config loaded - Theme: ${this.theme}, Location: ${this.locationName} (${this.receiverLat}, ${this.receiverLon})`); + } catch (error) { + console.warn('Could not fetch config, using defaults'); + // Fallback to receiver-location API for backwards compatibility + await this.fetchReceiverLocation(); + } + } + + loadBackgroundImages() { + const directions = [ + { deg: 0, name: 'north' }, + { deg: 90, name: 'east' }, + { deg: 180, name: 'south' }, + { deg: 270, name: 'west' } + ]; + directions.forEach(dir => { + this.backgroundImages[dir.deg].onload = () => { + this.backgroundImagesLoaded[dir.deg] = true; + console.log(`${this.theme}/${dir.name}.png loaded`); + }; + this.backgroundImages[dir.deg].onerror = () => { + console.warn(`Failed to load backgrounds/${this.theme}/${dir.name}.png`); + }; + this.backgroundImages[dir.deg].src = `backgrounds/${this.theme}/${dir.name}.png?v=1`; + }); + } + async fetchReceiverLocation() { try { // Fetch from same server that serves pixel-view diff --git a/server.py b/server.py index 6e30a58..9f3cb4d 100755 --- a/server.py +++ b/server.py @@ -24,7 +24,8 @@ config = { "lat": 0.0, "lon": 0.0 }, - "web_port": 2001 + "web_port": 2001, + "theme": "desert" } def load_config(): @@ -45,6 +46,7 @@ def load_config(): print(f" Receiver port: {config['receiver_port']}") print(f" Location: {config['location']['name']} ({config['location']['lat']}, {config['location']['lon']})") print(f" Web port: {config['web_port']}") + print(f" Theme: {config.get('theme', 'desert')}") # Flight data storage flights: Dict[str, dict] = {} @@ -302,6 +304,13 @@ async def handle_receiver_location(request): "name": config["location"]["name"] }) +async def handle_config(request): + """Return client-relevant configuration""" + return web.json_response({ + "theme": config.get("theme", "desert"), + "location": config["location"] + }) + async def handle_http(request): """Serve static files""" path = request.path @@ -319,6 +328,7 @@ async def start_http_server(): app = web.Application() app.router.add_get('/ws', websocket_handler) app.router.add_get('/api/receiver-location', handle_receiver_location) + app.router.add_get('/api/config', handle_config) app.router.add_get('/{tail:.*}', handle_http) runner = web.AppRunner(app)