-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathland_mask_map.html
More file actions
157 lines (136 loc) · 5.85 KB
/
Copy pathland_mask_map.html
File metadata and controls
157 lines (136 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Radar Land Mask Generator</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { background:#111; display:flex; flex-direction:column; align-items:center;
font-family:monospace; color:#ccc; }
h2 { margin:12px 0 4px; font-size:15px; }
p { margin:0 0 8px; font-size:11px; color:#888; }
.controls { display:flex; gap:10px; align-items:center; margin-bottom:8px; flex-wrap:wrap; justify-content:center; }
.controls label { font-size:11px; color:#aaa; }
.controls input { width:100px; padding:3px 5px; font-size:11px; background:#222; color:#fff;
border:1px solid #444; border-radius:3px; font-family:monospace; }
#map { width:480px; height:480px; border:1px solid #333; background:#181818; }
.leaflet-tile { outline:none; border:0; }
button { padding:6px 14px; font-size:12px; cursor:pointer; background:#2a6; color:#fff;
border:none; border-radius:4px; font-family:monospace; }
button:hover { background:#3c8; }
button:disabled { background:#444; cursor:wait; }
.status { font-size:11px; color:#aaa; margin-top:4px; min-height:16px; }
</style>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
</head>
<body>
<h2>Radar Land Mask Generator</h2>
<p>Set your home position, review the map, then download land_mask.h.</p>
<div class="controls">
<label>Lat: <input id="latInput" type="number" step="any" value="-34.9285"></label>
<label>Lon: <input id="lonInput" type="number" step="any" value="138.6007"></label>
<label>Range (NM): <input id="rangeInput" type="number" step="1" value="40" style="width:60px"></label>
<button id="updateBtn">Update Map</button>
<button id="downloadBtn">Download land_mask.h</button>
</div>
<div id="map"></div>
<div class="status" id="status"></div>
<p style="margin-top:10px">Move the downloaded file to <code>src/land_mask.h</code></p>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script>
let map;
function initMap(lat, lon, rangeNm) {
const dLat = rangeNm / 60.04;
const dLon = rangeNm / (60.04 * Math.cos(lat * Math.PI / 180));
const bounds = [[lat - dLat, lon - dLon], [lat + dLat, lon + dLon]];
if (map) { map.remove(); }
map = L.map('map', { zoomControl:false, attributionControl:false }).fitBounds(bounds);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png', {
maxZoom: 19
}).addTo(map);
}
function update() {
const lat = parseFloat(document.getElementById('latInput').value);
const lon = parseFloat(document.getElementById('lonInput').value);
const range = parseFloat(document.getElementById('rangeInput').value);
initMap(lat, lon, range);
}
function setStatus(msg) { document.getElementById('status').textContent = msg; }
async function downloadHeader() {
const btn = document.getElementById('downloadBtn');
btn.disabled = true;
setStatus('Rendering map...');
await new Promise(r => setTimeout(r, 800));
try {
const mapEl = document.getElementById('map');
const canvas = await html2canvas(mapEl, {
useCORS: true,
allowTaint: false,
backgroundColor: '#000000',
width: 480,
height: 480,
scale: 1
});
setStatus('Generating land_mask.h...');
// Read pixel data and classify land vs water
const ctx = canvas.getContext('2d');
const imgData = ctx.getImageData(0, 0, 480, 480);
const pixels = imgData.data; // [r,g,b,a, r,g,b,a, ...]
const W = 480, H = 480, SIZE = W * H * 2;
const buf = new Uint8Array(new ArrayBuffer(SIZE));
let off = 0;
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i], g = pixels[i+1], b = pixels[i+2];
// dark_nolabels: land=#090909 (sum=27), water=#262626 (sum=114)
if (r + g + b < 70) {
buf[off++] = 0x02; buf[off++] = 0x01; // land
} else {
buf[off++] = 0x00; buf[off++] = 0x00; // water
}
}
// Build C header text
let lines = [];
lines.push('/* Auto-generated land mask — tool at tools/land_mask_map.html */');
lines.push(`/* ${W}x${H} pixels, RGB565 format */`);
lines.push('#pragma once');
lines.push('#include <stdint.h>');
lines.push('');
lines.push(`#define LAND_MASK_W ${W}`);
lines.push(`#define LAND_MASK_H ${H}`);
lines.push(`#define LAND_MASK_SIZE ${SIZE}`);
lines.push('');
lines.push('/* 0x0000 = water (transparent), 0x0102 = land (#002210) */');
lines.push(`static const uint8_t land_mask_rgb565[${SIZE}] = {`);
for (let i = 0; i < SIZE; i += 16) {
const chunk = Array.from(buf.slice(i, i + 16));
lines.push(' ' + chunk.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ') + ',');
}
lines.push('};');
lines.push('');
// Trigger download
const blob = new Blob([lines.join('\n')], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'land_mask.h';
a.click();
URL.revokeObjectURL(url);
setStatus('Downloaded land_mask.h (' + (SIZE / 1024).toFixed(0) + ' KB)');
btn.disabled = false;
} catch (e) {
setStatus('Error: ' + e.message);
btn.disabled = false;
}
}
document.getElementById('updateBtn').addEventListener('click', update);
document.getElementById('downloadBtn').addEventListener('click', downloadHeader);
// Enter key triggers update from any input
document.querySelectorAll('.controls input').forEach(input => {
input.addEventListener('keydown', e => { if (e.key === 'Enter') update(); });
});
// Initial load
update();
</script>
</body>
</html>