This repository has been archived by the owner on Jun 28, 2021. It is now read-only.
forked from paulmach/go.geo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
projections.go
166 lines (133 loc) · 4.13 KB
/
projections.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package geo
import (
"fmt"
"math"
)
// A Projector is a function that converts the given point to a different space.
type Projector func(p *Point)
// A Projection is a set of projectors to map forward and backwards to the projected space.
type Projection struct {
Project Projector
Inverse Projector
}
const mercatorPole = 20037508.34
// Mercator projection, performs EPSG:3857, sometimes also described as EPSG:900913.
var Mercator = Projection{
Project: func(p *Point) {
p.SetX(mercatorPole / 180.0 * p.Lng())
y := math.Log(math.Tan((90.0+p.Lat())*math.Pi/360.0)) / math.Pi * mercatorPole
p.SetY(math.Max(-mercatorPole, math.Min(y, mercatorPole)))
},
Inverse: func(p *Point) {
p.SetLng(p.X() * 180.0 / mercatorPole)
p.SetLat(180.0 / math.Pi * (2*math.Atan(math.Exp((p.Y()/mercatorPole)*math.Pi)) - math.Pi/2.0))
},
}
// MercatorScaleFactor returns the mercator scaling factor for a given degree latitude.
func MercatorScaleFactor(degreesLatitude float64) float64 {
if degreesLatitude < -90.0 || degreesLatitude > 90.0 {
panic(fmt.Sprintf("geo: latitude out of range, given %f", degreesLatitude))
}
return 1.0 / math.Cos(degreesLatitude/180.0*math.Pi)
}
// BuildTransverseMercator builds a transverse Mercator projection
// that automatically recenters the longitude around the provided centerLng.
// Works correctly around the anti-meridian.
// http://en.wikipedia.org/wiki/Transverse_Mercator_projection
func BuildTransverseMercator(centerLng float64) Projection {
return Projection{
Project: func(p *Point) {
lng := p.Lng() - centerLng
if lng < 180 {
lng += 360.0
}
if lng > 180 {
lng -= 360.0
}
p.SetLng(lng)
TransverseMercator.Project(p)
},
Inverse: func(p *Point) {
TransverseMercator.Inverse(p)
lng := p.Lng() + centerLng
if lng < 180 {
lng += 360.0
}
if lng > 180 {
lng -= 360.0
}
p.SetLng(lng)
},
}
}
// TransverseMercator implements a default transverse Mercator projector
// that will only work well +-10 degrees around longitude 0.
var TransverseMercator = Projection{
Project: func(p *Point) {
radLat := deg2rad(p.Lat())
radLng := deg2rad(p.Lng())
sincos := math.Sin(radLng) * math.Cos(radLat)
p.SetX(0.5 * math.Log((1+sincos)/(1-sincos)) * EarthRadius)
p.SetY(math.Atan(math.Tan(radLat)/math.Cos(radLng)) * EarthRadius)
},
Inverse: func(p *Point) {
x := p.X() / EarthRadius
y := p.Y() / EarthRadius
lng := math.Atan(math.Sinh(x) / math.Cos(y))
lat := math.Asin(math.Sin(y) / math.Cosh(x))
p.SetLng(rad2deg(lng))
p.SetLat(rad2deg(lat))
},
}
// ScalarMercator converts from lng/lat float64 to x,y uint64.
// This is the same as Google's world coordinates.
var ScalarMercator struct {
Level uint64
Project func(lng, lat float64, level ...uint64) (x, y uint64)
Inverse func(x, y uint64, level ...uint64) (lng, lat float64)
}
func init() {
ScalarMercator.Level = 31
ScalarMercator.Project = func(lng, lat float64, level ...uint64) (x, y uint64) {
l := ScalarMercator.Level
if len(level) != 0 {
l = level[0]
}
return scalarMercatorProject(lng, lat, l)
}
ScalarMercator.Inverse = func(x, y uint64, level ...uint64) (lng, lat float64) {
l := ScalarMercator.Level
if len(level) != 0 {
l = level[0]
}
return scalarMercatorInverse(x, y, l)
}
}
func scalarMercatorProject(lng, lat float64, level uint64) (x, y uint64) {
var factor uint64
factor = 1 << level
maxtiles := float64(factor)
lng = lng/360.0 + 0.5
x = uint64(lng * maxtiles)
// bound it because we have a top of the world problem
siny := math.Sin(lat * math.Pi / 180.0)
if siny < -0.9999 {
lat = 0.5 + 0.5*math.Log((1.0+siny)/(1.0-siny))/(-2*math.Pi)
y = 0
} else if siny > 0.9999 {
lat = 0.5 + 0.5*math.Log((1.0+siny)/(1.0-siny))/(-2*math.Pi)
y = factor - 1
} else {
lat = 0.5 + 0.5*math.Log((1.0+siny)/(1.0-siny))/(-2*math.Pi)
y = uint64(lat * maxtiles)
}
return
}
func scalarMercatorInverse(x, y, level uint64) (lng, lat float64) {
var factor uint64
factor = 1 << level
maxtiles := float64(factor)
lng = 360.0 * (float64(x)/maxtiles - 0.5)
lat = (2.0*math.Atan(math.Exp(math.Pi-(2*math.Pi)*(float64(y))/maxtiles)))*(180.0/math.Pi) - 90.0
return
}