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 }