173 lines
4.3 KiB
Go
173 lines
4.3 KiB
Go
package server
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/binary"
|
|
"net/http"
|
|
"time"
|
|
"weatherstation/core/internal/data"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type radarTileRecord struct {
|
|
DT time.Time
|
|
Z int
|
|
Y int
|
|
X int
|
|
Width int
|
|
Height int
|
|
West float64
|
|
South float64
|
|
East float64
|
|
North float64
|
|
ResDeg float64
|
|
Data []byte
|
|
}
|
|
|
|
type radarTileResponse struct {
|
|
DT string `json:"dt"`
|
|
Z int `json:"z"`
|
|
Y int `json:"y"`
|
|
X int `json:"x"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
West float64 `json:"west"`
|
|
South float64 `json:"south"`
|
|
East float64 `json:"east"`
|
|
North float64 `json:"north"`
|
|
ResDeg float64 `json:"res_deg"`
|
|
Values [][]*float64 `json:"values"`
|
|
}
|
|
|
|
func handleRadarTimes(c *gin.Context) {
|
|
z := parseInt(c.Query("z"), 7)
|
|
y := parseInt(c.Query("y"), 40)
|
|
x := parseInt(c.Query("x"), 102)
|
|
fromStr := c.Query("from")
|
|
toStr := c.Query("to")
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
if loc == nil {
|
|
loc = time.FixedZone("CST", 8*3600)
|
|
}
|
|
|
|
var rows *sql.Rows
|
|
var err error
|
|
if fromStr != "" && toStr != "" {
|
|
from, err1 := time.ParseInLocation("2006-01-02 15:04:05", fromStr, loc)
|
|
to, err2 := time.ParseInLocation("2006-01-02 15:04:05", toStr, loc)
|
|
if err1 != nil || err2 != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid time range"})
|
|
return
|
|
}
|
|
const qRange = `SELECT dt FROM radar_tiles WHERE z=$1 AND y=$2 AND x=$3 AND dt BETWEEN $4 AND $5 ORDER BY dt DESC`
|
|
rows, err = data.DB().Query(qRange, z, y, x, from, to)
|
|
} else {
|
|
limit := parseInt(c.Query("limit"), 48)
|
|
const q = `SELECT dt FROM radar_tiles WHERE z=$1 AND y=$2 AND x=$3 ORDER BY dt DESC LIMIT $4`
|
|
rows, err = data.DB().Query(q, z, y, x, limit)
|
|
}
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var times []string
|
|
for rows.Next() {
|
|
var dt time.Time
|
|
if err := rows.Scan(&dt); err == nil {
|
|
times = append(times, dt.In(loc).Format("2006-01-02 15:04:05"))
|
|
}
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"times": times})
|
|
}
|
|
|
|
func handleRadarTilesAt(c *gin.Context) {
|
|
z := parseInt(c.Query("z"), 7)
|
|
dtStr := c.Query("dt")
|
|
if dtStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "missing dt"})
|
|
return
|
|
}
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
if loc == nil {
|
|
loc = time.FixedZone("CST", 8*3600)
|
|
}
|
|
dt, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid dt"})
|
|
return
|
|
}
|
|
|
|
const q = `SELECT dt,z,y,x,width,height,west,south,east,north,res_deg,data FROM radar_tiles WHERE z=$1 AND dt=$2 ORDER BY y,x`
|
|
rows, qerr := data.DB().Query(q, z, dt)
|
|
if qerr != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "db failed"})
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
var tiles []radarTileResponse
|
|
for rows.Next() {
|
|
var r radarTileRecord
|
|
if err := rows.Scan(&r.DT, &r.Z, &r.Y, &r.X, &r.Width, &r.Height, &r.West, &r.South, &r.East, &r.North, &r.ResDeg, &r.Data); err != nil {
|
|
continue
|
|
}
|
|
w, h := r.Width, r.Height
|
|
if w <= 0 || h <= 0 || len(r.Data) < w*h*2 {
|
|
continue
|
|
}
|
|
vals := make([][]*float64, h)
|
|
off := 0
|
|
for row := 0; row < h; row++ {
|
|
rowVals := make([]*float64, w)
|
|
for col := 0; col < w; col++ {
|
|
v := int16(binary.BigEndian.Uint16(r.Data[off : off+2]))
|
|
off += 2
|
|
if v >= 32766 {
|
|
rowVals[col] = nil
|
|
continue
|
|
}
|
|
dbz := float64(v) / 10.0
|
|
if dbz < 0 {
|
|
dbz = 0
|
|
} else if dbz > 75 {
|
|
dbz = 75
|
|
}
|
|
vv := dbz
|
|
rowVals[col] = &vv
|
|
}
|
|
vals[row] = rowVals
|
|
}
|
|
tiles = append(tiles, radarTileResponse{DT: r.DT.In(loc).Format("2006-01-02 15:04:05"), Z: r.Z, Y: r.Y, X: r.X, Width: w, Height: h, West: r.West, South: r.South, East: r.East, North: r.North, ResDeg: r.ResDeg, Values: vals})
|
|
}
|
|
if len(tiles) == 0 {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"tiles": tiles})
|
|
}
|
|
|
|
func parseInt(s string, def int) int {
|
|
if s == "" {
|
|
return def
|
|
}
|
|
n := 0
|
|
sign := 1
|
|
i := 0
|
|
if s[0] == '-' || s[0] == '+' {
|
|
if s[0] == '-' {
|
|
sign = -1
|
|
}
|
|
i = 1
|
|
}
|
|
for ; i < len(s); i++ {
|
|
ch := s[i]
|
|
if ch < '0' || ch > '9' {
|
|
return def
|
|
}
|
|
n = n*10 + int(ch-'0')
|
|
}
|
|
return sign * n
|
|
}
|