-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* changed to match official API * added example
- Loading branch information
Showing
4 changed files
with
224 additions
and
169 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file was deleted.
Oops, something went wrong.