├── .gitignore ├── ci ├── install_go_bin.sh └── go_test_multi_package_coverprofile.sh ├── vendor ├── github.com │ ├── stretchr │ │ └── testify │ │ │ ├── assert │ │ │ ├── assertion_format.go.tmpl │ │ │ ├── assertion_forward.go.tmpl │ │ │ ├── errors.go │ │ │ ├── forward_assertions.go │ │ │ ├── yaml │ │ │ │ ├── yaml_fail.go │ │ │ │ ├── yaml_custom.go │ │ │ │ └── yaml_default.go │ │ │ ├── doc.go │ │ │ ├── assertion_order.go │ │ │ └── http_assertions.go │ │ │ └── LICENSE │ ├── davecgh │ │ └── go-spew │ │ │ ├── LICENSE │ │ │ └── spew │ │ │ ├── bypasssafe.go │ │ │ ├── bypass.go │ │ │ └── spew.go │ └── pmezard │ │ └── go-difflib │ │ └── LICENSE ├── modules.txt └── gopkg.in │ └── yaml.v3 │ ├── NOTICE │ ├── writerc.go │ ├── LICENSE │ ├── sorter.go │ ├── README.md │ └── yamlprivateh.go ├── CONTRIBUTORS.md ├── go.mod ├── core ├── Meridian_test.go ├── Meridian.go ├── OperationDescription_test.go ├── Ellipsoid_test.go ├── Operation_test.go ├── Coordinate_test.go ├── Operation.go ├── System_test.go ├── Coordinate.go ├── OperationDescription.go └── full_test.go ├── support ├── Msfn.go ├── Adjlon_test.go ├── UnitsTable_test.go ├── ProjectionsTable_test.go ├── Tsfn.go ├── constants.go ├── Aasincos_test.go ├── Adjlon.go ├── MeridiansTable_test.go ├── Qsfn.go ├── ParseDate_test.go ├── EllipsoidsTable_test.go ├── DatumsTable_test.go ├── Phi2.go ├── ParseDate.go ├── MeridiansTable.go ├── Aasincos.go ├── UnitsTable.go ├── Mlfn.go ├── DatumsTable.go ├── DMS.go ├── DMS_test.go ├── ProjString_test.go ├── EllipsoidsTable.go └── ProjString.go ├── operations ├── common.go ├── August.go ├── Eqc.go ├── Airy.go ├── Merc.go ├── operations_test.go └── Aea.go ├── gie ├── gie_data │ ├── unitconvert.gie │ ├── deformation.gie │ ├── axisswap.gie │ ├── GDA.gie │ └── ellipsoid.gie ├── Gie_test.go └── Gie.go ├── go.sum ├── .travis.yml ├── merror ├── Error_test.go ├── error_strings.go └── Error.go ├── cmd └── proj │ ├── proj_test.go │ └── proj.go ├── mlog ├── Log_test.go └── Log.go ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── Convert_test.go ├── README.md └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | .cover 2 | .idea 3 | 4 | -------------------------------------------------------------------------------- /ci/install_go_bin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | go_install(){ 5 | if [ -z ${PROJECT_DIR+x} ]; then 6 | PROJECT_DIR=`dirname $0`/.. 7 | fi 8 | OLDPWD=`pwd` 9 | cd $PROJECT_DIR/vendor/"$@" 10 | go install . 11 | cd $OLDPWD 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Thank you to all the people who have [contributed](https://github.com/go-spatial/proj/graphs/contributors) to this project. 2 | 3 | Thank you also to everyone who has submitted an [issue or feature](https://github.com/go-spatial/proj/issues?utf8=%E2%9C%93&q=is%3Aissue) request. 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-spatial/proj 2 | 3 | go 1.23.4 4 | 5 | require github.com/stretchr/testify v1.10.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/davecgh/go-spew v1.1.1 2 | ## explicit 3 | github.com/davecgh/go-spew/spew 4 | # github.com/pmezard/go-difflib v1.0.0 5 | ## explicit 6 | github.com/pmezard/go-difflib/difflib 7 | # github.com/stretchr/testify v1.10.0 8 | ## explicit; go 1.17 9 | github.com/stretchr/testify/assert 10 | github.com/stretchr/testify/assert/yaml 11 | # gopkg.in/yaml.v3 v3.0.1 12 | ## explicit 13 | gopkg.in/yaml.v3 14 | -------------------------------------------------------------------------------- /core/Meridian_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "testing" 12 | ) 13 | 14 | func TestMeridian(t *testing.T) { 15 | // TODO 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /support/Msfn.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import "math" 11 | 12 | // Msfn is to "determine constant small m" 13 | func Msfn(sinphi, cosphi, es float64) float64 { 14 | return (cosphi / math.Sqrt(1.-es*sinphi*sinphi)) 15 | } 16 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 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 | -------------------------------------------------------------------------------- /core/Meridian.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core 9 | 10 | // PrimeMeridian contains information about a prime meridian 11 | // 12 | // Someday, this will be a rich type with lots of methods and stuff. 13 | // 14 | // Today, it is not. 15 | type PrimeMeridian struct { 16 | ID string 17 | Definition string 18 | } 19 | -------------------------------------------------------------------------------- /operations/common.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations 9 | 10 | type mode int 11 | 12 | const ( 13 | modeNPole mode = 0 14 | modeSPole = 1 15 | modeEquit = 2 16 | modeObliq = 3 17 | ) 18 | 19 | const tol7 = 1.e-7 20 | const tol10 = 1.0e-10 21 | 22 | const eps7 = 1.0e-7 23 | const eps10 = 1.e-10 24 | -------------------------------------------------------------------------------- /support/Adjlon_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "math" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/support" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestAdjlon(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | assert.InDelta(math.Pi*0.5, support.Adjlon(math.Pi*2.5), 1.0e-8) 22 | } 23 | -------------------------------------------------------------------------------- /core/OperationDescription_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/core" 14 | ) 15 | 16 | func TestOperationDescription(t *testing.T) { 17 | 18 | opDesc := core.OperationDescriptionTable["utm"] 19 | if opDesc == nil { 20 | t.Errorf("operaton description table for utm is nil") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /support/UnitsTable_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestUnitsTable(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.True(len(support.UnitsTable) > 15) 21 | for key, value := range support.UnitsTable { 22 | assert.Equal(key, value.ID) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default 2 | // +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default 3 | 4 | // Package yaml is an implementation of YAML functions that always fail. 5 | // 6 | // This implementation can be used at build time to replace the default implementation 7 | // to avoid linking with [gopkg.in/yaml.v3]: 8 | // 9 | // go test -tags testify_yaml_fail 10 | package yaml 11 | 12 | import "errors" 13 | 14 | var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") 15 | 16 | func Unmarshal([]byte, interface{}) error { 17 | return errNotImplemented 18 | } 19 | -------------------------------------------------------------------------------- /support/ProjectionsTable_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestProjectionsTable(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.True(len(support.ProjectionsTable) > 150) 21 | for key, value := range support.ProjectionsTable { 22 | assert.Equal(key, value.ID) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /support/Tsfn.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | // Tsfn is to "determine small t" 15 | func Tsfn(phi, sinphi, e float64) float64 { 16 | sinphi *= e 17 | 18 | /* avoid zero division, fail gracefully */ 19 | denominator := 1.0 + sinphi 20 | if denominator == 0.0 { 21 | return math.MaxFloat64 22 | } 23 | 24 | return (math.Tan(.5*(PiOverTwo-phi)) / 25 | math.Pow((1.-sinphi)/(denominator), .5*e)) 26 | } 27 | -------------------------------------------------------------------------------- /support/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import "math" 11 | 12 | // some more useful math constants and aliases */ 13 | const ( 14 | Pi = math.Pi 15 | PiOverTwo = Pi / 2 16 | PiOverFour = Pi / 4 17 | TwoOverPi = 2 / Pi 18 | PiHalfPi = 1.5 * Pi 19 | TwoPi = 2 * Pi 20 | TwoPiHalfPi = 2.5 * Pi 21 | ) 22 | 23 | // DegToRad is the multiplication factor to convert degrees to radians 24 | const DegToRad = 0.017453292519943296 25 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /support/Aasincos_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "math" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/support" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestAasincos(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | assert.InDelta(math.Asin(0.5), support.Aasin(0.5), 1.0e-8) 22 | assert.InDelta(math.Asin(0.5), support.Aasin(0.5), 1.0e-8) 23 | assert.InDelta(math.Sqrt(0.5), support.Asqrt(0.5), 1.0e-8) 24 | assert.InDelta(math.Atan2(0.5, 0.5), support.Aatan2(0.5, 0.5), 1.0e-8) 25 | } 26 | -------------------------------------------------------------------------------- /support/Adjlon.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | // Adjlon reduces argument to range +/- PI 15 | func Adjlon(lon float64) float64 { 16 | /* Let lon slightly overshoot, to avoid spurious sign switching at the date line */ 17 | if math.Abs(lon) < Pi+1e-12 { 18 | return lon 19 | } 20 | 21 | /* adjust to 0..2pi range */ 22 | lon += Pi 23 | 24 | /* remove integral # of 'revolutions'*/ 25 | lon -= TwoPi * math.Floor(lon/TwoPi) 26 | 27 | /* adjust back to -pi..pi range */ 28 | lon -= Pi 29 | 30 | return lon 31 | } 32 | -------------------------------------------------------------------------------- /gie/gie_data/unitconvert.gie: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | Tests for the unitconvert operation 3 | ------------------------------------------------------------------------------- 4 | 5 | 6 | 7 | operation proj=unitconvert xy_in=m xy_out=dm z_in=cm z_out=mm 8 | tolerance 0.1 9 | accept 55.25 23.23 45.5 10 | expect 552.5 232.3 455.0 11 | 12 | operation proj=unitconvert +xy_in=m +xy_out=m +z_in=m +z_out=m 13 | tolerance 0.1 14 | accept 12.3 45.6 7.89 15 | expect 12.3 45.6 7.89 16 | 17 | operation proj=unitconvert xy_in=dm xy_out=dm 18 | tolerance 0.1 19 | accept 1 1 1 1 20 | expect 1 1 1 1 21 | 22 | 23 | operation proj=unitconvert xy_in=2.0 xy_out=4.0 24 | tolerance 0.1 25 | accept 1 1 1 1 26 | expect 0.5 0.5 1 1 27 | 28 | 29 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /support/MeridiansTable_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestMeridiansTable(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.True(len(support.MeridiansTable) > 10) 21 | for key, value := range support.MeridiansTable { 22 | assert.Equal(key, value.ID) 23 | } 24 | 25 | assert.Equal("paris", support.MeridiansTable["paris"].ID) 26 | assert.Equal("2d20'14.025\"E", support.MeridiansTable["paris"].Definition) 27 | } 28 | -------------------------------------------------------------------------------- /support/Qsfn.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | const epsilon = 1.0e-7 15 | 16 | // Qsfn is ..? 17 | func Qsfn(sinphi, e, oneEs float64) float64 { 18 | var con, div1, div2 float64 19 | 20 | if e >= epsilon { 21 | con = e * sinphi 22 | div1 = 1.0 - con*con 23 | div2 = 1.0 + con 24 | 25 | /* avoid zero division, fail gracefully */ 26 | if div1 == 0.0 || div2 == 0.0 { 27 | return math.MaxFloat64 28 | } 29 | 30 | return (oneEs * (sinphi/div1 - (.5/e)*math.Log((1.-con)/div2))) 31 | } 32 | return (sinphi + sinphi) 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default 2 | // +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default 3 | 4 | // Package yaml is an implementation of YAML functions that calls a pluggable implementation. 5 | // 6 | // This implementation is selected with the testify_yaml_custom build tag. 7 | // 8 | // go test -tags testify_yaml_custom 9 | // 10 | // This implementation can be used at build time to replace the default implementation 11 | // to avoid linking with [gopkg.in/yaml.v3]. 12 | // 13 | // In your test package: 14 | // 15 | // import assertYaml "github.com/stretchr/testify/assert/yaml" 16 | // 17 | // func init() { 18 | // assertYaml.Unmarshal = func (in []byte, out interface{}) error { 19 | // // ... 20 | // return nil 21 | // } 22 | // } 23 | package yaml 24 | 25 | var Unmarshal func(in []byte, out interface{}) error 26 | -------------------------------------------------------------------------------- /core/Ellipsoid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/core" 15 | "github.com/go-spatial/proj/support" 16 | 17 | "github.com/stretchr/testify/assert" 18 | ) 19 | 20 | func TestEllipsoid(t *testing.T) { 21 | assert := assert.New(t) 22 | 23 | ps, err := support.NewProjString("+proj=utm +zone=32 +ellps=GRS80") 24 | assert.NoError(err) 25 | sys, _, err := core.NewSystem(ps) 26 | assert.NoError(err) 27 | 28 | e := sys.Ellipsoid 29 | 30 | assert.Equal("GRS80", e.ID) 31 | 32 | s := fmt.Sprintf("%s", e) 33 | assert.True(len(s) > 1) 34 | } 35 | -------------------------------------------------------------------------------- /core/Operation_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/core" 15 | "github.com/go-spatial/proj/support" 16 | 17 | "github.com/stretchr/testify/assert" 18 | ) 19 | 20 | func TestOperation(t *testing.T) { 21 | assert := assert.New(t) 22 | 23 | ps, err := support.NewProjString("+proj=utm +zone=32 +ellps=GRS80") 24 | assert.NoError(err) 25 | _, opx, err := core.NewSystem(ps) 26 | assert.NoError(err) 27 | 28 | s := fmt.Sprintf("%s", opx) 29 | assert.True(len(s) > 1) 30 | 31 | id := opx.GetDescription().ID 32 | assert.Equal("utm", id) 33 | } 34 | -------------------------------------------------------------------------------- /support/ParseDate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestParseDate(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.InDelta(0.0, support.ParseDate("yow"), 1.0e-8) 21 | assert.InDelta(1999.5, support.ParseDate("1999.50"), 1.0e-8) 22 | assert.InDelta(2000.0, support.ParseDate("1999.99999999"), 1.0e-8) 23 | assert.InDelta(1999.0+(12.0*31.0-1.0)/(12.0*31.0), support.ParseDate("1999-12-31"), 1.0e-8) 24 | assert.InDelta(1999.0+(6.0*31.0)/(12.0*31.0), support.ParseDate("1999-07-01"), 1.0e-8) 25 | } 26 | -------------------------------------------------------------------------------- /support/EllipsoidsTable_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestEllipsoidsTable(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.True(len(support.EllipsoidsTable) > 40) 21 | for key, value := range support.EllipsoidsTable { 22 | assert.Equal(key, value.ID) 23 | } 24 | 25 | assert.Equal("danish", support.EllipsoidsTable["danish"].ID) 26 | assert.Equal("a=6377019.2563", support.EllipsoidsTable["danish"].Major) 27 | assert.Equal("rf=300.0", support.EllipsoidsTable["danish"].Ell) 28 | assert.Equal("Andrae 1876 (Denmark, Iceland)", support.EllipsoidsTable["danish"].Name) 29 | } 30 | -------------------------------------------------------------------------------- /support/DatumsTable_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestDatumsTable(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | assert.True(len(support.DatumsTable) > 8) 21 | 22 | for key, value := range support.DatumsTable { 23 | assert.True(key == value.ID || key == value.EllipseID) 24 | } 25 | 26 | assert.Equal("NAD83", support.DatumsTable["GRS80"].ID) 27 | assert.Equal("GRS80", support.DatumsTable["GRS80"].EllipseID) 28 | assert.Equal("towgs84=0,0,0", support.DatumsTable["GRS80"].DefinitionString) 29 | assert.Equal("North_American_Datum_1983", support.DatumsTable["GRS80"].Comments) 30 | } 31 | -------------------------------------------------------------------------------- /support/Phi2.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/merror" 14 | ) 15 | 16 | const tol = 1.0e-10 17 | const nIter = 15 18 | 19 | // Phi2 is to "determine latitude angle phi-2" 20 | func Phi2(ts, e float64) (float64, error) { 21 | var eccnth, Phi, con float64 22 | var i int 23 | 24 | eccnth = .5 * e 25 | Phi = PiOverTwo - 2.*math.Atan(ts) 26 | i = nIter 27 | for { 28 | 29 | con = e * math.Sin(Phi) 30 | dphi := PiOverTwo - 2.*math.Atan(ts*math.Pow((1.-con)/(1.+con), eccnth)) - Phi 31 | Phi += dphi 32 | i-- 33 | if math.Abs(dphi) > tol && i != 0 { 34 | continue 35 | } 36 | break 37 | } 38 | if i <= 0 { 39 | return 0.0, merror.New(merror.Phi2) 40 | } 41 | return Phi, nil 42 | } 43 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /support/ParseDate.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import "strconv" 11 | 12 | // ParseDate turns a date into a floating point year value. Acceptable 13 | // values are "yyyy.fraction" and "yyyy-mm-dd". Anything else 14 | // returns 0.0. 15 | func ParseDate(s string) float64 { 16 | if len(s) == 10 && s[4] == '-' && s[7] == '-' { 17 | year, err := strconv.Atoi(s[0:4]) 18 | if err != nil { 19 | return 0.0 20 | } 21 | month, err := strconv.Atoi(s[5:7]) 22 | if err != nil { 23 | return 0.0 24 | } 25 | day, err := strconv.Atoi(s[8:10]) 26 | if err != nil { 27 | return 0.0 28 | } 29 | 30 | /* simplified calculation so we don't need to know all about months */ 31 | return float64(year) + float64((month-1)*31+(day-1))/372.0 32 | } 33 | 34 | // TODO: handle the locale (as per pj_strtod) 35 | f, err := strconv.ParseFloat(s, 64) 36 | if err != nil { 37 | f = 0.0 38 | } 39 | return f 40 | } 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | # Does nothing when building on go-spatial/proj, but makes builds work cleanly on forks. 3 | go_import_path: github.com/go-spatial/proj 4 | 5 | git: 6 | depth: 3 7 | 8 | matrix: 9 | include: 10 | # - go: "1.10.x" 11 | # env: CGO_ENABLED=0 12 | # - go: "1.9.x" 13 | # env: CGO_ENABLED=0 14 | # - go: "1.8.x" 15 | # env: CGO_ENABLED=0 16 | - go: "1.10.x" 17 | # - go: "1.9.x" 18 | # - go: "1.8.x" 19 | 20 | script: 21 | - bash ci/go_test_multi_package_coverprofile.sh --coveralls 22 | 23 | notifications: 24 | email: 25 | on_success: never 26 | slack: 27 | secure: TzRnZdJ1dhQJg9nby2oJ6qFj9Bl20VJ2aJXmXwADsj2ck/UGsFWpYNnqZVWvZomCZEJinFpA/h2TG1odEzg1BpCT0+rLIGQDAhIMVuici+nIs+DyPQmL2owG1LZ6/hzX09Y1mIZci0nmnI1wousfM8WDPRq5NSOsBeAMlixGR4g4lF8cp/R0B6CtcewtQx4RicdPJtopHDwXm5KI1/7euFcJwu15FWwWoUp6mOw+r44T1Md+qbqDKBbLSy+YvMkPcxI7HeECa7OP9OjkeUfU0BoMP6qqo9QTVX5v6lOHxA+FhueyyPrSTtNV377tilDy88kytqu6w8gcrxZOddj7nFbv6ErjfVaMfeepzk/HH2bzlLevvL+ukeJKrBNdmPuDRU3BqG5ygrdGAQUNpM7VSXLDcGrM2+ocVn/HO1NyXWwpejq7Gq0HWhNkYPHG5c7+khkQkRGJtg0wp9R3qXDAcRHLR+CHh89g5Blz4Wo6R/Q48MLJycbH/W56zsoe0xQ5Ww2eti16uSFE4kkiPt99uewWkPSfEZn4pyk+xCwDxgNDNu/BaV+Mjm4JTL1GjWalto+NLK7NsEg+6sEYok0CHkUb8MQV++r1KVfIaxC3zVuojIiOSB9/+JPtPd9w2iuVOlz0rrbXfKkkSf5wcmOguqk3aAfAgmcjAV9p1kI9pSc= 28 | -------------------------------------------------------------------------------- /core/Coordinate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/core" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestCoordinate(t *testing.T) { 18 | assert := assert.New(t) 19 | 20 | { 21 | any := &core.CoordAny{V: [4]float64{1.0, 2.0, 3.0, 4.0}} 22 | lp := any.ToLP() 23 | assert.Equal(1.0, lp.Lam) 24 | assert.Equal(2.0, lp.Phi) 25 | lp.Lam = 10.0 26 | lp.Phi = 20.0 27 | any.FromLP(lp) 28 | assert.Equal(10.0, any.V[0]) 29 | assert.Equal(20.0, any.V[1]) 30 | assert.Equal(3.0, any.V[2]) 31 | assert.Equal(4.0, any.V[3]) 32 | } 33 | { 34 | any := &core.CoordAny{V: [4]float64{1.0, 2.0, 3.0, 4.0}} 35 | xy := any.ToXY() 36 | assert.Equal(1.0, xy.X) 37 | assert.Equal(2.0, xy.Y) 38 | xy.X = 10.0 39 | xy.Y = 20.0 40 | any.FromXY(xy) 41 | assert.Equal(10.0, any.V[0]) 42 | assert.Equal(20.0, any.V[1]) 43 | assert.Equal(3.0, any.V[2]) 44 | assert.Equal(4.0, any.V[3]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /support/MeridiansTable.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | // MeridiansTableEntry holds a constant prime meridian item 11 | type MeridiansTableEntry struct { 12 | ID string 13 | Definition string 14 | } 15 | 16 | // MeridiansTable holds all the globally known (prime) meridians 17 | var MeridiansTable = map[string]MeridiansTableEntry{ 18 | "greenwich": {"greenwich", "0dE"}, 19 | "lisbon": {"lisbon", "9d07'54.862\"W"}, 20 | "paris": {"paris", "2d20'14.025\"E"}, 21 | "bogota": {"bogota", "74d04'51.3\"W"}, 22 | "madrid": {"madrid", "3d41'16.58\"W"}, 23 | "rome": {"rome", "12d27'8.4\"E"}, 24 | "bern": {"bern", "7d26'22.5\"E"}, 25 | "jakarta": {"jakarta", "106d48'27.79\"E"}, 26 | "ferro": {"ferro", "17d40'W"}, 27 | "brussels": {"brussels", "4d22'4.71\"E"}, 28 | "stockholm": {"stockholm", "18d3'29.8\"E"}, 29 | "athens": {"athens", "23d42'58.815\"E"}, 30 | "oslo": {"oslo", "10d43'22.5\"E"}, 31 | "copenhagen": {"copenhagen", "12d34'40.35\"E"}, 32 | } 33 | -------------------------------------------------------------------------------- /core/Operation.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core 9 | 10 | // OperationType is the enum for the different kinds of conversions and transforms 11 | // 12 | // This may turn out not to be as useful as it originally seemed. 13 | type OperationType int 14 | 15 | // The operation type 16 | const ( 17 | OperationTypeInvalid OperationType = iota 18 | OperationTypeConversion 19 | OperationTypeTransformation 20 | ) 21 | 22 | // IOperation is what all the operations need to support 23 | type IOperation interface { 24 | GetSystem() *System 25 | GetDescription() *OperationDescription 26 | } 27 | 28 | // Operation is the base class for all operations 29 | type Operation struct { 30 | System *System 31 | Description *OperationDescription 32 | } 33 | 34 | // GetSystem returns the System object the operation is associated with 35 | func (op *Operation) GetSystem() *System { 36 | return op.System 37 | } 38 | 39 | // GetDescription returns the OperationDescription of the operation 40 | func (op *Operation) GetDescription() *OperationDescription { 41 | return op.Description 42 | } 43 | -------------------------------------------------------------------------------- /gie/Gie_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package gie_test 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "testing" 14 | 15 | "github.com/go-spatial/proj/gie" 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | // must be run from the "./proj/gie" directory, so it can access the "gie_data" directory 20 | func TestGie(t *testing.T) { 21 | assert := assert.New(t) 22 | 23 | g, err := gie.NewGie("./gie_data") 24 | assert.NoError(err) 25 | 26 | err = g.Parse() 27 | assert.NoError(err) 28 | 29 | total := 0 30 | actual := 0 31 | passed := 0 32 | failed := 0 33 | 34 | for _, command := range g.Commands { 35 | total++ 36 | tag := fmt.Sprintf("%s:%d", command.File, command.Line) 37 | 38 | if g.IsSupported(command) { 39 | actual++ 40 | 41 | err = command.Execute() 42 | assert.NoError(err, tag) 43 | 44 | if err != nil { 45 | failed++ 46 | } else { 47 | passed++ 48 | } 49 | } 50 | } 51 | 52 | log.Printf("total: %d", total) 53 | log.Printf("actual: %d", actual) 54 | log.Printf("passed: %d", passed) 55 | log.Printf("failed: %d", failed) 56 | } 57 | -------------------------------------------------------------------------------- /merror/Error_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package merror_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/merror" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestError(t *testing.T) { 18 | showSource := merror.ShowSource 19 | merror.ShowSource = true 20 | defer func() { merror.ShowSource = showSource }() 21 | 22 | assert := assert.New(t) 23 | 24 | err1 := merror.New("errtest-%d", 1) 25 | assert.Error(err1) 26 | exp1 := "errtest-1 (from merror_test.TestError at Error_test.go:24)" 27 | assert.Equal(exp1, err1.Error()) 28 | 29 | err2 := merror.Wrap(err1, "errtest-%d", 2) 30 | assert.Error(err2) 31 | exp2 := "errtest-2 (from merror_test.TestError at Error_test.go:29)" 32 | exp2 += " // Inner: " + exp1 33 | assert.Equal(exp2, err2.Error()) 34 | 35 | err3 := merror.Wrap(err2) 36 | assert.Error(err3) 37 | exp3 := "wrapped error (from merror_test.TestError at Error_test.go:35)" 38 | exp3 += " // Inner: " + exp2 39 | assert.Equal(exp3, err3.Error()) 40 | 41 | err4 := merror.Pass(err2) 42 | assert.Error(err4) 43 | assert.Equal(exp2, err4.Error()) 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // # Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // 7 | // import ( 8 | // "testing" 9 | // "github.com/stretchr/testify/assert" 10 | // ) 11 | // 12 | // func TestSomething(t *testing.T) { 13 | // 14 | // var a string = "Hello" 15 | // var b string = "Hello" 16 | // 17 | // assert.Equal(t, a, b, "The two words should be the same.") 18 | // 19 | // } 20 | // 21 | // if you assert many times, use the format below: 22 | // 23 | // import ( 24 | // "testing" 25 | // "github.com/stretchr/testify/assert" 26 | // ) 27 | // 28 | // func TestSomething(t *testing.T) { 29 | // assert := assert.New(t) 30 | // 31 | // var a string = "Hello" 32 | // var b string = "Hello" 33 | // 34 | // assert.Equal(a, b, "The two words should be the same.") 35 | // } 36 | // 37 | // # Assertions 38 | // 39 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 40 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 41 | // testing framework. This allows the assertion funcs to write the failings and other details to 42 | // the correct place. 43 | // 44 | // Every assertion function also takes an optional string message as the final argument, 45 | // allowing custom error messages to be appended to the message the assertion method outputs. 46 | package assert 47 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 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 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /core/System_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/core" 14 | "github.com/go-spatial/proj/support" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestSystem(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | ps, err := support.NewProjString("+proj=utm +zone=32 +ellps=GRS80") 22 | assert.NoError(err) 23 | assert.NotNil(ps) 24 | 25 | sys, op, err := core.NewSystem(ps) 26 | assert.NoError(err) 27 | assert.NotNil(sys) 28 | assert.NotNil(op) 29 | 30 | assert.NotEqual("", sys.String()) 31 | } 32 | 33 | func TestProjStringValidation(t *testing.T) { 34 | assert := assert.New(t) 35 | 36 | _, err := support.NewProjString("") 37 | assert.NoError(err) 38 | 39 | ps, err := support.NewProjString(" +proj=P99 +k1=a +k2=b \t ") 40 | assert.NoError(err) 41 | assert.Equal(3, ps.Len()) 42 | 43 | err = core.ValidateProjStringContents(ps) 44 | assert.NoError(err) 45 | 46 | // only 1 "init" allowed 47 | { 48 | ps, err = support.NewProjString("init=foo proj=foo init=foo") 49 | assert.NoError(err) 50 | err = core.ValidateProjStringContents(ps) 51 | assert.Error(err) 52 | } 53 | 54 | // "proj" may not be empty 55 | { 56 | ps, err = support.NewProjString("proj") 57 | assert.NoError(err) 58 | err = core.ValidateProjStringContents(ps) 59 | assert.Error(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /operations/August.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/core" 14 | ) 15 | 16 | func init() { 17 | core.RegisterConvertLPToXY("august", 18 | "August Epicycloidal", 19 | "\n\tMisc Sph, no inv.", 20 | NewAugust, 21 | ) 22 | } 23 | 24 | // August implements core.IOperation and core.ConvertLPToXY 25 | type August struct { 26 | core.Operation 27 | } 28 | 29 | const m = 1.333333333333333 30 | 31 | // NewAugust returns a new August 32 | func NewAugust(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 33 | op := &August{} 34 | op.System = system 35 | 36 | PE := op.System.Ellipsoid 37 | PE.Es = 0.0 38 | 39 | return op, nil 40 | } 41 | 42 | // Forward goes forewards 43 | func (op *August) Forward(lp *core.CoordLP) (*core.CoordXY, error) { 44 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 45 | 46 | var t, c1, c, x1, x12, y1, y12 float64 47 | 48 | t = math.Tan(.5 * lp.Phi) 49 | c1 = math.Sqrt(1. - t*t) 50 | lp.Lam *= .5 51 | c = 1. + c1*math.Cos(lp.Lam) 52 | x1 = math.Sin(lp.Lam) * c1 / c 53 | y1 = t / c 54 | x12 = x1 * x1 55 | y12 = y1 * y1 56 | xy.X = m * x1 * (3. + x12 - 3.*y12) 57 | xy.Y = m * y1 * (3. + 3.*x12 - y12) 58 | 59 | return xy, nil 60 | } 61 | 62 | // Inverse is not allowed 63 | func (*August) Inverse(*core.CoordXY) (*core.CoordLP, error) { 64 | panic("no such conversion") 65 | } 66 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go: -------------------------------------------------------------------------------- 1 | //go:build !testify_yaml_fail && !testify_yaml_custom 2 | // +build !testify_yaml_fail,!testify_yaml_custom 3 | 4 | // Package yaml is just an indirection to handle YAML deserialization. 5 | // 6 | // This package is just an indirection that allows the builder to override the 7 | // indirection with an alternative implementation of this package that uses 8 | // another implementation of YAML deserialization. This allows to not either not 9 | // use YAML deserialization at all, or to use another implementation than 10 | // [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). 11 | // 12 | // Alternative implementations are selected using build tags: 13 | // 14 | // - testify_yaml_fail: [Unmarshal] always fails with an error 15 | // - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it 16 | // before calling any of [github.com/stretchr/testify/assert.YAMLEq] or 17 | // [github.com/stretchr/testify/assert.YAMLEqf]. 18 | // 19 | // Usage: 20 | // 21 | // go test -tags testify_yaml_fail 22 | // 23 | // You can check with "go list" which implementation is linked: 24 | // 25 | // go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 26 | // go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 27 | // go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 28 | // 29 | // [PR #1120]: https://github.com/stretchr/testify/pull/1120 30 | package yaml 31 | 32 | import goyaml "gopkg.in/yaml.v3" 33 | 34 | // Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. 35 | func Unmarshal(in []byte, out interface{}) error { 36 | return goyaml.Unmarshal(in, out) 37 | } 38 | -------------------------------------------------------------------------------- /support/Aasincos.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/mlog" 14 | ) 15 | 16 | /* arc sin, cosine, tan2 and sqrt that will NOT fail */ 17 | 18 | const oneTol = 1.00000000000001 19 | const tol50 = 1e-50 20 | 21 | // Aasin is asin w/ error catching 22 | func Aasin(v float64) float64 { 23 | 24 | av := math.Abs(v) 25 | 26 | if av >= 1. { 27 | if av > oneTol { 28 | // TODO: we are supposed to signal an error, but not actually fail 29 | mlog.Printf("error signal in aasin()") 30 | } 31 | if v < 0. { 32 | return -math.Pi / 2.0 33 | } 34 | return math.Pi / 2.0 35 | } 36 | return math.Asin(v) 37 | } 38 | 39 | // Aacos is acos w/ error catching 40 | func Aacos(v float64) float64 { 41 | 42 | av := math.Abs(v) 43 | 44 | if av >= 1. { 45 | if av > oneTol { 46 | // TODO: we are supposed to signal an error, but not actually fail 47 | mlog.Printf("error signal in aacos()") 48 | } 49 | if v < 0. { 50 | return math.Pi 51 | } 52 | return 0. 53 | } 54 | return math.Acos(v) 55 | } 56 | 57 | // Asqrt is sqrt w/ error catching 58 | func Asqrt(v float64) float64 { 59 | if v <= 0 { 60 | return 0.0 61 | } 62 | return math.Sqrt(v) 63 | } 64 | 65 | // Aatan2 is atan2 w/ error catching 66 | func Aatan2(n, d float64) float64 { 67 | if math.Abs(n) < tol50 && math.Abs(d) < tol50 { 68 | return 0.0 69 | } 70 | return math.Atan2(n, d) 71 | } 72 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe !go1.4 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /operations/Eqc.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/go-spatial/proj/core" 7 | "github.com/go-spatial/proj/merror" 8 | ) 9 | 10 | func init() { 11 | core.RegisterConvertLPToXY("eqc", 12 | "Equidistant Cylindrical (Plate Carree)", 13 | "\n\tCyl, Sph\n\tlat_ts=[, lat_0=0]", 14 | NewEqc, 15 | ) 16 | } 17 | 18 | // Eqc implements core.IOperation and core.ConvertLPToXY 19 | type Eqc struct { 20 | core.Operation 21 | rc float64 22 | } 23 | 24 | // NewEqc creates a new Plate Carree system 25 | func NewEqc(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 26 | op := &Eqc{} 27 | op.System = system 28 | 29 | err := op.eqcSetup(system) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return op, nil 34 | } 35 | 36 | // Forward goes forewards 37 | func (op *Eqc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { 38 | return op.spheroidalForward(lp) 39 | } 40 | 41 | // Inverse goes backwards 42 | func (op *Eqc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { 43 | return op.spheroidalReverse(xy) 44 | } 45 | 46 | //--------------------------------------------------------------------- 47 | 48 | func (op *Eqc) spheroidalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Ellipsoidal, forward */ 49 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 50 | 51 | P := op.System 52 | 53 | xy.X = op.rc * lp.Lam 54 | xy.Y = lp.Phi - P.Phi0 55 | return xy, nil 56 | } 57 | 58 | func (op *Eqc) spheroidalReverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Ellipsoidal, inverse */ 59 | lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} 60 | 61 | P := op.System 62 | 63 | lp.Lam = xy.X / op.rc 64 | lp.Phi = xy.Y + P.Phi0 65 | return lp, nil 66 | } 67 | 68 | func (op *Eqc) eqcSetup(sys *core.System) error { 69 | ps := op.System.ProjString 70 | 71 | latts, _ := ps.GetAsFloat("lat_ts") 72 | if math.Cos(latts) <= 0 { 73 | return merror.New(merror.LatTSLargerThan90) 74 | } 75 | op.rc = math.Cos(latts) 76 | 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/writerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | // Set the writer error and return false. 26 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 27 | emitter.error = yaml_WRITER_ERROR 28 | emitter.problem = problem 29 | return false 30 | } 31 | 32 | // Flush the output buffer. 33 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 34 | if emitter.write_handler == nil { 35 | panic("write handler not set") 36 | } 37 | 38 | // Check if the buffer is empty. 39 | if emitter.buffer_pos == 0 { 40 | return true 41 | } 42 | 43 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 44 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 45 | } 46 | emitter.buffer_pos = 0 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /support/UnitsTable.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | // UnitsTableEntry holds info about a unit 11 | type UnitsTableEntry struct { 12 | ID string 13 | ToMetersS string // is this just a string version of the fourth field? 14 | Name string 15 | ToMeters float64 16 | } 17 | 18 | // UnitsTable is the global list of units we know about 19 | var UnitsTable = map[string]*UnitsTableEntry{ 20 | "km": {"km", "1000.", "Kilometer", 1000.0}, 21 | "m": {"m", "1.", "Meter", 1.0}, 22 | "dm": {"dm", "1/10", "Decimeter", 0.1}, 23 | "cm": {"cm", "1/100", "Centimeter", 0.01}, 24 | "mm": {"mm", "1/1000", "Millimeter", 0.001}, 25 | "kmi": {"kmi", "1852.0", "International Nautical Mile", 1852.0}, 26 | "in": {"in", "0.0254", "International Inch", 0.0254}, 27 | "ft": {"ft", "0.3048", "International Foot", 0.3048}, 28 | "yd": {"yd", "0.9144", "International Yard", 0.9144}, 29 | "mi": {"mi", "1609.344", "International Statute Mile", 1609.344}, 30 | "fath": {"fath", "1.8288", "International Fathom", 1.8288}, 31 | "ch": {"ch", "20.1168", "International Chain", 20.1168}, 32 | "link": {"link", "0.201168", "International Link", 0.201168}, 33 | "us-in": {"us-in", "1./39.37", "U.S. Surveyor's Inch", 0.0254}, 34 | "us-ft": {"us-ft", "0.304800609601219", "U.S. Surveyor's Foot", 0.304800609601219}, 35 | "us-yd": {"us-yd", "0.914401828803658", "U.S. Surveyor's Yard", 0.914401828803658}, 36 | "us-ch": {"us-ch", "20.11684023368047", "U.S. Surveyor's Chain", 20.11684023368047}, 37 | "us-mi": {"us-mi", "1609.347218694437", "U.S. Surveyor's Statute Mile", 1609.347218694437}, 38 | "ind-yd": {"ind-yd", "0.91439523", "Indian Yard", 0.91439523}, 39 | "ind-ft": {"ind-ft", "0.30479841", "Indian Foot", 0.30479841}, 40 | "ind-ch": {"ind-ch", "20.11669506", "Indian Chain", 20.11669506}, 41 | } 42 | -------------------------------------------------------------------------------- /support/Mlfn.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/merror" 14 | ) 15 | 16 | /* meridional distance for ellipsoid and inverse 17 | ** 8th degree - accurate to < 1e-5 meters when used in conjunction 18 | ** with typical major axis values. 19 | ** Inverse determines phi to EPS (1e-11) radians, about 1e-6 seconds. 20 | */ 21 | const c00 = 1. 22 | const c02 = .25 23 | const c04 = .046875 24 | const c06 = .01953125 25 | const c08 = .01068115234375 26 | const c22 = .75 27 | const c44 = .46875 28 | const c46 = .01302083333333333333 29 | const c48 = .00712076822916666666 30 | const c66 = .36458333333333333333 31 | const c68 = .00569661458333333333 32 | const c88 = .3076171875 33 | const eps = 1e-11 34 | const maxIter = 10 35 | const enSize = 5 36 | 37 | // Enfn is ..? 38 | func Enfn(es float64) []float64 { 39 | var t float64 40 | 41 | en := make([]float64, enSize) 42 | 43 | en[0] = c00 - es*(c02+es*(c04+es*(c06+es*c08))) 44 | en[1] = es * (c22 - es*(c04+es*(c06+es*c08))) 45 | t = es * es 46 | en[2] = t * (c44 - es*(c46+es*c48)) 47 | t *= es 48 | en[3] = t * (c66 - es*c68) 49 | en[4] = t * es * c88 50 | 51 | return en 52 | } 53 | 54 | // Mlfn is ..? 55 | func Mlfn(phi float64, sphi float64, cphi float64, en []float64) float64 { 56 | cphi *= sphi 57 | sphi *= sphi 58 | return (en[0]*phi - cphi*(en[1]+sphi*(en[2]+sphi*(en[3]+sphi*en[4])))) 59 | } 60 | 61 | // InvMlfn is ..? 62 | func InvMlfn(arg float64, es float64, en []float64) (float64, error) { 63 | var s, t, phi float64 64 | k := 1. / (1. - es) 65 | var i int 66 | 67 | phi = arg 68 | for i = maxIter; i != 0; i-- { /* rarely goes over 2 iterations */ 69 | s = math.Sin(phi) 70 | t = 1. - es*s*s 71 | t = (Mlfn(phi, s, math.Cos(phi), en) - arg) * (t * math.Sqrt(t)) * k 72 | phi -= t 73 | if math.Abs(t) < eps { 74 | return phi, nil 75 | } 76 | } 77 | 78 | return phi, merror.New(merror.InvMlfn) 79 | } 80 | -------------------------------------------------------------------------------- /cmd/proj/proj_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package main_test 9 | 10 | import ( 11 | "bytes" 12 | "fmt" 13 | "strconv" 14 | "strings" 15 | "testing" 16 | 17 | main "github.com/go-spatial/proj/cmd/proj" 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | func TestCmd(t *testing.T) { 22 | assert := assert.New(t) 23 | 24 | type testcase struct { 25 | args string 26 | input []float64 27 | output []float64 // set to nil for expected fails 28 | } 29 | 30 | testcases := []testcase{ 31 | { 32 | "proj -epsg 9999", 33 | []float64{0.0, 0.0}, 34 | nil, 35 | }, { 36 | "proj -epsg 9999 proj=merc", 37 | []float64{0.0, 0.0}, 38 | nil, 39 | }, { 40 | "proj -epsg 3395 -inverse", 41 | []float64{0.0, 0.0}, 42 | nil, 43 | }, { 44 | "proj -epsg 3395", 45 | []float64{-77.625583, 38.833846}, 46 | []float64{-8641240.37, 4671101.60}, 47 | }, { 48 | "proj +proj=utm +zone=32 +ellps=GRS80", 49 | []float64{12.0, 55.0}, 50 | []float64{691875.63, 6098907.83}, 51 | }, { 52 | "proj -verbose -inverse +proj=utm +zone=32 +ellps=GRS80", 53 | []float64{691875.63, 6098907.83}, 54 | []float64{12.0, 55.0}, 55 | }, 56 | } 57 | 58 | for _, tc := range testcases { 59 | 60 | s := fmt.Sprintf("%f %f", tc.input[0], tc.input[1]) 61 | inBuf := bytes.NewBuffer([]byte(s)) 62 | outBuf := &bytes.Buffer{} 63 | 64 | err := main.Main(inBuf, outBuf, strings.Fields(tc.args)) 65 | 66 | if tc.output == nil { 67 | assert.Error(err, tc.args) 68 | } else { 69 | assert.NoError(err, tc.args) 70 | 71 | tokens := strings.Fields(string(outBuf.Bytes())) 72 | assert.Len(tokens, 2, tc.args) 73 | actual0, err := strconv.ParseFloat(tokens[0], 64) 74 | assert.NoError(err, tc.args) 75 | assert.InDelta(tc.output[0], actual0, 1.0e-2, tc.args) 76 | actual1, err := strconv.ParseFloat(tokens[1], 64) 77 | assert.NoError(err, tc.args) 78 | assert.InDelta(tc.output[1], actual1, 1.0e-2, tc.args) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This project is covered by two different licenses: MIT and Apache. 3 | 4 | #### MIT License #### 5 | 6 | The following files were ported to Go from C files of libyaml, and thus 7 | are still covered by their original MIT license, with the additional 8 | copyright staring in 2011 when the project was ported over: 9 | 10 | apic.go emitterc.go parserc.go readerc.go scannerc.go 11 | writerc.go yamlh.go yamlprivateh.go 12 | 13 | Copyright (c) 2006-2010 Kirill Simonov 14 | Copyright (c) 2006-2011 Kirill Simonov 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of 17 | this software and associated documentation files (the "Software"), to deal in 18 | the Software without restriction, including without limitation the rights to 19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 20 | of the Software, and to permit persons to whom the Software is furnished to do 21 | so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | 34 | ### Apache License ### 35 | 36 | All the remaining project files are covered by the Apache license: 37 | 38 | Copyright (c) 2011-2019 Canonical Ltd 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /mlog/Log_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package mlog_test 9 | 10 | import ( 11 | "bufio" 12 | "fmt" 13 | "io" 14 | "os" 15 | "regexp" 16 | "testing" 17 | 18 | "github.com/go-spatial/proj/mlog" 19 | ) 20 | 21 | func TestLogger(t *testing.T) { 22 | //assert := assert.New(t) 23 | 24 | tmpfile, err := os.CreateTemp("", "mlog-test") 25 | if err != nil { 26 | t.Fatalf("Failed to create temporary file: %v", err) 27 | return 28 | } 29 | defer tmpfile.Close() 30 | tempFilename := tmpfile.Name() 31 | t.Cleanup(func() { os.Remove(tempFilename) }) 32 | 33 | log := mlog.NewLoggerSingleOutput(tmpfile) 34 | log.EnableDebug() 35 | log.EnableError() 36 | log.EnableInfo() 37 | 38 | // the following is put in an inlined lambda, so that 39 | // we have a place to put the defer: we need it to always get 40 | // called immediately after the log stmts run, even if 41 | // they crash -- otherwise, we'd have lost our stderr! 42 | 43 | log.Debugf("debug %d", 1) 44 | log.Printf("print %s", "2") 45 | e := fmt.Errorf("E") 46 | log.Error(e) 47 | x := "yow" 48 | log.Printv(x) 49 | 50 | log.DisableDebug() 51 | log.DisableError() 52 | log.DisableInfo() 53 | 54 | log.Debugf("nope") 55 | log.Printf("nope") 56 | log.Error(e) 57 | 58 | tmpfile.Seek(0, io.SeekStart) 59 | 60 | expectedLines := []string{ 61 | `\[DEBUG\] Log_test.go:\d+: debug 1`, 62 | `\[LOG\] Log_test.go:\d+: print 2`, 63 | `\[ERROR\] E`, 64 | `\[LOG\] Log_test.go:\d+: "yow"`, 65 | } 66 | 67 | scanner := bufio.NewScanner(tmpfile) 68 | count := 0 69 | for scanner.Scan() { 70 | scanner.Text() 71 | if count > len(expectedLines) { 72 | t.Errorf("Found too many lines, expected %d, got %d", len(expectedLines), count) 73 | return 74 | } 75 | txt := scanner.Text() 76 | m, err := regexp.MatchString(expectedLines[count], txt) 77 | if err != nil { 78 | t.Errorf("error failed to match regexp[%s]: error: %v", expectedLines[count], err) 79 | return 80 | } 81 | if !m { 82 | t.Errorf("failed to match regexp[%s]: %s", expectedLines[count], txt) 83 | return 84 | } 85 | count++ 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /merror/error_strings.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package merror 9 | 10 | // All the errors 11 | var ( 12 | UnknownProjection = "unknown projection: %s" 13 | UnknownEllipseParameter = "unknown ellipse parameter: %s" 14 | UnsupportedProjectionString = "unsupported projection string: %s" 15 | InvalidProjectionSyntax = "invalid projection syntax: %s" 16 | ProjectionStringRequiresEllipse = "projection string requires ellipse" 17 | MajorAxisNotGiven = "major axis not given" 18 | ReverseFlatteningIsZero = "reverse flattening (rf) is zero" 19 | EccentricityIsOne = "eccentricity is one" 20 | ToleranceCondition = "tolerance condition error" 21 | ProjValueMissing = "proj value missing" 22 | NoSuchDatum = "no such datum" 23 | NotYetSupported = "not yet supported" // TODO 24 | InvalidArg = "invalid argument in proj string" 25 | EsLessThanZero = "ES is less than zero" 26 | RefRadLargerThan90 = "ref rad is greater than 90" 27 | InvalidDMS = "invalid DMS (degrees-minutes-seconds)" 28 | EllipsoidUseRequired = "use of ellipsoid required" 29 | InvalidUTMZone = "invalid UTM zone" 30 | LatOrLonExceededLimit = "lat or lon limit exceeded" 31 | UnknownUnit = "unknown unit" 32 | UnitFactorLessThanZero = "unit factor less than zero" 33 | Axis = "invalid axis configuration" 34 | KLessThanZero = "K is less than zero" 35 | CoordinateError = "invalid coordinates" 36 | InvalidXOrY = "invalid X or Y" 37 | ConicLatEqual = "Conic lat is equal" 38 | AeaSetupFailed = "setup for aea projection failed" 39 | InvMlfn = "invalid mlfn computation" 40 | AeaProjString = "invalid projection string for aea" 41 | LatTSLargerThan90 = "lat ts is greater than 90" 42 | Phi2 = "invalid phi2 computation" 43 | ) 44 | -------------------------------------------------------------------------------- /support/DatumsTable.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | func init() { 11 | 12 | for _, entry := range DatumsTable { 13 | pl, err := NewProjString(entry.DefinitionString) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | entry.Definition = pl 19 | } 20 | } 21 | 22 | //--------------------------------------------------------------------- 23 | 24 | // DatumTableEntry holds a constant datum item 25 | type DatumTableEntry struct { 26 | ID string 27 | DefinitionString string 28 | EllipseID string // note this is what the global table will key off of, not ID 29 | Comments string 30 | Definition *ProjString 31 | } 32 | 33 | // DatumsTable is the global list of datum constants 34 | // 35 | // TODO: the EllipseIDs for Greek_Geodetic_Reference_System_1987 and North_American_Datum_1983 36 | // are the same, so I'm putting the Greek one under the key "GGRS87" 37 | // TODO: "Potsdam Rauenberg 1950 DHDN" and "Hermannskogel" have the same problem, so we'll use the ID for Hermannskogel 38 | var DatumsTable = map[string]*DatumTableEntry{ 39 | "WGS84": {"WGS84", "towgs84=0,0,0", "WGS84", "", nil}, 40 | "GGRS87": {"GGRS87", "towgs84=-199.87,74.79,246.62", "GRS80", "Greek_Geodetic_Reference_System_1987", nil}, 41 | "GRS80": {"NAD83", "towgs84=0,0,0", "GRS80", "North_American_Datum_1983", nil}, 42 | "clrk66": {"NAD27", "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", "clrk66", "North_American_Datum_1927", nil}, 43 | "bessel": {"potsdam" /*"towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7",*/, "nadgrids=@BETA2007.gsb", "bessel", "Potsdam Rauenberg 1950 DHDN", nil}, 44 | "clrk80ign": {"carthage", "towgs84=-263.0,6.0,431.0", "clrk80ign", "Carthage 1934 Tunisia", nil}, 45 | "hermannskogel": {"hermannskogel", "towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232", "bessel", "Hermannskogel", nil}, 46 | "mod_airy": {"ire65", "towgs84=482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", "mod_airy", "Ireland 1965", nil}, 47 | "intl": {"nzgd49", "towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", "intl", "New Zealand Geodetic Datum 1949", nil}, 48 | "airy": {"OSGB36", "towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", "airy", "Airy 1830", nil}, 49 | } 50 | -------------------------------------------------------------------------------- /core/Coordinate.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core 9 | 10 | // CoordType is the enum for the different types of coordinates 11 | type CoordType int 12 | 13 | // The coordinate types 14 | // 15 | // Today we are only using CoordTypeLP and CoordTypeXY. 16 | const ( 17 | CoordTypeAny = iota 18 | CoordTypeXYZT 19 | CoordTypeUVWT 20 | CoordTypeLPZT 21 | CoordTypeOPK 22 | CoordTypeENU 23 | CoordTypeGEOD 24 | CoordTypeUV 25 | CoordTypeXY 26 | CoordTypeLP 27 | CoordTypeXYZ 28 | CoordTypeUVW 29 | CoordTypeLPZ 30 | ) 31 | 32 | // CoordAny just generically holds data, not assigned to a coordinate type. 33 | // 34 | // Because unions. 35 | type CoordAny struct{ V [4]float64 } 36 | 37 | //--------------------------------------------------------------------- 38 | 39 | // ToLP returns a CoordLP 40 | func (c *CoordAny) ToLP() *CoordLP { 41 | return &CoordLP{Lam: c.V[0], Phi: c.V[1]} 42 | } 43 | 44 | // FromLP sets this CoordAny 45 | func (c *CoordAny) FromLP(lp *CoordLP) { 46 | c.V[0] = lp.Lam 47 | c.V[1] = lp.Phi 48 | } 49 | 50 | // ToXY returns a CoordXY 51 | func (c *CoordAny) ToXY() *CoordXY { 52 | return &CoordXY{X: c.V[0], Y: c.V[1]} 53 | } 54 | 55 | // FromXY sets this CoordAny 56 | func (c *CoordAny) FromXY(xy *CoordXY) { 57 | c.V[0] = xy.X 58 | c.V[1] = xy.Y 59 | } 60 | 61 | //--------------------------------------------------------------------- 62 | 63 | // CoordXYZT is X,Y,Z,T 64 | type CoordXYZT struct{ X, Y, Z, T float64 } 65 | 66 | // CoordUVWT is U,V,W,T 67 | type CoordUVWT struct{ U, V, W, T float64 } 68 | 69 | // CoordLPZT is Lam,Phi,Z,T 70 | type CoordLPZT struct{ Lam, Phi, Z, T float64 } 71 | 72 | // CoordOPK is Omega, Phi, Kappa (rotations) 73 | type CoordOPK struct{ O, P, K float64 } 74 | 75 | // CoordENU is East, North, Up 76 | type CoordENU struct{ E, N, U float64 } 77 | 78 | // CoordGEOD is geodesic length, fwd azi, rev azi 79 | type CoordGEOD struct{ S, A1, A2 float64 } 80 | 81 | // CoordUV is U,V 82 | type CoordUV struct{ U, V float64 } 83 | 84 | // CoordXY is X,Y 85 | type CoordXY struct{ X, Y float64 } 86 | 87 | // CoordLP is Lam,Phi 88 | type CoordLP struct{ Lam, Phi float64 } 89 | 90 | // CoordXYZ is X,Y,Z 91 | type CoordXYZ struct{ X, Y, Z float64 } 92 | 93 | // CoordUVW is U,V,W 94 | type CoordUVW struct{ U, V, W float64 } 95 | 96 | // CoordLPZ is Lam, Phi, Z 97 | type CoordLPZ struct{ Lam, Phi, Z float64 } 98 | -------------------------------------------------------------------------------- /gie/gie_data/deformation.gie: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | Test for the deformation operation - Kinematic Gridshifting 3 | 4 | For all the deformation tests the alaska and egm96_15.gtx grids are used even 5 | though they are not parts of a deformation model, they are in the proper format 6 | and for testing purposes it doesn't really matter all that much... 7 | 8 | The input coordinate is located at lon=60, lam=-160 - somewhere in Alaska. 9 | 10 | =============================================================================== 11 | 12 | 13 | 14 | ------------------------------------------------------------------------------- 15 | Test using both horizontal and vertical grids as well as the +tobs parameter 16 | ------------------------------------------------------------------------------- 17 | operation +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx 18 | +t_epoch=2016.0 +t_obs=2000.0 +ellps=GRS80 19 | ------------------------------------------------------------------------------- 20 | tolerance 0.1 mm 21 | ignore pjd_err_failed_to_load_grid 22 | accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 23 | expect -3004295.7025 -1093474.2106 5500477.3444 24 | roundtrip 5 25 | 26 | ------------------------------------------------------------------------------- 27 | Test using both horizontal and vertical grids 28 | ------------------------------------------------------------------------------- 29 | operation +proj=deformation 30 | +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +ellps=GRS80 31 | ------------------------------------------------------------------------------- 32 | tolerance 0.1 mm 33 | ignore pjd_err_failed_to_load_grid 34 | accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 2000.0 35 | expect -3004295.7025 -1093474.2106 5500477.3444 2000.0 36 | roundtrip 5 37 | 38 | ------------------------------------------------------------------------------- 39 | operation proj=deformation xy_grids=alaska t_epoch=2016.0 t_obs=2017.0 40 | ellps=GRS80 41 | expect failure pjd_err_no_args 42 | 43 | operation proj=deformation z_grids=egm96_15.gtx t_epoch=2016.0 t_obs=2017.0 44 | ellps=GRS80 45 | expect failure pjd_err_no_args 46 | 47 | operation proj=deformation xy_grids=nonexisting z_grids=egm96_15.gtx 48 | t_epoch=2016.0 t_obs=2017.0 ellps=GRS80 49 | expect failure pjd_err_failed_to_load_grid 50 | 51 | operation proj=deformation xy_grids=alaska z_grids=nonexisting 52 | t_epoch=2016.0 t_obs=2017.0 ellps=GRS80 53 | expect failure pjd_err_failed_to_load_grid 54 | 55 | operation proj=deformation xy_grids=alaska z_grids=nonexisting ellps=GRS80 56 | expect failure pjd_err_missing_args 57 | 58 | -------------------------------------------------------------------------------- /support/DMS.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "regexp" 12 | "strconv" 13 | 14 | "github.com/go-spatial/proj/merror" 15 | 16 | "github.com/go-spatial/proj/mlog" 17 | ) 18 | 19 | // DMSToDD converts a degrees-minutes-seconds string to decimal-degrees 20 | // 21 | // Using an 8-part regexp, we support this format: 22 | // [+-] nnn [°Dd] nnn ['Mm] nnn.nnn ["Ss] [NnEeWwSs] 23 | // 24 | // TODO: the original dmstor() may support more, but the parsing code 25 | // is messy and we don't have any testcases at this time. 26 | func DMSToDD(input string) (float64, error) { 27 | 28 | mlog.Debugf("%s", input) 29 | 30 | deg := `\s*(-|\+)?\s*(\d+)\s*([°Dd]?)` // t1, t2, t3 31 | min := `\s*(\d+)?\s*(['Mm]?)` // t4, t5 32 | sec := `\s*(\d+\.?\d*)?\s*(["Ss])?` // t6, t7 33 | news := `\s*([NnEeWwSs]?)` // t8 34 | expr := "^" + deg + min + sec + news + "$" 35 | r := regexp.MustCompile(expr) 36 | 37 | tokens := r.FindStringSubmatch(input) 38 | if tokens == nil { 39 | return 0.0, merror.New(merror.InvalidArg) 40 | } 41 | 42 | sign := tokens[1] 43 | d := tokens[2] 44 | m := tokens[4] 45 | s := tokens[6] 46 | dir := tokens[8] 47 | 48 | var df, mf, sf float64 49 | var err error 50 | 51 | df, err = strconv.ParseFloat(d, 64) 52 | if err != nil { 53 | return 0.0, err 54 | } 55 | if m != "" { 56 | mf, err = strconv.ParseFloat(m, 64) 57 | if err != nil { 58 | return 0.0, err 59 | } 60 | } 61 | if s != "" { 62 | sf, err = strconv.ParseFloat(s, 64) 63 | if err != nil { 64 | return 0.0, err 65 | } 66 | } 67 | 68 | dd := df + mf/60.0 + sf/3600.0 69 | if sign == "-" { 70 | dd = -dd 71 | } 72 | 73 | if dir == "S" || dir == "s" || dir == "W" || dir == "w" { 74 | dd = -dd 75 | } 76 | 77 | return dd, nil 78 | } 79 | 80 | // DMSToR converts a DMS string to radians 81 | func DMSToR(input string) (float64, error) { 82 | 83 | dd, err := DMSToDD(input) 84 | if err != nil { 85 | return 0.0, err 86 | } 87 | 88 | r := DDToR(dd) 89 | 90 | return r, nil 91 | } 92 | 93 | // DDToR converts decimal degrees to radians 94 | func DDToR(deg float64) float64 { 95 | const degToRad = 0.017453292519943296 96 | r := deg * degToRad 97 | return r 98 | } 99 | 100 | // RToDD converts radians to decimal degrees 101 | func RToDD(r float64) float64 { 102 | const radToDeg = 1.0 / 0.017453292519943296 103 | deg := r * radToDeg 104 | return deg 105 | } 106 | 107 | // ConvertArcsecondsToRadians converts from arc secs to rads 108 | func ConvertArcsecondsToRadians(s float64) float64 { 109 | // Pi/180/3600 110 | r := s * 4.84813681109535993589914102357e-6 111 | return r 112 | } 113 | -------------------------------------------------------------------------------- /core/OperationDescription.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/go-spatial/proj/merror" 14 | ) 15 | 16 | // OperationDescriptionTable is the global list of all the known operations 17 | // 18 | // The algorithms in the operations package call the Register functions 19 | // which populate this map. 20 | var OperationDescriptionTable = map[string]*OperationDescription{} 21 | 22 | // ConvertLPToXYCreatorFuncType is the type of the function which creates an operation-specific object 23 | // 24 | // This kind of function, when executed, creates an operation-specific object 25 | // which implements IConvertLPToXY. 26 | type ConvertLPToXYCreatorFuncType func(*System, *OperationDescription) (IConvertLPToXY, error) 27 | 28 | // OperationDescription stores the information about a particular kind of 29 | // operation. It is populated from each op in the "operations" package 30 | // into the global table. 31 | type OperationDescription struct { 32 | ID string 33 | Description string 34 | Description2 string 35 | OperationType OperationType 36 | InputType CoordType 37 | OutputType CoordType 38 | creatorFunc interface{} // for now, this will always be a ConvertLPToXYCreatorFuncType 39 | } 40 | 41 | // RegisterConvertLPToXY adds an OperationDescription entry to the OperationDescriptionTable 42 | // 43 | // Each file in the operations package has an init() routine which calls this function. 44 | // Each operation supplies its own "creatorFunc", of its own particular type. 45 | func RegisterConvertLPToXY( 46 | id string, 47 | description string, 48 | description2 string, 49 | creatorFunc ConvertLPToXYCreatorFuncType, 50 | ) { 51 | pi := &OperationDescription{ 52 | ID: id, 53 | Description: description, 54 | Description2: description2, 55 | OperationType: OperationTypeConversion, 56 | InputType: CoordTypeLP, 57 | OutputType: CoordTypeXY, 58 | creatorFunc: creatorFunc, 59 | } 60 | 61 | _, ok := OperationDescriptionTable[id] 62 | if ok { 63 | panic(fmt.Sprintf("duplicate operation description id '%s' : %s", id, description)) 64 | } 65 | OperationDescriptionTable[id] = pi 66 | } 67 | 68 | // CreateOperation returns a new object of the specific operation type, e.g. an operations.EtMerc 69 | func (desc *OperationDescription) CreateOperation(sys *System) (IOperation, error) { 70 | 71 | if desc.IsConvertLPToXY() { 72 | return NewConvertLPToXY(sys, desc) 73 | } 74 | 75 | return nil, merror.New(merror.NotYetSupported) 76 | } 77 | 78 | // IsConvertLPToXY returns true iff the operation can be cast to an IConvertLPToXY 79 | func (desc *OperationDescription) IsConvertLPToXY() bool { 80 | return desc.OperationType == OperationTypeConversion && 81 | desc.InputType == CoordTypeLP && 82 | desc.OutputType == CoordTypeXY 83 | } 84 | -------------------------------------------------------------------------------- /merror/Error.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package merror 9 | 10 | import ( 11 | "fmt" 12 | "runtime" 13 | "strings" 14 | 15 | "github.com/go-spatial/proj/mlog" 16 | ) 17 | 18 | // ShowSource determines whther to include source file and line information in 19 | // the Error() string 20 | var ShowSource = false 21 | 22 | // Error captures the type of error, where it occurred, inner errors, etc. 23 | // Error implements the error interface 24 | type Error struct { 25 | Message string 26 | Function string 27 | Line int 28 | File string 29 | Inner error 30 | } 31 | 32 | // New creates a new Error object 33 | func New(format string, v ...interface{}) error { 34 | file, line, function := stackinfo(2) 35 | 36 | err := Error{ 37 | Message: fmt.Sprintf(format, v...), 38 | Function: function, 39 | Line: line, 40 | File: file, 41 | } 42 | 43 | mlog.Error(err) 44 | 45 | return err 46 | } 47 | 48 | // Wrap returns a new Error object which contains the given error object 49 | // 50 | // If v is used, v[0] must be a format string. 51 | func Wrap(inner error, v ...interface{}) error { 52 | file, line, function := stackinfo(2) 53 | 54 | format := "wrapped error" 55 | if len(v) > 0 { 56 | v0, ok := v[0].(string) 57 | if !ok { 58 | mlog.Printf("malformed log message at %s:%d", file, line) 59 | v = nil 60 | } else { 61 | format = v0 62 | v = v[1:] 63 | } 64 | } 65 | 66 | err := Error{ 67 | Message: fmt.Sprintf(format, v...), 68 | Function: function, 69 | Line: line, 70 | File: file, 71 | Inner: inner, 72 | } 73 | 74 | mlog.Error(err) 75 | 76 | return err 77 | } 78 | 79 | // Pass just returns the error you give it, with the side effect of logging 80 | // that the event occurred 81 | func Pass(err error) error { 82 | mlog.Printf("PASSTHROUGH ERROR: %s", err.Error()) 83 | return err 84 | } 85 | 86 | func (e Error) Error() string { 87 | s := e.Message 88 | if ShowSource { 89 | s += fmt.Sprintf(" (from %s at %s:%d)", e.Function, e.File, e.Line) 90 | } 91 | if e.Inner != nil { 92 | s += " // Inner: " + e.Inner.Error() 93 | } 94 | 95 | return s 96 | } 97 | 98 | // stackinfo returns (file, line, function) 99 | func stackinfo(depth int) (string, int, string) { 100 | 101 | pc, file, line, ok := runtime.Caller(depth) 102 | if !ok { 103 | pc = 0 104 | file = "" 105 | line = 0 106 | } 107 | 108 | function := "" 109 | if pc != 0 { 110 | function = runtime.FuncForPC(pc).Name() 111 | } 112 | 113 | i := strings.LastIndex(file, "/") 114 | if i >= 0 { 115 | file = file[i+1:] 116 | } 117 | 118 | i = strings.LastIndex(function, "/") 119 | if i >= 0 { 120 | function = function[i+1:] 121 | } 122 | 123 | return file, line, function 124 | } 125 | -------------------------------------------------------------------------------- /core/full_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package core_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | 15 | "github.com/go-spatial/proj/core" 16 | "github.com/stretchr/testify/assert" 17 | 18 | // need to pull in the operations table entries 19 | _ "github.com/go-spatial/proj/operations" 20 | ) 21 | 22 | func TestUtm(t *testing.T) { 23 | assert := assert.New(t) 24 | 25 | ps, err := support.NewProjString("+proj=utm +zone=32 +ellps=GRS80") 26 | assert.NoError(err) 27 | 28 | sys, opx, err := core.NewSystem(ps) 29 | assert.NoError(err) 30 | assert.NotNil(sys) 31 | assert.NotNil(opx) 32 | assert.EqualValues(sys, opx.GetSystem()) 33 | 34 | op := opx.(core.IConvertLPToXY) 35 | 36 | // 55d N, 12d E (lon lat) (lam phi) 37 | input := &core.CoordLP{Lam: support.DDToR(12.0), Phi: support.DDToR(55.0)} 38 | output, err := op.Forward(input) 39 | assert.NoError(err) 40 | 41 | x, y := output.X, output.Y 42 | assert.InDelta(691875.63, x, 1e-2) 43 | assert.InDelta(6098907.83, y, 1e-2) 44 | 45 | input2 := output 46 | output2, err := op.Inverse(input2) 47 | assert.NoError(err) 48 | 49 | l, p := output2.Lam, output2.Phi 50 | l = support.RToDD(l) 51 | p = support.RToDD(p) 52 | assert.InDelta(12.0, l, 1e-8) 53 | assert.InDelta(55.0, p, 1e-8) 54 | } 55 | 56 | func TestEtMerc(t *testing.T) { 57 | assert := assert.New(t) 58 | 59 | ps, err := support.NewProjString("+proj=etmerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5 +zone=30") 60 | assert.NoError(err) 61 | 62 | sys, opx, err := core.NewSystem(ps) 63 | assert.NoError(err) 64 | assert.NotNil(sys) 65 | assert.NotNil(opx) 66 | assert.EqualValues(sys, opx.GetSystem()) 67 | 68 | assert.True(opx.GetDescription().IsConvertLPToXY()) 69 | op := opx.(core.IConvertLPToXY) 70 | 71 | input := &core.CoordLP{Lam: support.DDToR(2.0), Phi: support.DDToR(1.0)} 72 | output, err := op.Forward(input) 73 | assert.NoError(err) 74 | 75 | x, y := output.X, output.Y 76 | assert.InDelta(222650.796797586, x, 1e-8) 77 | assert.InDelta(110642.229411933, y, 1e-8) 78 | 79 | input2 := output 80 | output2, err := op.Inverse(input2) 81 | assert.NoError(err) 82 | 83 | l, p := output2.Lam, output2.Phi 84 | l = support.RToDD(l) 85 | p = support.RToDD(p) 86 | assert.InDelta(2.0, l, 1e-8) 87 | assert.InDelta(1.0, p, 1e-8) 88 | } 89 | 90 | func Test3395(t *testing.T) { 91 | 92 | assert := assert.New(t) 93 | 94 | ps, err := support.NewProjString("+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs") 95 | assert.NoError(err) 96 | 97 | t.Skip() 98 | 99 | sys, opx, err := core.NewSystem(ps) 100 | assert.NoError(err) 101 | assert.NotNil(sys) 102 | assert.NotNil(opx) 103 | assert.EqualValues(sys, opx.GetSystem()) 104 | } 105 | -------------------------------------------------------------------------------- /gie/gie_data/axisswap.gie: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | Tests for the axisswap operation 3 | ------------------------------------------------------------------------------- 4 | 5 | 6 | 7 | operation proj=axisswap order=1,2,3,4 8 | tolerance 0.000001 m 9 | accept 1 2 3 4 10 | expect 1 2 3 4 11 | roundtrip 100 12 | 13 | operation proj=axisswap order=4,3,2,1 14 | tolerance 0.000001 m 15 | accept 1 2 3 4 16 | expect 4 3 2 1 17 | roundtrip 100 18 | 19 | operation proj=axisswap order=-1,-2,-3,-4 20 | tolerance 0.000001 m 21 | accept 1 2 3 4 22 | expect -1 -2 -3 -4 23 | roundtrip 100 24 | 25 | operation proj=axisswap order=1,2,-3,4 26 | tolerance 0.000001 m 27 | accept 1 2 3 4 28 | expect 1 2 -3 4 29 | roundtrip 100 30 | 31 | operation proj=axisswap order=-1,2,3,4 32 | tolerance 0.000001 m 33 | accept 1 2 3 4 34 | expect -1 2 3 4 35 | roundtrip 100 36 | 37 | operation proj=axisswap order=1,2,3,-4 38 | tolerance 0.000001 m 39 | accept 1 2 3 4 40 | expect 1 2 3 -4 41 | roundtrip 100 42 | 43 | operation proj=axisswap order=-2,1 44 | tolerance 0.000001 m 45 | accept 1 2 3 4 46 | expect -2 1 3 4 47 | roundtrip 100 48 | 49 | operation proj=axisswap order=3,-2,1 50 | tolerance 0.000001 m 51 | accept 1 2 3 4 52 | expect 3 -2 1 4 53 | roundtrip 100 54 | 55 | operation proj=axisswap axis=neu 56 | tolerance 0 m 57 | accept 1 2 3 58 | expect 2 1 3 59 | 60 | # when using the +axis parameter we specify the order of the INPUT coordinate, 61 | # as opposed to +order which relates to the OUTPUT coordinate. Here we test 62 | # that n(1), u(2) and e(3) are swapped correctly to enu ordering. 63 | operation proj=axisswap axis=nue 64 | tolerance 0 m 65 | accept 1 2 3 66 | expect 2 3 1 67 | 68 | operation proj=axisswap axis=swd 69 | tolerance 0.000001 m 70 | accept 1 2 3 4 71 | expect -2 -1 -3 4 72 | 73 | operation proj=pipeline 74 | step proj=latlong 75 | step proj=axisswap 76 | order=1,2,3,4 77 | angularunits 78 | 79 | tolerance 0.00001 m 80 | accept 12 55 0 0 81 | expect 12 55 0 0 82 | 83 | operation proj=pipeline 84 | step proj=latlong 85 | step proj=axisswap 86 | order=-2,-1,3,4 87 | angularunits 88 | 89 | tolerance 0.00001 m 90 | accept 12 55 0 0 91 | expect -55 -12 0 0 92 | 93 | operation proj=axisswap order=1,2,3,4 axis=enu 94 | expect failure pjd_err_axis 95 | 96 | operation proj=axisswap 97 | expect failure pjd_err_axis 98 | 99 | operation proj=axisswap order=1,2,1,4 100 | expect failure pjd_err_axis 101 | 102 | operation proj=axisswap order=2,3 103 | expect failure pjd_err_axis 104 | 105 | operation proj=axisswap order=2,3,4 106 | expect failure pjd_err_axis 107 | 108 | operation proj=axisswap order=1,2,3,5 109 | expect failure pjd_err_axis 110 | 111 | 112 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_order.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // isOrdered checks that collection contains orderable elements. 9 | func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 10 | objKind := reflect.TypeOf(object).Kind() 11 | if objKind != reflect.Slice && objKind != reflect.Array { 12 | return false 13 | } 14 | 15 | objValue := reflect.ValueOf(object) 16 | objLen := objValue.Len() 17 | 18 | if objLen <= 1 { 19 | return true 20 | } 21 | 22 | value := objValue.Index(0) 23 | valueInterface := value.Interface() 24 | firstValueKind := value.Kind() 25 | 26 | for i := 1; i < objLen; i++ { 27 | prevValue := value 28 | prevValueInterface := valueInterface 29 | 30 | value = objValue.Index(i) 31 | valueInterface = value.Interface() 32 | 33 | compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) 34 | 35 | if !isComparable { 36 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) 37 | } 38 | 39 | if !containsValue(allowedComparesResults, compareResult) { 40 | return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) 41 | } 42 | } 43 | 44 | return true 45 | } 46 | 47 | // IsIncreasing asserts that the collection is increasing 48 | // 49 | // assert.IsIncreasing(t, []int{1, 2, 3}) 50 | // assert.IsIncreasing(t, []float{1, 2}) 51 | // assert.IsIncreasing(t, []string{"a", "b"}) 52 | func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 53 | return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) 54 | } 55 | 56 | // IsNonIncreasing asserts that the collection is not increasing 57 | // 58 | // assert.IsNonIncreasing(t, []int{2, 1, 1}) 59 | // assert.IsNonIncreasing(t, []float{2, 1}) 60 | // assert.IsNonIncreasing(t, []string{"b", "a"}) 61 | func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 62 | return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) 63 | } 64 | 65 | // IsDecreasing asserts that the collection is decreasing 66 | // 67 | // assert.IsDecreasing(t, []int{2, 1, 0}) 68 | // assert.IsDecreasing(t, []float{2, 1}) 69 | // assert.IsDecreasing(t, []string{"b", "a"}) 70 | func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 71 | return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) 72 | } 73 | 74 | // IsNonDecreasing asserts that the collection is not decreasing 75 | // 76 | // assert.IsNonDecreasing(t, []int{1, 1, 2}) 77 | // assert.IsNonDecreasing(t, []float{1, 2}) 78 | // assert.IsNonDecreasing(t, []string{"a", "b"}) 79 | func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 80 | return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) 81 | } 82 | -------------------------------------------------------------------------------- /support/DMS_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "math" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/support" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestDMSToDD(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | convert := func(d float64, m float64, s float64) float64 { 22 | dd := d + m/60.0 + s/3600.0 23 | return dd 24 | } 25 | 26 | assert.InDelta(15, convert(15, 0, 0), 1e-8) 27 | assert.InDelta(137, convert(135, 60, 3600), 1e-8) 28 | 29 | type Data struct { 30 | input string 31 | expected float64 32 | } 33 | 34 | FAIL := math.MaxFloat32 35 | ok := convert(12, 34, 56.78) 36 | 37 | data := []Data{ 38 | {`0.0d0m0s`, FAIL}, 39 | {`0d0.0m0s`, FAIL}, 40 | {`.0d0m0s`, FAIL}, 41 | {`0d.0m0s`, FAIL}, 42 | {`0d0m.0s`, FAIL}, 43 | {`0d0m`, 0}, 44 | {`0d`, 0}, 45 | {`0d0m0sx`, FAIL}, 46 | 47 | {`12d34m56.78s`, ok}, 48 | {`+12d34m56.78s`, ok}, 49 | {`-12d34m56.78s`, -ok}, 50 | 51 | {`12d34m56.78se`, ok}, 52 | {`12d34m56.78sE`, ok}, 53 | {`12d34m56.78sw`, -ok}, 54 | {`12d34m56.78sW`, -ok}, 55 | {`12d34m56.78sn`, ok}, 56 | {`12d34m56.78sN`, ok}, 57 | {`12d34m56.78ss`, -ok}, 58 | {`12d34m56.78sS`, -ok}, 59 | 60 | {`12D34M56.78S`, ok}, 61 | {`12°34'56.78"`, ok}, 62 | 63 | {` 12 ° 34 ' 56.78 " e`, ok}, 64 | 65 | {`15d0m0s`, convert(15, 0, 0)}, 66 | {`135d0m0s`, convert(135, 0, 0)}, 67 | {`134d60m0s`, convert(134, 60, 0)}, 68 | {`134d0m3600s`, convert(134, 0, 3600)}, 69 | {`134d1m3.3s`, convert(134, 1, 3.3)}, 70 | {`-134d1'3.3s E`, -convert(134, 1, 3.3)}, 71 | {`-134d1'3.3s W`, convert(134, 1, 3.3)}, 72 | } 73 | 74 | for _, d := range data { 75 | actual, err := support.DMSToDD(d.input) 76 | if d.expected == FAIL { 77 | assert.Error(err, "Test %s", d.input) 78 | } else { 79 | assert.NoError(err) 80 | assert.InDelta(d.expected, actual, 1e-6, "Test %s", d.input) 81 | } 82 | } 83 | } 84 | 85 | func TestDDToR(t *testing.T) { 86 | assert := assert.New(t) 87 | 88 | r := support.DDToR(15.0) 89 | assert.InDelta(math.Pi/12.0, r, 1e-6) 90 | 91 | deg := support.RToDD(math.Pi / 12.0) 92 | assert.InDelta(15.0, deg, 1e-6) 93 | 94 | r = support.DDToR(135.0) 95 | assert.InDelta(math.Pi*0.75, r, 1e-6) 96 | 97 | deg = support.RToDD(math.Pi * 0.75) 98 | assert.InDelta(135.0, deg, 1e-6) 99 | } 100 | 101 | func TestDMSToR(t *testing.T) { 102 | assert := assert.New(t) 103 | 104 | r, err := support.DMSToR("15d0m0s") 105 | assert.NoError(err) 106 | assert.InDelta(math.Pi/12.0, r, 1e-6) 107 | 108 | r, err = support.DMSToR("135d0m0s") 109 | assert.NoError(err) 110 | assert.InDelta(math.Pi*0.75, r, 1e-6) 111 | } 112 | 113 | func TestArcseconds(t *testing.T) { 114 | assert := assert.New(t) 115 | 116 | r := support.ConvertArcsecondsToRadians(15.0 * 3600.0) 117 | assert.InDelta(math.Pi/12.0, r, 1e-6) 118 | } 119 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # go-spatial/proj Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing or otherwise unacceptable behavior may be 58 | reported by contacting the project team in the [#go-spatial](https://invite.slack.golangbridge.org/) channel. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | -------------------------------------------------------------------------------- /support/ProjString_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support_test 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/go-spatial/proj/support" 14 | 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestPair(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | pair1 := support.Pair{Key: "k", Value: "v"} 22 | pair2 := support.Pair{Key: "k", Value: "v"} 23 | 24 | assert.Equal(pair1, pair2) 25 | } 26 | 27 | func TestPairListOperations(t *testing.T) { 28 | assert := assert.New(t) 29 | 30 | p1 := support.Pair{Key: "k1", Value: "v1"} 31 | p2 := support.Pair{Key: "k2", Value: "v2"} 32 | 33 | pl1, err := support.NewProjString("") 34 | assert.NoError(err) 35 | assert.NotNil(pl1) 36 | assert.Equal(0, pl1.Len()) 37 | 38 | pl1.Add(p1) 39 | assert.Equal(1, pl1.Len()) 40 | assert.Equal(p1, pl1.Get(0)) 41 | 42 | pl2, err := support.NewProjString("") 43 | assert.NoError(err) 44 | pl2.Add(p2) 45 | 46 | pl1.AddList(pl2) 47 | assert.Equal(2, pl1.Len()) 48 | 49 | assert.Equal("k1", pl1.Get(0).Key) 50 | assert.Equal("k2", pl1.Get(1).Key) 51 | assert.True(pl1.ContainsKey("k1")) 52 | assert.False(pl1.ContainsKey("k3")) 53 | assert.Equal(1, pl1.CountKey("k1")) 54 | 55 | pl1.Add(p1) 56 | assert.Equal(2, pl1.CountKey("k1")) 57 | assert.Equal(0, pl1.CountKey("k3")) 58 | assert.Equal(p1, pl1.Get(2)) 59 | } 60 | 61 | func TestPairListGets(t *testing.T) { 62 | assert := assert.New(t) 63 | 64 | //p1 := support.Pair{Key: "proj", Value: "v1"} 65 | p2 := support.Pair{Key: "k2", Value: "2.2"} 66 | p3 := support.Pair{Key: "k3", Value: "3.0,3.1,3.2"} 67 | p4 := support.Pair{Key: "k4", Value: "678"} 68 | 69 | pl, err := support.NewProjString("proj=v1") 70 | assert.NoError(err) 71 | pl.Add(p2) 72 | pl.Add(p3) 73 | pl.Add(p4) 74 | 75 | _, ok := pl.GetAsString("k99") 76 | assert.False(ok) 77 | 78 | vs, ok := pl.GetAsString("k2") 79 | assert.True(ok) 80 | assert.Equal("2.2", vs) 81 | 82 | vf, ok := pl.GetAsFloat("k2") 83 | assert.True(ok) 84 | assert.Equal(2.2, vf) 85 | 86 | _, ok = pl.GetAsFloat("proj") 87 | assert.False(ok) 88 | 89 | vfs, ok := pl.GetAsFloats("k2") 90 | assert.True(ok) 91 | assert.Equal([]float64{2.2}, vfs) 92 | 93 | vfs, ok = pl.GetAsFloats("k3") 94 | assert.True(ok) 95 | assert.Equal([]float64{3.0, 3.1, 3.2}, vfs) 96 | 97 | vi, ok := pl.GetAsInt("k4") 98 | assert.True(ok) 99 | assert.Equal(678, vi) 100 | } 101 | 102 | func TestPairListParsing(t *testing.T) { 103 | assert := assert.New(t) 104 | 105 | _, err := support.NewProjString("") 106 | assert.NoError(err) 107 | 108 | _, err = support.NewProjString("k1=v1=v2") 109 | assert.Error(err) 110 | 111 | pl, err := support.NewProjString(" +proj=v1 +k2=v2 k3=v3 \t\t k4= k5") 112 | assert.NoError(err) 113 | assert.Equal(4, pl.Len()) 114 | 115 | assert.True(pl.ContainsKey("proj")) 116 | assert.True(pl.ContainsKey("k2")) 117 | assert.True(pl.ContainsKey("k3")) 118 | assert.True(pl.ContainsKey("k4")) 119 | assert.False(pl.ContainsKey("k5")) 120 | assert.False(pl.ContainsKey("+k1")) 121 | 122 | assert.Equal("k5", pl.Get(3).Value) 123 | 124 | assert.True(len(pl.String()) > 10) 125 | } 126 | -------------------------------------------------------------------------------- /gie/Gie.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package gie 9 | 10 | import ( 11 | "fmt" 12 | "io/ioutil" 13 | "strings" 14 | 15 | // need to pull in the operations table entries 16 | _ "github.com/go-spatial/proj/operations" 17 | ) 18 | 19 | // These are the projections we know about. If the projection string has a 20 | // "proj=" key whose valued is not in this list, the Gie will not try to 21 | // execute the Command. 22 | var supportedProjections = []string{ 23 | "etmerc", "utm", 24 | "aea", "leac", 25 | "merc", 26 | "airy", 27 | "august", 28 | "eqc", 29 | } 30 | 31 | // If the proj string has one of these keys, we won't execute the Command. 32 | var unsupportedKeys = []string{ 33 | "axis", 34 | "geoidgrids", 35 | "to_meter", 36 | } 37 | 38 | // If the Command is from this file and line, we won't execute the 39 | // Command -- this acts as a way to shut off tests we don't like. 40 | var skippedTests = []string{ 41 | "ellipsoid.gie:64", 42 | } 43 | 44 | // Gie is the top-level object for the Gie test runner 45 | // 46 | // Gie manages reading and parsing the .gie files and then 47 | // executing the commands and their testcases. 48 | type Gie struct { 49 | dir string 50 | files []string 51 | Commands []*Command 52 | } 53 | 54 | // NewGie returns a new Gie object 55 | func NewGie(dir string) (*Gie, error) { 56 | 57 | g := &Gie{ 58 | dir: dir, 59 | files: []string{}, 60 | Commands: []*Command{}, 61 | } 62 | 63 | infos, err := ioutil.ReadDir(dir) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | for _, info := range infos { 69 | file := info.Name() 70 | if strings.HasSuffix(file, ".gie") { 71 | g.files = append(g.files, dir+"/"+file) 72 | } 73 | } 74 | return g, nil 75 | } 76 | 77 | // Parse reads the .gie files and creates the commands 78 | func (g *Gie) Parse() error { 79 | for _, file := range g.files { 80 | p, err := NewParser(file) 81 | if err != nil { 82 | return err 83 | } 84 | g.Commands = append(g.Commands, p.Commands...) 85 | } 86 | 87 | return nil 88 | } 89 | 90 | // IsSupported returns true iff we support the projection and they aren't asking 91 | // for proj strings things we don't do yet 92 | func (g *Gie) IsSupported(cmd *Command) bool { 93 | 94 | if !g.isSupportedProjection(cmd) { 95 | return false 96 | } 97 | 98 | if g.hasUnsupportedKey(cmd) { 99 | return false 100 | } 101 | 102 | if g.isSkippedTest(cmd) { 103 | return false 104 | } 105 | 106 | return true 107 | } 108 | 109 | func (g *Gie) isSkippedTest(cmd *Command) bool { 110 | 111 | s := fmt.Sprintf("%s:%d", cmd.File, cmd.Line) 112 | for _, skippy := range skippedTests { 113 | if strings.HasSuffix(s, skippy) { 114 | return true 115 | } 116 | } 117 | return false 118 | } 119 | 120 | func (g *Gie) hasUnsupportedKey(cmd *Command) bool { 121 | 122 | for _, badkey := range unsupportedKeys { 123 | if strings.Contains(cmd.ProjString, badkey) { 124 | return true 125 | } 126 | } 127 | return false 128 | } 129 | 130 | func (g *Gie) isSupportedProjection(cmd *Command) bool { 131 | 132 | proj := cmd.ProjectionName() 133 | 134 | for _, sp := range supportedProjections { 135 | if proj == sp { 136 | return true 137 | } 138 | } 139 | 140 | return false 141 | } 142 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Proj License 2 | 3 | Proj is a port of [PROJ.4](http://proj4.org) to the Go language (Golang). 4 | The license terms for this port are the same as the original PROJ.4 terms 5 | (included verbatim below). 6 | 7 | See the `CONTRIBUTORS.md` file for a list of all Proj contributors. 8 | 9 | 10 | # Proj License 11 | 12 | Copyright (c) 2018, Michael P. Gerlek (Flaxen Consulting) 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a 15 | copy of this software and associated documentation files (the "Software"), 16 | to deal in the Software without restriction, including without limitation 17 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 18 | and/or sell copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included 22 | in all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 25 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 | DEALINGS IN THE SOFTWARE. 31 | 32 | 33 | # Copyright Header 34 | 35 | The copyright header for this project's files is: 36 | 37 | ``` 38 | Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 39 | 40 | Portions of this code were derived from the PROJ.4 software. 41 | In keeping with the terms of the PROJ.4 project, this software 42 | is provided under the MIT-style license in `LICENSE.md` and 43 | may additionally be subject to the copyrights of the PROJ.4 authors. 44 | ``` 45 | 46 | 47 | # Original PROJ.4 License 48 | 49 | "All source, data files and other contents of the PROJ.4 package are 50 | available under the following terms. Note that the PROJ 4.3 and earlier 51 | was "public domain" as is common with US government work, but apparently 52 | this is not a well defined legal term in many countries. I am placing 53 | everything under the following MIT style license because I believe it is 54 | effectively the same as public domain, allowing anyone to use the code as 55 | they wish, including making proprietary derivatives." 56 | 57 | "Though I have put my own name as copyright holder, I don't mean to imply 58 | I did the work. Essentially all work was done by Gerald Evenden." 59 | 60 | -------------- 61 | 62 | Copyright (c) 2000, Frank Warmerdam 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining a 65 | copy of this software and associated documentation files (the "Software"), 66 | to deal in the Software without restriction, including without limitation 67 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 68 | and/or sell copies of the Software, and to permit persons to whom the 69 | Software is furnished to do so, subject to the following conditions: 70 | 71 | The above copyright notice and this permission notice shall be included 72 | in all copies or substantial portions of the Software. 73 | 74 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 75 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 76 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 77 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 78 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 79 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 80 | DEALINGS IN THE SOFTWARE. 81 | 82 | -------------------------------------------------------------------------------- /gie/gie_data/GDA.gie: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------------------------------- 2 | Australian datum transformations 3 | ----------------------------------------------------------------------------------- 4 | Based on material from: 5 | 6 | Intergovernmental Committee on Surveying and Mapping (ICSM) 7 | Permanent Committee on Geodesy (PCG): 8 | 9 | Geocentric Datum of Australia 2020 Technical Manual 10 | Version 1.0, 25 July 2017 11 | 12 | Which is distributed under Creative Commons CC-BY 4.0 13 | 14 | These tests will probably be useful as a template for an AU setup file, defining 15 | transformations for Australian systems, but I'm reluctant to provide such a file 16 | myself - it probably should come from official AU sources. 17 | 18 | Thomas Knudsen, thokn@sdfe.dk, 2017-11-27 19 | ----------------------------------------------------------------------------------- 20 | 21 | 22 | 23 | ----------------------------------------------------------------------------------- 24 | GDA94 to GDA2020 25 | ----------------------------------------------------------------------------------- 26 | Just the Helmert transformation, to verify that we are within 100 um 27 | ----------------------------------------------------------------------------------- 28 | operation proj=helmert 29 | x = 0.06155 rx = -0.0394924 30 | y = -0.01087 ry = -0.0327221 31 | z = -0.04019 rz = -0.0328979 s = -0.009994 32 | 33 | ----------------------------------------------------------------------------------- 34 | tolerance 75 um 35 | accept -4052051.7643 4212836.2017 -2545106.0245 36 | expect -4052052.7379 4212835.9897 -2545104.5898 37 | ------------------------------------------------------------------------------- 38 | 39 | 40 | ----------------------------------------------------------------------------------- 41 | GDA94 to GDA2020 42 | ----------------------------------------------------------------------------------- 43 | All the way from geographic-to-cartesian-and-back-to-geographic 44 | ----------------------------------------------------------------------------------- 45 | operation proj = pipeline ellps=GRS80; 46 | step proj = cart; 47 | step proj = helmert 48 | x = 0.06155; rx = -0.0394924; 49 | y = -0.01087; ry = -0.0327221; 50 | z = -0.04019; rz = -0.0328979; s = -0.009994; 51 | step proj = cart inv; 52 | ----------------------------------------------------------------------------------- 53 | tolerance 2 mm 54 | accept 133.88551329 -23.67012389 603.3466 0 # Alice Springs GDA94 55 | expect 133.8855216 -23.67011014 603.2489 0 # Alice Springs GDA2020 56 | ------------------------------------------------------------------------------- 57 | 58 | 59 | ----------------------------------------------------------------------------------- 60 | ITRF2014@2018 to GDA2020 - Test point ALIC (Alice Springs) 61 | ----------------------------------------------------------------------------------- 62 | Just the Helmert transformation, to verify that we are within 100 um 63 | ----------------------------------------------------------------------------------- 64 | operation proj = helmert ellps=GRS80 exact 65 | 66 | x = 0 rx = 0 dx = 0 drx = 0.00150379 67 | y = 0 ry = 0 dy = 0 dry = 0.00118346 68 | z = 0 rz = 0 dz = 0 drz = 0.00120716 69 | 70 | s = 0 ds = 0 t_epoch=2020.0 71 | ----------------------------------------------------------------------------------- 72 | tolerance 40 um 73 | accept -4052052.6588 4212835.9938 -2545104.6946 2018.0 # ITRF2014@2018.0 74 | expect -4052052.7373 4212835.9835 -2545104.5867 # GDA2020 75 | ----------------------------------------------------------------------------------- 76 | 77 | 78 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/sorter.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "reflect" 20 | "unicode" 21 | ) 22 | 23 | type keyList []reflect.Value 24 | 25 | func (l keyList) Len() int { return len(l) } 26 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 27 | func (l keyList) Less(i, j int) bool { 28 | a := l[i] 29 | b := l[j] 30 | ak := a.Kind() 31 | bk := b.Kind() 32 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 33 | a = a.Elem() 34 | ak = a.Kind() 35 | } 36 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 37 | b = b.Elem() 38 | bk = b.Kind() 39 | } 40 | af, aok := keyFloat(a) 41 | bf, bok := keyFloat(b) 42 | if aok && bok { 43 | if af != bf { 44 | return af < bf 45 | } 46 | if ak != bk { 47 | return ak < bk 48 | } 49 | return numLess(a, b) 50 | } 51 | if ak != reflect.String || bk != reflect.String { 52 | return ak < bk 53 | } 54 | ar, br := []rune(a.String()), []rune(b.String()) 55 | digits := false 56 | for i := 0; i < len(ar) && i < len(br); i++ { 57 | if ar[i] == br[i] { 58 | digits = unicode.IsDigit(ar[i]) 59 | continue 60 | } 61 | al := unicode.IsLetter(ar[i]) 62 | bl := unicode.IsLetter(br[i]) 63 | if al && bl { 64 | return ar[i] < br[i] 65 | } 66 | if al || bl { 67 | if digits { 68 | return al 69 | } else { 70 | return bl 71 | } 72 | } 73 | var ai, bi int 74 | var an, bn int64 75 | if ar[i] == '0' || br[i] == '0' { 76 | for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 77 | if ar[j] != '0' { 78 | an = 1 79 | bn = 1 80 | break 81 | } 82 | } 83 | } 84 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 85 | an = an*10 + int64(ar[ai]-'0') 86 | } 87 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 88 | bn = bn*10 + int64(br[bi]-'0') 89 | } 90 | if an != bn { 91 | return an < bn 92 | } 93 | if ai != bi { 94 | return ai < bi 95 | } 96 | return ar[i] < br[i] 97 | } 98 | return len(ar) < len(br) 99 | } 100 | 101 | // keyFloat returns a float value for v if it is a number/bool 102 | // and whether it is a number/bool or not. 103 | func keyFloat(v reflect.Value) (f float64, ok bool) { 104 | switch v.Kind() { 105 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 | return float64(v.Int()), true 107 | case reflect.Float32, reflect.Float64: 108 | return v.Float(), true 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | return float64(v.Uint()), true 111 | case reflect.Bool: 112 | if v.Bool() { 113 | return 1, true 114 | } 115 | return 0, true 116 | } 117 | return 0, false 118 | } 119 | 120 | // numLess returns whether a < b. 121 | // a and b must necessarily have the same kind. 122 | func numLess(a, b reflect.Value) bool { 123 | switch a.Kind() { 124 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 125 | return a.Int() < b.Int() 126 | case reflect.Float32, reflect.Float64: 127 | return a.Float() < b.Float() 128 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 129 | return a.Uint() < b.Uint() 130 | case reflect.Bool: 131 | return !a.Bool() && b.Bool() 132 | } 133 | panic("not a number") 134 | } 135 | -------------------------------------------------------------------------------- /ci/go_test_multi_package_coverprofile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Generate test coverage statistics for Go packages. 3 | # 4 | # Works around the fact that `go test -coverprofile` currently does not work 5 | # with multiple packages, see https://code.google.com/p/go/issues/detail?id=6909 6 | # 7 | # Usage: go_test_multi_package_coverprofile --coverprofilename= [--html] [--coveralls] 8 | # 9 | # --coverprofilename basefilename for coverprofile data 10 | # --html Additionally create HTML report .html 11 | # --coveralls Push coverage statistics to coveralls.io 12 | # 13 | # 14 | # S.C. - borrowed/adapted from https://github.com/mlafeldt/chef-runner/blob/v0.7.0/script/coverage; see also https://mlafeldt.github.io/blog/test-coverage-in-go/ 15 | 16 | set -e 17 | 18 | CI_DIR=`dirname $0` 19 | PROJECT_DIR="$CI_DIR/.." 20 | source $CI_DIR/install_go_bin.sh 21 | 22 | workdir=.cover 23 | coverprofilename=default 24 | 25 | while [ $# -gt 0 ]; do 26 | case "$1" in 27 | --coverprofilename=*) 28 | coverprofilename="${1#*=}" 29 | ;; 30 | --html) 31 | genhtml=true 32 | ;; 33 | --coveralls) 34 | pushtocoveralls=true 35 | ;; 36 | *) 37 | printf "\tError: Invalid argument \"$1\"\n" 38 | exit 1 39 | esac 40 | shift 41 | done 42 | 43 | coverprofile="$workdir/$coverprofilename.coverprofile" 44 | 45 | # from official go test docs: 46 | # -covermode set,count,atomic 47 | # Set the mode for coverage analysis for the package[s] 48 | # being tested. The default is "set" unless -race is enabled, 49 | # in which case it is "atomic". 50 | # The values: 51 | # set: bool: does this statement run? 52 | # count: int: how many times does this statement run? 53 | # atomic: int: count, but correct in multithreaded tests; 54 | # significantly more expensive. 55 | # Sets -cover. 56 | mode=count 57 | 58 | # functions section 59 | generate_cover_data() { 60 | rm -rf "$workdir" 61 | mkdir "$workdir" 62 | 63 | for pkg in "$@"; do 64 | 65 | # Make a list of each proj-related package we depend on (plus ourself). 66 | # We will collect coverage stats on all those packages, not just ourself, 67 | # so that we can get coverage data across package boundaries. 68 | mypkgs=$pkg 69 | for i in `go list -f '{{.Deps}}' $pkg` ; do 70 | if [[ "$i" = "github.com/go-spatial/proj"* ]]; then 71 | mypkgs="$mypkgs,$i" 72 | fi 73 | done 74 | 75 | f="$workdir/$(echo $pkg | tr / -).pkgcoverprofile" 76 | go test -covermode="$mode" -coverprofile="$f" -coverpkg=$mypkgs "$pkg" 77 | done 78 | 79 | echo "mode: $mode" > "$coverprofile" 80 | grep -h -v "^mode:" "$workdir"/*.pkgcoverprofile >> "$coverprofile" 81 | } 82 | 83 | generate_cover_report() { 84 | case ${1} in 85 | html) 86 | go tool cover -html="$coverprofile" -o "$coverprofilename".coverprofile.html 87 | ;; 88 | func) 89 | go tool cover -func="$coverprofile" 90 | ;; 91 | *) 92 | echo >&2 "error: generate_cover_report invalid arg: $1" 93 | exit 1 94 | ;; 95 | esac 96 | } 97 | 98 | push_to_coveralls() { 99 | echo "Pushing coverage statistics to coveralls.io" 100 | goveralls -coverprofile="$coverprofile" 101 | } 102 | 103 | 104 | 105 | # body of script 106 | # first get go cover tool in case it does not exist locally 107 | go_install golang.org/x/tools/cmd/cover 108 | # generate coverage data, but skip the vendor directory. 109 | # skipping the vendor directory is necessary for go versions less than Go 1.9.x 110 | generate_cover_data $(go list ./... | grep -v vendor) 111 | generate_cover_report func 112 | if [ "$genhtml" = true ] ; then 113 | generate_cover_report html 114 | fi 115 | if [ "$pushtocoveralls" = true ] ; then 116 | go_install github.com/mattn/goveralls 117 | push_to_coveralls 118 | fi 119 | -------------------------------------------------------------------------------- /operations/Airy.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/merror" 14 | 15 | "github.com/go-spatial/proj/core" 16 | "github.com/go-spatial/proj/support" 17 | ) 18 | 19 | func init() { 20 | core.RegisterConvertLPToXY("airy", 21 | "Airy", 22 | "\n\tMisc Sph, no inv.\n\tno_cut lat_b=", 23 | NewAiry, 24 | ) 25 | } 26 | 27 | // Airy implements core.IOperation and core.ConvertLPToXY 28 | type Airy struct { 29 | core.Operation 30 | phalfpi float64 31 | sinph0 float64 32 | cosph0 float64 33 | Cb float64 34 | mode mode 35 | nocut bool /* do not cut at hemisphere limit */ 36 | } 37 | 38 | // NewAiry returns a new Airy 39 | func NewAiry(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 40 | op := &Airy{} 41 | op.System = system 42 | 43 | err := op.setup(system) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return op, nil 48 | } 49 | 50 | // Forward goes forewards 51 | func (op *Airy) Forward(lp *core.CoordLP) (*core.CoordXY, error) { 52 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 53 | 54 | Q := op 55 | 56 | var sinlam, coslam, cosphi, sinphi, t, Krho, cosz float64 57 | 58 | sinlam = math.Sin(lp.Lam) 59 | coslam = math.Cos(lp.Lam) 60 | 61 | switch Q.mode { 62 | 63 | case modeEquit, modeObliq: 64 | sinphi = math.Sin(lp.Phi) 65 | cosphi = math.Cos(lp.Phi) 66 | cosz = cosphi * coslam 67 | if Q.mode == modeObliq { 68 | cosz = Q.sinph0*sinphi + Q.cosph0*cosz 69 | } 70 | if !Q.nocut && cosz < -eps10 { 71 | return nil, merror.New(merror.ToleranceCondition) 72 | } 73 | s := 1. - cosz 74 | if math.Abs(s) > eps10 { 75 | t = 0.5 * (1. + cosz) 76 | Krho = -math.Log(t)/s - Q.Cb/t 77 | } else { 78 | Krho = 0.5 - Q.Cb 79 | } 80 | xy.X = Krho * cosphi * sinlam 81 | if Q.mode == modeObliq { 82 | xy.Y = Krho * (Q.cosph0*sinphi - Q.sinph0*cosphi*coslam) 83 | } else { 84 | xy.Y = Krho * sinphi 85 | } 86 | 87 | case modeSPole, modeNPole: 88 | lp.Phi = math.Abs(Q.phalfpi - lp.Phi) 89 | if !Q.nocut && (lp.Phi-eps10) > support.PiOverTwo { 90 | return nil, merror.New(merror.ToleranceCondition) 91 | } 92 | lp.Phi *= 0.5 93 | if lp.Phi > eps10 { 94 | t = math.Tan(lp.Phi) 95 | Krho = -2. * (math.Log(math.Cos(lp.Phi))/t + t*Q.Cb) 96 | xy.X = Krho * sinlam 97 | xy.Y = Krho * coslam 98 | if Q.mode == modeNPole { 99 | xy.Y = -xy.Y 100 | } 101 | } else { 102 | xy.X = 0. 103 | xy.Y = 0. 104 | } 105 | } 106 | 107 | return xy, nil 108 | } 109 | 110 | // Inverse is not allowed 111 | func (*Airy) Inverse(*core.CoordXY) (*core.CoordLP, error) { 112 | panic("no such conversion") 113 | } 114 | 115 | func (op *Airy) setup(sys *core.System) error { 116 | var beta float64 117 | 118 | Q := op 119 | P := op.System 120 | PE := op.System.Ellipsoid 121 | 122 | Q.nocut = P.ProjString.ContainsKey("no_cut") 123 | latb, ok := P.ProjString.GetAsFloat("lat_b") 124 | if !ok { 125 | latb = 0.0 126 | } 127 | latb = support.DDToR(latb) 128 | 129 | beta = 0.5 * (support.PiOverTwo - latb) 130 | if math.Abs(beta) < eps10 { 131 | Q.Cb = -0.5 132 | } else { 133 | Q.Cb = 1. / math.Tan(beta) 134 | Q.Cb *= Q.Cb * math.Log(math.Cos(beta)) 135 | } 136 | 137 | if math.Abs(math.Abs(P.Phi0)-support.PiOverTwo) < eps10 { 138 | if P.Phi0 < 0. { 139 | Q.phalfpi = -support.PiOverTwo 140 | Q.mode = modeSPole 141 | } else { 142 | Q.phalfpi = support.PiOverTwo 143 | Q.mode = modeNPole 144 | } 145 | } else { 146 | if math.Abs(P.Phi0) < eps10 { 147 | Q.mode = modeEquit 148 | } else { 149 | Q.mode = modeObliq 150 | Q.sinph0 = math.Sin(P.Phi0) 151 | Q.cosph0 = math.Cos(P.Phi0) 152 | } 153 | } 154 | 155 | PE.Es = 0. 156 | 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.2, but preserves some behavior 16 | from 1.1 for backwards compatibility. 17 | 18 | Specifically, as of v3 of the yaml package: 19 | 20 | - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being 21 | decoded into a typed bool value. Otherwise they behave as a string. Booleans 22 | in YAML 1.2 are _true/false_ only. 23 | - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ 24 | as specified in YAML 1.2, because most parsers still use the old format. 25 | Octals in the _0o777_ format are supported though, so new files work. 26 | - Does not support base-60 floats. These are gone from YAML 1.2, and were 27 | actually never supported by this package as it's clearly a poor choice. 28 | 29 | and offers backwards 30 | compatibility with YAML 1.1 in some cases. 31 | 1.2, including support for 32 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 33 | implemented, and base-60 floats from YAML 1.1 are purposefully not 34 | supported since they're a poor design and are gone in YAML 1.2. 35 | 36 | Installation and usage 37 | ---------------------- 38 | 39 | The import path for the package is *gopkg.in/yaml.v3*. 40 | 41 | To install it, run: 42 | 43 | go get gopkg.in/yaml.v3 44 | 45 | API documentation 46 | ----------------- 47 | 48 | If opened in a browser, the import path itself leads to the API documentation: 49 | 50 | - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) 51 | 52 | API stability 53 | ------------- 54 | 55 | The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). 56 | 57 | 58 | License 59 | ------- 60 | 61 | The yaml package is licensed under the MIT and Apache License 2.0 licenses. 62 | Please see the LICENSE file for details. 63 | 64 | 65 | Example 66 | ------- 67 | 68 | ```Go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | "log" 74 | 75 | "gopkg.in/yaml.v3" 76 | ) 77 | 78 | var data = ` 79 | a: Easy! 80 | b: 81 | c: 2 82 | d: [3, 4] 83 | ` 84 | 85 | // Note: struct fields must be public in order for unmarshal to 86 | // correctly populate the data. 87 | type T struct { 88 | A string 89 | B struct { 90 | RenamedC int `yaml:"c"` 91 | D []int `yaml:",flow"` 92 | } 93 | } 94 | 95 | func main() { 96 | t := T{} 97 | 98 | err := yaml.Unmarshal([]byte(data), &t) 99 | if err != nil { 100 | log.Fatalf("error: %v", err) 101 | } 102 | fmt.Printf("--- t:\n%v\n\n", t) 103 | 104 | d, err := yaml.Marshal(&t) 105 | if err != nil { 106 | log.Fatalf("error: %v", err) 107 | } 108 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 109 | 110 | m := make(map[interface{}]interface{}) 111 | 112 | err = yaml.Unmarshal([]byte(data), &m) 113 | if err != nil { 114 | log.Fatalf("error: %v", err) 115 | } 116 | fmt.Printf("--- m:\n%v\n\n", m) 117 | 118 | d, err = yaml.Marshal(&m) 119 | if err != nil { 120 | log.Fatalf("error: %v", err) 121 | } 122 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 123 | } 124 | ``` 125 | 126 | This example will generate the following output: 127 | 128 | ``` 129 | --- t: 130 | {Easy! {2 [3 4]}} 131 | 132 | --- t dump: 133 | a: Easy! 134 | b: 135 | c: 2 136 | d: [3, 4] 137 | 138 | 139 | --- m: 140 | map[a:Easy! b:map[c:2 d:[3 4]]] 141 | 142 | --- m dump: 143 | a: Easy! 144 | b: 145 | c: 2 146 | d: 147 | - 3 148 | - 4 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /support/EllipsoidsTable.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | // EllipsoidsTableEntry represents a constant ellipsoid 11 | type EllipsoidsTableEntry struct { 12 | ID string 13 | Major string 14 | Ell string 15 | Name string 16 | } 17 | 18 | // EllipsoidsTable is the global table of ellipsoid constants 19 | var EllipsoidsTable = map[string]*EllipsoidsTableEntry{ 20 | "MERIT": {"MERIT", "a=6378137.0", "rf=298.257", "MERIT 1983"}, 21 | "SGS85": {"SGS85", "a=6378136.0", "rf=298.257", "Soviet Geodetic System 85"}, 22 | "GRS80": {"GRS80", "a=6378137.0", "rf=298.257222101", "GRS 1980(IUGG, 1980)"}, 23 | "IAU76": {"IAU76", "a=6378140.0", "rf=298.257", "IAU 1976"}, 24 | "airy": {"airy", "a=6377563.396", "b=6356256.910", "Airy 1830"}, 25 | "APL4.9": {"APL4.9", "a=6378137.0.", "rf=298.25", "Appl. Physics. 1965"}, 26 | "NWL9D": {"NWL9D", "a=6378145.0.", "rf=298.25", "Naval Weapons Lab., 1965"}, 27 | "mod_airy": {"mod_airy", "a=6377340.189", "b=6356034.446", "Modified Airy"}, 28 | "andrae": {"andrae", "a=6377104.43", "rf=300.0", "Andrae 1876 (Den., Iclnd.)"}, 29 | "danish": {"danish", "a=6377019.2563", "rf=300.0", "Andrae 1876 (Denmark, Iceland)"}, 30 | "aust_SA": {"aust_SA", "a=6378160.0", "rf=298.25", "Australian Natl & S. Amer. 1969"}, 31 | "GRS67": {"GRS67", "a=6378160.0", "rf=298.2471674270", "GRS 67(IUGG 1967)"}, 32 | "GSK2011": {"GSK2011", "a=6378136.5", "rf=298.2564151", "GSK-2011"}, 33 | "bessel": {"bessel", "a=6377397.155", "rf=299.1528128", "Bessel 1841"}, 34 | "bess_nam": {"bess_nam", "a=6377483.865", "rf=299.1528128", "Bessel 1841 (Namibia)"}, 35 | "clrk66": {"clrk66", "a=6378206.4", "b=6356583.8", "Clarke 1866"}, 36 | "clrk80": {"clrk80", "a=6378249.145", "rf=293.4663", "Clarke 1880 mod."}, 37 | "clrk80ign": {"clrk80ign", "a=6378249.2", "rf=293.4660212936269", "Clarke 1880 (IGN)."}, 38 | "CPM": {"CPM", "a=6375738.7", "rf=334.29", "Comm. des Poids et Mesures 1799"}, 39 | "delmbr": {"delmbr", "a=6376428.", "rf=311.5", "Delambre 1810 (Belgium)"}, 40 | "engelis": {"engelis", "a=6378136.05", "rf=298.2566", "Engelis 1985"}, 41 | "evrst30": {"evrst30", "a=6377276.345", "rf=300.8017", "Everest 1830"}, 42 | "evrst48": {"evrst48", "a=6377304.063", "rf=300.8017", "Everest 1948"}, 43 | "evrst56": {"evrst56", "a=6377301.243", "rf=300.8017", "Everest 1956"}, 44 | "evrst69": {"evrst69", "a=6377295.664", "rf=300.8017", "Everest 1969"}, 45 | "evrstSS": {"evrstSS", "a=6377298.556", "rf=300.8017", "Everest (Sabah & Sarawak)"}, 46 | "fschr60": {"fschr60", "a=6378166.", "rf=298.3", "Fischer (Mercury Datum) 1960"}, 47 | "fschr60m": {"fschr60m", "a=6378155.", "rf=298.3", "Modified Fischer 1960"}, 48 | "fschr68": {"fschr68", "a=6378150.", "rf=298.3", "Fischer 1968"}, 49 | "helmert": {"helmert", "a=6378200.", "rf=298.3", "Helmert 1906"}, 50 | "hough": {"hough", "a=6378270.0", "rf=297.", "Hough"}, 51 | "intl": {"intl", "a=6378388.0", "rf=297.", "International 1909 (Hayford)"}, 52 | "krass": {"krass", "a=6378245.0", "rf=298.3", "Krassovsky, 1942"}, 53 | "kaula": {"kaula", "a=6378163.", "rf=298.24", "Kaula 1961"}, 54 | "lerch": {"lerch", "a=6378139.", "rf=298.257", "Lerch 1979"}, 55 | "mprts": {"mprts", "a=6397300.", "rf=191.", "Maupertius 1738"}, 56 | "new_intl": {"new_intl", "a=6378157.5", "b=6356772.2", "New International 1967"}, 57 | "plessis": {"plessis", "a=6376523.", "b=6355863.", "Plessis 1817 (France)"}, 58 | "PZ90": {"PZ90", "a=6378136.0", "rf=298.25784", "PZ-90"}, 59 | "SEasia": {"SEasia", "a=6378155.0", "b=6356773.3205", "Southeast Asia"}, 60 | "walbeck": {"walbeck", "a=6376896.0", "b=6355834.8467", "Walbeck"}, 61 | "WGS60": {"WGS60", "a=6378165.0", "rf=298.3", "WGS 60"}, 62 | "WGS66": {"WGS66", "a=6378145.0", "rf=298.25", "WGS 66"}, 63 | "WGS72": {"WGS72", "a=6378135.0", "rf=298.26", "WGS 72"}, 64 | "WGS84": {"WGS84", "a=6378137.0", "rf=298.257223563", "WGS 84"}, 65 | "sphere": {"sphere", "a=6370997.0", "b=6370997.0", "Normal Sphere (r=6370997)"}, 66 | } 67 | -------------------------------------------------------------------------------- /operations/Merc.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/core" 14 | "github.com/go-spatial/proj/merror" 15 | "github.com/go-spatial/proj/support" 16 | ) 17 | 18 | func init() { 19 | core.RegisterConvertLPToXY("merc", 20 | "Universal Transverse Mercator (UTM)", 21 | "\n\tCyl, Sph&Ell\n\tlat_ts=", 22 | NewMerc, 23 | ) 24 | } 25 | 26 | // Merc implements core.IOperation and core.ConvertLPToXY 27 | type Merc struct { 28 | core.Operation 29 | isSphere bool 30 | } 31 | 32 | // NewMerc returns a new Merc 33 | func NewMerc(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 34 | op := &Merc{ 35 | isSphere: false, 36 | } 37 | op.System = system 38 | 39 | err := op.mercSetup(system) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return op, nil 44 | } 45 | 46 | // Forward goes forewards 47 | func (op *Merc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { 48 | 49 | if op.isSphere { 50 | return op.sphericalForward(lp) 51 | } 52 | return op.ellipsoidalForward(lp) 53 | } 54 | 55 | // Inverse goes backwards 56 | func (op *Merc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { 57 | 58 | if op.isSphere { 59 | return op.sphericalInverse(xy) 60 | } 61 | return op.ellipsoidalInverse(xy) 62 | } 63 | 64 | //--------------------------------------------------------------------- 65 | 66 | func (op *Merc) ellipsoidalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Ellipsoidal, forward */ 67 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 68 | 69 | P := op.System 70 | PE := op.System.Ellipsoid 71 | 72 | if math.Abs(math.Abs(lp.Phi)-support.PiOverTwo) <= eps10 { 73 | return xy, merror.New(merror.ToleranceCondition) 74 | } 75 | xy.X = P.K0 * lp.Lam 76 | xy.Y = -P.K0 * math.Log(support.Tsfn(lp.Phi, math.Sin(lp.Phi), PE.E)) 77 | return xy, nil 78 | } 79 | 80 | func (op *Merc) sphericalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Spheroidal, forward */ 81 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 82 | 83 | P := op.System 84 | 85 | if math.Abs(math.Abs(lp.Phi)-support.PiOverTwo) <= eps10 { 86 | return xy, merror.New(merror.ToleranceCondition) 87 | } 88 | xy.X = P.K0 * lp.Lam 89 | xy.Y = P.K0 * math.Log(math.Tan(support.PiOverFour+.5*lp.Phi)) 90 | return xy, nil 91 | } 92 | 93 | func (op *Merc) ellipsoidalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Ellipsoidal, inverse */ 94 | lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} 95 | 96 | P := op.System 97 | PE := op.System.Ellipsoid 98 | var err error 99 | 100 | lp.Phi, err = support.Phi2(math.Exp(-xy.Y/P.K0), PE.E) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if lp.Phi == math.MaxFloat64 { 105 | return lp, merror.New(merror.ToleranceCondition) 106 | } 107 | lp.Lam = xy.X / P.K0 108 | return lp, nil 109 | } 110 | 111 | func (op *Merc) sphericalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Spheroidal, inverse */ 112 | lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} 113 | 114 | P := op.System 115 | 116 | lp.Phi = support.PiOverTwo - 2.*math.Atan(math.Exp(-xy.Y/P.K0)) 117 | lp.Lam = xy.X / P.K0 118 | return lp, nil 119 | } 120 | 121 | func (op *Merc) mercSetup(sys *core.System) error { 122 | var phits float64 123 | 124 | ps := op.System.ProjString 125 | 126 | isPhits := ps.ContainsKey("lat_ts") 127 | if isPhits { 128 | phits, _ = ps.GetAsFloat("lat_ts") 129 | phits = support.DDToR(phits) 130 | phits = math.Abs(phits) 131 | if phits >= support.PiOverTwo { 132 | return merror.New(merror.LatTSLargerThan90) 133 | } 134 | } 135 | 136 | P := op.System 137 | PE := op.System.Ellipsoid 138 | 139 | if PE.Es != 0.0 { /* ellipsoid */ 140 | op.isSphere = false 141 | if isPhits { 142 | P.K0 = support.Msfn(math.Sin(phits), math.Cos(phits), PE.Es) 143 | } 144 | } else { /* sphere */ 145 | op.isSphere = true 146 | if isPhits { 147 | P.K0 = math.Cos(phits) 148 | } 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /mlog/Log.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package mlog 9 | 10 | import ( 11 | "encoding/json" 12 | "fmt" 13 | "io" 14 | "log" 15 | "os" 16 | ) 17 | 18 | // DebugEnabled controls whether Debug log messages are generated 19 | var DebugEnabled = false 20 | 21 | // InfoEnabled controls whether "regular" log messages are generated 22 | var InfoEnabled = true 23 | 24 | // ErrorEnabled controls whether Error log messages are generated 25 | var ErrorEnabled = true 26 | 27 | func EnableError() { 28 | defaultLogger.EnableError() 29 | } 30 | 31 | func EnableInfo() { 32 | defaultLogger.EnableInfo() 33 | } 34 | 35 | func EnableDebug() { 36 | defaultLogger.EnableDebug() 37 | } 38 | 39 | func DisableError() { 40 | defaultLogger.DisableError() 41 | } 42 | 43 | func DisableInfo() { 44 | defaultLogger.DisableInfo() 45 | } 46 | 47 | func DisableDebug() { 48 | defaultLogger.DisableDebug() 49 | } 50 | 51 | // Debugf writes a debug message to stderr 52 | func Debugf(format string, v ...interface{}) { 53 | if !defaultLogger.debug.enabled { 54 | return 55 | } 56 | _ = defaultLogger.debug.Output(2, fmt.Sprintf(format, v...)) 57 | } 58 | 59 | // Printf writes a regular log message to stderr 60 | func Printf(format string, v ...interface{}) { 61 | if !defaultLogger.info.enabled { 62 | return 63 | } 64 | _ = defaultLogger.info.Output(2, fmt.Sprintf(format, v...)) 65 | } 66 | 67 | // Printv writes a variable as a regular log message to stderr 68 | // 69 | // TODO: would be nice if this could print the variable name 70 | // (and ideally the private fields too, if reflection allows 71 | // us access to them) 72 | func Printv(v interface{}) error { 73 | if !defaultLogger.info.enabled { 74 | return nil 75 | } 76 | b, err := json.MarshalIndent(v, "", " ") 77 | if err != nil { 78 | return fmt.Errorf("failed to marshal indent: %w", err) 79 | } 80 | return defaultLogger.info.Output(2, string(b)) 81 | } 82 | 83 | // Error writes an error message to stderr 84 | func Error(err error) { 85 | if !defaultLogger.error.enabled { 86 | return 87 | } 88 | _ = defaultLogger.error.Output(2, err.Error()) 89 | } 90 | 91 | type Outputer interface { 92 | // Output writes the output for a logging event. 93 | // The string s contains the text to print after the prefix specified by the flags of the Logger. 94 | // A newline is appended if the last character of s is not already a newline. 95 | // calldepth is used to recover the PC and is provided for generality, although at the moment on 96 | // all pre-defined paths it will be 2 97 | Output(calldepth int, s string) error 98 | } 99 | 100 | var defaultLogger = NewLoggerSingleOutput(os.Stderr) 101 | 102 | type levelLogger struct { 103 | enabled bool 104 | Outputer 105 | } 106 | 107 | type Logger struct { 108 | info levelLogger 109 | error levelLogger 110 | debug levelLogger 111 | } 112 | 113 | func (l Logger) Debugf(format string, v ...interface{}) error { 114 | if !l.debug.enabled { 115 | return nil 116 | } 117 | return l.debug.Output(2, fmt.Sprintf(format, v...)) 118 | } 119 | func (l Logger) Printf(format string, v ...interface{}) error { 120 | if !l.info.enabled { 121 | return nil 122 | } 123 | return l.info.Output(2, fmt.Sprintf(format, v...)) 124 | } 125 | 126 | func (l Logger) Printv(v interface{}) error { 127 | if !l.info.enabled { 128 | return nil 129 | } 130 | b, err := json.MarshalIndent(v, "", " ") 131 | if err != nil { 132 | return err 133 | } 134 | s := string(b) 135 | return l.info.Output(2, s) 136 | } 137 | 138 | func (l Logger) Error(err error) error { 139 | if !l.error.enabled { 140 | return nil 141 | } 142 | return l.error.Output(2, err.Error()) 143 | } 144 | 145 | func (l *Logger) EnableError() { 146 | l.error.enabled = true 147 | } 148 | 149 | func (l *Logger) EnableInfo() { 150 | l.info.enabled = true 151 | } 152 | 153 | func (l *Logger) EnableDebug() { 154 | l.debug.enabled = true 155 | } 156 | 157 | func (l *Logger) DisableError() { 158 | l.error.enabled = false 159 | } 160 | 161 | func (l *Logger) DisableInfo() { 162 | l.info.enabled = false 163 | } 164 | 165 | func (l *Logger) DisableDebug() { 166 | l.debug.enabled = false 167 | } 168 | 169 | func NewLoggerSingleOutput(w io.Writer) Logger { 170 | return Logger{ 171 | info: levelLogger{ 172 | Outputer: log.New(w, "[LOG] ", log.Lshortfile), 173 | }, 174 | debug: levelLogger{ 175 | Outputer: log.New(w, "[DEBUG] ", log.Lshortfile), 176 | }, 177 | error: levelLogger{ 178 | Outputer: log.New(w, "[ERROR] ", 0), 179 | }, 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /cmd/proj/proj.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "fmt" 13 | "io" 14 | "os" 15 | "strings" 16 | 17 | "github.com/go-spatial/proj" 18 | 19 | "github.com/go-spatial/proj/core" 20 | "github.com/go-spatial/proj/merror" 21 | "github.com/go-spatial/proj/mlog" 22 | "github.com/go-spatial/proj/support" 23 | ) 24 | 25 | func main() { 26 | err := Main(os.Stdin, os.Stdout, os.Args) 27 | if err != nil { 28 | fmt.Fprintf(os.Stderr, err.Error()+"\n") 29 | os.Exit(1) 30 | } 31 | } 32 | 33 | // Main is just a callable version of main(), for testing purposes 34 | func Main(inS io.Reader, outS io.Writer, args []string) error { 35 | 36 | // unverbosify all the things 37 | merror.ShowSource = false 38 | mlog.DisableDebug() 39 | mlog.DisableInfo() 40 | mlog.DisableError() 41 | 42 | cli := flag.NewFlagSet(args[0], flag.ContinueOnError) 43 | cli.SetOutput(outS) 44 | 45 | verbose := cli.Bool("verbose", false, "enable logging") 46 | inverse := cli.Bool("inverse", false, "run the inverse transform") 47 | epsgDest := cli.Int("epsg", 0, "perform conversion from 4326 to given destination system") 48 | 49 | err := cli.Parse(args[1:]) 50 | if err != nil { 51 | return err 52 | } 53 | projString := strings.Join(cli.Args(), " ") 54 | 55 | if *verbose { 56 | mlog.Printf("verbose: %t", *verbose) 57 | mlog.Printf("inverse: %t", *inverse) 58 | if *epsgDest == 0 { 59 | mlog.Printf("epsg: (not specified)") 60 | } else { 61 | mlog.Printf("epsg: %d", epsgDest) 62 | } 63 | if projString == "" { 64 | mlog.Printf("proj: (not specified)") 65 | } else { 66 | mlog.Printf("proj: %s", projString) 67 | } 68 | 69 | merror.ShowSource = true 70 | mlog.EnableDebug() 71 | mlog.EnableInfo() 72 | mlog.EnableError() 73 | } 74 | 75 | // handle "-epsg" usage, using the Convert API 76 | if *epsgDest != 0 { 77 | if *inverse { 78 | return fmt.Errorf("-inverse not allowed with -epsg") 79 | } 80 | if projString != "" { 81 | return fmt.Errorf("projection string not allowed with -epsg") 82 | } 83 | input := make([]float64, 2) 84 | 85 | // wrap the converter in a little lambda to be run inside a REPL loop 86 | f := func(a, b float64) (float64, float64, error) { 87 | input[0] = a 88 | input[1] = b 89 | output, err := proj.Convert(proj.EPSGCode(*epsgDest), input) 90 | if err != nil { 91 | return 0.0, 0.0, err 92 | } 93 | return output[0], output[1], nil 94 | } 95 | 96 | return repl(inS, outS, f) 97 | } 98 | 99 | // args is a proj string, so use the Core API 100 | 101 | // parse the proj string into key/value pairs 102 | ps, err := support.NewProjString(projString) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | // make a coordinate system object, and the operation object 108 | _, opx, err := core.NewSystem(ps) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | // we only support one kind of operation object right now anyway 114 | op := opx.(core.IConvertLPToXY) 115 | 116 | // make a lambda with the forward or inverse function, and 117 | // send it to the REPL loop 118 | if !*inverse { 119 | 120 | f := func(a, b float64) (float64, float64, error) { 121 | input := &core.CoordLP{Lam: support.DDToR(a), Phi: support.DDToR(b)} 122 | output, err := op.Forward(input) 123 | if err != nil { 124 | return 0.0, 0.0, err 125 | } 126 | return output.X, output.Y, nil 127 | } 128 | return repl(inS, outS, f) 129 | } 130 | 131 | f := func(a, b float64) (float64, float64, error) { 132 | input := &core.CoordXY{X: a, Y: b} 133 | output, err := op.Inverse(input) 134 | if err != nil { 135 | return 0.0, 0.0, err 136 | } 137 | return support.RToDD(output.Lam), support.RToDD(output.Phi), nil 138 | } 139 | 140 | return repl(inS, outS, f) 141 | } 142 | 143 | // the type of our lambdas 144 | type converter func(a, b float64) (float64, float64, error) 145 | 146 | // the repl loop reads two input numbers, runs the conversion 147 | // (which has been wrapped up into a tidy little lambda), 148 | // and prints the results 149 | func repl(inS io.Reader, outS io.Writer, f converter) error { 150 | 151 | var a, b float64 152 | 153 | for { 154 | n, err := fmt.Fscanf(inS, "%f %f\n", &a, &b) 155 | if err == io.EOF { 156 | return nil 157 | } 158 | if err != nil { 159 | return err 160 | } 161 | if n != 2 { 162 | return fmt.Errorf("error reading input") 163 | } 164 | 165 | c, d, err := f(a, b) 166 | if err != nil { 167 | return err 168 | } 169 | 170 | fmt.Fprintf(outS, "%f %f\n", c, d) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /Convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package proj_test 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | var inputA = []float64{ 19 | -0.127758, 51.507351, // London 20 | 2.352222, 48.856614, // Paris 21 | 12.496366, 41.902783, // Rome 22 | } 23 | var inputB = []float64{ 24 | -77.625583, 38.833846, // mpg 25 | } 26 | 27 | type testcase struct { 28 | dest proj.EPSGCode 29 | expectedA []float64 30 | expectedB []float64 31 | } 32 | 33 | var testcases = []testcase{ 34 | { 35 | dest: proj.EPSG3395, 36 | expectedA: []float64{ 37 | -14221.96, 6678068.96, 38 | 261848.16, 6218371.80, 39 | 1391089.10, 5117883.04, 40 | }, 41 | expectedB: []float64{ 42 | -8641240.37, 4671101.60, 43 | }, 44 | }, 45 | { 46 | dest: proj.EPSG3857, 47 | expectedA: []float64{ 48 | -14221.96, 6711533.71, 49 | 261848.16, 6250566.72, 50 | 1391089.10, 5146427.91, 51 | }, 52 | expectedB: []float64{ 53 | -8641240.37, 4697899.31, 54 | }, 55 | }, 56 | { 57 | dest: proj.EPSG4087, 58 | expectedA: []float64{ 59 | -14221.96, 5733772.09, 60 | 261848.16, 5438693.39, 61 | 1391089.10, 4664596.47, 62 | }, 63 | expectedB: []float64{ 64 | -8641240.37, 4322963.96, 65 | }, 66 | }, 67 | } 68 | 69 | func TestConvert(t *testing.T) { 70 | assert := assert.New(t) 71 | 72 | for _, tc := range testcases { 73 | 74 | outputA, err := proj.Convert(tc.dest, inputA) 75 | assert.NoError(err) 76 | 77 | outputB, err := proj.Convert(tc.dest, inputB) 78 | assert.NoError(err) 79 | 80 | invA, err := proj.Inverse(tc.dest, tc.expectedA) 81 | assert.NoError(err) 82 | 83 | invB, err := proj.Inverse(tc.dest, tc.expectedB) 84 | assert.NoError(err) 85 | 86 | const tol = 1.0e-2 87 | 88 | for i := range tc.expectedA { 89 | tag := fmt.Sprintf("epsg:%d, input=A.%d", int(tc.dest), i) 90 | assert.InDelta(tc.expectedA[i], outputA[i], tol, tag) 91 | assert.InDelta(tc.expectedA[i], outputA[i], tol, tag) 92 | } 93 | for i := range tc.expectedB { 94 | tag := fmt.Sprintf("epsg:%d, input=B.%d", int(tc.dest), i) 95 | assert.InDelta(tc.expectedB[i], outputB[i], tol, tag) 96 | assert.InDelta(tc.expectedB[i], outputB[i], tol, tag) 97 | } 98 | 99 | for i := range tc.expectedA { 100 | tag := fmt.Sprintf("inverse: epsg:%d, input=A.%d", int(tc.dest), i) 101 | assert.InDelta(invA[i], inputA[i], tol, tag) 102 | } 103 | 104 | for i := range tc.expectedB { 105 | tag := fmt.Sprintf("inverse: epsg:%d, input=B.%d", int(tc.dest), i) 106 | assert.InDelta(invB[i], inputB[i], tol, tag) 107 | } 108 | } 109 | } 110 | 111 | func TestEnsureRaisedError(t *testing.T) { 112 | type testcase struct { 113 | op string 114 | pt []float64 115 | expectedErr string 116 | srid proj.EPSGCode 117 | } 118 | 119 | fn := func(tc testcase) func(t *testing.T) { 120 | return func(t *testing.T) { 121 | var err error 122 | 123 | if tc.op == "convert" { 124 | _, err = proj.Convert(proj.EPSGCode(tc.srid), tc.pt) 125 | } else { 126 | _, err = proj.Inverse(proj.EPSGCode(tc.srid), tc.pt) 127 | } 128 | 129 | if err == nil { 130 | t.Errorf("didn't get expected error: %v", tc.expectedErr) 131 | return 132 | } 133 | 134 | if err.Error() != tc.expectedErr { 135 | t.Errorf("error: %v not equal to expected error: %v", err.Error(), tc.expectedErr) 136 | } 137 | } 138 | } 139 | 140 | tests := map[string]testcase{ 141 | "3857 out of bounds WGS84": { 142 | op: "convert", 143 | srid: proj.WebMercator, 144 | pt: []float64{-180.0, 90.0}, 145 | expectedErr: "tolerance condition error", 146 | }, 147 | "4326 not supported as source srid": { 148 | op: "convert", 149 | srid: proj.EPSG4326, 150 | pt: []float64{0, 0}, 151 | expectedErr: "epsg code is not a supported projection", 152 | }, 153 | "convert bad point count": { 154 | op: "convert", 155 | srid: proj.WorldMercator, 156 | pt: []float64{-180.0, 90.0, 11.0}, 157 | expectedErr: "input array of lon/lat values must be an even number", 158 | }, 159 | "inverse bad point count": { 160 | op: "inverse", 161 | srid: proj.WorldMercator, 162 | pt: []float64{-180.0, 90.0, 11.0}, 163 | expectedErr: "input array of x/y values must be an even number", 164 | }, 165 | } 166 | 167 | for name, tc := range tests { 168 | t.Run(name, fn(tc)) 169 | } 170 | } 171 | 172 | func ExampleConvert() { 173 | 174 | var dd = []float64{ 175 | -77.625583, 38.833846, 176 | } 177 | 178 | xy, err := proj.Convert(proj.EPSG3395, dd) 179 | if err != nil { 180 | panic(err) 181 | } 182 | 183 | fmt.Printf("%.2f, %.2f\n", xy[0], xy[1]) 184 | // Output: -8641240.37, 4671101.60 185 | } 186 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // Go versions prior to 1.4 are disabled because they use a different layout 20 | // for interfaces which make the implementation of unsafeReflectValue more complex. 21 | // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 | 23 | package spew 24 | 25 | import ( 26 | "reflect" 27 | "unsafe" 28 | ) 29 | 30 | const ( 31 | // UnsafeDisabled is a build-time constant which specifies whether or 32 | // not access to the unsafe package is available. 33 | UnsafeDisabled = false 34 | 35 | // ptrSize is the size of a pointer on the current arch. 36 | ptrSize = unsafe.Sizeof((*byte)(nil)) 37 | ) 38 | 39 | type flag uintptr 40 | 41 | var ( 42 | // flagRO indicates whether the value field of a reflect.Value 43 | // is read-only. 44 | flagRO flag 45 | 46 | // flagAddr indicates whether the address of the reflect.Value's 47 | // value may be taken. 48 | flagAddr flag 49 | ) 50 | 51 | // flagKindMask holds the bits that make up the kind 52 | // part of the flags field. In all the supported versions, 53 | // it is in the lower 5 bits. 54 | const flagKindMask = flag(0x1f) 55 | 56 | // Different versions of Go have used different 57 | // bit layouts for the flags type. This table 58 | // records the known combinations. 59 | var okFlags = []struct { 60 | ro, addr flag 61 | }{{ 62 | // From Go 1.4 to 1.5 63 | ro: 1 << 5, 64 | addr: 1 << 7, 65 | }, { 66 | // Up to Go tip. 67 | ro: 1<<5 | 1<<6, 68 | addr: 1 << 8, 69 | }} 70 | 71 | var flagValOffset = func() uintptr { 72 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 | if !ok { 74 | panic("reflect.Value has no flag field") 75 | } 76 | return field.Offset 77 | }() 78 | 79 | // flagField returns a pointer to the flag field of a reflect.Value. 80 | func flagField(v *reflect.Value) *flag { 81 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 | } 83 | 84 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 | // the typical safety restrictions preventing access to unaddressable and 86 | // unexported data. It works by digging the raw pointer to the underlying 87 | // value out of the protected value and generating a new unprotected (unsafe) 88 | // reflect.Value to it. 89 | // 90 | // This allows us to check for implementations of the Stringer and error 91 | // interfaces to be used for pretty printing ordinarily unaddressable and 92 | // inaccessible values such as unexported struct fields. 93 | func unsafeReflectValue(v reflect.Value) reflect.Value { 94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 | return v 96 | } 97 | flagFieldPtr := flagField(&v) 98 | *flagFieldPtr &^= flagRO 99 | *flagFieldPtr |= flagAddr 100 | return v 101 | } 102 | 103 | // Sanity checks against future reflect package changes 104 | // to the type or semantics of the Value.flag field. 105 | func init() { 106 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 | if !ok { 108 | panic("reflect.Value has no flag field") 109 | } 110 | if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 | panic("reflect.Value flag field has changed kind") 112 | } 113 | type t0 int 114 | var t struct { 115 | A t0 116 | // t0 will have flagEmbedRO set. 117 | t0 118 | // a will have flagStickyRO set 119 | a t0 120 | } 121 | vA := reflect.ValueOf(t).FieldByName("A") 122 | va := reflect.ValueOf(t).FieldByName("a") 123 | vt0 := reflect.ValueOf(t).FieldByName("t0") 124 | 125 | // Infer flagRO from the difference between the flags 126 | // for the (otherwise identical) fields in t. 127 | flagPublic := *flagField(&vA) 128 | flagWithRO := *flagField(&va) | *flagField(&vt0) 129 | flagRO = flagPublic ^ flagWithRO 130 | 131 | // Infer flagAddr from the difference between a value 132 | // taken from a pointer and not. 133 | vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 | flagNoPtr := *flagField(&vA) 135 | flagPtr := *flagField(&vPtrA) 136 | flagAddr = flagNoPtr ^ flagPtr 137 | 138 | // Check that the inferred flags tally with one of the known versions. 139 | for _, f := range okFlags { 140 | if flagRO == f.ro && flagAddr == f.addr { 141 | return 142 | } 143 | } 144 | panic("reflect.Value read-only flag has changed semantics") 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/go-spatial/proj.svg?branch=master)](https://travis-ci.org/go-spatial/proj) 2 | [![Report Card](https://goreportcard.com/badge/github.com/go-spatial/proj)](https://goreportcard.com/report/github.com/go-spatial/proj) 3 | [![Coverage Status](https://coveralls.io/repos/github/go-spatial/proj/badge.svg?branch=master)](https://coveralls.io/github/go-spatial/proj?branch=master) 4 | [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/go-spatial/proj) 5 | [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://github.com/go-spatial/proj/blob/master/LICENSE.md) 6 | 7 | 8 | # proj: PROJ4, for Go! 9 | 10 | Proj is a _selective_ and _on-going_ port of the venerable PROJ.4 project to 11 | the Go language. 12 | 13 | We do not intend to port all of PROJ.4: there is stuff in PROJ.4 that we'll probably never have a sufficient justification for bringing over. Likewise, we do not intend to do a verbatim port of the original code: naively translated C code doesn't make for maintainable (or idiomatic) Go code. 14 | 15 | 16 | # Installation 17 | 18 | To install the packages, preparatory to using them in your own code: 19 | 20 | > go get -U github.com/go-spatial/proj 21 | 22 | To copy the repo, preparatory to doing development: 23 | 24 | > git clone https://github.com/go-spatial/proj 25 | > go test ./... 26 | 27 | See below for API usage instructions. 28 | 29 | 30 | # Guiding Principles and Goals and Plans 31 | 32 | In no particular order, these are the conditions we're imposing on ourselves: 33 | 34 | * We are going to use the PROJ 5.0.1 release as our starting point. 35 | * We will look to the `proj4js` project for suggestions as to what PROJ4 code does and does not need to be ported, and how. 36 | * We will consider numerical results returned by PROJ.4 to be "truth" (within appropriate tolerances). 37 | * We will try to port the "mathy" parts with close to a 1:1 correspondence, so as to avoid inadvertently damaging the algoirthms. 38 | * The "infrastructure" parts, however, such as proj string parsing and the coordinate system classes -- _I'm looking at you, `PJ`_ -- will be generally rewritten in idiomatic Go. 39 | * The `proj` command-line app will not be fully ported. Instead, we will provide a much simpler tool. 40 | * All code will pass muster with the various Go linting and formatting tools. 41 | * Unit tests will be implemented for pretty much everything, using the "side-by-side" `_test` package style. Even without testing all error return paths, but we expect to reach about 80% coverage. 42 | * We will not port PROJ.4's new `gie` test harness directly; we will do a rewrite of a subset of it's features instead. The Go version fo `gie` should nonetheless be able to _parse_ all of PROJ.4's supplied `.gie` files. 43 | * Go-style source code documentation will be provided. 44 | * A set of small, clean usage examples will be provided. 45 | 46 | 47 | # The APIs 48 | 49 | There are two APIs at present, helpfully known as "the conversion API" and "the core API". 50 | 51 | ## The Conversion API 52 | 53 | This API is intended to be a dead-simple way to do a 2D projection from 4326. That is: 54 | 55 | **You Have:** a point which uses two `float64` numbers to represent lon/lat degrees in an `epsg:4326` coordinate reference system 56 | 57 | **You Want:** a point which uses two `float64` numbers to represent meters in a projected coordinate system such as "web mercator" (`epsg:3857`). 58 | 59 | If that's what you need to do, then just do this: 60 | 61 | ``` 62 | var lonlat = []float64{77.625583, 38.833846} 63 | 64 | xy, err := proj.Convert(proj.EPSG3395, lonlat) 65 | if err != nil { 66 | panic(err) 67 | } 68 | 69 | fmt.Printf("%.2f, %.2f\n", xy[0], xy[1]) 70 | ``` 71 | 72 | Note that the `lonlat` array can contain more than two elements, so that you can project a whole set of points at once. 73 | 74 | This API is stable and unlikely to change much. If the projected EPSG code you need is not supported, just let us know. 75 | 76 | 77 | ## The Core API 78 | 79 | Beneath the Conversion API, in the `core` package, lies the _real_ API. With this API, you can provide a proj string (`+proj=utm +zone=32...`) and get back in return a coordinate system object and access to functions that perform forward and inverse operations (transformations or conversions). 80 | 81 | _The Core API is a work in progress._ Only a subset of the full PROJ.4 operations are currently supported, and the structs and interfaces can be expected to evolve as we climb the hill to support more proj string keys, more projections, grid shifts, `.def` files, and so on. 82 | 83 | For examples of how to sue the Core API, see the implementation of `proj.Convert` (in `Convert.go`) or the sample app in `cmd/proj`. 84 | 85 | 86 | # The Packages 87 | 88 | The proj repo contains these packages (directories): 89 | 90 | * `proj` (top-level): the Conversion API 91 | * `proj/cmd/proj`: the simple `proj` command-line tool 92 | * `proj/core`: the Core API, representing coordinate systems and conversion operations 93 | * `proj/gie`: a naive implementation of the PROJ.4 `gie` tool, plus the full set of PROJ.4 test case files 94 | * `proj/merror`: a little error package 95 | * `proj/mlog`: a little logging package 96 | * `proj/operations`: the actual coordinate operations; these routines tend to be closest to the original C code 97 | * `proj/support`: misc structs and functions in support of the `core` package 98 | 99 | Most of the packages have `_test.go` files that demonstrate how the various types and functions are (intended to be) used. 100 | 101 | 102 | # Future Work 103 | 104 | We need to support grid shifts, turn on more proj string keys, make the Ellipse and Datum types be more independent, port a zillion different projection formulae, the icky operation typing needs to be rethought, and on and on. Such future work on `proj` will likely be driven by what coordinate systems and operations people need to be supported: someone will provide a proj string that leads to successful numerical outputs in PROJ.4 but dies in proj. 105 | 106 | We welcome your participation! See `CONTRIBUTING.md` and/or contact `mpg@flaxen.com` if you'd like to help out on the project. 107 | -------------------------------------------------------------------------------- /support/ProjString.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package support 9 | 10 | import ( 11 | "encoding/json" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | 16 | "github.com/go-spatial/proj/merror" 17 | ) 18 | 19 | // Pair is a simple key-value pair 20 | // Pairs use copy semantics (pass-by-value). 21 | type Pair struct { 22 | Key string 23 | Value string 24 | } 25 | 26 | //--------------------------------------------------------------------- 27 | 28 | // ProjString represents a "projection string", such as "+proj=utm +zone=11 +datum=WGS84" 29 | // 30 | // It is just an array of Pair objects. 31 | // (We can't use a map because order of the items is important and 32 | // because we might have duplicate keys.) 33 | // 34 | // TODO: we don't support the "pipeline" or "step" keywords 35 | type ProjString struct { 36 | Pairs []Pair 37 | } 38 | 39 | // NewProjString returns a new ProjString from a string 40 | // of the form "+proj=utm +zone=11 +datum=WGS84", 41 | // with the leading "+" is optional and ignoring extra whitespace 42 | func NewProjString(source string) (*ProjString, error) { 43 | 44 | ret := &ProjString{ 45 | Pairs: []Pair{}, 46 | } 47 | 48 | source = collapse(source) 49 | 50 | words := strings.Fields(source) 51 | for _, w := range words { 52 | 53 | var pair Pair 54 | 55 | if w[0:1] == "+" { 56 | w = w[1:] 57 | } 58 | 59 | v := strings.Split(w, "=") 60 | 61 | if v[0] == "" { 62 | return nil, merror.New(merror.InvalidProjectionSyntax, source) 63 | } 64 | 65 | switch len(v) { 66 | case 0: 67 | pair.Key = w 68 | pair.Value = "" 69 | case 1: 70 | // "proj=" is okay 71 | pair.Key = v[0] 72 | pair.Value = "" 73 | case 2: 74 | pair.Key = v[0] 75 | pair.Value = v[1] 76 | 77 | default: 78 | // "proj=utm=bzzt" 79 | return nil, merror.New(merror.InvalidProjectionSyntax, v) 80 | } 81 | 82 | ret.Add(pair) 83 | } 84 | 85 | return ret, nil 86 | } 87 | 88 | // handle extra whitespace in lines like " +proj = merc x = 1.2 " 89 | func collapse(s string) string { 90 | re_leadclose_whtsp := regexp.MustCompile(`^[\s\p{Zs}]+|[\s\p{Zs}]+$`) 91 | re_inside_whtsp := regexp.MustCompile(`[\s\p{Zs}]{2,}`) 92 | re_equal_whtsp := regexp.MustCompile(`\s?[=]\s?`) 93 | s = re_leadclose_whtsp.ReplaceAllString(s, "") 94 | s = re_inside_whtsp.ReplaceAllString(s, " ") 95 | s = re_equal_whtsp.ReplaceAllString(s, "=") 96 | 97 | return s 98 | } 99 | 100 | // DeepCopy returns a detached deep copy of the ProjString 101 | func (pl *ProjString) DeepCopy() *ProjString { 102 | 103 | copy := &ProjString{ 104 | Pairs: []Pair{}, 105 | } 106 | 107 | for _, pair := range pl.Pairs { 108 | pair2 := pair 109 | copy.Pairs = append(copy.Pairs, pair2) 110 | } 111 | 112 | return copy 113 | } 114 | 115 | func (pl *ProjString) String() string { 116 | b, err := json.MarshalIndent(pl, "", " ") 117 | if err != nil { 118 | panic(err) 119 | } 120 | 121 | return string(b) 122 | } 123 | 124 | // Len returns the number of pairs in the list 125 | func (pl *ProjString) Len() int { 126 | return len(pl.Pairs) 127 | } 128 | 129 | // Get returns the ith pair in the list 130 | func (pl *ProjString) Get(i int) Pair { 131 | return pl.Pairs[i] 132 | } 133 | 134 | // Add adds a Pair to the end of the list 135 | func (pl *ProjString) Add(pair Pair) { 136 | pl.Pairs = append(pl.Pairs, pair) 137 | } 138 | 139 | // AddList adds a ProjString's items to the end of the list 140 | func (pl *ProjString) AddList(list *ProjString) { 141 | pl.Pairs = append(pl.Pairs, list.Pairs...) 142 | } 143 | 144 | // ContainsKey returns true iff the key is present in the list 145 | func (pl *ProjString) ContainsKey(key string) bool { 146 | 147 | for _, pair := range pl.Pairs { 148 | if pair.Key == key { 149 | return true 150 | } 151 | } 152 | 153 | return false 154 | } 155 | 156 | // CountKey returns the number of times the key is in the list 157 | func (pl *ProjString) CountKey(key string) int { 158 | 159 | count := 0 160 | for _, pair := range pl.Pairs { 161 | if pair.Key == key { 162 | count++ 163 | } 164 | } 165 | 166 | return count 167 | } 168 | 169 | // get returns the (string) value of the first occurrence of the key 170 | func (pl *ProjString) get(key string) (string, bool) { 171 | 172 | for _, pair := range pl.Pairs { 173 | if pair.Key == key { 174 | return pair.Value, true 175 | } 176 | } 177 | 178 | return "", false 179 | } 180 | 181 | // GetAsString returns the value of the first occurrence of the key, as a string 182 | func (pl *ProjString) GetAsString(key string) (string, bool) { 183 | 184 | return pl.get(key) 185 | } 186 | 187 | // GetAsInt returns the value of the first occurrence of the key, as an int 188 | func (pl *ProjString) GetAsInt(key string) (int, bool) { 189 | value, ok := pl.get(key) 190 | if !ok { 191 | return 0, false 192 | } 193 | i64, err := strconv.ParseInt(value, 10, 32) 194 | if err != nil { 195 | return 0, false 196 | } 197 | 198 | return int(i64), true 199 | } 200 | 201 | // GetAsFloat returns the value of the first occurrence of the key, as a float64 202 | func (pl *ProjString) GetAsFloat(key string) (float64, bool) { 203 | 204 | value, ok := pl.get(key) 205 | if !ok { 206 | return 0.0, false 207 | } 208 | 209 | f, err := strconv.ParseFloat(value, 64) 210 | if err != nil { 211 | return 0.0, false 212 | } 213 | 214 | return f, true 215 | } 216 | 217 | // GetAsFloats returns the value of the first occurrence of the key, 218 | // interpreted as comma-separated floats 219 | func (pl *ProjString) GetAsFloats(key string) ([]float64, bool) { 220 | 221 | value, ok := pl.get(key) 222 | if !ok { 223 | return nil, false 224 | } 225 | 226 | nums := strings.Split(value, ",") 227 | 228 | floats := make([]float64, 0, len(nums)) 229 | 230 | for _, num := range nums { 231 | f, err := strconv.ParseFloat(num, 64) 232 | if err != nil { 233 | return nil, false 234 | } 235 | floats = append(floats, f) 236 | } 237 | 238 | return floats, true 239 | } 240 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | Thank you for even thinking about contributing! We are excited to have you. This document is intended as a guide to help your through the contribution process. This guide assumes a you have a basic understanding of Git and Go. 4 | 5 | For sensitive security-related issue please start a conversation with a core contributor on the [#go-spatial](https://invite.slack.golangbridge.org/) channel in the [gophers slack](https://invite.slack.golangbridge.org/) organization. 6 | 7 | This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). 8 | By participating, you are expected to uphold this code. Please report unacceptable behavior to the [#go-spatial](https://invite.slack.golangbridge.org/) channel in the [gophers slack](https://invite.slack.golangbridge.org/) organization or to one of the Core Contributors. 9 | 10 | ## There are several places where you can contribute. 11 | 12 | ### Found a bug or something doesn’t feel right? 13 | 14 | Everything we do is done through [issues](https://github.com/go-spatial/proj/issues)(https://github.com/go-spatial/proj/issues). The first thing to do is to search the current issues to see if it is something that has been reported or requested already. If you are unable to find an issue that is similar or are unsure just file a new issue. If you find one that is similar, you can add a comment to add additional details, or if you have nothing new to add you can “+1” the issue. 15 | 16 | * If you are unable to find an issue that is similar or are unsure go ahead and file a new one. 17 | * If it is a bug, your can use the following [template](https://github.com/go-spatial/proj/issues/new?template=bug.md). 18 | * If this is a rendering bug, please include the relevant data set and configuration file. 19 | * If it is a feature request use the following [template](https://github.com/go-spatial/proj/issues/new?template=feature.md). 20 | * If this is a feature request, please include a description of what the feature is, and the use case for the feature. 21 | 22 | Once you have filed an issue, we will discuss it in the issue. If we need more information or you have further questions about that issue, this is the place to ask. This is the place where we will discuss the design of the fix or feature. Any pull request that adds a feature or fixes an issue should reference the issue number. 23 | 24 | Don’t be afraid to reach out if you have any questions. You can reach us on the gophers Slack on the channel #proj or #go-spatial. You can get an invite into the gophers Slack via (https://invite.slack.golangbridge.org/) 25 | 26 | ## Making a Contribution to the code base. 27 | 28 | For the Proj project our master branch is always the most recent stable version of the code base. The current release candidate will be in a branch name for the next version of the software. For example if the current release is v0.6.1 the next release will be v0.7.0, the release candidate branch will be called “v0.7.0”. Please, base all of your pull requests on the release candidate branch. 29 | 30 | ### Discuss your design 31 | 32 | All contributions are welcome, but please let everyone know what you are working on. The way to do this is to first file an issue (or claim an existing issue). In this issue, please, discuss what your plan is to fix or add the feature. Also, all design discussions should happen on the issue. If design discussions happen in a channel, reconcile the decisions to the relevant issue(s). Once, your contribution is ready, create a pull request referencing the issue. Once, a pull request is created one or more of the Core Contributors will review the pull request and may request changes. Once the changes are approved, it will be merged into the current release candidate branch. 33 | 34 | Be sure to keep the pull request updated as merge conflicts may occur as other things get merged into the release branch before yours. 35 | 36 | Please, note that we may push your pull request to the next release candidate at which point you will have to resolve any conflicts that occur. 37 | 38 | ### Not sure where to contribute? 39 | 40 | Want to contribute but not sure where? Not a problem, the best thing to do is look through the issues and find one that interests you. If the issue has the label `good first issue`, it means that one of the core contributors thinks this is a good issue to start with. But, this doesn’t mean that you have to start with these issues. Go through the issues and see if someone is already working on it. If no one is, state that you will be working on the issue to claim it. If you are unsure where to start on the issue, ask in the issue and one of the Core Contributors will help you out. 41 | 42 | ## How to build from source 43 | 44 | Clone the `go-spatial/proj` repo to your `GOPATH`. The simplest way to do this is to use `go get -u github.com/go-spatial/proj`, navigate to the repository root then: 45 | 46 | * Checkout the current release candidate branch, (i.e. v0.7.0) 47 | 48 | (`git checkout v0.7.0`) 49 | 50 | * Create a new feature branch. 51 | 52 | (`git checkout -b issue-XXX-new_feature`) 53 | 54 | * Work on the fix, and run all the tests. We need to run tests with CGO enabled and disabled. 55 | 56 | (`go test ./…`) 57 | 58 | * Commit your changes (`git commit -am ‘Add some feature #XXX\n\nExtened description.'`) 59 | 60 | ### Contribute upstream: 61 | 62 | * On github, fork the repo to into your account. 63 | * Add a new remote pointing to your fork. 64 | 65 | (`git remote add fork git@github.com:yourname/rep.git`) 66 | 67 | * Push to the branch 68 | 69 | (`git push fork issue-XXX-new_feature`) 70 | 71 | * Create a new Pull Request on GitHub against the Release Candidate branch. 72 | 73 | For more information about this work flow, please refer to this [great explanation by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). 74 | 75 | ## Conventions 76 | 77 | * All code should be formatted using: 78 | 79 | (`gofmt -s ./…`). 80 | 81 | - if you find that running `gofmt` produces changes across parts of the code base you're not working on, submit the formatting change in a separate Pull Request. This helps decouple engineering changes from formatting changes and focused the code review efforts. 82 | 83 | * When declaring errors variables, follow the style in the `error_strings.go` file in the `merror` package. 84 | 85 | 86 | ## Testing 87 | 88 | We try to provide both unit and system tests, and we try to provide a high code coverage percentage. 89 | -------------------------------------------------------------------------------- /gie/gie_data/ellipsoid.gie: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | 3 | Test pj_ellipsoid, the reimplementation of pj_ell_set 4 | 5 | =============================================================================== 6 | 7 | 8 | 9 | 10 | ------------------------------------------------------------------------------- 11 | First a spherical example 12 | ------------------------------------------------------------------------------- 13 | operation proj=merc R=6400000 14 | ------------------------------------------------------------------------------- 15 | tolerance 10 nm 16 | accept 1 2 17 | expect 111701.0721276371 223447.5262032605 18 | 19 | accept 12 55 20 | expect 1340412.8655316452 7387101.1430967357 21 | ------------------------------------------------------------------------------- 22 | 23 | 24 | ------------------------------------------------------------------------------- 25 | Then an explicitly defined ellipsoidal example 26 | ------------------------------------------------------------------------------- 27 | operation proj=merc a=6400000 rf=297 28 | ------------------------------------------------------------------------------- 29 | tolerance 10 nm 30 | accept 1 2 31 | expect 111701.0721276371 221945.9681832088 32 | 33 | accept 12 55 34 | expect 1340412.8655316452 7351803.9151705895 35 | ------------------------------------------------------------------------------- 36 | 37 | 38 | ------------------------------------------------------------------------------- 39 | Then try using a built in ellipsoid 40 | ------------------------------------------------------------------------------- 41 | operation proj=merc ellps=GRS80 42 | ------------------------------------------------------------------------------- 43 | tolerance 10 nm 44 | accept 1 2 45 | expect 111319.4907932736 221194.0771604237 46 | 47 | accept 12 55 48 | expect 1335833.8895192828 7326837.7148738774 49 | ------------------------------------------------------------------------------- 50 | 51 | 52 | ------------------------------------------------------------------------------- 53 | Then try to fail deliberately 54 | ------------------------------------------------------------------------------- 55 | operation proj=merc ellps=GRS80000000000 56 | expect failure errno unknown_ellp_param 57 | operation proj=merc +a=-1 58 | expect failure errno major_axis_not_given 59 | 60 | operation proj=merc no_defs 61 | expect failure errno major_axis_not_given 62 | 63 | # This one should succeed due to ellps=WGS84 in proj_def.dat 64 | operation proj=merc 65 | accept 0 0 66 | expect 0 0 67 | 68 | operation proj=merc +es=-1 69 | expect failure errno major_axis_not_given 70 | 71 | operation 72 | expect failure 73 | operation cobra 74 | expect failure 75 | ------------------------------------------------------------------------------- 76 | 77 | 78 | ------------------------------------------------------------------------------- 79 | Finally test the spherification functionality 80 | ------------------------------------------------------------------------------- 81 | operation proj=merc ellps=GRS80 R_A 82 | tolerance 10 nm 83 | accept 12 55 84 | expect 1334340.6237297705 7353636.6296552019 85 | ------------------------------------------------------------------------------- 86 | operation proj=merc ellps=GRS80 R_V 87 | tolerance 10 nm 88 | accept 12 55 89 | expect 1334339.2852675652 7353629.2533042720 90 | ------------------------------------------------------------------------------- 91 | operation proj=merc ellps=GRS80 R_a 92 | tolerance 10 nm 93 | accept 12 55 94 | expect 1333594.4904527504 7349524.6413825499 95 | ------------------------------------------------------------------------------- 96 | operation proj=merc ellps=GRS80 R_g 97 | tolerance 10 nm 98 | accept 12 55 99 | expect 1333592.6102291327 7349514.2793497816 100 | ------------------------------------------------------------------------------- 101 | operation proj=merc ellps=GRS80 R_h 102 | tolerance 10 nm 103 | accept 12 55 104 | expect 1333590.7300081658 7349503.9173316229 105 | ------------------------------------------------------------------------------- 106 | operation proj=merc ellps=GRS80 R_lat_a=60 107 | tolerance 10 nm 108 | accept 12 55 109 | expect 1338073.7436268919 7374210.0924803326 110 | ------------------------------------------------------------------------------- 111 | operation proj=merc ellps=GRS80 R_lat_g=60 112 | tolerance 10 nm 113 | accept 12 55 114 | expect 1338073.2696101593 7374207.4801437631 115 | ------------------------------------------------------------------------------- 116 | 117 | 118 | ------------------------------------------------------------------------------- 119 | This one from testvarious failed at first version of the pull request 120 | ------------------------------------------------------------------------------- 121 | operation proj=healpix a=1 lon_0=0 ellps=WGS84 122 | ------------------------------------------------------------------------------- 123 | accept 0 41.937853904844985 124 | expect 0 0.78452 125 | accept -90 0 126 | expect -1.56904 0 127 | ------------------------------------------------------------------------------- 128 | 129 | ------------------------------------------------------------------------------- 130 | Shape parameters 131 | ------------------------------------------------------------------------------- 132 | operation proj=utm zone=32 ellps=GRS80 rf=0 133 | expect failure errno rev_flattening_is_zero 134 | 135 | operation proj=utm zone=32 ellps=GRS80 es=1 136 | expect failure errno eccentricity_is_one 137 | 138 | operation proj=utm zone=32 ellps=GRS80 b=0 139 | expect failure errno eccentricity_is_one 140 | 141 | operation proj=utm zone=32 ellps=GRS80 b=6000000 142 | accept 12 55 143 | expect 699293.0880 5674591.5295 144 | 145 | operation proj=utm zone=32 ellps=GRS80 rf=300 146 | accept 12 55 147 | expect 691873.1212 6099054.9661 148 | 149 | operation proj=utm zone=32 ellps=GRS80 f=0.00333333333333 150 | accept 12 55 151 | expect 691873.1212 6099054.9661 152 | 153 | operation proj=utm zone=32 ellps=GRS80 b=6000000 154 | accept 12 55 155 | expect 699293.0880 5674591.5295 156 | 157 | operation proj=utm zone=32 a=6400000 b=6000000 158 | accept 12 55 159 | expect 700416.5900 5669475.8884 160 | ------------------------------------------------------------------------------- 161 | 162 | 163 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 and 12 | // an error if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url, http.NoBody) 16 | if err != nil { 17 | return -1, err 18 | } 19 | req.URL.RawQuery = values.Encode() 20 | handler(w, req) 21 | return w.Code, nil 22 | } 23 | 24 | // HTTPSuccess asserts that a specified handler returns a success status code. 25 | // 26 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 27 | // 28 | // Returns whether the assertion was successful (true) or not (false). 29 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 30 | if h, ok := t.(tHelper); ok { 31 | h.Helper() 32 | } 33 | code, err := httpCode(handler, method, url, values) 34 | if err != nil { 35 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 36 | } 37 | 38 | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent 39 | if !isSuccessCode { 40 | Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 41 | } 42 | 43 | return isSuccessCode 44 | } 45 | 46 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 47 | // 48 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 49 | // 50 | // Returns whether the assertion was successful (true) or not (false). 51 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 52 | if h, ok := t.(tHelper); ok { 53 | h.Helper() 54 | } 55 | code, err := httpCode(handler, method, url, values) 56 | if err != nil { 57 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 58 | } 59 | 60 | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 61 | if !isRedirectCode { 62 | Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 63 | } 64 | 65 | return isRedirectCode 66 | } 67 | 68 | // HTTPError asserts that a specified handler returns an error status code. 69 | // 70 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 71 | // 72 | // Returns whether the assertion was successful (true) or not (false). 73 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 74 | if h, ok := t.(tHelper); ok { 75 | h.Helper() 76 | } 77 | code, err := httpCode(handler, method, url, values) 78 | if err != nil { 79 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 80 | } 81 | 82 | isErrorCode := code >= http.StatusBadRequest 83 | if !isErrorCode { 84 | Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 85 | } 86 | 87 | return isErrorCode 88 | } 89 | 90 | // HTTPStatusCode asserts that a specified handler returns a specified status code. 91 | // 92 | // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) 93 | // 94 | // Returns whether the assertion was successful (true) or not (false). 95 | func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { 96 | if h, ok := t.(tHelper); ok { 97 | h.Helper() 98 | } 99 | code, err := httpCode(handler, method, url, values) 100 | if err != nil { 101 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 102 | } 103 | 104 | successful := code == statuscode 105 | if !successful { 106 | Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) 107 | } 108 | 109 | return successful 110 | } 111 | 112 | // HTTPBody is a helper that returns HTTP body of the response. It returns 113 | // empty string if building a new request fails. 114 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 115 | w := httptest.NewRecorder() 116 | if len(values) > 0 { 117 | url += "?" + values.Encode() 118 | } 119 | req, err := http.NewRequest(method, url, http.NoBody) 120 | if err != nil { 121 | return "" 122 | } 123 | handler(w, req) 124 | return w.Body.String() 125 | } 126 | 127 | // HTTPBodyContains asserts that a specified handler returns a 128 | // body that contains a string. 129 | // 130 | // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 131 | // 132 | // Returns whether the assertion was successful (true) or not (false). 133 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 134 | if h, ok := t.(tHelper); ok { 135 | h.Helper() 136 | } 137 | body := HTTPBody(handler, method, url, values) 138 | 139 | contains := strings.Contains(body, fmt.Sprint(str)) 140 | if !contains { 141 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) 142 | } 143 | 144 | return contains 145 | } 146 | 147 | // HTTPBodyNotContains asserts that a specified handler returns a 148 | // body that does not contain a string. 149 | // 150 | // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 151 | // 152 | // Returns whether the assertion was successful (true) or not (false). 153 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 154 | if h, ok := t.(tHelper); ok { 155 | h.Helper() 156 | } 157 | body := HTTPBody(handler, method, url, values) 158 | 159 | contains := strings.Contains(body, fmt.Sprint(str)) 160 | if contains { 161 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) 162 | } 163 | 164 | return !contains 165 | } 166 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /operations/operations_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations_test 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/go-spatial/proj/core" 15 | "github.com/go-spatial/proj/support" 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | type data struct { 20 | proj string 21 | delta float64 22 | fwd [][]float64 23 | inv [][]float64 24 | } 25 | 26 | var testdata = []data{ 27 | { 28 | // builtins.gie:21 29 | proj: "+proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2", 30 | delta: 0.1 * 0.001, 31 | fwd: [][]float64{ 32 | {2, 1, 222571.608757106, 110653.326743030}, 33 | {2, -1, 222706.306508391, -110484.267144400}, 34 | {-2, 1, -222571.608757106, 110653.326743030}, 35 | {-2, -1, -222706.306508391, -110484.267144400}, 36 | }, 37 | inv: [][]float64{ 38 | {200, 100, 0.001796631, 0.000904369}, 39 | {200, -100, 0.001796630, -0.000904370}, 40 | {-200, 100, -0.001796631, 0.000904369}, 41 | {-200, -100, -0.001796630, -0.000904370}, 42 | }, 43 | }, { 44 | // builtins.gie:2317 45 | proj: "+proj=leac +ellps=GRS80 +lat_1=0 +lat_2=2", 46 | delta: 0.1 * 0.001, 47 | fwd: [][]float64{ 48 | {2.0, 1.0, 220685.140542979, 112983.500889396}, 49 | }, 50 | inv: [][]float64{ 51 | {200, 100, 0.001796645, 0.000904352}, 52 | }, 53 | }, { 54 | // builtins.gie:1247 55 | proj: "+proj=etmerc +ellps=GRS80 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5 +zone=30", 56 | delta: 0.1 * 0.001, 57 | fwd: [][]float64{ 58 | {2, 1, 222650.796797586, 110642.229411933}, 59 | }, 60 | inv: [][]float64{ 61 | {200, 100, 0.001796631, 0.000904369}, 62 | }, 63 | }, { 64 | // builtins.gie:4684 65 | proj: "+proj=utm +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5 +zone=30", 66 | delta: 0.1 * 0.001, 67 | fwd: [][]float64{ 68 | {2, 1, 1057002.405491298, 110955.141175949}, 69 | }, 70 | inv: [][]float64{ 71 | {200, 100, -7.486952083, 0.000901940}, 72 | }, 73 | }, { 74 | // builtins.gie:2626 75 | proj: "+proj=merc +ellps=GRS80 +lat_1=0.5 +lat_2=2", 76 | delta: 0.1 * 0.001, 77 | fwd: [][]float64{ 78 | {2, 1, 222638.981586547, 110579.965218250}, 79 | }, 80 | inv: [][]float64{ 81 | {200, 100, 0.001796631, 0.000904369}, 82 | }, 83 | }, { 84 | // ellipsoid.gie:141 85 | proj: "proj=utm zone=32 ellps=GRS80 b=6000000", 86 | delta: 0.1 * 0.001, 87 | fwd: [][]float64{ 88 | {12, 55, 699293.0880, 5674591.5295}, 89 | }, 90 | }, { 91 | // builtins.gie:309 92 | proj: "+proj=airy +a=6400000 +lat_1=0 +lat_2=2", 93 | delta: 0.1 * 0.001, 94 | fwd: [][]float64{ 95 | {2, 1, 189109.886908621, 94583.752387504}, 96 | }, 97 | }, { 98 | // builtins.gie:428 99 | proj: "+proj=august +a=6400000 +lat_1=0 +lat_2=2", 100 | delta: 0.1 * 0.001, 101 | fwd: [][]float64{ 102 | {2, 1, 223404.978180972, 111722.340289763}, 103 | }, 104 | }, { 105 | // builtins.gie:1104 106 | proj: "proj=eqc +a=6400000 +lat_1=0.5 +lat_2=2", 107 | delta: 0.1 * 0.001, 108 | fwd: [][]float64{ 109 | {2, 1, 223402.144255274, 111701.072127637}, 110 | {2, -1, 223402.144255274, -111701.072127637}, 111 | {-2, 1, -223402.144255274, 111701.072127637}, 112 | {-2, -1, -223402.144255274, -111701.072127637}, 113 | }, 114 | inv: [][]float64{ 115 | {200, 100, 0.001790493, 0.000895247}, 116 | {200, -100, 0.001790493, -0.000895247}, 117 | {-200, 100, -0.001790493, 0.000895247}, 118 | {-200, -100, -0.001790493, -0.000895247}, 119 | }, 120 | }, 121 | } 122 | 123 | func TestConvert(t *testing.T) { 124 | assert := assert.New(t) 125 | 126 | for _, td := range testdata { 127 | 128 | ps, err := support.NewProjString(td.proj) 129 | assert.NoError(err) 130 | 131 | sys, opx, err := core.NewSystem(ps) 132 | assert.NoError(err) 133 | assert.NotNil(sys) 134 | assert.NotNil(opx) 135 | assert.EqualValues(sys, opx.GetSystem()) 136 | 137 | op := opx.(core.IConvertLPToXY) 138 | 139 | for i, tc := range td.fwd { 140 | tag := fmt.Sprintf("%s (fwd/%d)", td.proj, i) 141 | input := &core.CoordLP{Lam: support.DDToR(tc[0]), Phi: support.DDToR(tc[1])} 142 | output, err := op.Forward(input) 143 | assert.NoError(err) 144 | 145 | x, y := output.X, output.Y 146 | assert.InDelta(tc[2], x, td.delta, tag) 147 | assert.InDelta(tc[3], y, td.delta, tag) 148 | } 149 | 150 | for i, tc := range td.inv { 151 | tag := fmt.Sprintf("%s (inv/%d)", td.proj, i) 152 | 153 | input := &core.CoordXY{X: tc[0], Y: tc[1]} 154 | output, err := op.Inverse(input) 155 | assert.NoError(err) 156 | 157 | l, p := output.Lam, output.Phi 158 | assert.InDelta(tc[2], support.RToDD(l), td.delta, tag) 159 | assert.InDelta(tc[3], support.RToDD(p), td.delta, tag) 160 | } 161 | } 162 | } 163 | 164 | func BenchmarkConvertEtMerc(b *testing.B) { 165 | 166 | ps, _ := support.NewProjString("+proj=utm +zone=32 +ellps=GRS80") 167 | _, opx, _ := core.NewSystem(ps) 168 | op := opx.(core.IConvertLPToXY) 169 | input := &core.CoordLP{Lam: support.DDToR(12.0), Phi: support.DDToR(55.0)} 170 | 171 | b.ResetTimer() 172 | 173 | for i := 0; i < b.N; i++ { 174 | _, _ = op.Forward(input) 175 | } 176 | } 177 | 178 | func BenchmarkConvertAea(b *testing.B) { 179 | 180 | ps, _ := support.NewProjString("+proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2") 181 | _, opx, _ := core.NewSystem(ps) 182 | op := opx.(core.IConvertLPToXY) 183 | input := &core.CoordLP{Lam: support.DDToR(12.0), Phi: support.DDToR(55.0)} 184 | 185 | b.ResetTimer() 186 | 187 | for i := 0; i < b.N; i++ { 188 | _, _ = op.Forward(input) 189 | } 190 | } 191 | 192 | func BenchmarkConvertAiry(b *testing.B) { 193 | 194 | ps, _ := support.NewProjString("+proj=airy +a=6400000 +lat_1=0 +lat_2=2") 195 | _, opx, _ := core.NewSystem(ps) 196 | op := opx.(core.IConvertLPToXY) 197 | input := &core.CoordLP{Lam: support.DDToR(12.0), Phi: support.DDToR(55.0)} 198 | 199 | b.ResetTimer() 200 | 201 | for i := 0; i < b.N; i++ { 202 | _, _ = op.Forward(input) 203 | } 204 | } 205 | 206 | func BenchmarkConvertEqc(b *testing.B) { 207 | ps, _ := support.NewProjString("+proj=eqc +a=6400000 +lat_1=0.5 +lat_2=2") 208 | _, opx, _ := core.NewSystem(ps) 209 | op := opx.(core.IConvertLPToXY) 210 | input := &core.CoordLP{Lam: support.DDToR(12.0), Phi: support.DDToR(55.0)} 211 | 212 | b.ResetTimer() 213 | 214 | for i := 0; i < b.N; i++ { 215 | _, _ = op.Forward(input) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | const ( 26 | // The size of the input raw buffer. 27 | input_raw_buffer_size = 512 28 | 29 | // The size of the input buffer. 30 | // It should be possible to decode the whole raw buffer. 31 | input_buffer_size = input_raw_buffer_size * 3 32 | 33 | // The size of the output buffer. 34 | output_buffer_size = 128 35 | 36 | // The size of the output raw buffer. 37 | // It should be possible to encode the whole output buffer. 38 | output_raw_buffer_size = (output_buffer_size*2 + 2) 39 | 40 | // The size of other stacks and queues. 41 | initial_stack_size = 16 42 | initial_queue_size = 16 43 | initial_string_size = 16 44 | ) 45 | 46 | // Check if the character at the specified position is an alphabetical 47 | // character, a digit, '_', or '-'. 48 | func is_alpha(b []byte, i int) bool { 49 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 50 | } 51 | 52 | // Check if the character at the specified position is a digit. 53 | func is_digit(b []byte, i int) bool { 54 | return b[i] >= '0' && b[i] <= '9' 55 | } 56 | 57 | // Get the value of a digit. 58 | func as_digit(b []byte, i int) int { 59 | return int(b[i]) - '0' 60 | } 61 | 62 | // Check if the character at the specified position is a hex-digit. 63 | func is_hex(b []byte, i int) bool { 64 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 65 | } 66 | 67 | // Get the value of a hex-digit. 68 | func as_hex(b []byte, i int) int { 69 | bi := b[i] 70 | if bi >= 'A' && bi <= 'F' { 71 | return int(bi) - 'A' + 10 72 | } 73 | if bi >= 'a' && bi <= 'f' { 74 | return int(bi) - 'a' + 10 75 | } 76 | return int(bi) - '0' 77 | } 78 | 79 | // Check if the character is ASCII. 80 | func is_ascii(b []byte, i int) bool { 81 | return b[i] <= 0x7F 82 | } 83 | 84 | // Check if the character at the start of the buffer can be printed unescaped. 85 | func is_printable(b []byte, i int) bool { 86 | return ((b[i] == 0x0A) || // . == #x0A 87 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 88 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 89 | (b[i] > 0xC2 && b[i] < 0xED) || 90 | (b[i] == 0xED && b[i+1] < 0xA0) || 91 | (b[i] == 0xEE) || 92 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 93 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 94 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 95 | } 96 | 97 | // Check if the character at the specified position is NUL. 98 | func is_z(b []byte, i int) bool { 99 | return b[i] == 0x00 100 | } 101 | 102 | // Check if the beginning of the buffer is a BOM. 103 | func is_bom(b []byte, i int) bool { 104 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 105 | } 106 | 107 | // Check if the character at the specified position is space. 108 | func is_space(b []byte, i int) bool { 109 | return b[i] == ' ' 110 | } 111 | 112 | // Check if the character at the specified position is tab. 113 | func is_tab(b []byte, i int) bool { 114 | return b[i] == '\t' 115 | } 116 | 117 | // Check if the character at the specified position is blank (space or tab). 118 | func is_blank(b []byte, i int) bool { 119 | //return is_space(b, i) || is_tab(b, i) 120 | return b[i] == ' ' || b[i] == '\t' 121 | } 122 | 123 | // Check if the character at the specified position is a line break. 124 | func is_break(b []byte, i int) bool { 125 | return (b[i] == '\r' || // CR (#xD) 126 | b[i] == '\n' || // LF (#xA) 127 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 128 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 129 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 130 | } 131 | 132 | func is_crlf(b []byte, i int) bool { 133 | return b[i] == '\r' && b[i+1] == '\n' 134 | } 135 | 136 | // Check if the character is a line break or NUL. 137 | func is_breakz(b []byte, i int) bool { 138 | //return is_break(b, i) || is_z(b, i) 139 | return ( 140 | // is_break: 141 | b[i] == '\r' || // CR (#xD) 142 | b[i] == '\n' || // LF (#xA) 143 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 144 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 145 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 146 | // is_z: 147 | b[i] == 0) 148 | } 149 | 150 | // Check if the character is a line break, space, or NUL. 151 | func is_spacez(b []byte, i int) bool { 152 | //return is_space(b, i) || is_breakz(b, i) 153 | return ( 154 | // is_space: 155 | b[i] == ' ' || 156 | // is_breakz: 157 | b[i] == '\r' || // CR (#xD) 158 | b[i] == '\n' || // LF (#xA) 159 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 160 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 161 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 162 | b[i] == 0) 163 | } 164 | 165 | // Check if the character is a line break, space, tab, or NUL. 166 | func is_blankz(b []byte, i int) bool { 167 | //return is_blank(b, i) || is_breakz(b, i) 168 | return ( 169 | // is_blank: 170 | b[i] == ' ' || b[i] == '\t' || 171 | // is_breakz: 172 | b[i] == '\r' || // CR (#xD) 173 | b[i] == '\n' || // LF (#xA) 174 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 175 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 176 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 177 | b[i] == 0) 178 | } 179 | 180 | // Determine the width of the character. 181 | func width(b byte) int { 182 | // Don't replace these by a switch without first 183 | // confirming that it is being inlined. 184 | if b&0x80 == 0x00 { 185 | return 1 186 | } 187 | if b&0xE0 == 0xC0 { 188 | return 2 189 | } 190 | if b&0xF0 == 0xE0 { 191 | return 3 192 | } 193 | if b&0xF8 == 0xF0 { 194 | return 4 195 | } 196 | return 0 197 | 198 | } 199 | -------------------------------------------------------------------------------- /operations/Aea.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) 2 | // 3 | // Portions of this code were derived from the PROJ.4 software 4 | // In keeping with the terms of the PROJ.4 project, this software 5 | // is provided under the MIT-style license in `LICENSE.md` and may 6 | // additionally be subject to the copyrights of the PROJ.4 authors. 7 | 8 | package operations 9 | 10 | import ( 11 | "math" 12 | 13 | "github.com/go-spatial/proj/core" 14 | "github.com/go-spatial/proj/merror" 15 | "github.com/go-spatial/proj/support" 16 | ) 17 | 18 | func init() { 19 | core.RegisterConvertLPToXY("aea", 20 | "Albers Equal Area", 21 | "\n\tConic Sph&Ell\n\tlat_1= lat_2=", 22 | NewAea, 23 | ) 24 | core.RegisterConvertLPToXY("leac", 25 | "Lambert Equal Area Conic", 26 | "\n\tConic, Sph&Ell\n\tlat_1= south", 27 | NewLeac) 28 | } 29 | 30 | // Aea implements core.IOperation and core.ConvertLPToXY 31 | type Aea struct { 32 | core.Operation 33 | isLambert bool 34 | 35 | // the "opaque" parts 36 | 37 | ec float64 38 | n float64 39 | c float64 40 | dd float64 41 | n2 float64 42 | rho0 float64 43 | rho float64 44 | phi1 float64 45 | phi2 float64 46 | en []float64 47 | ellips bool 48 | } 49 | 50 | // NewAea is from PJ_aea.c 51 | func NewAea(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 52 | op := &Aea{ 53 | isLambert: false, 54 | } 55 | op.System = system 56 | 57 | err := op.aeaSetup(system) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return op, nil 62 | } 63 | 64 | // NewLeac is too 65 | func NewLeac(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { 66 | op := &Aea{ 67 | isLambert: true, 68 | } 69 | op.System = system 70 | 71 | err := op.leacSetup(system) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return op, nil 76 | } 77 | 78 | //--------------------------------------------------------------------- 79 | 80 | /* determine latitude angle phi-1 */ 81 | const nIter = 15 82 | 83 | func phi1(qs, Te, tOneEs float64) float64 { 84 | var i int 85 | var Phi, sinpi, cospi, con, com, dphi float64 86 | 87 | Phi = math.Asin(.5 * qs) 88 | if Te < eps7 { 89 | return (Phi) 90 | } 91 | i = nIter 92 | for { 93 | sinpi = math.Sin(Phi) 94 | cospi = math.Cos(Phi) 95 | con = Te * sinpi 96 | com = 1. - con*con 97 | dphi = .5 * com * com / cospi * (qs/tOneEs - 98 | sinpi/com + .5/Te*math.Log((1.-con)/ 99 | (1.+con))) 100 | Phi += dphi 101 | i-- 102 | if !(math.Abs(dphi) > tol10 && i != 0) { 103 | break 104 | } 105 | } 106 | if i != 0 { 107 | return Phi 108 | } 109 | return math.MaxFloat64 110 | } 111 | 112 | func (op *Aea) setup(sys *core.System) error { 113 | var cosphi, sinphi float64 114 | var secant bool 115 | 116 | Q := op 117 | P := op.System 118 | PE := P.Ellipsoid 119 | 120 | if math.Abs(Q.phi1+Q.phi2) < eps10 { 121 | return merror.New(merror.ConicLatEqual) 122 | } 123 | sinphi = math.Sin(Q.phi1) 124 | Q.n = sinphi 125 | cosphi = math.Cos(Q.phi1) 126 | secant = math.Abs(Q.phi1-Q.phi2) >= eps10 127 | Q.ellips = (P.Ellipsoid.Es > 0.0) 128 | if Q.ellips { 129 | var ml1, m1 float64 130 | 131 | Q.en = support.Enfn(PE.Es) 132 | m1 = support.Msfn(sinphi, cosphi, PE.Es) 133 | ml1 = support.Qsfn(sinphi, PE.E, PE.OneEs) 134 | if secant { // secant cone 135 | var ml2, m2 float64 136 | 137 | sinphi = math.Sin(Q.phi2) 138 | cosphi = math.Cos(Q.phi2) 139 | m2 = support.Msfn(sinphi, cosphi, PE.Es) 140 | ml2 = support.Qsfn(sinphi, PE.E, PE.OneEs) 141 | if ml2 == ml1 { 142 | return merror.New(merror.AeaSetupFailed) 143 | } 144 | 145 | Q.n = (m1*m1 - m2*m2) / (ml2 - ml1) 146 | } 147 | Q.ec = 1. - .5*PE.OneEs*math.Log((1.-PE.E)/ 148 | (1.+PE.E))/PE.E 149 | Q.c = m1*m1 + Q.n*ml1 150 | Q.dd = 1. / Q.n 151 | Q.rho0 = Q.dd * math.Sqrt(Q.c-Q.n*support.Qsfn(math.Sin(P.Phi0), 152 | PE.E, PE.OneEs)) 153 | } else { 154 | if secant { 155 | Q.n = .5 * (Q.n + math.Sin(Q.phi2)) 156 | } 157 | Q.n2 = Q.n + Q.n 158 | Q.c = cosphi*cosphi + Q.n2*sinphi 159 | Q.dd = 1. / Q.n 160 | Q.rho0 = Q.dd * math.Sqrt(Q.c-Q.n2*math.Sin(P.Phi0)) 161 | } 162 | 163 | return nil 164 | } 165 | 166 | // Forward goes frontwords 167 | func (op *Aea) Forward(lp *core.CoordLP) (*core.CoordXY, error) { 168 | xy := &core.CoordXY{X: 0.0, Y: 0.0} 169 | Q := op 170 | PE := op.System.Ellipsoid 171 | 172 | var t float64 173 | if Q.ellips { 174 | t = Q.n * support.Qsfn(math.Sin(lp.Phi), PE.E, PE.OneEs) 175 | } else { 176 | t = Q.n2 * math.Sin(lp.Phi) 177 | } 178 | Q.rho = Q.c - t 179 | if Q.rho < 0. { 180 | return xy, merror.New(merror.ToleranceCondition) 181 | } 182 | Q.rho = Q.dd * math.Sqrt(Q.rho) 183 | lp.Lam *= Q.n 184 | xy.X = Q.rho * math.Sin(lp.Lam) 185 | xy.Y = Q.rho0 - Q.rho*math.Cos(lp.Lam) 186 | return xy, nil 187 | } 188 | 189 | // Inverse goes backwards 190 | func (op *Aea) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { 191 | 192 | lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} 193 | Q := op 194 | PE := op.System.Ellipsoid 195 | 196 | xy.Y = Q.rho0 - xy.Y 197 | Q.rho = math.Hypot(xy.X, xy.Y) 198 | if Q.rho != 0.0 { 199 | if Q.n < 0. { 200 | Q.rho = -Q.rho 201 | xy.X = -xy.X 202 | xy.Y = -xy.Y 203 | } 204 | lp.Phi = Q.rho / Q.dd 205 | if Q.ellips { 206 | lp.Phi = (Q.c - lp.Phi*lp.Phi) / Q.n 207 | if math.Abs(Q.ec-math.Abs(lp.Phi)) > tol7 { 208 | lp.Phi = phi1(lp.Phi, PE.E, PE.OneEs) 209 | if lp.Phi == math.MaxFloat64 { 210 | return lp, merror.New(merror.ToleranceCondition) 211 | } 212 | } else { 213 | if lp.Phi < 0. { 214 | lp.Phi = -support.PiOverTwo 215 | } else { 216 | lp.Phi = support.PiOverTwo 217 | } 218 | } 219 | } else { 220 | lp.Phi = (Q.c - lp.Phi*lp.Phi) / Q.n2 221 | if math.Abs(lp.Phi) <= 1. { 222 | lp.Phi = math.Asin(lp.Phi) 223 | } else { 224 | if lp.Phi < 0. { 225 | lp.Phi = -support.PiOverTwo 226 | } else { 227 | lp.Phi = support.PiOverTwo 228 | } 229 | } 230 | } 231 | lp.Lam = math.Atan2(xy.X, xy.Y) / Q.n 232 | } else { 233 | lp.Lam = 0. 234 | if Q.n > 0. { 235 | lp.Phi = support.PiOverTwo 236 | } else { 237 | lp.Phi = -support.PiOverTwo 238 | } 239 | } 240 | return lp, nil 241 | } 242 | 243 | func (op *Aea) aeaSetup(sys *core.System) error { 244 | 245 | lat1, ok := op.System.ProjString.GetAsFloat("lat_1") 246 | if !ok { 247 | lat1 = 0.0 248 | } 249 | lat2, ok := op.System.ProjString.GetAsFloat("lat_2") 250 | if !ok { 251 | lat2 = 0.0 252 | } 253 | 254 | op.phi1 = support.DDToR(lat1) 255 | op.phi2 = support.DDToR(lat2) 256 | 257 | return op.setup(op.System) 258 | } 259 | 260 | func (op *Aea) leacSetup(sys *core.System) error { 261 | 262 | lat1, ok := op.System.ProjString.GetAsFloat("lat_1") 263 | if !ok { 264 | lat1 = 0.0 265 | } 266 | 267 | south := -support.PiOverTwo 268 | _, ok = op.System.ProjString.GetAsInt("south") 269 | if !ok { 270 | south = support.PiOverTwo 271 | } 272 | 273 | op.phi2 = support.DDToR(lat1) 274 | op.phi1 = south 275 | 276 | return op.setup(op.System) 277 | } 278 | --------------------------------------------------------------------------------