Skip to content

Commit

Permalink
Add an important new generalized test category, and fix a couple bugs…
Browse files Browse the repository at this point in the history
… with it (#2)

Verify projected points are within the projection's planar bounds, add Eckert IV projection, fix bounds for HEALPix
  • Loading branch information
robertkleffner authored Jan 18, 2024
1 parent 9fdc7a0 commit a5a278d
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 16 deletions.
111 changes: 111 additions & 0 deletions bounded_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package flatsphere

import (
"math"
"testing"
)

func FuzzMercatorProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewMercator())
}

func FuzzPlateCarreeProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewPlateCarree())
}

func FuzzEquirectangularPositiveProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewEquirectangular(45*math.Pi/180))
}

func FuzzEquirectangularNegativeProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewEquirectangular(-45*math.Pi/180))
}

func FuzzLambertCylindricalProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewCylindricalEqualArea(0))
}

func FuzzBehrmannProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewBehrmann())
}

func FuzzGallOrthographicProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewGallOrthographic())
}

func FuzzHoboDyerProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewHoboDyer())
}

func FuzzGallStereographicProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewGallStereographic())
}

func FuzzMillerProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewMiller())
}

func FuzzCentralProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewCentral())
}

func FuzzSinusoidalProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewSinusoidal())
}

func FuzzHEALPixStandardProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewHEALPixStandard())
}

func FuzzMollweideProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewMollweide())
}

func FuzzHomolosineProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewHomolosine())
}

func FuzzEckertIVProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewEckertIV())
}

func FuzzStereographicProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewStereographic())
}

func FuzzPolarProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewPolar())
}

func FuzzLambertAzimuthalProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewLambertAzimuthal())
}

//func FuzzTransverseMercatorProjectBounded(f *testing.F) {
// projectionBoundedFuzz(f, NewObliqueProjection(NewMercator(), 0, math.Pi/2, -math.Pi/2))
//}

//func FuzzGnomonicProjectBounded(f *testing.F) {
// projectionBoundedFuzz(f, NewGnomonic())
//}

func FuzzOrthographicProjectBounded(f *testing.F) {
projectionBoundedFuzz(f, NewOrthographic())
}

func projectionBoundedFuzz(f *testing.F, proj Projection) {
f.Add(0.0, 0.0)
f.Add(0.0, math.Pi)
f.Add(math.Pi/2, math.Pi/4)
f.Add(math.Pi/2, 0.0)
f.Add(-math.Pi/2, -math.Pi/4)
f.Add(math.Pi/2, math.Pi)
f.Fuzz(func(t *testing.T, lat float64, lon float64) {
lat = math.Mod(lat, math.Pi/2)
lon = math.Mod(lon, math.Pi)
x, y := proj.Project(lat, lon)
if !proj.PlanarBounds().Within(x, y) {
t.Errorf("projected point for %e,%e (equal to %e,%e) was outside of the planar bounds for %v", lat, lon, x, y, proj)
}
})
}
4 changes: 2 additions & 2 deletions healpix.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ func (h HEALPixStandard) Inverse(x float64, y float64) (float64, float64) {

func (h HEALPixStandard) PlanarBounds() Bounds {
return Bounds{
XMin: 0,
XMax: 2 * math.Pi,
XMin: -math.Pi,
XMax: math.Pi,
YMin: -math.Pi / 2,
YMax: math.Pi / 2,
}
Expand Down
28 changes: 14 additions & 14 deletions invertability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,59 +21,59 @@ func FuzzEquirectangularNegativeProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewEquirectangular(-45*math.Pi/180))
}

func FuzzLambertCylindrical(f *testing.F) {
func FuzzLambertCylindricalProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewCylindricalEqualArea(0))
}

func FuzzBehrmann(f *testing.F) {
func FuzzBehrmannProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewBehrmann())
}

func FuzzGallOrthographic(f *testing.F) {
func FuzzGallOrthographicProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewGallOrthographic())
}

func FuzzHoboDyer(f *testing.F) {
func FuzzHoboDyerProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewHoboDyer())
}

func FuzzGallStereographic(f *testing.F) {
func FuzzGallStereographicProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewGallStereographic())
}

func FuzzMiller(f *testing.F) {
func FuzzMillerProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewMiller())
}

func FuzzCentral(f *testing.F) {
func FuzzCentralProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewCentral())
}

func FuzzSinusoidal(f *testing.F) {
func FuzzSinusoidalProjectInverse(f *testing.F) {
projectInverseFuzz(f, NewSinusoidal())
}

//func FuzzHEALPixStandard(f *testing.F) {
//func FuzzHEALPixStandardProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewHEALPixStandard())
//}

//func FuzzMollweide(f *testing.F) {
//func FuzzMollweideProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewMollweide())
//}

//func FuzzStereographic(f *testing.F) {
//func FuzzStereographicProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewStereographic())
//}

//func FuzzPolar(f *testing.F) {
//func FuzzPolarProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewPolar())
//}

//func FuzzLambertAzimuthal(f *testing.F) {
//func FuzzLambertAzimuthalProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewLambertAzimuthal())
//}

//func FuzzTransverseMercator(f *testing.F) {
//func FuzzTransverseMercatorProjectInverse(f *testing.F) {
// projectInverseFuzz(f, NewObliqueProjection(NewMercator(), 0, math.Pi/2, -math.Pi/2))
//}

Expand Down
28 changes: 28 additions & 0 deletions pseudocylindrical.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,31 @@ func (h Homolosine) Inverse(x float64, y float64) (lat float64, lon float64) {
func (h Homolosine) PlanarBounds() Bounds {
return NewRectangleBounds(2*math.Pi, math.Pi)
}

// An equal-area pseudocylindrical projection, in which the polar lines are half the size of the equator.
// https://en.wikipedia.org/wiki/Eckert_IV_projection
type EckertIV struct{}

func NewEckertIV() EckertIV {
return EckertIV{}
}

func (e EckertIV) Project(latitude float64, longitude float64) (float64, float64) {
theta := newtonsMethod(latitude,
func(t float64) float64 { return t + math.Sin(2*t)/2 + 2*math.Sin(t) },
func(t float64) float64 { return 1 + math.Cos(2*t) + 2*math.Cos(t) },
1e-4, 1e-15, 125)
return longitude / math.Pi * (1 + math.Cos(theta)), math.Sin(theta)
}

func (e EckertIV) Inverse(x float64, y float64) (float64, float64) {
theta := math.Asin(y)
latNumer := theta + math.Sin(2*theta)/2 + 2*math.Sin(theta)
lat := math.Asin(latNumer / (2 + math.Pi/2))
lon := x / (1 + math.Cos(theta)) * math.Pi
return lat, lon
}

func (e EckertIV) PlanarBounds() Bounds {
return NewRectangleBounds(4, 2)
}

0 comments on commit a5a278d

Please sign in to comment.