186 lines
3.9 KiB
Go
186 lines
3.9 KiB
Go
package fields
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"database/sql/driver"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
const (
|
|
timeFormat = "2006-01-02 15:04:05"
|
|
dateFormat = "2006-01-02"
|
|
)
|
|
|
|
type STPoint struct {
|
|
Lng float64
|
|
Lat float64
|
|
}
|
|
|
|
type Time time.Time
|
|
|
|
func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
|
now, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
|
|
*t = Time(now)
|
|
return
|
|
}
|
|
|
|
func (t *Time) MarshalJSON() ([]byte, error) {
|
|
b := make([]byte, 0, len(timeFormat)+2)
|
|
b = append(b, '"')
|
|
b = time.Time(*t).AppendFormat(b, timeFormat)
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (t *Time) String() string {
|
|
return time.Time(*t).Format(timeFormat)
|
|
}
|
|
|
|
func (t *Time) Value() (driver.Value, error) {
|
|
return time.Time(*t).Format(timeFormat), nil
|
|
}
|
|
|
|
func (t *Time) Time() time.Time {
|
|
return time.Time(*t)
|
|
}
|
|
|
|
func NowTime() Time {
|
|
return Time(time.Now())
|
|
}
|
|
|
|
type Date time.Time
|
|
|
|
func (t *Date) UnmarshalJSON(data []byte) (err error) {
|
|
now, err := time.ParseInLocation(`"`+dateFormat+`"`, string(data), time.Local)
|
|
*t = Date(now)
|
|
return
|
|
}
|
|
|
|
func (t *Date) MarshalJSON() ([]byte, error) {
|
|
b := make([]byte, 0, len(dateFormat)+2)
|
|
b = append(b, '"')
|
|
b = time.Time(*t).AppendFormat(b, dateFormat)
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (t *Date) String() string {
|
|
return time.Time(*t).Format(dateFormat)
|
|
}
|
|
|
|
func (t *Date) FromStr(s string) error {
|
|
now, err := time.ParseInLocation(dateFormat, string(s), time.Local)
|
|
*t = Date(now)
|
|
return err
|
|
}
|
|
|
|
func (t *Date) Time() time.Time {
|
|
return time.Time(*t)
|
|
}
|
|
|
|
type Month time.Time
|
|
|
|
const (
|
|
monthFormat = "2006-01"
|
|
)
|
|
|
|
func (t *Month) UnmarshalJSON(data []byte) (err error) {
|
|
if len(data) <= 2 {
|
|
return nil
|
|
}
|
|
now, err := time.ParseInLocation(`"`+monthFormat+`"`, string(data), time.Local)
|
|
*t = Month(now)
|
|
return
|
|
}
|
|
|
|
func (t *Month) MarshalJSON() ([]byte, error) {
|
|
b := make([]byte, 0, len(monthFormat)+2)
|
|
b = append(b, '"')
|
|
b = time.Time(*t).AppendFormat(b, monthFormat)
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (t *Month) String() string {
|
|
return time.Time(*t).Format(monthFormat)
|
|
}
|
|
|
|
type Point struct {
|
|
Lat float64 `json:"lat"`
|
|
Lng float64 `json:"lng"`
|
|
}
|
|
|
|
func (p Point) Value() (driver.Value, error) {
|
|
return fmt.Sprintf("POINT(%f %f)", p.Lng, p.Lat), nil
|
|
}
|
|
|
|
func (p *Point) Scan(src any) error {
|
|
switch src.(type) {
|
|
case []byte:
|
|
var b = src.([]byte)
|
|
if len(b) != 25 {
|
|
return errors.New(fmt.Sprintf("Expected []bytes with length 25, got %d", len(b)))
|
|
}
|
|
var longitude float64
|
|
var latitude float64
|
|
buf := bytes.NewReader(b[9:17])
|
|
err := binary.Read(buf, binary.LittleEndian, &longitude)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buf = bytes.NewReader(b[17:25])
|
|
err = binary.Read(buf, binary.LittleEndian, &latitude)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.Lng = longitude
|
|
p.Lat = latitude
|
|
default:
|
|
return errors.New(fmt.Sprintf("Expected []byte for Location type, got %T", src))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (loc Point) GormDataType() string {
|
|
return "point"
|
|
}
|
|
|
|
func (loc Point) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
|
|
return clause.Expr{
|
|
SQL: "ST_PointFromText(?)",
|
|
Vars: []any{fmt.Sprintf("POINT(%f %f)", loc.Lng, loc.Lat)},
|
|
}
|
|
}
|
|
|
|
func (p Point) DistanceTo(other Point) float64 {
|
|
const earthRadius = 6371000 // 地球半径,单位为米
|
|
|
|
lat1 := p.Lat * math.Pi / 180
|
|
lng1 := p.Lng * math.Pi / 180
|
|
lat2 := other.Lat * math.Pi / 180
|
|
lng2 := other.Lng * math.Pi / 180
|
|
|
|
dlat := lat2 - lat1
|
|
dlng := lng2 - lng1
|
|
|
|
a := math.Sin(dlat/2)*math.Sin(dlat/2) +
|
|
math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlng/2)*math.Sin(dlng/2)
|
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
|
|
|
distance := earthRadius * c
|
|
// point1 := Point{Lat: 40.7128, Lng: -74.0060}
|
|
// point2 := Point{Lat: 34.0522, Lng: -118.2437}
|
|
// distance := point1.DistanceTo(point2)
|
|
// fmt.Printf("Distance between points: %.2f km\n", distance)
|
|
// fmt.Printf("Distance between points: %d meters\n", distance)
|
|
return distance
|
|
}
|