latlng.go (3456B)
1 // Copyright 2014 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package s2 16 17 import ( 18 "fmt" 19 "math" 20 21 "github.com/golang/geo/r3" 22 "github.com/golang/geo/s1" 23 ) 24 25 const ( 26 northPoleLat = s1.Angle(math.Pi/2) * s1.Radian 27 southPoleLat = -northPoleLat 28 ) 29 30 // LatLng represents a point on the unit sphere as a pair of angles. 31 type LatLng struct { 32 Lat, Lng s1.Angle 33 } 34 35 // LatLngFromDegrees returns a LatLng for the coordinates given in degrees. 36 func LatLngFromDegrees(lat, lng float64) LatLng { 37 return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree} 38 } 39 40 // IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π]. 41 func (ll LatLng) IsValid() bool { 42 return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi 43 } 44 45 // Normalized returns the normalized version of the LatLng, 46 // with Lat clamped to [-π/2,π/2] and Lng wrapped in [-π,π]. 47 func (ll LatLng) Normalized() LatLng { 48 lat := ll.Lat 49 if lat > northPoleLat { 50 lat = northPoleLat 51 } else if lat < southPoleLat { 52 lat = southPoleLat 53 } 54 lng := s1.Angle(math.Remainder(ll.Lng.Radians(), 2*math.Pi)) * s1.Radian 55 return LatLng{lat, lng} 56 } 57 58 func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) } 59 60 // Distance returns the angle between two LatLngs. 61 func (ll LatLng) Distance(ll2 LatLng) s1.Angle { 62 // Haversine formula, as used in C++ S2LatLng::GetDistance. 63 lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians() 64 lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians() 65 dlat := math.Sin(0.5 * (lat2 - lat1)) 66 dlng := math.Sin(0.5 * (lng2 - lng1)) 67 x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2) 68 return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian 69 } 70 71 // NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude 72 // functions. Let's see if that's really necessary before exposing the same functionality. 73 74 func latitude(p Point) s1.Angle { 75 return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian 76 } 77 78 func longitude(p Point) s1.Angle { 79 return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian 80 } 81 82 // PointFromLatLng returns an Point for the given LatLng. 83 // The maximum error in the result is 1.5 * dblEpsilon. (This does not 84 // include the error of converting degrees, E5, E6, or E7 into radians.) 85 func PointFromLatLng(ll LatLng) Point { 86 phi := ll.Lat.Radians() 87 theta := ll.Lng.Radians() 88 cosphi := math.Cos(phi) 89 return Point{r3.Vector{math.Cos(theta) * cosphi, math.Sin(theta) * cosphi, math.Sin(phi)}} 90 } 91 92 // LatLngFromPoint returns an LatLng for a given Point. 93 func LatLngFromPoint(p Point) LatLng { 94 return LatLng{latitude(p), longitude(p)} 95 } 96 97 // ApproxEqual reports whether the latitude and longitude of the two LatLngs 98 // are the same up to a small tolerance. 99 func (ll LatLng) ApproxEqual(other LatLng) bool { 100 return ll.Lat.ApproxEqual(other.Lat) && ll.Lng.ApproxEqual(other.Lng) 101 }