Skip to content

Commit

Permalink
Issue3 (#15)
Browse files Browse the repository at this point in the history
* changed to match official API

* added example
  • Loading branch information
mpgerlek authored Apr 19, 2018
1 parent 462783c commit f13e7f0
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 169 deletions.
134 changes: 134 additions & 0 deletions Convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package proj

import (
"fmt"

"github.com/go-spatial/proj/core"
"github.com/go-spatial/proj/support"

// need to pull in the operations table entries
_ "github.com/go-spatial/proj/operations"
)

// EPSGCode is the enum type for coordinate systems
type EPSGCode int

// Supported EPSG codes
const (
EPSG3395 EPSGCode = 3395
WorldMercator = EPSG3395
EPSG3857 = 3857
WebMercator = EPSG3857
)

// Convert performs a conversion from a 4326 coordinate system (lon/lat
// degrees, 2D) to the given projected system (x/y meters, 2D).
//
// The input is assumed to be an array of lon/lat points, e.g. [lon0, lat0,
// lon1, lat1, lon2, lat2, ...]. The length of the array must, therefore, be
// even.
//
// The returned output is a similar array of x/y points, e.g. [x0, y0, x1,
// y1, x2, y2, ...].
func Convert(dest EPSGCode, input []float64) ([]float64, error) {

conv, err := newConversion(dest)
if err != nil {
return nil, nil
}

return conv.convert(input)
}

//---------------------------------------------------------------------------

// conversion holds the objects needed to perform a conversion
type conversion struct {
dest EPSGCode
projString *support.ProjString
system *core.System
operation core.IOperation
converter core.IConvertLPToXY
}

var conversions = map[EPSGCode]*conversion{}

var projStrings = map[EPSGCode]string{
EPSG3395: "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84", // TODO: support +units=m +no_defs
EPSG3857: "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0", // TODO: support +units=m +nadgrids=@null +wktext +no_defs
}

// newConversion creates a conversion object for the destination systems. If
// such a conversion already exists in the cache, use that.
func newConversion(dest EPSGCode) (*conversion, error) {

str, ok := projStrings[dest]
if !ok {
return nil, fmt.Errorf("epsg code is not a supported projection")
}

conv, ok := conversions[dest]
if ok {
return conv, nil
}

// need to build it

ps, err := support.NewProjString(str)
if err != nil {
return nil, err
}

sys, opx, err := core.NewSystem(ps)
if err != nil {
return nil, err
}

if !opx.GetDescription().IsConvertLPToXY() {
return nil, fmt.Errorf("projection type is not supported")
}

conv = &conversion{
dest: dest,
projString: ps,
system: sys,
operation: opx,
converter: opx.(core.IConvertLPToXY),
}

// cache it
conversions[dest] = conv

return conv, nil
}

// convert performs the projection on the given input points
func (conv *conversion) convert(input []float64) ([]float64, error) {

if conv == nil || conv.converter == nil {
return nil, fmt.Errorf("conversion not initialized")
}

if len(input)%2 != 0 {
return nil, fmt.Errorf("input array of lon/lat values must be an even number")
}

output := make([]float64, len(input))

lp := &core.CoordLP{}

for i := 0; i < len(input); i += 2 {
lp.Lam = support.DDToR(input[i])
lp.Phi = support.DDToR(input[i+1])

xy, err := conv.converter.Forward(lp)
if err != nil {
return nil, err
}

output[i] = xy.X
output[i+1] = xy.Y
}

return output, nil
}
90 changes: 90 additions & 0 deletions Convert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package proj_test

import (
"fmt"
"testing"

"github.com/go-spatial/proj"
"github.com/stretchr/testify/assert"
)

var inputA = []float64{
-0.127758, 51.507351, // London
2.352222, 48.856614, // Paris
12.496366, 41.902783, // Rome
}
var inputB = []float64{
-77.625583, 38.833846, // mpg
}

type testcase struct {
dest proj.EPSGCode
expectedA []float64
expectedB []float64
}

var testcases = []testcase{
{
dest: proj.EPSG3395,
expectedA: []float64{
-14221.96, 6678068.96,
261848.16, 6218371.80,
1391089.10, 5117883.04,
},
expectedB: []float64{
-8641240.37, 4671101.60,
},
},
{
dest: proj.EPSG3857,
expectedA: []float64{
-14221.96, 6711533.71,
261848.16, 6250566.72,
1391089.10, 5146427.91,
},
expectedB: []float64{
-8641240.37, 4697899.31,
},
},
}

func TestConvert(t *testing.T) {
assert := assert.New(t)

for _, tc := range testcases {

outputA, err := proj.Convert(tc.dest, inputA)
assert.NoError(err)

outputB, err := proj.Convert(tc.dest, inputB)
assert.NoError(err)

const tol = 1.0e-2

for i := range tc.expectedA {
tag := fmt.Sprintf("epsg:%d, input=A.%d", int(tc.dest), i)
assert.InDelta(tc.expectedA[i], outputA[i], tol, tag)
assert.InDelta(tc.expectedA[i], outputA[i], tol, tag)
}
for i := range tc.expectedB {
tag := fmt.Sprintf("epsg:%d, input=B.%d", int(tc.dest), i)
assert.InDelta(tc.expectedB[i], outputB[i], tol, tag)
assert.InDelta(tc.expectedB[i], outputB[i], tol, tag)
}
}
}

func ExampleConvert() {

var dd = []float64{
-77.625583, 38.833846,
}

xy, err := proj.Convert(proj.EPSG3395, dd)
if err != nil {
panic(err)
}

fmt.Printf("%.2f, %.2f\n", xy[0], xy[1])
// Output: -8641240.37, 4671101.60
}
105 changes: 0 additions & 105 deletions Proj.go

This file was deleted.

64 changes: 0 additions & 64 deletions Proj_test.go

This file was deleted.

0 comments on commit f13e7f0

Please sign in to comment.