78 lines
2.0 KiB
Go

package database
import (
"context"
"crypto/md5"
"database/sql"
"encoding/hex"
"fmt"
"math"
"time"
)
// UpsertRainTile stores a rain tile into table `rain_tiles`.
// The tiling scheme is equal-angle EPSG:4326 like radar tiles.
func UpsertRainTile(ctx context.Context, db *sql.DB, product string, dt time.Time, z, y, x int, width, height int, data []byte) error {
if width == 0 {
width = 256
}
if height == 0 {
height = 256
}
step := 360.0 / math.Pow(2, float64(z))
west := -180.0 + float64(x)*step
south := -90.0 + float64(y)*step
east := west + step
north := south + step
res := step / float64(width)
sum := md5.Sum(data)
md5hex := hex.EncodeToString(sum[:])
q := `
INSERT INTO rain_tiles (
product, dt, z, y, x, width, height,
west, south, east, north, res_deg,
data, checksum_md5
) VALUES (
$1,$2,$3,$4,$5,$6,$7,
$8,$9,$10,$11,$12,
$13,$14
)
ON CONFLICT (product, dt, z, y, x)
DO UPDATE SET
width = EXCLUDED.width,
height = EXCLUDED.height,
west = EXCLUDED.west,
south = EXCLUDED.south,
east = EXCLUDED.east,
north = EXCLUDED.north,
res_deg = EXCLUDED.res_deg,
data = EXCLUDED.data,
checksum_md5 = EXCLUDED.checksum_md5`
_, err := db.ExecContext(ctx, q,
product, dt, z, y, x, width, height,
west, south, east, north, res,
data, md5hex,
)
if err != nil {
return fmt.Errorf("upsert rain tile (%s %s z=%d y=%d x=%d): %w", product, dt.Format(time.RFC3339), z, y, x, err)
}
return nil
}
// HasRainTile reports whether a rain tile exists for the given key.
func HasRainTile(ctx context.Context, db *sql.DB, product string, dt time.Time, z, y, x int) (bool, error) {
const q = `SELECT 1 FROM rain_tiles WHERE product=$1 AND dt=$2 AND z=$3 AND y=$4 AND x=$5 LIMIT 1`
var one int
err := db.QueryRowContext(ctx, q, product, dt, z, y, x).Scan(&one)
if err == sql.ErrNoRows {
return false, nil
}
if err != nil {
return false, fmt.Errorf("check rain tile exists: %w", err)
}
return true, nil
}