package model import ( "database/sql/driver" "fmt" "math" "regexp" "strconv" "time" "github.com/uptrace/bun" ) type Place struct { bun.BaseModel `bun:"table:places,alias:p"` GUID string `bun:",pk,unique"` Position *Point `bun:"type:geometry(Point, 4326)"` Img string Name string Count int CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"` UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"` DeletedAt time.Time `bun:",soft_delete,nullzero"` } type Point struct { Lat float64 Lon float64 } func (p *Point) Value() (driver.Value, error) { return fmt.Sprintf(`SRID=4326;POINT(%f %f)`, p.Lon, p.Lat), nil } func (p *Point) Scan(src any) (err error) { re := regexp.MustCompile(`/\((.+?) (.+?)\)/`) s := "" //nolint:revive switch src := src.(type) { case string: s = src case []uint8: s = string(src) default: return fmt.Errorf("unsupported data type: %T", src) } //nolint:gomnd subs := re.FindAllString(s, 2) lon, err := strconv.ParseFloat(subs[0], 64) if err != nil { return err } lat, err := strconv.ParseFloat(subs[1], 64) if err != nil { return err } p.Lat = lat p.Lon = lon return nil } const radius = 6371 // Earth's mean radius in kilometers func degrees2radians(degrees float64) float64 { return degrees * math.Pi / 180 } func (p *Point) Distance(destination *Point) float64 { degreesLat := degrees2radians(destination.Lat - p.Lat) degreesLong := degrees2radians(destination.Lon - p.Lon) a := (math.Sin(degreesLat/2)*math.Sin(degreesLat/2) + math.Cos(degrees2radians(p.Lat))* math.Cos(degrees2radians(destination.Lat))*math.Sin(degreesLong/2)* math.Sin(degreesLong/2)) c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) d := radius * c return d }