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

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-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 location
    20  
    21  import (
    22  	"encoding/json"
    23  	"fmt"
    24  
    25  	"github.com/golang/geo/s1"
    26  	"github.com/golang/geo/s2"
    27  
    28  	"github.com/e154/smart-home/common/web"
    29  	m "github.com/e154/smart-home/models"
    30  )
    31  
    32  const (
    33  	// IpApi ...
    34  	IpApi = "http://ip-api.com/json"
    35  	// IPAPI ...
    36  	IPAPI = "https://ipapi.co/json/"
    37  )
    38  
    39  // GeoLocationFromIP ...
    40  func GeoLocationFromIP(ip string) (location m.GeoLocation, err error) {
    41  
    42  	crawler := web.New()
    43  
    44  	var body []byte
    45  	if _, body, err = crawler.Probe(web.Request{Method: "GET", Url: fmt.Sprintf("%s/%s", IpApi, ip)}); err != nil {
    46  		return
    47  	}
    48  	location = m.GeoLocation{}
    49  	err = json.Unmarshal(body, &location)
    50  
    51  	return
    52  }
    53  
    54  // GetRegionInfo ...
    55  func GetRegionInfo() (info m.RegionInfo, err error) {
    56  
    57  	crawler := web.New()
    58  
    59  	var body []byte
    60  	if _, body, err = crawler.Probe(web.Request{Method: "GET", Url: IPAPI}); err != nil {
    61  		return
    62  	}
    63  	info = m.RegionInfo{}
    64  	err = json.Unmarshal(body, &info)
    65  
    66  	return
    67  }
    68  
    69  const (
    70  	earthRadiusKm    = 6371.0
    71  	earthRadiusMiles = 3959.0
    72  )
    73  
    74  const (
    75  	Miles      = string("M")
    76  	Kilometers = string("K")
    77  )
    78  
    79  // GetDistanceBetweenPoints ...
    80  func GetDistanceBetweenPoints(point1, point2 m.Point, unit ...string) (distance float64) {
    81  
    82  	point := s2.PointFromLatLng(s2.LatLngFromDegrees(point1.Lat, point1.Lon))
    83  	minDistance := point.Distance(
    84  		s2.PointFromLatLng(
    85  			s2.LatLngFromDegrees(point2.Lat, point2.Lon),
    86  		),
    87  	)
    88  
    89  	distance = angleToDistance(minDistance, unit...)
    90  
    91  	return
    92  }
    93  
    94  // GetDistanceToPolygon ...
    95  func GetDistanceToPolygon(point1 m.Point, polygon1 []m.Point, unit ...string) (distance float64) {
    96  
    97  	point := s2.PointFromLatLng(s2.LatLngFromDegrees(point1.Lat, point1.Lon))
    98  
    99  	var points []s2.Point
   100  	for _, point := range polygon1 {
   101  		points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(point.Lat, point.Lon)))
   102  	}
   103  
   104  	loop := s2.LoopFromPoints(points)
   105  
   106  	var minDistance s1.Angle
   107  	minDistanceSet := false
   108  	for _, vertex := range loop.Vertices() {
   109  		distance := point.Distance(s2.PointFromLatLng(s2.LatLngFromPoint(vertex)))
   110  		if !minDistanceSet || distance < minDistance {
   111  			minDistance = distance
   112  			minDistanceSet = true
   113  		}
   114  	}
   115  
   116  	distance = angleToDistance(minDistance, unit...)
   117  
   118  	return
   119  }
   120  
   121  func PointInsidePolygon(point1 m.Point, polygon1 []m.Point) bool {
   122  
   123  	point := s2.PointFromLatLng(s2.LatLngFromDegrees(point1.Lat, point1.Lon))
   124  
   125  	var points []s2.Point
   126  	for _, point := range polygon1 {
   127  		points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(point.Lat, point.Lon)))
   128  	}
   129  
   130  	loop := s2.LoopFromPoints(points)
   131  
   132  	return !loop.ContainsPoint(point)
   133  }
   134  
   135  // Преобразовать угол в расстояние с учетом радиуса Земли.
   136  func angleToDistance(angle s1.Angle, unit ...string) (distance float64) {
   137  	if len(unit) > 0 {
   138  		switch unit[0] {
   139  		case Miles:
   140  			distance = float64(angle.Radians()) * earthRadiusMiles
   141  		case Kilometers:
   142  			distance = float64(angle.Radians()) * earthRadiusKm
   143  		}
   144  	} else {
   145  		distance = float64(angle.Radians()) * earthRadiusKm
   146  	}
   147  	return
   148  }