From 01a2c040681fedc6e62a79e32c8cf517ffaff5ce Mon Sep 17 00:00:00 2001 From: "Michael P. Gerlek" Date: Fri, 20 Apr 2018 17:21:11 -0400 Subject: [PATCH] Issue 17 (#26) * OBE * updated * documentation work --- README.md | 101 +++++++++++++--- TODO.md | 215 ----------------------------------- cmd/proj/proj.go | 15 +++ cmd/proj/proj_test.go | 2 +- core/ConvertLPToXY.go | 25 +++- core/Coordinate.go | 7 +- core/Ellipsoid.go | 23 ++-- core/Meridian.go | 4 + core/Meridian_test.go | 2 +- core/Operation.go | 10 +- core/OperationDescription.go | 8 +- core/System.go | 12 +- gie/Command.go | 15 ++- gie/Gie.go | 13 ++- gie/Gie_test.go | 1 + gie/Parser.go | 16 +-- merror/Error.go | 3 +- mlog/Log.go | 4 + mlog/Log_test.go | 13 ++- operations/Aea.go | 60 +++++----- operations/Aeqd.go | 2 +- operations/Airy.go | 22 ++-- operations/August.go | 10 +- operations/EtMerc.go | 66 +++++------ operations/Merc.go | 56 ++++----- support/Hypot.go | 53 --------- support/ProjString.go | 13 +-- 27 files changed, 324 insertions(+), 447 deletions(-) delete mode 100644 TODO.md delete mode 100644 support/Hypot.go diff --git a/README.md b/README.md index 7c9bc37..06d3ffc 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,103 @@ [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/go-spatial/proj) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://github.com/go-spatial/proj/blob/master/LICENSE.md) + # proj: PROJ4, for Go! -This project is **UNDER ACTIVE DEVELOPMENT** and is, therefore, **NOT STABLE**, which subsequently means it **SHOULD NOT BE USED FOR PRODUCTION PURPOSES**. +Proj is a _selective_ and _on-going_ port of the venerable PROJ.4 project to +the Go language. -Contact `mpg@flaxen.com` if you'd like to help out on the project. +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. -# Guiding Principles and Goals and Plans +# Installation + +To install the packages, preparatory to using them in your own code: + +> go get -U github.com/go-spatial/proj + +To copy the repo, preparatory to doing development: + +> git clone https://github.com/go-spatial/proj +> go test ./... + +See below for API usage instructions. + -As much as we all love PROJ4, we are not going to attempt to blindly (or even blithely) port every jot and tittle of the project. +# Guiding Principles and Goals and Plans In no particular order, these are the conditions we're imposing on ourselves: * We are going to use the PROJ 5.0.1 release as our starting point. * We will look to the `proj4js` project for suggestions as to what PROJ4 code does and does not need to be ported, and how. -* We will be "DONE" when the targetted test cases from PROJ are passing, including both direct invocations of the coordinate operations and proj-string tests. -* The coordinate operations are going to be ported pretty directly, but the function signatures and "catalog" will be idiomatic Go. -* The `proj` command-line app will be ported and will be idiomatic. -* All code will pass [the Go `metalinter`](https://github.com/alecthomas/gometalinter) cleanly all the time. -* Unit tests will be implemented for pretty much everything, using the "side-by-side" `_test` package style. -* Tests will be extracted from the various PROJ tests into idiomatic Go test cases. We will not port the new PROJ test harness. +* We will consider numerical results returned by PROJ.4 to be "truth" (within appropriate tolerances). +* We will try to port the "mathy" parts with close to a 1:1 correspondence, so as to avoid inadvertently damaging the algoirthms. +* 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. +* The `proj` command-line app will not be fully ported. Instead, we will provide a much simpler tool. +* All code will pass muster with the various Go linting and formatting tools. +* 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. +* 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. * Go-style source code documentation will be provided. * A set of small, clean usage examples will be provided. -# Project Layout +# The APIs + +There are two APIs at present, helpfully known as "the conversion API" and "the core API". + +## The Conversion API + +This API is intended to be a dead-simple way to do a 2D projection from 4326. That is: + +**You Have:** a point which uses two `float64` numbers to represent lon/lat degrees in an `epsg:4326` coordinate reference system + +**You Want:** a point which uses two `float64` numbers to represent meters in a projected coordinate system such as "web mercator" (`epsg:3857`). + +If that's what you need to do, then just do this: + +``` + var lonlat = []float64{77.625583, 38.833846} + + xy, err := proj.Convert(proj.EPSG3395, lonlat) + if err != nil { + panic(err) + } + + fmt.Printf("%.2f, %.2f\n", xy[0], xy[1]) +``` + +Note that the `lonlat` array can contain more than two elements, so that you can project a whole set of points at once. + +This API is stable and unlikely to change much. If the projected EPSG code you need is not supported, just let us know. + + +## The Core API + +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). + +_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. + +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`. + + +# The Packages + +The proj repo contains these packages (directories): + +* `proj` (top-level): the Conversion API +* `proj/cmd/proj`: the simple `proj` command-line tool +* `proj/core`: the Core API, representing coordinate systems and conversion operations +* `proj/gie`: a naive implementation of the PROJ.4 `gie` tool, plus the full set of PROJ.4 test case files +* `proj/merror`: a little error package +* `proj/mlog`: a little logging package +* `proj/operations`: the actual coordinate operations; these routines tend to be closest to the original C code +* `proj/support`: misc structs and functions in support of the `core` package + +Most of the packages have `_test.go` files that demonstrate how the various types and functions are (intended to be) used. + + +# Future Work -Packages (directories): -* `proj`: the `proj` command-line tool -* `operations`: the actual coordinate operations -* `support`: misc stuff, including the core API -* `examples`: simple yet instructive self-validating demos +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. -The API: -* ... +We welcome your participation! See `CONTRIBUTING.md` and/or contact `mpg@flaxen.com` if you'd like to help out on the project. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index d835f1a..0000000 --- a/TODO.md +++ /dev/null @@ -1,215 +0,0 @@ -# Progress Reporting - -There are 204 `.c` and `.h` files in PROJ. We will try to check them off here as we go: _completed,_ _underway,_ and _not started._ - -We will also counted passed/failed/skipped tests. - -## Completed - -## Underway - -## Not Started -1. PJ_aea.c -1. PJ_aeqd.c -1. PJ_airy.c -1. PJ_aitoff.c -1. PJ_august.c -1. PJ_axisswap.c -1. PJ_bacon.c -1. PJ_bipc.c -1. PJ_boggs.c -1. PJ_bonne.c -1. PJ_calcofi.c -1. PJ_cart.c -1. PJ_cass.c -1. PJ_cc.c -1. PJ_ccon.c -1. PJ_cea.c -1. PJ_chamb.c -1. PJ_collg.c -1. PJ_comill.c -1. PJ_crast.c -1. PJ_deformation.c -1. PJ_denoy.c -1. PJ_eck1.c -1. PJ_eck2.c -1. PJ_eck3.c -1. PJ_eck4.c -1. PJ_eck5.c -1. PJ_eqc.c -1. PJ_eqdc.c -1. PJ_fahey.c -1. PJ_fouc_s.c -1. PJ_gall.c -1. PJ_geoc.c -1. PJ_geos.c -1. PJ_gins8.c -1. PJ_gn_sinu.c -1. PJ_gnom.c -1. PJ_goode.c -1. PJ_gstmerc.c -1. PJ_hammer.c -1. PJ_hatano.c -1. PJ_healpix.c -1. PJ_helmert.c -1. PJ_hgridshift.c -1. PJ_horner.c -1. PJ_igh.c -1. PJ_imw_p.c -1. PJ_isea.c -1. PJ_krovak.c -1. PJ_labrd.c -1. PJ_laea.c -1. PJ_lagrng.c -1. PJ_larr.c -1. PJ_lask.c -1. PJ_latlong.c -1. PJ_lcc.c -1. PJ_lcca.c -1. PJ_loxim.c -1. PJ_lsat.c -1. PJ_mbt_fps.c -1. PJ_mbtfpp.c -1. PJ_mbtfpq.c -1. PJ_merc.c -1. PJ_mill.c -1. PJ_misrsom.c -1. PJ_mod_ster.c -1. PJ_moll.c -1. PJ_molodensky.c -1. PJ_natearth.c -1. PJ_natearth2.c -1. PJ_nell.c -1. PJ_nell_h.c -1. PJ_nocol.c -1. PJ_nsper.c -1. PJ_nzmg.c -1. PJ_ob_tran.c -1. PJ_ocea.c -1. PJ_oea.c -1. PJ_omerc.c -1. PJ_ortho.c -1. PJ_patterson.c -1. PJ_pipeline.c -1. PJ_poly.c -1. PJ_putp2.c -1. PJ_putp3.c -1. PJ_putp4p.c -1. PJ_putp5.c -1. PJ_putp6.c -1. PJ_qsc.c -1. PJ_robin.c -1. PJ_rpoly.c -1. PJ_sch.c -1. PJ_sconics.c -1. PJ_somerc.c -1. PJ_stere.c -1. PJ_sterea.c -1. PJ_sts.c -1. PJ_tcc.c -1. PJ_tcea.c -1. PJ_times.c -1. PJ_tmerc.c -1. PJ_tpeqd.c -1. PJ_unitconvert.c -1. PJ_urm5.c -1. PJ_urmfps.c -1. PJ_vandg.c -1. PJ_vandg2.c -1. PJ_vandg4.c -1. PJ_vgridshift.c -1. PJ_wag2.c -1. PJ_wag3.c -1. PJ_wag7.c -1. PJ_wink1.c -1. PJ_wink2.c -1. aasincos.c -1. adjlon.c -1. bch2bps.c -1. bchgen.c -1. biveval.c -1. cct.c -1. cs2cs.c -1. dmstor.c -1. emess.c -1. emess.h -1. gen_cheb.c -1. geocent.c -1. geocent.h -1. geod.c -1. geod_interface.c -1. geod_interface.h -1. geod_set.c -1. geodesic.c -1. geodesic.h -1. geodtest.c -1. gie.c -1. hypot.c -1. jniproj.c -1. mk_cheby.c -1. multistresstest.c -1. nad2bin.c -1. nad_cvt.c -1. nad_init.c -1. nad_intr.c -1. nad_list.h -1. optargpm.h -1. org_proj4_PJ.h -1. org_proj4_Projections.h -1. p_series.c -1. pj_apply_gridshift.c -1. pj_apply_vgridshift.c -1. pj_auth.c -1. pj_ctx.c -1. pj_datum_set.c -1. pj_datums.c -1. pj_deriv.c -1. pj_ell_set.c -1. pj_ellps.c -1. pj_errno.c -1. pj_factors.c -1. pj_fileapi.c -1. pj_fwd.c -1. pj_gauss.c -1. pj_gc_reader.c -1. pj_geocent.c -1. pj_gridcatalog.c -1. pj_gridinfo.c -1. pj_gridlist.c -1. pj_init.c -1. pj_initcache.c -1. pj_internal.c -1. pj_inv.c -1. pj_list.c -1. pj_list.h -1. pj_log.c -1. pj_malloc.c -1. pj_mlfn.c -1. pj_msfn.c -1. pj_mutex.c -1. pj_open_lib.c -1. pj_param.c -1. pj_phi2.c -1. pj_pr_list.c -1. pj_qsfn.c -1. pj_release.c -1. pj_strerrno.c -1. pj_strtod.c -1. pj_transform.c -1. pj_tsfn.c -1. pj_units.c -1. pj_utils.c -1. pj_zpoly1.c -1. proj.c -1. proj.h -1. proj_4D_api.c -1. proj_api.h -1. proj_etmerc.c -1. proj_internal.h -1. proj_mdist.c -1. proj_rouss.c -1. proj_strtod.c -1. projects.h -1. rtodms.c -1. test228.c -1. vector1.c diff --git a/cmd/proj/proj.go b/cmd/proj/proj.go index 58e85ab..17a9ef2 100644 --- a/cmd/proj/proj.go +++ b/cmd/proj/proj.go @@ -33,6 +33,7 @@ func main() { // Main is just a callable version of main(), for testing purposes func Main(inS io.Reader, outS io.Writer, args []string) error { + // unverbosify all the things merror.ShowSource = false mlog.DebugEnabled = false mlog.InfoEnabled = false @@ -71,6 +72,7 @@ func Main(inS io.Reader, outS io.Writer, args []string) error { mlog.ErrorEnabled = true } + // handle "-epsg" usage, using the Convert API if *epsgDest != 0 { if *inverse { return fmt.Errorf("-inverse not allowed with -epsg") @@ -80,6 +82,7 @@ func Main(inS io.Reader, outS io.Writer, args []string) error { } input := make([]float64, 2) + // wrap the converter in a little lambda to be run inside a REPL loop f := func(a, b float64) (float64, float64, error) { input[0] = a input[1] = b @@ -93,18 +96,25 @@ func Main(inS io.Reader, outS io.Writer, args []string) error { return repl(inS, outS, f) } + // args is a proj string, so use the Core API + + // parse the proj string into key/value pairs ps, err := support.NewProjString(projString) if err != nil { return err } + // make a coordinate system object, and the operation object _, opx, err := core.NewSystem(ps) if err != nil { return err } + // we only support one kind of operation object right now anyway op := opx.(core.IConvertLPToXY) + // make a lambda with the forward or inverse function, and + // send it to the REPL loop if !*inverse { f := func(a, b float64) (float64, float64, error) { @@ -126,11 +136,16 @@ func Main(inS io.Reader, outS io.Writer, args []string) error { } return support.RToDD(output.Lam), support.RToDD(output.Phi), nil } + return repl(inS, outS, f) } +// the type of our lambdas type converter func(a, b float64) (float64, float64, error) +// the repl loop reads two input numbers, runs the conversion +// (which has been wrapped up into a tidy little lambda), +// and prints the results func repl(inS io.Reader, outS io.Writer, f converter) error { var a, b float64 diff --git a/cmd/proj/proj_test.go b/cmd/proj/proj_test.go index 988b2a8..1e47f58 100644 --- a/cmd/proj/proj_test.go +++ b/cmd/proj/proj_test.go @@ -24,7 +24,7 @@ func TestCmd(t *testing.T) { type testcase struct { args string input []float64 - output []float64 + output []float64 // set to nil for expected fails } testcases := []testcase{ diff --git a/core/ConvertLPToXY.go b/core/ConvertLPToXY.go index e9106af..5c6135e 100644 --- a/core/ConvertLPToXY.go +++ b/core/ConvertLPToXY.go @@ -15,19 +15,36 @@ import ( ) // IConvertLPToXY is for 2D LP->XY conversions +// +// This interface requires you support a forward LP-to-XY +// function and an inverse XY-to-LP function. This is +// the only conversion type we support today; someday, +// there will be more interfaces like this, for different +// input/output types. +// +// (Yes, sometimes my interface names still start with "I". +// Everyone has their own personal moral failings, and this +// is one of mine.) type IConvertLPToXY interface { IOperation Forward(*CoordLP) (*CoordXY, error) Inverse(*CoordXY) (*CoordLP, error) } -// ConvertLPToXY is the specific kind of operation +// ConvertLPToXY is a specific kind of operation, which satisfies +// the IConvertLPToXY interfaces. +// +// Wrapping an operation in this type allows us to provide hooks +// for the algorithm's forward and inverse functions -- for example, +// the Forward function needs to be preceeded and suceeded, +// respectively, by calls to forwardPrepare and forwardFinalize. type ConvertLPToXY struct { Operation Algorithm IConvertLPToXY } -// NewConvertLPToXY makes a new operation, and the associated alg object +// NewConvertLPToXY makes a new ConvertLPToXT operation and its associated +// algorithm object, and returns the operation as an IOperation. func NewConvertLPToXY(sys *System, desc *OperationDescription) (IOperation, error) { if !desc.IsConvertLPToXY() { @@ -50,7 +67,7 @@ func NewConvertLPToXY(sys *System, desc *OperationDescription) (IOperation, erro //--------------------------------------------------------------------- -// Forward is the real entry point +// Forward is the hook-providing entry point to the algorithm. func (op *ConvertLPToXY) Forward(lp *CoordLP) (*CoordXY, error) { lp, err := op.forwardPrepare(lp) @@ -71,7 +88,7 @@ func (op *ConvertLPToXY) Forward(lp *CoordLP) (*CoordXY, error) { return xy, err } -// Inverse is the real entry point +// Inverse is the hook-providing entry point to the inverse algorithm. func (op *ConvertLPToXY) Inverse(xy *CoordXY) (*CoordLP, error) { xy, err := op.inversePrepare(xy) diff --git a/core/Coordinate.go b/core/Coordinate.go index b51174c..9e4c938 100644 --- a/core/Coordinate.go +++ b/core/Coordinate.go @@ -7,10 +7,12 @@ package core -// CoordType is the enum for the differetn intepretations of a Coordinate object +// CoordType is the enum for the different types of coordinates type CoordType int -// The coordinate type +// The coordinate types +// +// Today we are only using CoordTypeLP and CoordTypeXY. const ( CoordTypeAny = iota CoordTypeXYZT @@ -28,6 +30,7 @@ const ( ) // CoordAny just generically holds data, not assigned to a coordinate type. +// // Because unions. type CoordAny struct{ V [4]float64 } diff --git a/core/Ellipsoid.go b/core/Ellipsoid.go index 4825eea..9ede877 100644 --- a/core/Ellipsoid.go +++ b/core/Ellipsoid.go @@ -16,6 +16,10 @@ import ( ) // Ellipsoid represents an ellipsoid +// +// A System object contains a pointer to one Ellipsoid object. It is pretty +// close to the original C type, but when it grows up it wants to look and feel +// like a real Go type. type Ellipsoid struct { ID string Major string @@ -62,11 +66,12 @@ type Ellipsoid struct { EsOrig, AOrig float64 /* es and a before any +proj related adjustment */ } -// NewEllipsoid creates an Ellipsoid and initializes it from the proj string -func NewEllipsoid(op *System) (*Ellipsoid, error) { +// NewEllipsoid creates an Ellipsoid and initializes it from the +// information in the given System object +func NewEllipsoid(sys *System) (*Ellipsoid, error) { ellipsoid := &Ellipsoid{} - err := ellipsoid.initialize(op) + err := ellipsoid.initialize(sys) if err != nil { return nil, err } @@ -83,9 +88,9 @@ func (e *Ellipsoid) String() string { return string(b) } -func (e *Ellipsoid) initialize(op *System) error { +func (e *Ellipsoid) initialize(sys *System) error { - ps := op.ProjString + ps := sys.ProjString /* Specifying R overrules everything */ if ps.ContainsKey("R") { @@ -98,19 +103,19 @@ func (e *Ellipsoid) initialize(op *System) error { } /* If an ellps argument is specified, start by using that */ - err := e.doEllps(op.ProjString) + err := e.doEllps(sys.ProjString) if err != nil { return err } /* We may overwrite the size */ - err = e.doSize(op.ProjString) + err = e.doSize(sys.ProjString) if err != nil { return err } /* We may also overwrite the shape */ - err = e.doShape(op.ProjString) + err = e.doShape(sys.ProjString) if err != nil { return err } @@ -122,7 +127,7 @@ func (e *Ellipsoid) initialize(op *System) error { } /* And finally, we may turn it into a sphere */ - return e.doSpherification(op.ProjString) + return e.doSpherification(sys.ProjString) } func (e *Ellipsoid) doCalcParams(a float64, es float64) error { diff --git a/core/Meridian.go b/core/Meridian.go index 0a4465b..326a165 100644 --- a/core/Meridian.go +++ b/core/Meridian.go @@ -8,6 +8,10 @@ package core // PrimeMeridian contains information about a prime meridian +// +// Someday, this will be a rich type with lots of methods and stuff. +// +// Today, it is not. type PrimeMeridian struct { ID string Definition string diff --git a/core/Meridian_test.go b/core/Meridian_test.go index 0f7184a..9ccd9ad 100644 --- a/core/Meridian_test.go +++ b/core/Meridian_test.go @@ -12,5 +12,5 @@ import ( ) func TestMeridian(t *testing.T) { - + // TODO } diff --git a/core/Operation.go b/core/Operation.go index 6bf9e98..ccaace8 100644 --- a/core/Operation.go +++ b/core/Operation.go @@ -8,6 +8,8 @@ package core // OperationType is the enum for the different kinds of conversions and transforms +// +// This may turn out not to be as useful as it originally seemed. type OperationType int // The operation type @@ -17,24 +19,24 @@ const ( OperationTypeTransformation ) -// IOperation is for all the operation +// IOperation is what all the operations need to support type IOperation interface { GetSystem() *System GetDescription() *OperationDescription } -// Operation is for all operations +// Operation is the base class for all operations type Operation struct { System *System Description *OperationDescription } -// GetSystem returns the system +// GetSystem returns the System object the operation is associated with func (op *Operation) GetSystem() *System { return op.System } -// GetDescription returns the descr +// GetDescription returns the OperationDescription of the operation func (op *Operation) GetDescription() *OperationDescription { return op.Description } diff --git a/core/OperationDescription.go b/core/OperationDescription.go index a849954..15cda16 100644 --- a/core/OperationDescription.go +++ b/core/OperationDescription.go @@ -12,11 +12,14 @@ import ( ) // OperationDescriptionTable is the global list of all the known operations +// +// The algorithms in the operations package call the Register functions +// which populate this map. var OperationDescriptionTable = map[string]*OperationDescription{} // ConvertLPToXYCreatorFuncType is the type of the function which creates an operation-specific object // -// This kind of function, when executed, creates an operation-specific type +// This kind of function, when executed, creates an operation-specific object // which implements IConvertLPToXY. type ConvertLPToXYCreatorFuncType func(*System, *OperationDescription) (IConvertLPToXY, error) @@ -34,6 +37,9 @@ type OperationDescription struct { } // RegisterConvertLPToXY adds an OperationDescription entry to the OperationDescriptionTable +// +// Each file in the operations package has an init() routine which calls this function. +// Each operation supplies its own "creatorFunc", of its own particular type. func RegisterConvertLPToXY( id string, description string, diff --git a/core/System.go b/core/System.go index 4dadef4..97ccb41 100644 --- a/core/System.go +++ b/core/System.go @@ -20,7 +20,7 @@ import ( // DatumType is the enum for the types of datums we support type DatumType int -// All the DatumType constants +// All the DatumType constants (taken directly from the C) const ( DatumTypeUnknown DatumType = 0 DatumType3Param = 1 @@ -53,10 +53,12 @@ const ( const epsLat = 1.0e-12 -// System contains all the info needed to describe an "operation", -// i.e. a "conversion" (no datum change) or a "transformation". +// System contains all the info needed to describe a coordinate system and +// related info. // -// In PROJ.4, a "projection" is a conversion from "angular" input to "scaled linear" output. +// This type needs to be improved, as it is still quite close to the +// original C. Types like Ellipsoid and Datum represent a first step +// towards a refactoring. type System struct { ProjString *support.ProjString OpDescr *OperationDescription @@ -123,7 +125,7 @@ type System struct { //double last_after_date; /* TODO: Description needed */ } -// NewSystem returns a new Operation object +// NewSystem returns a new System object func NewSystem(ps *support.ProjString) (*System, IOperation, error) { err := ValidateProjStringContents(ps) diff --git a/gie/Command.go b/gie/Command.go index 0b4435e..f75e842 100644 --- a/gie/Command.go +++ b/gie/Command.go @@ -18,17 +18,23 @@ import ( "github.com/go-spatial/proj/support" ) +// the 4 input (or output) values, although +// we only support 2D (a,b) right now type coord struct { a, b, c, d float64 // lam,phi or x,y } +// represents a single invocation of the operation type testcase struct { inv bool accept coord expect coord } -// Command holds a set of tests as we build them up +// Command holds a set of testcases +// +// As the gie file is read, the current Command object +// gets modified, testcases get added, etc. type Command struct { ProjString string tolerance float64 @@ -41,7 +47,7 @@ type Command struct { roundtripDelta float64 } -// NewCommand returns a command +// NewCommand returns a new Command func NewCommand(file string, line int, ps string) *Command { c := &Command{ ProjString: ps, @@ -199,7 +205,10 @@ func unitsValue(s string) float64 { panic(s) } -// Execute runs the tests +// Execute runs the testcases +// +// First it parses the proj string, then it creates the coordinate system, +// then it executes the operation for each of the inputs. func (c *Command) Execute() error { ps, err := support.NewProjString(c.ProjString) diff --git a/gie/Gie.go b/gie/Gie.go index 6cf6974..389cc69 100644 --- a/gie/Gie.go +++ b/gie/Gie.go @@ -16,6 +16,9 @@ import ( _ "github.com/go-spatial/proj/operations" ) +// These are the projections we know about. If the projection string has a +// "proj=" key whose valued is not in this list, the Gie will not try to +// execute the Command. var supportedProjections = []string{ "etmerc", "utm", "aea", "leac", @@ -24,17 +27,23 @@ var supportedProjections = []string{ "august", } +// If the proj string has one of these keys, we won't execute the Command. var unsupportedKeys = []string{ "axis", "geoidgrids", "to_meter", } +// If the Command is from this file and line, we won't execute the +// Command -- this acts as a way to shut off tests we don't like. var skippedTests = []string{ "ellipsoid.gie:64", } -// Gie manages the GIE reading and executing processes +// Gie is the top-level object for the Gie test runner +// +// Gie manages reading and parsing the .gie files and then +// executing the commands and their testcases. type Gie struct { dir string files []string @@ -64,7 +73,7 @@ func NewGie(dir string) (*Gie, error) { return g, nil } -// Parse reads the .gie file and creates the commands +// Parse reads the .gie files and creates the commands func (g *Gie) Parse() error { for _, file := range g.files { p, err := NewParser(file) diff --git a/gie/Gie_test.go b/gie/Gie_test.go index 24f2bc1..ff4d8a9 100644 --- a/gie/Gie_test.go +++ b/gie/Gie_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" ) +// must be run from the "./proj/gie" directory, so it can access the "gie_data" directory func TestGie(t *testing.T) { assert := assert.New(t) diff --git a/gie/Parser.go b/gie/Parser.go index 5c712a8..99b9ec6 100644 --- a/gie/Parser.go +++ b/gie/Parser.go @@ -13,7 +13,8 @@ import ( "strings" ) -// Parser reads a .gie file and returns the commands +// Parser reads the .gie files and returns the list of commands +// it constructed type Parser struct { lines []string Commands []*Command @@ -22,6 +23,10 @@ type Parser struct { } // NewParser creates a Parser object and runs the parser +// +// The parser runs one line at time. It's not quite clear +// what the official .gie format is supposed to be, so we +// use a very dumb but effective approach. func NewParser(fname string) (*Parser, error) { lines, err := readLines(fname) @@ -58,16 +63,11 @@ func NewParser(fname string) (*Parser, error) { continue } - //if p.lines[0] != "" && (p.lines[0][0:1] >= "A" && p.lines[0][0:1] >= "Z") { - //fmt.Printf("[%s:%d] JUNK: %s\n", p.fname, p.lineNum, p.lines[0]) - //} - + // anything left here has to be part of a comment section, + // so we throw it away p.pop() } - //for _, c := range p.cmds { - //fmt.Printf("CMD %s\n", c.proj) - //} return p, nil } diff --git a/merror/Error.go b/merror/Error.go index 096836d..a3eb0d1 100644 --- a/merror/Error.go +++ b/merror/Error.go @@ -45,7 +45,8 @@ func New(format string, v ...interface{}) error { return err } -// Wrap returns a new Error object which contains another error object +// Wrap returns a new Error object which contains the given error object +// // If v is used, v[0] must be a format string. func Wrap(inner error, v ...interface{}) error { file, line, function := stackinfo(2) diff --git a/mlog/Log.go b/mlog/Log.go index 445bee1..b2d7a64 100644 --- a/mlog/Log.go +++ b/mlog/Log.go @@ -48,6 +48,10 @@ func Printf(format string, v ...interface{}) { } // Printv writes a variable as a regular log message to stderr +// +// TODO: would be nice if this could print the variable name +// (and ideally the private fields too, if reflection allows +// us access to them) func Printv(v interface{}) { if InfoEnabled { //s := fmt.Sprintf("%#v", v) diff --git a/mlog/Log_test.go b/mlog/Log_test.go index 58a20d4..042e032 100644 --- a/mlog/Log_test.go +++ b/mlog/Log_test.go @@ -60,10 +60,11 @@ func TestLogger(t *testing.T) { oldInfo := mlog.InfoEnabled oldError := mlog.ErrorEnabled + // the following is put in an inlined lambda, so that + // we have a place to put the defer: we need it to always get + // called immediately after the log stmts run, even if + // they crash -- otherwise, we'd have lost our stderr! func() { - // the defer is inside a lambda so that it always gets - // called immediately after the log stmts run, even if - // they crash -- otherwise, we'd have lost our stderr! defer unredirectStderr(savedFd) mlog.DebugEnabled = true @@ -98,10 +99,10 @@ func TestLogger(t *testing.T) { buf = buf[0:n] ex := []string{ - "[DEBUG] Log_test.go:73: debug 1", - "[LOG] Log_test.go:74: print 2", + "[DEBUG] Log_test.go:74: debug 1", + "[LOG] Log_test.go:75: print 2", "[ERROR] E", - "[LOG] Log_test.go:78: \"yow\"", + "[LOG] Log_test.go:79: \"yow\"", } expected := strings.Join(ex, "\n") + "\n" assert.Equal(expected, string(buf)) diff --git a/operations/Aea.go b/operations/Aea.go index 59af170..381b80e 100644 --- a/operations/Aea.go +++ b/operations/Aea.go @@ -47,32 +47,32 @@ type Aea struct { ellips bool } -// NewAea is +// NewAea is from PJ_aea.c func NewAea(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &Aea{ + op := &Aea{ isLambert: false, } - xxx.System = system + op.System = system - err := xxx.aeaSetup(system) + err := op.aeaSetup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } // NewLeac is too func NewLeac(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &Aea{ + op := &Aea{ isLambert: true, } - xxx.System = system + op.System = system - err := xxx.leacSetup(system) + err := op.leacSetup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } //--------------------------------------------------------------------- @@ -109,12 +109,12 @@ func phi1(qs, Te, tOneEs float64) float64 { return math.MaxFloat64 } -func (aea *Aea) localSetup(sys *core.System) error { +func (op *Aea) setup(sys *core.System) error { var cosphi, sinphi float64 var secant bool - Q := aea - P := aea.System + Q := op + P := op.System PE := P.Ellipsoid if math.Abs(Q.phi1+Q.phi2) < eps10 { @@ -164,10 +164,10 @@ func (aea *Aea) localSetup(sys *core.System) error { } // Forward goes frontwords -func (aea *Aea) Forward(lp *core.CoordLP) (*core.CoordXY, error) { +func (op *Aea) Forward(lp *core.CoordLP) (*core.CoordXY, error) { xy := &core.CoordXY{X: 0.0, Y: 0.0} - Q := aea - PE := aea.System.Ellipsoid + Q := op + PE := op.System.Ellipsoid var t float64 if Q.ellips { @@ -187,11 +187,11 @@ func (aea *Aea) Forward(lp *core.CoordLP) (*core.CoordXY, error) { } // Inverse goes backwards -func (aea *Aea) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { +func (op *Aea) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} - Q := aea - PE := aea.System.Ellipsoid + Q := op + PE := op.System.Ellipsoid xy.Y = Q.rho0 - xy.Y Q.rho = math.Hypot(xy.X, xy.Y) @@ -240,38 +240,38 @@ func (aea *Aea) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { return lp, nil } -func (aea *Aea) aeaSetup(sys *core.System) error { +func (op *Aea) aeaSetup(sys *core.System) error { - lat1, ok := aea.System.ProjString.GetAsFloat("lat_1") + lat1, ok := op.System.ProjString.GetAsFloat("lat_1") if !ok { lat1 = 0.0 } - lat2, ok := aea.System.ProjString.GetAsFloat("lat_2") + lat2, ok := op.System.ProjString.GetAsFloat("lat_2") if !ok { lat2 = 0.0 } - aea.phi1 = support.DDToR(lat1) - aea.phi2 = support.DDToR(lat2) + op.phi1 = support.DDToR(lat1) + op.phi2 = support.DDToR(lat2) - return aea.localSetup(aea.System) + return op.setup(op.System) } -func (aea *Aea) leacSetup(sys *core.System) error { +func (op *Aea) leacSetup(sys *core.System) error { - lat1, ok := aea.System.ProjString.GetAsFloat("lat_1") + lat1, ok := op.System.ProjString.GetAsFloat("lat_1") if !ok { lat1 = 0.0 } south := -support.PiOverTwo - _, ok = aea.System.ProjString.GetAsInt("south") + _, ok = op.System.ProjString.GetAsInt("south") if !ok { south = support.PiOverTwo } - aea.phi2 = support.DDToR(lat1) - aea.phi1 = south + op.phi2 = support.DDToR(lat1) + op.phi1 = south - return aea.localSetup(aea.System) + return op.setup(op.System) } diff --git a/operations/Aeqd.go b/operations/Aeqd.go index 246b713..e67dea4 100644 --- a/operations/Aeqd.go +++ b/operations/Aeqd.go @@ -7,7 +7,7 @@ package operations -/*** we need to port the geodesic.h functions before we can uncomment this ***/ +/*** WE NEED TO PORT THE geodesic.h FUNCTIONS BEFORE WE CAN UNCOMMENT THIS ***/ /*** diff --git a/operations/Airy.go b/operations/Airy.go index 319cbc3..b7f0e00 100644 --- a/operations/Airy.go +++ b/operations/Airy.go @@ -37,21 +37,21 @@ type Airy struct { // NewAiry returns a new Airy func NewAiry(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &Airy{} - xxx.System = system + op := &Airy{} + op.System = system - err := xxx.setup(system) + err := op.setup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } // Forward goes forewards -func (airy *Airy) Forward(lp *core.CoordLP) (*core.CoordXY, error) { +func (op *Airy) Forward(lp *core.CoordLP) (*core.CoordXY, error) { xy := &core.CoordXY{X: 0.0, Y: 0.0} - Q := airy + Q := op var sinlam, coslam, cosphi, sinphi, t, Krho, cosz float64 @@ -108,16 +108,16 @@ func (airy *Airy) Forward(lp *core.CoordLP) (*core.CoordXY, error) { } // Inverse is not allowed -func (airy *Airy) Inverse(*core.CoordXY) (*core.CoordLP, error) { +func (*Airy) Inverse(*core.CoordXY) (*core.CoordLP, error) { panic("no such conversion") } -func (airy *Airy) setup(sys *core.System) error { +func (op *Airy) setup(sys *core.System) error { var beta float64 - Q := airy - P := airy.System - PE := airy.System.Ellipsoid + Q := op + P := op.System + PE := op.System.Ellipsoid Q.nocut = P.ProjString.ContainsKey("no_cut") latb, ok := P.ProjString.GetAsFloat("lat_b") diff --git a/operations/August.go b/operations/August.go index 1e582e0..9bd476e 100644 --- a/operations/August.go +++ b/operations/August.go @@ -30,17 +30,17 @@ const m = 1.333333333333333 // NewAugust returns a new August func NewAugust(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &August{} - xxx.System = system + op := &August{} + op.System = system - PE := xxx.System.Ellipsoid + PE := op.System.Ellipsoid PE.Es = 0.0 - return xxx, nil + return op, nil } // Forward goes forewards -func (august *August) Forward(lp *core.CoordLP) (*core.CoordXY, error) { +func (op *August) Forward(lp *core.CoordLP) (*core.CoordXY, error) { xy := &core.CoordXY{X: 0.0, Y: 0.0} var t, c1, c, x1, x12, y1, y12 float64 diff --git a/operations/EtMerc.go b/operations/EtMerc.go index 1d16831..aef8b08 100644 --- a/operations/EtMerc.go +++ b/operations/EtMerc.go @@ -44,30 +44,30 @@ type EtMerc struct { // NewEtMerc returns a new EtMerc func NewEtMerc(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &EtMerc{ + op := &EtMerc{ isUtm: false, } - xxx.System = system + op.System = system - err := xxx.etmercSetup(system) + err := op.etmercSetup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } // NewUtm returns a new EtMerc func NewUtm(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &EtMerc{ + op := &EtMerc{ isUtm: true, } - xxx.System = system + op.System = system - err := xxx.utmSetup(system) + err := op.utmSetup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } //--------------------------------------------------------------------- @@ -91,7 +91,7 @@ func log1py(x float64) float64 { /* Compute log(1+x) accurately */ func asinhy(x float64) float64 { /* Compute asinh(x) accurately */ y := math.Abs(x) /* Enforce odd parity */ - y = log1py(y * (1 + y/(support.Hypot(1.0, y)+1))) + y = log1py(y * (1 + y/(math.Hypot(1.0, y)+1))) if x < 0 { return -y } @@ -180,12 +180,14 @@ func clens(a []float64, lenA int, argR float64) float64 { return math.Sin(argR) * hr } +//--------------------------------------------------------------------------- + // Forward operation -- Ellipsoidal, forward -func (xxx *EtMerc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { +func (op *EtMerc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { xy := &core.CoordXY{X: 0.0, Y: 0.0} - var Q = xxx + var Q = op var sinCn, cosCn, cosCe, sinCe, dCn, dCe float64 Cn := lp.Phi Ce := lp.Lam @@ -197,7 +199,7 @@ func (xxx *EtMerc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { sinCe, cosCe = math.Sincos(Ce) Cn = math.Atan2(sinCn, cosCe*cosCn) - Ce = math.Atan2(sinCe*cosCn, support.Hypot(sinCn, cosCn*cosCe)) + Ce = math.Atan2(sinCe*cosCn, math.Hypot(sinCn, cosCn*cosCe)) /* compl. sph. N, E -> ell. norm. N, E */ Ce = asinhy(math.Tan(Ce)) /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */ @@ -214,11 +216,11 @@ func (xxx *EtMerc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { } // Inverse operation (Ellipsoidal, inverse) -func (xxx *EtMerc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { +func (op *EtMerc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} - Q := xxx + Q := op var sinCn, cosCn, cosCe, sinCe, dCn, dCe float64 Cn := xy.Y Ce := xy.X @@ -236,7 +238,7 @@ func (xxx *EtMerc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { sinCn, cosCn = math.Sincos(Cn) sinCe, cosCe = math.Sincos(Ce) Ce = math.Atan2(sinCe, cosCe*cosCn) - Cn = math.Atan2(sinCn*cosCe, support.Hypot(sinCe, cosCe*cosCn)) + Cn = math.Atan2(sinCn*cosCe, math.Hypot(sinCe, cosCe*cosCn)) /* Gaussian LAT, LNG -> ell. LAT, LNG */ lp.Phi = gatg(Q.cgb[:], etmercOrder, Cn) lp.Lam = Ce @@ -248,10 +250,10 @@ func (xxx *EtMerc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { } /* general initialization */ -func (xxx *EtMerc) localSetup(P *core.System) error { +func (op *EtMerc) setup(P *core.System) error { var f, n, np, Z float64 - Q := xxx + Q := op PE := P.Ellipsoid @@ -338,29 +340,29 @@ func (xxx *EtMerc) localSetup(P *core.System) error { return nil } -func (xxx *EtMerc) etmercSetup(op *core.System) error { +func (op *EtMerc) etmercSetup(sys *core.System) error { - return xxx.localSetup(op) + return op.setup(sys) } /* utm uses etmerc for the underlying projection */ -func (xxx *EtMerc) utmSetup(op *core.System) error { +func (op *EtMerc) utmSetup(sys *core.System) error { - if op.Ellipsoid.Es == 0.0 { + if sys.Ellipsoid.Es == 0.0 { return merror.New(merror.EllipsoidUseRequired) } - if op.Lam0 < -1000.0 || op.Lam0 > 1000.0 { + if sys.Lam0 < -1000.0 || sys.Lam0 > 1000.0 { return merror.New(merror.InvalidUTMZone) } - op.Y0 = 0.0 - if op.ProjString.ContainsKey("south") { - op.Y0 = 10000000.0 + sys.Y0 = 0.0 + if sys.ProjString.ContainsKey("south") { + sys.Y0 = 10000000.0 } - op.X0 = 500000.0 + sys.X0 = 500000.0 - zone, ok := op.ProjString.GetAsInt("zone") /* zone input ? */ + zone, ok := sys.ProjString.GetAsInt("zone") /* zone input ? */ if ok { if zone > 0 && zone <= 60 { zone-- @@ -368,16 +370,16 @@ func (xxx *EtMerc) utmSetup(op *core.System) error { return merror.New(merror.InvalidUTMZone) } } else { /* nearest central meridian input */ - zone = (int)(math.Floor((support.Adjlon(op.Lam0) + support.Pi) * 30. / support.Pi)) + zone = (int)(math.Floor((support.Adjlon(sys.Lam0) + support.Pi) * 30. / support.Pi)) if zone < 0 { zone = 0 } else if zone >= 60 { zone = 59 } } - op.Lam0 = (float64(zone)+0.5)*support.Pi/30.0 - support.Pi - op.K0 = 0.9996 - op.Phi0 = 0.0 + sys.Lam0 = (float64(zone)+0.5)*support.Pi/30.0 - support.Pi + sys.K0 = 0.9996 + sys.Phi0 = 0.0 - return xxx.localSetup(op) + return op.setup(sys) } diff --git a/operations/Merc.go b/operations/Merc.go index ced7f7a..f4054c6 100644 --- a/operations/Merc.go +++ b/operations/Merc.go @@ -31,43 +31,43 @@ type Merc struct { // NewMerc returns a new Merc func NewMerc(system *core.System, desc *core.OperationDescription) (core.IConvertLPToXY, error) { - xxx := &Merc{ + op := &Merc{ isSphere: false, } - xxx.System = system + op.System = system - err := xxx.mercSetup(system) + err := op.mercSetup(system) if err != nil { return nil, err } - return xxx, nil + return op, nil } // Forward goes forewards -func (merc *Merc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { +func (op *Merc) Forward(lp *core.CoordLP) (*core.CoordXY, error) { - if merc.isSphere { - return merc.sphericalForward(lp) + if op.isSphere { + return op.sphericalForward(lp) } - return merc.ellipsoidalForward(lp) + return op.ellipsoidalForward(lp) } // Inverse goes backwards -func (merc *Merc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { +func (op *Merc) Inverse(xy *core.CoordXY) (*core.CoordLP, error) { - if merc.isSphere { - return merc.sphericalInverse(xy) + if op.isSphere { + return op.sphericalInverse(xy) } - return merc.ellipsoidalInverse(xy) + return op.ellipsoidalInverse(xy) } //--------------------------------------------------------------------- -func (merc *Merc) ellipsoidalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Ellipsoidal, forward */ +func (op *Merc) ellipsoidalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Ellipsoidal, forward */ xy := &core.CoordXY{X: 0.0, Y: 0.0} - P := merc.System - PE := merc.System.Ellipsoid + P := op.System + PE := op.System.Ellipsoid if math.Abs(math.Abs(lp.Phi)-support.PiOverTwo) <= eps10 { return xy, merror.New(merror.ToleranceCondition) @@ -77,10 +77,10 @@ func (merc *Merc) ellipsoidalForward(lp *core.CoordLP) (*core.CoordXY, error) { return xy, nil } -func (merc *Merc) sphericalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Spheroidal, forward */ +func (op *Merc) sphericalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* Spheroidal, forward */ xy := &core.CoordXY{X: 0.0, Y: 0.0} - P := merc.System + P := op.System if math.Abs(math.Abs(lp.Phi)-support.PiOverTwo) <= eps10 { return xy, merror.New(merror.ToleranceCondition) @@ -90,11 +90,11 @@ func (merc *Merc) sphericalForward(lp *core.CoordLP) (*core.CoordXY, error) { /* return xy, nil } -func (merc *Merc) ellipsoidalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Ellipsoidal, inverse */ +func (op *Merc) ellipsoidalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Ellipsoidal, inverse */ lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} - P := merc.System - PE := merc.System.Ellipsoid + P := op.System + PE := op.System.Ellipsoid var err error lp.Phi, err = support.Phi2(math.Exp(-xy.Y/P.K0), PE.E) @@ -108,20 +108,20 @@ func (merc *Merc) ellipsoidalInverse(xy *core.CoordXY) (*core.CoordLP, error) { return lp, nil } -func (merc *Merc) sphericalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Spheroidal, inverse */ +func (op *Merc) sphericalInverse(xy *core.CoordXY) (*core.CoordLP, error) { /* Spheroidal, inverse */ lp := &core.CoordLP{Lam: 0.0, Phi: 0.0} - P := merc.System + P := op.System lp.Phi = support.PiOverTwo - 2.*math.Atan(math.Exp(-xy.Y/P.K0)) lp.Lam = xy.X / P.K0 return lp, nil } -func (merc *Merc) mercSetup(sys *core.System) error { +func (op *Merc) mercSetup(sys *core.System) error { var phits float64 - ps := merc.System.ProjString + ps := op.System.ProjString isPhits := ps.ContainsKey("lat_ts") if isPhits { @@ -133,16 +133,16 @@ func (merc *Merc) mercSetup(sys *core.System) error { } } - P := merc.System - PE := merc.System.Ellipsoid + P := op.System + PE := op.System.Ellipsoid if PE.Es != 0.0 { /* ellipsoid */ - merc.isSphere = false + op.isSphere = false if isPhits { P.K0 = support.Msfn(math.Sin(phits), math.Cos(phits), PE.Es) } } else { /* sphere */ - merc.isSphere = true + op.isSphere = true if isPhits { P.K0 = math.Cos(phits) } diff --git a/support/Hypot.go b/support/Hypot.go deleted file mode 100644 index 32afa9c..0000000 --- a/support/Hypot.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018, Michael P. Gerlek (Flaxen Consulting) -// -// Portions of this code were derived from the PROJ.4 software -// In keeping with the terms of the PROJ.4 project, this software -// is provided under the MIT-style license in `LICENSE.md` and may -// additionally be subject to the copyrights of the PROJ.4 authors. - -package support - -import ( - "math" -) - -// Hypot is sqrt(x * x + y * y) -// -// Because this was omitted from the ANSI standards, this version -// is included for those systems that do not include hypot as an -// extension to libm.a. Note: GNU version was not used because it -// was not properly coded to minimize potential overflow. -// -// The proper technique for determining hypot is to factor out the -// larger of the two terms, thus leaving a possible case of float -// overflow when max(x,y)*sqrt(2) > max machine value. This allows -// a wider range of numbers than the alternative of the sum of the -// squares < max machine value. For an Intel x87 IEEE double of -// approximately 1.8e308, only argument values > 1.27e308 are at -// risk of causing overflow. Whereas, not using this method limits -// the range to values less that 9.5e153 --- a considerable reduction -// in range! -func Hypot(x, y float64) float64 { - /* - if x < 0. { - x = -x - } else if x == 0. { - if y < 0. { - return -y - } - return y - } - if y < 0. { - y = -y - } else if y == 0. { - return (x) - } - if x < y { - x /= y - return (y * math.Sqrt(1.+x*x)) - } - y /= x - return (x * math.Sqrt(1.+y*y)) - */ - return math.Hypot(x, y) -} diff --git a/support/ProjString.go b/support/ProjString.go index 553310f..16f3ecf 100644 --- a/support/ProjString.go +++ b/support/ProjString.go @@ -15,11 +15,6 @@ import ( "github.com/go-spatial/proj/merror" ) -// ProjString represents a "projection string", such as "+proj=utm +zone=11 +datum=WGS84" -// TODO: we don't support the "pipeline" or "step" keywords - -//--------------------------------------------------------------------- - // Pair is a simple key-value pair // Pairs use copy semantics (pass-by-value). type Pair struct { @@ -29,9 +24,13 @@ type Pair struct { //--------------------------------------------------------------------- -// ProjString is an array of Pair objects. +// ProjString represents a "projection string", such as "+proj=utm +zone=11 +datum=WGS84" +// +// It is just an array of Pair objects. // (We can't use a map because order of the items is important and // because we might have duplicate keys.) +// +// TODO: we don't support the "pipeline" or "step" keywords type ProjString struct { Pairs []Pair } @@ -105,7 +104,7 @@ func collapse(s string) string { return s } -// DeepCopy returns a detached copy of the ProjString +// DeepCopy returns a detached deep copy of the ProjString func (pl *ProjString) DeepCopy() *ProjString { copy := &ProjString{