├── LICENSE.txt ├── README.md ├── go.mod └── proj ├── proj.c ├── proj.go ├── proj.h ├── proj_test.go ├── utm.go └── v5 ├── proj.c ├── proj.go ├── proj_go.h ├── proj_test.go └── utm.go /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015, Peter Kleiweg 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 16 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The [Go](http://golang.org/) package _proj_ provides a limited interface to the Cartographic Projections Library [PROJ](https://proj.org/). 2 | 3 | For PROJ version 5 and beyond, see also: https://github.com/pebbe/proj 4 | 5 | Keywords: cartography, cartographic projection 6 | 7 | ## Install 8 | 9 | For PROJ.4 and PROJ versions 5 and 6: 10 | 11 | go get github.com/pebbe/go-proj-4/proj 12 | 13 | For PROJ version 5 and beyond: 14 | 15 | go get github.com/pebbe/go-proj-4/proj/v5 16 | 17 | 18 | ## Docs 19 | 20 | * [package help](http://godoc.org/github.com/pebbe/go-proj-4/proj) 21 | * [package help v5](http://godoc.org/github.com/pebbe/go-proj-4/proj/v5) 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pebbe/go-proj-4 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /proj/proj.c: -------------------------------------------------------------------------------- 1 | #include "proj.h" 2 | 3 | char *get_err() 4 | { 5 | int 6 | *i; 7 | i = pj_get_errno_ref(); 8 | if (*i) 9 | return pj_strerrno(*i); 10 | else 11 | return NULL; 12 | } 13 | 14 | char *fwd(projPJ pj, double *x, double *y) { 15 | projUV 16 | p; 17 | 18 | p.u = *x * DEG_TO_RAD; 19 | p.v = *y * DEG_TO_RAD; 20 | p = pj_fwd(p, pj); 21 | 22 | *x = p.u; 23 | *y = p.v; 24 | 25 | return get_err(); 26 | } 27 | 28 | char *inv(projPJ pj, double *x, double *y) { 29 | projUV 30 | p; 31 | 32 | p.u = *x; 33 | p.v = *y; 34 | p = pj_inv(p, pj); 35 | 36 | *x = p.u / DEG_TO_RAD; 37 | *y = p.v / DEG_TO_RAD; 38 | 39 | return get_err(); 40 | } 41 | 42 | char *transform(projPJ srcdefn, projPJ dstdefn, long point_count, double *x, double *y, double *z) { 43 | int 44 | err; 45 | err = pj_transform(srcdefn, dstdefn, point_count, 1, x, y, z); 46 | return err ? pj_strerrno(err) : NULL; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /proj/proj.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package proj provides an interface to the Cartographic Projections Library PROJ.4 [cartography]. 3 | 4 | See: https://proj.org 5 | 6 | This version works with the old PROJ.4 library as well as the newer PROJ library up to and including version 6. 7 | 8 | Example usage: 9 | 10 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 11 | defer merc.Close() // if omitted, this will be called on garbage collection 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | 16 | ll, err := proj.NewProj("+proj=latlong") 17 | defer ll.Close() 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25)) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | fmt.Printf("%.2f %.2f", x, y) // should print: -1495284.21 1920596.79 27 | */ 28 | package proj 29 | 30 | /* 31 | #cgo darwin pkg-config: proj 32 | #cgo !darwin LDFLAGS: -lproj 33 | #include "proj.h" 34 | */ 35 | import "C" 36 | 37 | import ( 38 | "errors" 39 | "math" 40 | "runtime" 41 | "sync" 42 | "unsafe" 43 | ) 44 | 45 | var ( 46 | mu sync.Mutex 47 | ) 48 | 49 | type Proj struct { 50 | pj C.projPJ 51 | opened bool 52 | } 53 | 54 | func NewProj(definition string) (*Proj, error) { 55 | cs := C.CString(definition) 56 | defer C.free(unsafe.Pointer(cs)) 57 | proj := &Proj{opened: false} 58 | 59 | mu.Lock() 60 | defer mu.Unlock() 61 | 62 | proj.pj = C.pj_init_plus(cs) 63 | 64 | if proj.pj == nil { 65 | return proj, errors.New(C.GoString(C.get_err())) 66 | } 67 | 68 | proj.opened = true 69 | runtime.SetFinalizer(proj, (*Proj).Close) 70 | 71 | return proj, nil 72 | } 73 | 74 | func (p *Proj) Close() { 75 | if p.opened { 76 | C.pj_free(p.pj) 77 | p.opened = false 78 | } 79 | } 80 | 81 | func transform(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) { 82 | if !(srcpj.opened && dstpj.opened) { 83 | return []float64{}, []float64{}, []float64{}, errors.New("projection is closed") 84 | } 85 | 86 | var x1, y1, z1 []C.double 87 | 88 | ln := len(x) 89 | if len(y) < ln { 90 | ln = len(y) 91 | } 92 | if z != nil && len(z) < ln { 93 | ln = len(z) 94 | } 95 | 96 | if ln == 0 { 97 | return []float64{}, []float64{}, []float64{}, nil 98 | } 99 | 100 | x1 = make([]C.double, ln) 101 | y1 = make([]C.double, ln) 102 | if z != nil { 103 | z1 = make([]C.double, ln) 104 | } 105 | for i := 0; i < ln; i++ { 106 | x1[i] = C.double(x[i]) 107 | y1[i] = C.double(y[i]) 108 | if z != nil { 109 | z1[i] = C.double(z[i]) 110 | } 111 | } 112 | 113 | var e *C.char 114 | if z != nil { 115 | e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], &z1[0]) 116 | } else { 117 | e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], nil) 118 | } 119 | 120 | if e != nil { 121 | return []float64{}, []float64{}, []float64{}, errors.New(C.GoString(e)) 122 | } 123 | 124 | var x2, y2, z2 []float64 125 | x2 = make([]float64, ln) 126 | y2 = make([]float64, ln) 127 | if z != nil { 128 | z2 = make([]float64, ln) 129 | } 130 | for i := 0; i < ln; i++ { 131 | x2[i] = float64(x1[i]) 132 | y2[i] = float64(y1[i]) 133 | if z != nil { 134 | z2[i] = float64(z1[i]) 135 | } 136 | } 137 | return x2, y2, z2, nil 138 | } 139 | 140 | func Transform2(srcpj, dstpj *Proj, x, y float64) (float64, float64, error) { 141 | xx, yy, _, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, nil) 142 | if err != nil { 143 | return math.NaN(), math.NaN(), err 144 | } 145 | return xx[0], yy[0], err 146 | } 147 | 148 | func Transform3(srcpj, dstpj *Proj, x, y, z float64) (float64, float64, float64, error) { 149 | xx, yy, zz, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, []float64{z}) 150 | if err != nil { 151 | return math.NaN(), math.NaN(), math.NaN(), err 152 | } 153 | return xx[0], yy[0], zz[0], err 154 | } 155 | 156 | func Transform2lst(srcpj, dstpj *Proj, x, y []float64) ([]float64, []float64, error) { 157 | xx, yy, _, err := transform(srcpj, dstpj, x, y, nil) 158 | return xx, yy, err 159 | } 160 | 161 | func Transform3lst(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) { 162 | return transform(srcpj, dstpj, x, y, z) 163 | } 164 | 165 | // Longitude and latitude in degrees 166 | func Fwd(proj *Proj, long, lat float64) (x, y float64, err error) { 167 | if !proj.opened { 168 | return math.NaN(), math.NaN(), errors.New("projection is closed") 169 | } 170 | x1 := C.double(long) 171 | y1 := C.double(lat) 172 | e := C.fwd(proj.pj, &x1, &y1) 173 | if e != nil { 174 | return math.NaN(), math.NaN(), errors.New(C.GoString(e)) 175 | } 176 | return float64(x1), float64(y1), nil 177 | } 178 | 179 | // Longitude and latitude in degrees 180 | func Inv(proj *Proj, x, y float64) (long, lat float64, err error) { 181 | if !proj.opened { 182 | return math.NaN(), math.NaN(), errors.New("projection is closed") 183 | } 184 | x2 := C.double(x) 185 | y2 := C.double(y) 186 | e := C.inv(proj.pj, &x2, &y2) 187 | if e != nil { 188 | return math.NaN(), math.NaN(), errors.New(C.GoString(e)) 189 | } 190 | return float64(x2), float64(y2), nil 191 | } 192 | 193 | func DegToRad(deg float64) float64 { 194 | return deg / 180.0 * math.Pi 195 | } 196 | 197 | func RadToDeg(rad float64) float64 { 198 | return rad / math.Pi * 180.0 199 | } 200 | -------------------------------------------------------------------------------- /proj/proj.h: -------------------------------------------------------------------------------- 1 | #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 2 | #include 3 | 4 | char *transform(projPJ srcdefn, projPJ dstdefn, long point_count, double *x, double *y, double *z); 5 | char *fwd(projPJ src, double *lng, double *lat); 6 | char *inv(projPJ dst, double *lng, double *lat); 7 | char *get_err(void); 8 | -------------------------------------------------------------------------------- /proj/proj_test.go: -------------------------------------------------------------------------------- 1 | package proj_test 2 | 3 | import ( 4 | "github.com/pebbe/go-proj-4/proj" 5 | 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestLatlongToMerc(t *testing.T) { 11 | ll, err := proj.NewProj("+proj=latlong +datum=WGS84") 12 | defer ll.Close() 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | 17 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 18 | defer merc.Close() 19 | if err != nil { 20 | t.Error(err) 21 | } 22 | 23 | x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25)) 24 | if err != nil { 25 | t.Error(err) 26 | } else { 27 | s := fmt.Sprintf("%.2f %.2f", x, y) 28 | s1 := "-1495284.21 1920596.79" 29 | if s != s1 { 30 | t.Errorf("LatlongToMerc = %v, want %v", s, s1) 31 | } 32 | } 33 | 34 | x1 := []float64{-16, -10, 0, 30.4} 35 | y1 := []float64{20.25, 25, 0, 40.8} 36 | for i := range x1 { 37 | x1[i] = proj.DegToRad(x1[i]) 38 | y1[i] = proj.DegToRad(y1[i]) 39 | } 40 | x2, y2, err := proj.Transform2lst(ll, merc, x1, y1) 41 | if err != nil { 42 | t.Error(err) 43 | } else { 44 | s := fmt.Sprintf("[%.2f %.2f] [%.2f %.2f] [%.2f %.2f] [%.2f %.2f]", x2[0], y2[0], x2[1], y2[1], x2[2], y2[2], x2[3], y2[3]) 45 | s1 := "[-1495284.21 1920596.79] [-934552.63 2398930.20] [0.00 0.00] [2841040.00 4159542.20]" 46 | if s != s1 { 47 | t.Errorf("LatlongToMerc = %v, want %v", s, s1) 48 | } 49 | } 50 | } 51 | 52 | func TestInvalidErrorProblem(t *testing.T) { 53 | ll, err := proj.NewProj("+proj=latlong +datum=WGS84") 54 | defer ll.Close() 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | 59 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 60 | defer merc.Close() 61 | if err != nil { 62 | t.Error(err) 63 | } 64 | 65 | _, _, err = proj.Transform2(ll, merc, proj.DegToRad(3000), proj.DegToRad(500)) 66 | if err == nil { 67 | t.Error("err should not be nil") 68 | } 69 | 70 | // Try create a new projection after an error 71 | merc2, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 72 | defer merc2.Close() 73 | if err != nil { 74 | t.Error(err) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /proj/utm.go: -------------------------------------------------------------------------------- 1 | package proj 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Get UTM zone for longitude and latitude in degrees. 8 | // 9 | // Reference: 10 | // UTM Grid Zones of the World compiled by Alan Morton 11 | // http://www.dmap.co.uk/utmworld.htm 12 | func UTMzone(lng, lat float64) (xzone int, yzone string, err error) { 13 | 14 | if lat < -80 || lat > 84 { 15 | err = errors.New("Arctic and antarctic region are not in UTM") 16 | return 17 | } 18 | 19 | for lng < -180 { 20 | lng += 360 21 | } 22 | for lng > 180 { 23 | lng -= 360 24 | } 25 | 26 | xzone = 1 + int((lng + 180) / 6) 27 | if lat > 72 && lng > 0 && lng < 42 { 28 | if lng < 9 { 29 | xzone = 31 30 | } else if lng < 21 { 31 | xzone = 33 32 | } else if lng < 33 { 33 | xzone = 35 34 | } else { 35 | xzone = 37 36 | } 37 | } 38 | if lat > 56 && lat < 64 && lng > 3 && lng < 12 { 39 | xzone = 32 40 | } 41 | 42 | yzone = string("CDEFGHJKLMNPQRSTUVWXX"[int((lat + 80) / 8)]) 43 | 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /proj/v5/proj.c: -------------------------------------------------------------------------------- 1 | #include "proj_go.h" 2 | 3 | const char *get_err() { 4 | int 5 | i; 6 | i = proj_errno(0); 7 | if (i) 8 | return proj_errno_string(i); 9 | else 10 | return NULL; 11 | } 12 | 13 | const char *get_proj_err(PJ *pj) { 14 | int 15 | i; 16 | i = proj_errno(pj); 17 | if (i) 18 | return proj_errno_string(i); 19 | else 20 | return NULL; 21 | } 22 | 23 | PJ *create(const char *definition) { 24 | return proj_create(0, definition); 25 | } 26 | 27 | const char *fwd(PJ *pj, double *x, double *y) { 28 | PJ_COORD 29 | in, 30 | out; 31 | 32 | in.uv.u = *x * DEG_TO_RAD; 33 | in.uv.v = *y * DEG_TO_RAD; 34 | 35 | out = proj_trans(pj, PJ_FWD, in); 36 | 37 | *x = out.uv.u; 38 | *y = out.uv.v; 39 | 40 | return get_proj_err(pj); 41 | } 42 | 43 | const char *inv(PJ *pj, double *x, double *y) { 44 | PJ_COORD 45 | in, 46 | out; 47 | 48 | in.uv.u = *x; 49 | in.uv.v = *y; 50 | 51 | out = proj_trans(pj, PJ_INV, in); 52 | 53 | *x = out.uv.u / DEG_TO_RAD; 54 | *y = out.uv.v / DEG_TO_RAD; 55 | 56 | return get_proj_err(pj); 57 | } 58 | 59 | const char *transform(PJ *srcdefn, PJ *dstdefn, long point_count, double *x, double *y, double *z) { 60 | size_t 61 | err; 62 | long 63 | i; 64 | PJ_COORD 65 | *coord; 66 | 67 | coord = (PJ_COORD *) malloc(point_count * sizeof(PJ_COORD)); 68 | if (!coord) 69 | return "allocation failed"; 70 | for (i = 0; i < point_count; i++) { 71 | coord[i].xyz.x = x[i]; 72 | coord[i].xyz.y = y[i]; 73 | if (z) 74 | coord[i].xyz.z = z[i]; 75 | } 76 | 77 | err = proj_trans_array(srcdefn, PJ_INV, point_count, coord); 78 | if (!err) 79 | err = proj_trans_array(dstdefn, PJ_FWD, point_count, coord); 80 | if (err) { 81 | free(coord); 82 | return proj_errno_string(err); 83 | } 84 | 85 | for (i = 0; i < point_count; i++) { 86 | x[i] = coord[i].xyz.x; 87 | y[i] = coord[i].xyz.y; 88 | if (z) 89 | z[i] = coord[i].xyz.z; 90 | } 91 | 92 | free(coord); 93 | return NULL; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /proj/v5/proj.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package proj provides an interface to what once was the Cartographic Projections Library PROJ.4 [cartography]. 3 | 4 | See: https://proj.org 5 | 6 | This version works with the newer PROJ library, starting with version 5. 7 | 8 | Example usage: 9 | 10 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 11 | defer merc.Close() // if omitted, this will be called on garbage collection 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | 16 | ll, err := proj.NewProj("+proj=latlong") 17 | defer ll.Close() 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25)) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | fmt.Printf("%.2f %.2f", x, y) // should print: -1495284.21 1920596.79 27 | */ 28 | package proj 29 | 30 | /* 31 | #cgo darwin pkg-config: proj 32 | #cgo !darwin LDFLAGS: -lproj 33 | #include "proj_go.h" 34 | */ 35 | import "C" 36 | 37 | import ( 38 | "errors" 39 | "math" 40 | "runtime" 41 | "sync" 42 | "unsafe" 43 | ) 44 | 45 | var ( 46 | mu sync.Mutex 47 | ) 48 | 49 | type Proj struct { 50 | pj *C.PJ 51 | opened bool 52 | } 53 | 54 | func NewProj(definition string) (*Proj, error) { 55 | cs := C.CString(definition) 56 | defer C.free(unsafe.Pointer(cs)) 57 | proj := &Proj{opened: false} 58 | 59 | mu.Lock() 60 | defer mu.Unlock() 61 | 62 | proj.pj = C.create(cs) 63 | 64 | if proj.pj == nil { 65 | return proj, errors.New(C.GoString(C.get_err())) 66 | } 67 | 68 | proj.opened = true 69 | runtime.SetFinalizer(proj, (*Proj).Close) 70 | 71 | return proj, nil 72 | } 73 | 74 | func (p *Proj) Close() { 75 | if p.opened { 76 | C.proj_destroy(p.pj) 77 | p.opened = false 78 | } 79 | } 80 | 81 | func transform(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) { 82 | if !(srcpj.opened && dstpj.opened) { 83 | return []float64{}, []float64{}, []float64{}, errors.New("projection is closed") 84 | } 85 | 86 | var x1, y1, z1 []C.double 87 | 88 | ln := len(x) 89 | if len(y) < ln { 90 | ln = len(y) 91 | } 92 | if z != nil && len(z) < ln { 93 | ln = len(z) 94 | } 95 | 96 | if ln == 0 { 97 | return []float64{}, []float64{}, []float64{}, nil 98 | } 99 | 100 | x1 = make([]C.double, ln) 101 | y1 = make([]C.double, ln) 102 | if z != nil { 103 | z1 = make([]C.double, ln) 104 | } 105 | for i := 0; i < ln; i++ { 106 | x1[i] = C.double(x[i]) 107 | y1[i] = C.double(y[i]) 108 | if z != nil { 109 | z1[i] = C.double(z[i]) 110 | } 111 | } 112 | 113 | var e *C.char 114 | if z != nil { 115 | e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], &z1[0]) 116 | } else { 117 | e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], nil) 118 | } 119 | 120 | if e != nil { 121 | return []float64{}, []float64{}, []float64{}, errors.New(C.GoString(e)) 122 | } 123 | 124 | var x2, y2, z2 []float64 125 | x2 = make([]float64, ln) 126 | y2 = make([]float64, ln) 127 | if z != nil { 128 | z2 = make([]float64, ln) 129 | } 130 | for i := 0; i < ln; i++ { 131 | x2[i] = float64(x1[i]) 132 | y2[i] = float64(y1[i]) 133 | if z != nil { 134 | z2[i] = float64(z1[i]) 135 | } 136 | } 137 | return x2, y2, z2, nil 138 | } 139 | 140 | func Transform2(srcpj, dstpj *Proj, x, y float64) (float64, float64, error) { 141 | xx, yy, _, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, nil) 142 | if err != nil { 143 | return math.NaN(), math.NaN(), err 144 | } 145 | return xx[0], yy[0], err 146 | } 147 | 148 | func Transform3(srcpj, dstpj *Proj, x, y, z float64) (float64, float64, float64, error) { 149 | xx, yy, zz, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, []float64{z}) 150 | if err != nil { 151 | return math.NaN(), math.NaN(), math.NaN(), err 152 | } 153 | return xx[0], yy[0], zz[0], err 154 | } 155 | 156 | func Transform2lst(srcpj, dstpj *Proj, x, y []float64) ([]float64, []float64, error) { 157 | xx, yy, _, err := transform(srcpj, dstpj, x, y, nil) 158 | return xx, yy, err 159 | } 160 | 161 | func Transform3lst(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) { 162 | return transform(srcpj, dstpj, x, y, z) 163 | } 164 | 165 | // Longitude and latitude in degrees 166 | func Fwd(proj *Proj, long, lat float64) (x, y float64, err error) { 167 | if !proj.opened { 168 | return math.NaN(), math.NaN(), errors.New("projection is closed") 169 | } 170 | x1 := C.double(long) 171 | y1 := C.double(lat) 172 | e := C.fwd(proj.pj, &x1, &y1) 173 | if e != nil { 174 | return math.NaN(), math.NaN(), errors.New(C.GoString(e)) 175 | } 176 | return float64(x1), float64(y1), nil 177 | } 178 | 179 | // Longitude and latitude in degrees 180 | func Inv(proj *Proj, x, y float64) (long, lat float64, err error) { 181 | if !proj.opened { 182 | return math.NaN(), math.NaN(), errors.New("projection is closed") 183 | } 184 | x2 := C.double(x) 185 | y2 := C.double(y) 186 | e := C.inv(proj.pj, &x2, &y2) 187 | if e != nil { 188 | return math.NaN(), math.NaN(), errors.New(C.GoString(e)) 189 | } 190 | return float64(x2), float64(y2), nil 191 | } 192 | 193 | func DegToRad(deg float64) float64 { 194 | return deg / 180.0 * math.Pi 195 | } 196 | 197 | func RadToDeg(rad float64) float64 { 198 | return rad / math.Pi * 180.0 199 | } 200 | -------------------------------------------------------------------------------- /proj/v5/proj_go.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define RAD_TO_DEG 57.295779513082321 5 | #define DEG_TO_RAD .017453292519943296 6 | 7 | PJ *create(const char *definition); 8 | const char *transform(PJ *srcdefn, PJ *dstdefn, long point_count, double *x, double *y, double *z); 9 | const char *fwd(PJ *src, double *lng, double *lat); 10 | const char *inv(PJ *dst, double *lng, double *lat); 11 | const char *get_err(void); 12 | const char *get_proj_err(PJ *pj); 13 | -------------------------------------------------------------------------------- /proj/v5/proj_test.go: -------------------------------------------------------------------------------- 1 | package proj_test 2 | 3 | import ( 4 | "github.com/pebbe/go-proj-4/proj/v5" 5 | 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestLatlongToMerc(t *testing.T) { 11 | ll, err := proj.NewProj("+proj=latlong +datum=WGS84") 12 | defer ll.Close() 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | 17 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 18 | defer merc.Close() 19 | if err != nil { 20 | t.Error(err) 21 | } 22 | 23 | x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25)) 24 | if err != nil { 25 | t.Error(err) 26 | } else { 27 | s := fmt.Sprintf("%.2f %.2f", x, y) 28 | s1 := "-1495284.21 1920596.79" 29 | if s != s1 { 30 | t.Errorf("LatlongToMerc = %v, want %v", s, s1) 31 | } 32 | } 33 | 34 | x1 := []float64{-16, -10, 0, 30.4} 35 | y1 := []float64{20.25, 25, 0, 40.8} 36 | for i := range x1 { 37 | x1[i] = proj.DegToRad(x1[i]) 38 | y1[i] = proj.DegToRad(y1[i]) 39 | } 40 | x2, y2, err := proj.Transform2lst(ll, merc, x1, y1) 41 | if err != nil { 42 | t.Error(err) 43 | } else { 44 | s := fmt.Sprintf("[%.2f %.2f] [%.2f %.2f] [%.2f %.2f] [%.2f %.2f]", x2[0], y2[0], x2[1], y2[1], x2[2], y2[2], x2[3], y2[3]) 45 | s1 := "[-1495284.21 1920596.79] [-934552.63 2398930.20] [0.00 0.00] [2841040.00 4159542.20]" 46 | if s != s1 { 47 | t.Errorf("LatlongToMerc = %v, want %v", s, s1) 48 | } 49 | } 50 | } 51 | 52 | func TestInvalidErrorProblem(t *testing.T) { 53 | ll, err := proj.NewProj("+proj=latlong +datum=WGS84") 54 | defer ll.Close() 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | 59 | merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 60 | defer merc.Close() 61 | if err != nil { 62 | t.Error(err) 63 | } 64 | 65 | _, _, err = proj.Transform2(ll, merc, proj.DegToRad(3000), proj.DegToRad(500)) 66 | if err == nil { 67 | t.Error("err should not be nil") 68 | } 69 | 70 | // Try create a new projection after an error 71 | merc2, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33") 72 | defer merc2.Close() 73 | if err != nil { 74 | t.Error(err) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /proj/v5/utm.go: -------------------------------------------------------------------------------- 1 | package proj 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Get UTM zone for longitude and latitude in degrees. 8 | // 9 | // Reference: 10 | // UTM Grid Zones of the World compiled by Alan Morton 11 | // http://www.dmap.co.uk/utmworld.htm 12 | func UTMzone(lng, lat float64) (xzone int, yzone string, err error) { 13 | 14 | if lat < -80 || lat > 84 { 15 | err = errors.New("Arctic and antarctic region are not in UTM") 16 | return 17 | } 18 | 19 | for lng < -180 { 20 | lng += 360 21 | } 22 | for lng > 180 { 23 | lng -= 360 24 | } 25 | 26 | xzone = 1 + int((lng + 180) / 6) 27 | if lat > 72 && lng > 0 && lng < 42 { 28 | if lng < 9 { 29 | xzone = 31 30 | } else if lng < 21 { 31 | xzone = 33 32 | } else if lng < 33 { 33 | xzone = 35 34 | } else { 35 | xzone = 37 36 | } 37 | } 38 | if lat > 56 && lat < 64 && lng > 3 && lng < 12 { 39 | xzone = 32 40 | } 41 | 42 | yzone = string("CDEFGHJKLMNPQRSTUVWXX"[int((lat + 80) / 8)]) 43 | 44 | return 45 | } 46 | --------------------------------------------------------------------------------