-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpj.go
309 lines (262 loc) · 9.68 KB
/
pj.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
package proj
// #include "go-proj.h"
// #cgo nocallback proj_errno
// #cgo nocallback proj_errno_reset
// #cgo nocallback proj_errno_restore
// #cgo nocallback proj_geod
// #cgo nocallback proj_is_crs
// #cgo nocallback proj_lp_dist
// #cgo nocallback proj_lpz_dist
// #cgo nocallback proj_normalize_for_visualization
// #cgo nocallback proj_pj_info
// #cgo nocallback proj_trans
// #cgo nocallback proj_trans_array
// #cgo nocallback proj_trans_bounds
// #cgo nocallback proj_trans_generic
// #cgo nocallback proj_trans_get_last_used_operation
// #cgo noescape proj_errno
// #cgo noescape proj_errno_reset
// #cgo noescape proj_errno_restore
// #cgo noescape proj_geod
// #cgo noescape proj_is_crs
// #cgo noescape proj_lp_dist
// #cgo noescape proj_lpz_dist
// #cgo noescape proj_normalize_for_visualization
// #cgo noescape proj_pj_info
// #cgo noescape proj_trans
// #cgo noescape proj_trans_array
// #cgo noescape proj_trans_bounds
// #cgo noescape proj_trans_generic
// #cgo noescape proj_trans_get_last_used_operation
import "C"
import (
"unsafe"
)
// A Direction is a direction.
type Direction C.PJ_DIRECTION
// Directions.
const (
DirectionFwd Direction = C.PJ_FWD
DirectionIdent Direction = C.PJ_IDENT
DirectionInv Direction = C.PJ_INV
)
// A PJ is a projection or a transformation.
type PJ struct {
context *Context
cPJ *C.PJ
}
// A PJInfo contains information about a PJ.
type PJInfo struct {
ID string
Description string
Definition string
HasInverse bool
Accuracy float64
}
// Returns a new PJ instance whose axis order is the one expected for
// visualization purposes. If the axis order of its source or target CRS is
// northing, easting, then an axis swap operation will be inserted.
//
// The axis order of geographic CRS will be longitude, latitude[, height], and
// the one of projected CRS will be easting, northing [, height].
func (pj *PJ) NormalizeForVisualization() (*PJ, error) {
pj.context.Lock()
defer pj.context.Unlock()
return pj.context.newPJ(C.proj_normalize_for_visualization(pj.context.cPJContext, pj.cPJ))
}
// Forward transforms coord in the forward direction.
func (pj *PJ) Forward(coord Coord) (Coord, error) {
return pj.Trans(DirectionFwd, coord)
}
// ForwardBounds transforms bounds in the forward direction.
func (pj *PJ) ForwardBounds(bounds Bounds, densifyPoints int) (Bounds, error) {
return pj.TransBounds(DirectionFwd, bounds, densifyPoints)
}
// ForwardArray transforms coords in the forward direction.
func (pj *PJ) ForwardArray(coords []Coord) error {
return pj.TransArray(DirectionFwd, coords)
}
// ForwardFlatCoords transforms flatCoords in the forward direction.
func (pj *PJ) ForwardFlatCoords(flatCoords []float64, stride, zIndex, mIndex int) error {
return pj.TransFlatCoords(DirectionFwd, flatCoords, stride, zIndex, mIndex)
}
// ForwardFloat64Slice transforms float64 in place in the forward direction.
func (pj *PJ) ForwardFloat64Slice(float64Slice []float64) ([]float64, error) {
return pj.TransFloat64Slice(DirectionFwd, float64Slice)
}
// ForwardFloat64Slices transforms float64Slices in the forward direction.
func (pj *PJ) ForwardFloat64Slices(float64Slices [][]float64) error {
return pj.TransFloat64Slices(DirectionFwd, float64Slices)
}
// Geod returns the distance, forward azimuth, and reverse azimuth between a and b.
func (pj *PJ) Geod(a, b Coord) (float64, float64, float64) {
pj.context.Lock()
defer pj.context.Unlock()
cCoord := C.proj_geod(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b)))
cGeod := *(*C.PJ_GEOD)(unsafe.Pointer(&cCoord))
return (float64)(cGeod.s), (float64)(cGeod.a1), (float64)(cGeod.a2)
}
// GetLastUsedOperation returns the operation used in the last call to Trans.
func (pj *PJ) GetLastUsedOperation() (*PJ, error) {
pj.context.Lock()
defer pj.context.Unlock()
return pj.context.newPJ(C.proj_trans_get_last_used_operation(pj.cPJ))
}
// Info returns information about pj.
func (pj *PJ) Info() PJInfo {
pj.context.Lock()
defer pj.context.Unlock()
cProjInfo := C.proj_pj_info(pj.cPJ)
return PJInfo{
ID: C.GoString(cProjInfo.id),
Description: C.GoString(cProjInfo.description),
Definition: C.GoString(cProjInfo.definition),
HasInverse: cProjInfo.has_inverse != 0,
Accuracy: (float64)(cProjInfo.accuracy),
}
}
// IsCRS returns whether pj is a CRS.
func (pj *PJ) IsCRS() bool {
return C.proj_is_crs(pj.cPJ) != 0
}
// Inverse transforms coord in the inverse direction.
func (pj *PJ) Inverse(coord Coord) (Coord, error) {
return pj.Trans(DirectionInv, coord)
}
// InverseArray transforms coords in the inverse direction.
func (pj *PJ) InverseArray(coords []Coord) error {
return pj.TransArray(DirectionInv, coords)
}
// InverseBounds transforms bounds in the forward direction.
func (pj *PJ) InverseBounds(bounds Bounds, densifyPoints int) (Bounds, error) {
return pj.TransBounds(DirectionInv, bounds, densifyPoints)
}
// InverseFlatCoords transforms flatCoords in the inverse direction.
func (pj *PJ) InverseFlatCoords(flatCoords []float64, stride, zIndex, mIndex int) error {
return pj.TransFlatCoords(DirectionInv, flatCoords, stride, zIndex, mIndex)
}
// InverseFloat64Slice transforms float64 in place in the forward direction.
func (pj *PJ) InverseFloat64Slice(float64Slice []float64) ([]float64, error) {
return pj.TransFloat64Slice(DirectionInv, float64Slice)
}
// InverseFloat64Slices transforms float64Slices in the inverse direction.
func (pj *PJ) InverseFloat64Slices(float64Slices [][]float64) error {
return pj.TransFloat64Slices(DirectionInv, float64Slices)
}
// LPDist returns the geodesic distance between a and b in geodetic coordinates.
func (pj *PJ) LPDist(a, b Coord) float64 {
pj.context.Lock()
defer pj.context.Unlock()
return (float64)(C.proj_lp_dist(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
}
// LPZDist returns the geodesic distance between a and b in geodetic
// coordinates, taking height above the ellipsoid into account.
func (pj *PJ) LPZDist(a, b Coord) float64 {
pj.context.Lock()
defer pj.context.Unlock()
return (float64)(C.proj_lpz_dist(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
}
// Trans transforms a single Coord in place.
func (pj *PJ) Trans(direction Direction, coord Coord) (Coord, error) {
pj.context.Lock()
defer pj.context.Unlock()
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)
pjCoord := C.proj_trans(pj.cPJ, (C.PJ_DIRECTION)(direction), *(*C.PJ_COORD)(unsafe.Pointer(&coord)))
if errno := int(C.proj_errno(pj.cPJ)); errno != 0 {
return Coord{}, pj.context.newError(errno)
}
return *(*Coord)(unsafe.Pointer(&pjCoord)), nil
}
// TransArray transforms an array of Coords.
func (pj *PJ) TransArray(direction Direction, coords []Coord) error {
if len(coords) == 0 {
return nil
}
pj.context.Lock()
defer pj.context.Unlock()
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)
if errno := int(C.proj_trans_array(pj.cPJ, (C.PJ_DIRECTION)(direction), (C.size_t)(len(coords)), (*C.PJ_COORD)(unsafe.Pointer(&coords[0])))); errno != 0 {
return pj.context.newError(errno)
}
return nil
}
// TransBounds transforms bounds.
func (pj *PJ) TransBounds(direction Direction, bounds Bounds, densifyPoints int) (Bounds, error) {
pj.context.Lock()
defer pj.context.Unlock()
var transBounds Bounds
if C.proj_trans_bounds(pj.context.cPJContext, pj.cPJ, (C.PJ_DIRECTION)(direction),
(C.double)(bounds.XMin), (C.double)(bounds.YMin), (C.double)(bounds.XMax), (C.double)(bounds.YMax),
(*C.double)(&transBounds.XMin), (*C.double)(&transBounds.YMin), (*C.double)(&transBounds.XMax), (*C.double)(&transBounds.YMax),
C.int(densifyPoints)) == 0 {
return Bounds{}, pj.context.newError(int(C.proj_errno(pj.cPJ)))
}
return transBounds, nil
}
// TransFlatCoords transforms an array of flat coordinates.
func (pj *PJ) TransFlatCoords(direction Direction, flatCoords []float64, stride, zIndex, mIndex int) error {
if len(flatCoords) == 0 {
return nil
}
n := len(flatCoords) / stride
var x, y, z, m *float64
var sx, sy, sz, sm int
var nx, ny, nz, nm int
x = &flatCoords[0]
y = &flatCoords[1]
sx = 8 * stride
sy = 8 * stride
nx = n
ny = n
if zIndex != -1 {
z = &flatCoords[zIndex]
sz = 8 * stride
nz = n
}
if mIndex != -1 {
m = &flatCoords[mIndex]
sm = 8 * stride
nm = n
}
return pj.TransGeneric(direction, x, sx, nx, y, sy, ny, z, sz, nz, m, sm, nm)
}
// TransFloat64Slice transforms a []float64 in place.
func (pj *PJ) TransFloat64Slice(direction Direction, float64Slice []float64) ([]float64, error) {
var coord Coord
copy(coord[:], float64Slice)
transCoord, err := pj.Trans(direction, coord)
if err != nil {
return nil, err
}
copy(float64Slice, transCoord[:])
return float64Slice, nil
}
// TransFloat64Slices transforms float64Slices.
func (pj *PJ) TransFloat64Slices(direction Direction, float64Slices [][]float64) error {
coords := Float64SlicesToCoords(float64Slices)
if err := pj.TransArray(direction, coords); err != nil {
return err
}
for i, coord := range coords {
copy(float64Slices[i], coord[:])
}
return nil
}
// TransGeneric transforms a series of coordinates.
func (pj *PJ) TransGeneric(direction Direction, x *float64, sx, nx int, y *float64, sy, ny int, z *float64, sz, nz int, m *float64, sm, nm int) error {
pj.context.Lock()
defer pj.context.Unlock()
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)
if int(C.proj_trans_generic(pj.cPJ, (C.PJ_DIRECTION)(direction),
(*C.double)(x), C.size_t(sx), C.size_t(nx),
(*C.double)(y), C.size_t(sy), C.size_t(ny),
(*C.double)(z), C.size_t(sz), C.size_t(nz),
(*C.double)(m), C.size_t(sm), C.size_t(nm),
)) != max(nx, ny, nz, nm) {
return pj.context.newError(int(C.proj_errno(pj.cPJ)))
}
return nil
}