github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/db/polygon.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package db
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"strings"
    26  
    27  	"github.com/pkg/errors"
    28  	"gorm.io/gorm"
    29  	"gorm.io/gorm/clause"
    30  )
    31  
    32  type Point struct {
    33  	Lon float64 `json:"lon"`
    34  	Lat float64 `json:"lat"`
    35  }
    36  
    37  type Polygon struct {
    38  	Points []Point
    39  }
    40  
    41  func (p *Polygon) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
    42  	points, _ := formatPoints(p.Points)
    43  	return clause.Expr{
    44  		SQL:  "?::polygon",
    45  		Vars: []interface{}{fmt.Sprintf("(%s)", points)},
    46  	}
    47  }
    48  
    49  func (p *Polygon) Scan(src any) (err error) {
    50  	value := fmt.Sprintf("%v", src)
    51  	value = strings.ReplaceAll(value, "(", "[")
    52  	value = strings.ReplaceAll(value, ")", "]")
    53  
    54  	data := [][]float64{}
    55  	if err = json.Unmarshal([]byte(value), &data); err != nil {
    56  		return
    57  	}
    58  	for _, point := range data {
    59  		p.Points = append(p.Points, Point{
    60  			Lon: point[0],
    61  			Lat: point[1],
    62  		})
    63  	}
    64  	return nil
    65  }
    66  
    67  func (Polygon) GormDataType() string {
    68  	return "polygon"
    69  }
    70  
    71  func formatPoints(polygonPoints []Point) (string, error) {
    72  	var point []Point
    73  	point = append(point, polygonPoints...)
    74  
    75  	if len(point) > 0 &&
    76  		point[0].Lon == point[len(point)-1].Lon &&
    77  		point[0].Lat == point[len(point)-1].Lat {
    78  		point = point[:len(point)-1]
    79  	}
    80  
    81  	if len(point) < 3 {
    82  		return "", errors.New("polygon must have at least 3 unique points")
    83  	}
    84  
    85  	var points string
    86  	for _, loc := range point {
    87  		points += fmt.Sprintf("(%f, %f),", loc.Lon, loc.Lat)
    88  	}
    89  
    90  	points += fmt.Sprintf("(%f, %f)", point[0].Lon, point[0].Lat)
    91  
    92  	return points, nil
    93  }