Add chart.js dashboard
This commit is contained in:
114
main.go
114
main.go
@ -64,11 +64,9 @@ func estimateCloudCover(url string) (float64, error) {
|
||||
defer valMask.Close()
|
||||
defer cloudMask.Close()
|
||||
|
||||
// s < 60 (low saturation)
|
||||
// Low saturation, high brightness = likely clouds
|
||||
gocv.InRangeWithScalar(s, gocv.NewScalar(0, 0, 0, 0), gocv.NewScalar(60, 0, 0, 0), &satMask)
|
||||
// v > 120 (bright)
|
||||
gocv.InRangeWithScalar(v, gocv.NewScalar(120, 0, 0, 0), gocv.NewScalar(255, 0, 0, 0), &valMask)
|
||||
|
||||
gocv.BitwiseAnd(satMask, valMask, &cloudMask)
|
||||
|
||||
totalPixels := cloudMask.Rows() * cloudMask.Cols()
|
||||
@ -92,9 +90,8 @@ func backgroundCollector() {
|
||||
|
||||
trendLock.Lock()
|
||||
trendHistory = append(trendHistory, result)
|
||||
// Keep last 100 entries
|
||||
if len(trendHistory) > 100 {
|
||||
trendHistory = trendHistory[len(trendHistory)-100:]
|
||||
if len(trendHistory) > 200 {
|
||||
trendHistory = trendHistory[len(trendHistory)-200:]
|
||||
}
|
||||
trendLock.Unlock()
|
||||
|
||||
@ -122,28 +119,93 @@ func getEnvInt(key string, def int) int {
|
||||
func main() {
|
||||
go backgroundCollector()
|
||||
|
||||
http.HandleFunc("/cloudcover", func(w http.ResponseWriter, r *http.Request) {
|
||||
trendLock.Lock()
|
||||
defer trendLock.Unlock()
|
||||
|
||||
if len(trendHistory) == 0 {
|
||||
http.Error(w, "No data collected yet", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
latest := trendHistory[len(trendHistory)-1]
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(latest)
|
||||
})
|
||||
|
||||
http.HandleFunc("/cloudtrend", func(w http.ResponseWriter, r *http.Request) {
|
||||
trendLock.Lock()
|
||||
defer trendLock.Unlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(trendHistory)
|
||||
})
|
||||
http.HandleFunc("/", dashboardHandler)
|
||||
http.HandleFunc("/cloudcover", handleLatest)
|
||||
http.HandleFunc("/cloudtrend", handleTrend)
|
||||
|
||||
fmt.Printf("☁️ Cloud cover API started at http://localhost:8080\n")
|
||||
fmt.Printf("🔁 Refresh interval: %ds | Source: %s\n", refreshSecs, imageURL)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
func handleLatest(w http.ResponseWriter, r *http.Request) {
|
||||
trendLock.Lock()
|
||||
defer trendLock.Unlock()
|
||||
|
||||
if len(trendHistory) == 0 {
|
||||
http.Error(w, "No data collected yet", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
latest := trendHistory[len(trendHistory)-1]
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(latest)
|
||||
}
|
||||
|
||||
func handleTrend(w http.ResponseWriter, r *http.Request) {
|
||||
trendLock.Lock()
|
||||
defer trendLock.Unlock()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(trendHistory)
|
||||
}
|
||||
|
||||
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
html := `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>☁️ Cloud Cover Dashboard</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
body { font-family: sans-serif; background: #111; color: #eee; text-align: center; margin: 0; padding: 1em; }
|
||||
canvas { background: #222; border-radius: 10px; margin-top: 20px; max-width: 90vw; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>☁️ Cloud Cover Trend</h1>
|
||||
<p>Live view of sky cloudiness from <code>` + imageURL + `</code></p>
|
||||
<canvas id="chart" width="800" height="400"></canvas>
|
||||
|
||||
<script>
|
||||
const ctx = document.getElementById('chart');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Cloud Cover (%)',
|
||||
data: [],
|
||||
borderColor: '#4fc3f7',
|
||||
backgroundColor: 'rgba(79,195,247,0.2)',
|
||||
fill: true,
|
||||
tension: 0.3
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: { title: { display: true, text: 'Time' }, ticks: { color: '#aaa' } },
|
||||
y: { beginAtZero: true, title: { display: true, text: 'Cloud %' }, ticks: { color: '#aaa' } }
|
||||
},
|
||||
plugins: {
|
||||
legend: { labels: { color: '#ccc' } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchData() {
|
||||
const res = await fetch('/cloudtrend');
|
||||
const data = await res.json();
|
||||
chart.data.labels = data.map(d => new Date(d.timestamp).toLocaleTimeString());
|
||||
chart.data.datasets[0].data = data.map(d => d.cloud_cover);
|
||||
chart.update();
|
||||
}
|
||||
|
||||
fetchData();
|
||||
setInterval(fetchData, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Write([]byte(html))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user