Skip to content

Commit

Permalink
Add in Inverse convenience method + 4087 proj support
Browse files Browse the repository at this point in the history
  • Loading branch information
meilinger committed Oct 9, 2019
1 parent f0dac9b commit 1ce6d1e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 6 deletions.
67 changes: 61 additions & 6 deletions Convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ type EPSGCode int

// Supported EPSG codes
const (
EPSG3395 EPSGCode = 3395
WorldMercator = EPSG3395
EPSG3857 = 3857
WebMercator = EPSG3857
EPSG4087 = 4087
EPSG3395 EPSGCode = 3395
WorldMercator = EPSG3395
EPSG3857 = 3857
WebMercator = EPSG3857
EPSG4087 = 4087
WorldEquidistantCylindrical = EPSG4087
EPSG4326 = 4326
WGS84 = EPSG4326
)

// ensure only one person is updating our cache of converters at a time
Expand All @@ -54,6 +57,26 @@ func Convert(dest EPSGCode, input []float64) ([]float64, error) {
return conv.convert(input)
}

// Inverse converts from a projected X/Y of a coordinate system to
// 4326 (lat/lon, 2D).
//
// The input is assumed to be an array of x/y points, e.g. [x0, y0,
// x1, y1, x2, y2, ...]. The length of the array must, therefore, be
// even.
//
// The returned output is a similar array of lon/lat points, e.g. [lon0, lat0, lon1,
// lat1, lon2, lat2, ...].
func Inverse(src EPSGCode, input []float64) ([]float64, error) {
cacheLock.Lock()
conv, err := newConversion(src)
cacheLock.Unlock()
if err != nil {
return nil, err
}

return conv.inverse(input)
}

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

// conversion holds the objects needed to perform a conversion
Expand All @@ -70,7 +93,7 @@ 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
EPSG4087: "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84",
EPSG4087: "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84", // TODO: support +units=m +no_defs
}

// newConversion creates a conversion object for the destination systems. If
Expand Down Expand Up @@ -147,3 +170,35 @@ func (conv *conversion) convert(input []float64) ([]float64, error) {

return output, nil
}

func (conv *conversion) inverse(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 x/y values must be an even number")
}

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

xy := &core.CoordXY{}

for i := 0; i < len(input); i += 2 {
xy.X = input[i]
xy.Y = input[i+1]

lp, err := conv.converter.Inverse(xy)

if err != nil {
return nil, err
}

l, p := lp.Lam, lp.Phi

output[i] = support.RToDD(l)
output[i+1] = support.RToDD(p)
}

return output, nil
}
15 changes: 15 additions & 0 deletions Convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ func TestConvert(t *testing.T) {
outputB, err := proj.Convert(tc.dest, inputB)
assert.NoError(err)

invA, err := proj.Inverse(tc.dest, tc.expectedA)
assert.NoError(err)

invB, err := proj.Inverse(tc.dest, tc.expectedB)

const tol = 1.0e-2

for i := range tc.expectedA {
Expand All @@ -89,6 +94,16 @@ func TestConvert(t *testing.T) {
assert.InDelta(tc.expectedB[i], outputB[i], tol, tag)
assert.InDelta(tc.expectedB[i], outputB[i], tol, tag)
}

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

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

Expand Down

0 comments on commit 1ce6d1e

Please sign in to comment.