mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
package domain
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Point represents a PostGIS Point geometry with SRID 4326 (WGS84)
|
|
// Implements sql.Scanner and driver.Valuer for proper GORM integration
|
|
type Point struct {
|
|
Longitude float64
|
|
Latitude float64
|
|
Valid bool
|
|
}
|
|
|
|
// GormDataType specifies the database column type for GORM
|
|
// We use text type for GORM migrations, and handle PostGIS geometry conversion in Value/Scan
|
|
func (Point) GormDataType() string {
|
|
return "text"
|
|
}
|
|
|
|
// Scan implements sql.Scanner interface to read PostGIS geometry from database
|
|
func (p *Point) Scan(value interface{}) error {
|
|
if value == nil {
|
|
p.Valid = false
|
|
return nil
|
|
}
|
|
|
|
var str string
|
|
switch v := value.(type) {
|
|
case []byte:
|
|
str = string(v)
|
|
case string:
|
|
str = v
|
|
default:
|
|
return fmt.Errorf("cannot scan %T into Point", value)
|
|
}
|
|
|
|
// Handle empty or invalid values
|
|
if str == "" || str == "POINT EMPTY" {
|
|
p.Valid = false
|
|
return nil
|
|
}
|
|
|
|
// Try WKT format: POINT(lng lat) or POINT(lat lng)
|
|
// This works whether the data comes from PostGIS geometry or plain text storage
|
|
if strings.HasPrefix(str, "POINT(") && strings.HasSuffix(str, ")") {
|
|
// Extract coordinates from POINT(lng lat) format
|
|
coords := strings.TrimPrefix(str, "POINT(")
|
|
coords = strings.TrimSuffix(coords, ")")
|
|
|
|
var lng, lat float64
|
|
_, err := fmt.Sscanf(coords, "%f %f", &lng, &lat)
|
|
if err != nil {
|
|
p.Valid = false
|
|
return nil
|
|
}
|
|
|
|
p.Longitude = lng
|
|
p.Latitude = lat
|
|
p.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// If we can't parse it, mark as invalid
|
|
p.Valid = false
|
|
return nil
|
|
}
|
|
|
|
// Value implements driver.Valuer interface to write PostGIS geometry to database
|
|
func (p Point) Value() (driver.Value, error) {
|
|
if !p.Valid {
|
|
return nil, nil
|
|
}
|
|
// Return WKT format that works with or without PostGIS
|
|
// When PostGIS is available, it will be stored as geometry
|
|
// When PostGIS is not available, it will be stored as text
|
|
return fmt.Sprintf("POINT(%f %f)", p.Longitude, p.Latitude), nil
|
|
}
|
|
|
|
// String returns the WKT representation of the point
|
|
func (p Point) String() string {
|
|
if !p.Valid {
|
|
return "POINT EMPTY"
|
|
}
|
|
return fmt.Sprintf("POINT(%f %f)", p.Longitude, p.Latitude)
|
|
}
|
|
|
|
// IsEmpty returns true if the point is not valid
|
|
func (p Point) IsEmpty() bool {
|
|
return !p.Valid
|
|
}
|
|
|
|
// NewPoint creates a new Point from longitude and latitude
|
|
func NewPoint(longitude, latitude float64) Point {
|
|
return Point{
|
|
Longitude: longitude,
|
|
Latitude: latitude,
|
|
Valid: true,
|
|
}
|
|
}
|
|
|
|
// AfterFind hook helper to populate Point from lat/lng if geometry is not set
|
|
// This can be used in GORM hooks to ensure geometry is always populated
|
|
func (p *Point) EnsureFromLatLng(lat, lng float64) {
|
|
if !p.Valid && lat != 0 && lng != 0 {
|
|
p.Longitude = lng
|
|
p.Latitude = lat
|
|
p.Valid = true
|
|
}
|
|
}
|
|
|
|
// BeforeSave hook helper to ensure geometry is set from lat/lng
|
|
// This can be used in GORM BeforeSave hooks
|
|
func (p *Point) EnsureValid(lat, lng float64) {
|
|
if lat != 0 && lng != 0 {
|
|
p.Longitude = lng
|
|
p.Latitude = lat
|
|
p.Valid = true
|
|
}
|
|
}
|