Skip to content

Commit 0e6d02e

Browse files
authored
Issue 18 (#20)
* bug fix * remove old comment * fix error string * added `ShowSource` flag * implemented app & tests * added -inverse test * update tests
1 parent a5c9a86 commit 0e6d02e

File tree

7 files changed

+209
-22
lines changed

7 files changed

+209
-22
lines changed

Convert.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Convert(dest EPSGCode, input []float64) ([]float64, error) {
3434

3535
conv, err := newConversion(dest)
3636
if err != nil {
37-
return nil, nil
37+
return nil, err
3838
}
3939

4040
return conv.convert(input)

cmd/proj/proj.go

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,148 @@ package main
33
import (
44
"flag"
55
"fmt"
6+
"io"
67
"os"
78
"strings"
89

10+
"github.com/go-spatial/proj"
11+
12+
"github.com/go-spatial/proj/core"
13+
"github.com/go-spatial/proj/merror"
14+
"github.com/go-spatial/proj/mlog"
915
"github.com/go-spatial/proj/support"
1016
)
1117

1218
func main() {
19+
err := Main(os.Stdin, os.Stdout, os.Args)
20+
if err != nil {
21+
fmt.Fprintf(os.Stderr, err.Error()+"\n")
22+
os.Exit(1)
23+
}
24+
}
25+
26+
// Main is just a callable version of main(), for testing purposes
27+
func Main(inS io.Reader, outS io.Writer, args []string) error {
28+
29+
merror.ShowSource = false
30+
mlog.DebugEnabled = false
31+
mlog.InfoEnabled = false
32+
mlog.ErrorEnabled = false
33+
34+
cli := flag.NewFlagSet(args[0], flag.ContinueOnError)
35+
cli.SetOutput(outS)
1336

14-
verbose := flag.Bool("verbose", false, "do lots of logging")
15-
inverse := flag.Bool("inverse", false, "go backwards")
37+
verbose := cli.Bool("verbose", false, "enable logging")
38+
inverse := cli.Bool("inverse", false, "run the inverse transform")
39+
epsgDest := cli.Int("epsg", 0, "perform conversion from 4326 to given destination system")
1640

17-
flag.Parse()
41+
err := cli.Parse(args[1:])
42+
if err != nil {
43+
return err
44+
}
45+
projString := strings.Join(cli.Args(), " ")
1846

19-
fmt.Printf("verbose: %t\n", *verbose)
20-
fmt.Printf("inverse: %t\n", *inverse)
47+
if *verbose {
48+
mlog.Printf("verbose: %t", *verbose)
49+
mlog.Printf("inverse: %t", *inverse)
50+
if *epsgDest == 0 {
51+
mlog.Printf("epsg: (not specified)")
52+
} else {
53+
mlog.Printf("epsg: %d", epsgDest)
54+
}
55+
if projString == "" {
56+
mlog.Printf("proj: (not specified)")
57+
} else {
58+
mlog.Printf("proj: %s", projString)
59+
}
60+
61+
merror.ShowSource = true
62+
mlog.DebugEnabled = true
63+
mlog.InfoEnabled = true
64+
mlog.ErrorEnabled = true
65+
}
2166

22-
s := strings.Join(flag.Args(), " ")
67+
if *epsgDest != 0 {
68+
if *inverse {
69+
return fmt.Errorf("-inverse not allowed with -epsg")
70+
}
71+
if projString != "" {
72+
return fmt.Errorf("projection string not allowed with -epsg")
73+
}
74+
input := make([]float64, 2)
2375

24-
s = "+proj=utm +zone=32 +ellps=GRS80" // TODO
76+
f := func(a, b float64) (float64, float64, error) {
77+
input[0] = a
78+
input[1] = b
79+
output, err := proj.Convert(proj.EPSGCode(*epsgDest), input)
80+
if err != nil {
81+
return 0.0, 0.0, err
82+
}
83+
return output[0], output[1], nil
84+
}
2585

26-
fmt.Printf("string: %s\n", s)
86+
return repl(inS, outS, f)
87+
}
88+
89+
ps, err := support.NewProjString(projString)
90+
if err != nil {
91+
return err
92+
}
2793

28-
_, err := support.NewProjString(s)
94+
_, opx, err := core.NewSystem(ps)
2995
if err != nil {
30-
panic(err)
96+
return err
97+
}
98+
99+
op := opx.(core.IConvertLPToXY)
100+
101+
if !*inverse {
102+
103+
f := func(a, b float64) (float64, float64, error) {
104+
input := &core.CoordLP{Lam: support.DDToR(a), Phi: support.DDToR(b)}
105+
output, err := op.Forward(input)
106+
if err != nil {
107+
return 0.0, 0.0, err
108+
}
109+
return output.X, output.Y, nil
110+
}
111+
return repl(inS, outS, f)
112+
}
113+
114+
f := func(a, b float64) (float64, float64, error) {
115+
input := &core.CoordXY{X: a, Y: b}
116+
output, err := op.Inverse(input)
117+
if err != nil {
118+
return 0.0, 0.0, err
119+
}
120+
return support.RToDD(output.Lam), support.RToDD(output.Phi), nil
31121
}
122+
return repl(inS, outS, f)
123+
}
124+
125+
type converter func(a, b float64) (float64, float64, error)
126+
127+
func repl(inS io.Reader, outS io.Writer, f converter) error {
128+
129+
var a, b float64
32130

33-
var a1, a2 float64
34-
fmt.Fscanf(os.Stdin, "%f %f\n", &a1, &a2)
35-
fmt.Printf("-> %f %f\n", a1, a2)
131+
for {
132+
n, err := fmt.Fscanf(inS, "%f %f\n", &a, &b)
133+
if err == io.EOF {
134+
return nil
135+
}
136+
if err != nil {
137+
return err
138+
}
139+
if n != 2 {
140+
return fmt.Errorf("error reading input")
141+
}
142+
143+
c, d, err := f(a, b)
144+
if err != nil {
145+
return err
146+
}
147+
148+
fmt.Fprintf(outS, "%f %f\n", c, d)
149+
}
36150
}

cmd/proj/proj_test.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,74 @@
11
package main_test
22

33
import (
4+
"bytes"
5+
"fmt"
6+
"strconv"
7+
"strings"
48
"testing"
59

10+
main "github.com/go-spatial/proj/cmd/proj"
611
"github.com/stretchr/testify/assert"
712
)
813

9-
func TestXYZZY(t *testing.T) {
14+
func TestCmd(t *testing.T) {
1015
assert := assert.New(t)
11-
assert.True(true)
16+
17+
type testcase struct {
18+
args string
19+
input []float64
20+
output []float64
21+
}
22+
23+
testcases := []testcase{
24+
{
25+
"proj -epsg 9999",
26+
[]float64{0.0, 0.0},
27+
nil,
28+
}, {
29+
"proj -epsg 9999 proj=merc",
30+
[]float64{0.0, 0.0},
31+
nil,
32+
}, {
33+
"proj-epsg 9999 -inverse",
34+
[]float64{0.0, 0.0},
35+
nil,
36+
}, {
37+
"proj -epsg 3395",
38+
[]float64{-77.625583, 38.833846},
39+
[]float64{-8641240.37, 4671101.60},
40+
}, {
41+
"proj +proj=utm +zone=32 +ellps=GRS80",
42+
[]float64{12.0, 55.0},
43+
[]float64{691875.63, 6098907.83},
44+
}, {
45+
"proj -inverse +proj=utm +zone=32 +ellps=GRS80",
46+
[]float64{691875.63, 6098907.83},
47+
[]float64{12.0, 55.0},
48+
},
49+
}
50+
51+
for _, tc := range testcases {
52+
53+
s := fmt.Sprintf("%f %f", tc.input[0], tc.input[1])
54+
inBuf := bytes.NewBuffer([]byte(s))
55+
outBuf := &bytes.Buffer{}
56+
57+
err := main.Main(inBuf, outBuf, strings.Fields(tc.args))
58+
59+
if tc.output == nil {
60+
assert.Error(err, tc.args)
61+
} else {
62+
assert.NoError(err, tc.args)
63+
64+
tokens := strings.Fields(string(outBuf.Bytes()))
65+
assert.Len(tokens, 2, tc.args)
66+
actual0, err := strconv.ParseFloat(tokens[0], 64)
67+
assert.NoError(err, tc.args)
68+
assert.InDelta(tc.output[0], actual0, 1.0e-2, tc.args)
69+
actual1, err := strconv.ParseFloat(tokens[1], 64)
70+
assert.NoError(err, tc.args)
71+
assert.InDelta(tc.output[1], actual1, 1.0e-2, tc.args)
72+
}
73+
}
1274
}

core/System.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func ValidateProjStringContents(pl *support.ProjString) error {
160160

161161
// you have to say +proj=...
162162
if pl.CountKey("proj") != 1 {
163-
return merror.New(merror.InvalidProjectionSyntax, "proj...proj")
163+
return merror.New(merror.InvalidProjectionSyntax, "proj must appear exactly once")
164164
}
165165
projName, ok := pl.GetAsString("proj")
166166
if !ok || projName == "" {

core/full_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestUtm(t *testing.T) {
3232
assert.NoError(err)
3333

3434
x, y := output.X, output.Y
35-
assert.InDelta(691875.63, x, 1e-2) // TODO: should be like 1e-8?
35+
assert.InDelta(691875.63, x, 1e-2)
3636
assert.InDelta(6098907.83, y, 1e-2)
3737

3838
input2 := output

merror/Error.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
"github.com/go-spatial/proj/mlog"
99
)
1010

11+
// ShowSource determines whther to include source file and line information in
12+
// the Error() string
13+
var ShowSource = false
14+
1115
// Error captures the type of error, where it occurred, inner errors, etc.
1216
// Error implements the error interface
1317
type Error struct {
@@ -72,7 +76,10 @@ func Pass(err error) error {
7276
}
7377

7478
func (e Error) Error() string {
75-
s := fmt.Sprintf("%s (from %s at %s:%d)", e.Message, e.Function, e.File, e.Line)
79+
s := e.Message
80+
if ShowSource {
81+
s += fmt.Sprintf(" (from %s at %s:%d)", e.Function, e.File, e.Line)
82+
}
7683
if e.Inner != nil {
7784
s += " // Inner: " + e.Inner.Error()
7885
}

merror/Error_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,26 @@ import (
88
)
99

1010
func TestError(t *testing.T) {
11+
showSource := merror.ShowSource
12+
merror.ShowSource = true
13+
defer func() { merror.ShowSource = showSource }()
14+
1115
assert := assert.New(t)
1216

1317
err1 := merror.New("errtest-%d", 1)
1418
assert.Error(err1)
15-
exp1 := "errtest-1 (from merror_test.TestError at Error_test.go:13)"
19+
exp1 := "errtest-1 (from merror_test.TestError at Error_test.go:17)"
1620
assert.Equal(exp1, err1.Error())
1721

1822
err2 := merror.Wrap(err1, "errtest-%d", 2)
1923
assert.Error(err2)
20-
exp2 := "errtest-2 (from merror_test.TestError at Error_test.go:18)"
24+
exp2 := "errtest-2 (from merror_test.TestError at Error_test.go:22)"
2125
exp2 += " // Inner: " + exp1
2226
assert.Equal(exp2, err2.Error())
2327

2428
err3 := merror.Wrap(err2)
2529
assert.Error(err3)
26-
exp3 := "wrapped error (from merror_test.TestError at Error_test.go:24)"
30+
exp3 := "wrapped error (from merror_test.TestError at Error_test.go:28)"
2731
exp3 += " // Inner: " + exp2
2832
assert.Equal(exp3, err3.Error())
2933
}

0 commit comments

Comments
 (0)