forked from ruiaylin/pgparser
/
geographiclib.go
136 lines (123 loc) · 3.81 KB
/
geographiclib.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
// Package geographiclib is a wrapper around the GeographicLib library.
package geographiclib
// #cgo CXXFLAGS: -std=c++14
// #cgo LDFLAGS: -lm
//
// #include "geodesic.h"
// #include "geographiclib.h"
import "C"
import (
"math"
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
)
var (
// WGS84Spheroid represents the default WGS84 ellipsoid.
WGS84Spheroid = NewSpheroid(6378137, 1/298.257223563)
)
// Spheroid is an object that can perform geodesic operations
// on a given spheroid.
type Spheroid struct {
cRepr C.struct_geod_geodesic
Radius float64
Flattening float64
SphereRadius float64
}
// NewSpheroid creates a spheroid from a radius and flattening.
func NewSpheroid(radius float64, flattening float64) *Spheroid {
minorAxis := radius - radius*flattening
s := &Spheroid{
Radius: radius,
Flattening: flattening,
SphereRadius: (radius*2 + minorAxis) / 3,
}
C.geod_init(&s.cRepr, C.double(radius), C.double(flattening))
return s
}
// Inverse solves the geodetic inverse problem on the given spheroid
// (https://en.wikipedia.org/wiki/Geodesy#Geodetic_problems).
// Returns s12 (distance in meters), az1 (azimuth at point 1) and az2 (azimuth at point 2).
func (s *Spheroid) Inverse(a, b s2.LatLng) (s12, az1, az2 float64) {
var retS12, retAZ1, retAZ2 C.double
C.geod_inverse(
&s.cRepr,
C.double(a.Lat.Degrees()),
C.double(a.Lng.Degrees()),
C.double(b.Lat.Degrees()),
C.double(b.Lng.Degrees()),
&retS12,
&retAZ1,
&retAZ2,
)
return float64(retS12), float64(retAZ1), float64(retAZ2)
}
// InverseBatch computes the sum of the length of the lines represented
// by the line of points.
// This is intended for use for LineStrings. LinearRings/Polygons should use "AreaAndPerimeter".
// Returns the sum of the s12 (distance in meters) units.
func (s *Spheroid) InverseBatch(points []s2.Point) float64 {
lats := make([]C.double, len(points))
lngs := make([]C.double, len(points))
for i, p := range points {
latlng := s2.LatLngFromPoint(p)
lats[i] = C.double(latlng.Lat.Degrees())
lngs[i] = C.double(latlng.Lng.Degrees())
}
var result C.double
C.CR_GEOGRAPHICLIB_InverseBatch(
&s.cRepr,
&lats[0],
&lngs[0],
C.int(len(points)),
&result,
)
return float64(result)
}
// AreaAndPerimeter computes the area and perimeter of a polygon on a given spheroid.
// The points must never be duplicated (i.e. do not include the "final" point of a Polygon LinearRing).
// Area is in meter^2, Perimeter is in meters.
func (s *Spheroid) AreaAndPerimeter(points []s2.Point) (area float64, perimeter float64) {
lats := make([]C.double, len(points))
lngs := make([]C.double, len(points))
for i, p := range points {
latlng := s2.LatLngFromPoint(p)
lats[i] = C.double(latlng.Lat.Degrees())
lngs[i] = C.double(latlng.Lng.Degrees())
}
var areaDouble, perimeterDouble C.double
C.geod_polygonarea(
&s.cRepr,
&lats[0],
&lngs[0],
C.int(len(points)),
&areaDouble,
&perimeterDouble,
)
return float64(areaDouble), float64(perimeterDouble)
}
// Project returns computes the location of the projected point.
//
// Using the direct geodesic problem from GeographicLib (Karney 2013).
func (s *Spheroid) Project(point s2.LatLng, distance float64, azimuth s1.Angle) s2.LatLng {
var lat, lng C.double
C.geod_direct(
&s.cRepr,
C.double(point.Lat.Degrees()),
C.double(point.Lng.Degrees()),
C.double(azimuth*180.0/math.Pi),
C.double(distance),
&lat,
&lng,
nil,
)
return s2.LatLngFromDegrees(float64(lat), float64(lng))
}