diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8227689 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Trajectory, Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9699273 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# geodesy + +Go module geodesy provides the coordinate system conversion and the distance calculation. +The default coordinate system is WGS84. +This wraps [github.com/wroge/wgs84](github.com/wroge/wgs84) with object orientation, +therefore you can change the coordinate system with its system. + +## Prerequisites + +- **[Go](https://go.dev)**: **1.20+**. + +## Installation + +Simply append the following import to your code, and then `go [build|run|test]` +will automatically fetch the necessary dependencies: + +```go +import "github.com/trajectoryjp/geodesy_go" +``` + +## Contribution + +You are very welcome to: + +- Create pull requests of any kind +- Let me know if you are using this library and find it useful +- Open issues with request for support because they will help you and many others diff --git a/coordinates/Geocentric.go b/coordinates/Geocentric.go new file mode 100644 index 0000000..1640a68 --- /dev/null +++ b/coordinates/Geocentric.go @@ -0,0 +1,36 @@ +package coordinates + +import ( + "github.com/go-gl/mathgl/mgl64" + "github.com/wroge/wgs84" +) + +// Geocentric is a type for geocentric coordinates. +type Geocentric mgl64.Vec3 + +// GeocentricReferenceSystem is the reference system for Geocentric. +var GeocentricReferenceSystem = wgs84.GeocentricReferenceSystem{} + +// GeocentricFromGeodetic converts geodetic coordinates to geocentric coordinates. +func GeocentricFromGeodetic(geodetic Geodetic) (geocentric Geocentric) { + geocentric[0], geocentric[1], geocentric[2] = GeodeticReferenceSystem.To(GeocentricReferenceSystem)(geodetic[0], geodetic[1], geodetic[2]) + return +} + +// X returns the x coordinate. +// The unit is meter. +func (geocentric *Geocentric) X() *float64 { + return &geocentric[0] +} + +// Y returns the y coordinate. +// The unit is meter. +func (geocentric *Geocentric) Y() *float64 { + return &geocentric[1] +} + +// Z returns the z coordinate. +// The unit is meter. +func (geocentric *Geocentric) Z() *float64 { + return &geocentric[2] +} diff --git a/coordinates/Geodetic.go b/coordinates/Geodetic.go new file mode 100644 index 0000000..a5ecf20 --- /dev/null +++ b/coordinates/Geodetic.go @@ -0,0 +1,71 @@ +// Package coordinates contains types and functions of coordinates for geodesy. +// The all types are based on [github.com/go-gl/mathgl/mgl64.Vec3], therefore you can convert each other. +package coordinates + +import ( + "math" + + "github.com/go-gl/mathgl/mgl64" + "github.com/wroge/wgs84" +) + +// Geodetic is a type for geodetic coordinates. +type Geodetic mgl64.Vec3 + +// GeodeticReferenceSystem is the reference system for Geodetic. +// The default is WGS84. +var GeodeticReferenceSystem = wgs84.LonLat() + +// GeodeticFromGeocentric converts geocentric coordinates to geodetic coordinates. +func GeodeticFromGeocentric(geocentric Geocentric) (geodetic Geodetic) { + geodetic[0], geodetic[1], geodetic[2] = GeocentricReferenceSystem.To(GeodeticReferenceSystem)(geocentric[0], geocentric[1], geocentric[2]) + return +} + +// Longitude returns the longitude. +// The unit is degree. +func (geodetic *Geodetic) Longitude() *float64 { + return &geodetic[0] +} + +// Latitude returns the latitude. +// The unit is degree. +func (geodetic *Geodetic) Latitude() *float64 { + return &geodetic[1] +} + +// Altitude returns the altitude. +// The unit is meter. +func (geodetic *Geodetic) Altitude() *float64 { + return &geodetic[2] +} + +// GenerateLocalFromGeocentric generates a function to convert geocentric coordinates to local coordinates. +func (origin Geodetic) GenerateLocalFromGeocentric() func(geocentric Geocentric) mgl64.Vec3 { + vector := mgl64.Vec3(GeocentricFromGeodetic(origin)) + γᵣ := vector.Len() + ψ := math.Asin(vector.Z() / γᵣ) + λ := origin[0] * math.Pi / 180.0 + rotator := mgl64.Rotate3DZ(-math.Pi / 2.0).Mul3(mgl64.Rotate3DY(ψ - math.Pi/2.0)).Mul3(mgl64.Rotate3DZ(-λ)) + + return func(geocentric Geocentric) mgl64.Vec3 { + local := rotator.Mul3x1(mgl64.Vec3(geocentric)) + local[2] -= γᵣ + return local + } +} + +// GenerateGeocentricFromLocal generates a function to convert local coordinates to geocentric coordinates. +func (origin Geodetic) GenerateGeocentricFromLocal() func(local mgl64.Vec3) Geocentric { + vector := mgl64.Vec3(GeocentricFromGeodetic(origin)) + γᵣ := vector.Len() + ψ := math.Asin(vector.Z() / γᵣ) + λ := origin[0] * math.Pi / 180.0 + rotator := mgl64.Rotate3DZ(λ).Mul3(mgl64.Rotate3DY(math.Pi/2.0 - ψ)).Mul3(mgl64.Rotate3DZ(math.Pi / 2.0)) + + return func(local mgl64.Vec3) Geocentric { + local[2] += γᵣ + geocentric := Geocentric(rotator.Mul3x1(mgl64.Vec3(local))) + return geocentric + } +} diff --git a/coordinates/Spherical.go b/coordinates/Spherical.go new file mode 100644 index 0000000..2fb4334 --- /dev/null +++ b/coordinates/Spherical.go @@ -0,0 +1,98 @@ +package coordinates + +import ( + "math" + + "github.com/go-gl/mathgl/mgl64" +) + +// Spherical is a type for spherical coordinates. +// This is for calculating [Great-circle navigation]. +// +// [Great-circle navigation]: https://en.wikipedia.org/wiki/Great-circle_navigation +type Spherical mgl64.Vec3 + +// Longitude returns the longitude. +// The unit is degree. +func (spherical *Spherical) Longitude() *float64 { + return &spherical[0] +} + +// Latitude returns the latitude. +// The unit is degree. +func (spherical *Spherical) Latitude() *float64 { + return &spherical[1] +} + +// Altitude returns the altitude. +// The unit is meter. +func (spherical *Spherical) Altitude() *float64 { + return &spherical[2] +} + +// GetLengthTo returns the length to the arrival. +// The unit is meter. +func (departure Spherical) GetLengthTo(arrival Spherical) float64 { + greatCircleDistance := departure.GetGreatCircleDistanceTo(arrival) + altitudeDifference := *arrival.Altitude() - *departure.Altitude() + return math.Sqrt(greatCircleDistance*greatCircleDistance + altitudeDifference*altitudeDifference) +} + +// GetGreatCircleDistanceTo returns the great circle distance to the arrival. +// The unit is meter. +func (departure Spherical) GetGreatCircleDistanceTo(arrival Spherical) float64 { + φDeparture := *departure.Latitude() * math.Pi / 180.0 + φArrival := *arrival.Latitude() * math.Pi / 180.0 + λDeparture := *departure.Longitude() * math.Pi / 180.0 + λArrival := *arrival.Longitude() * math.Pi / 180.0 + λDefference := λArrival - λDeparture + + sinφDeparture := math.Sin(φDeparture) + sinφArrival := math.Sin(φArrival) + cosφDeparture := math.Cos(φDeparture) + cosφArrival := math.Cos(φArrival) + sinλDefference := math.Sin(λDefference) + cosλDefference := math.Cos(λDefference) + + b := cosφDeparture*sinφArrival - sinφDeparture*cosφArrival*cosλDefference + c := cosφArrival * sinλDefference + d := sinφDeparture*sinφArrival + cosφDeparture*cosφArrival*cosλDefference + tanσDifference := math.Sqrt(b*b + c*c) / d + σDifference := math.Atan(tanσDifference) + return GeodeticReferenceSystem.Datum.A() * σDifference +} + +// GetDirectionTo returns the direction to the arrival. +// The unit is degree. +func (departure Spherical) GetDirectionTo(arrival Spherical) float64 { + φDeparture := *departure.Latitude() * math.Pi / 180.0 + φArrival := *arrival.Latitude() * math.Pi / 180.0 + λDeparture := *departure.Longitude() * math.Pi / 180.0 + λArrival := *arrival.Longitude() * math.Pi / 180.0 + λDefference := λArrival - λDeparture + + sinφDeparture := math.Sin(φDeparture) + sinφArrival := math.Sin(φArrival) + cosφDeparture := math.Cos(φDeparture) + cosφArrival := math.Cos(φArrival) + sinλDefference := math.Sin(λDefference) + cosλDefference := math.Cos(λDefference) + + tanα := cosφArrival*sinλDefference / (cosφDeparture*sinφArrival - sinφDeparture*cosφArrival*cosλDefference) + return math.Atan(tanα) * 180.0 / math.Pi +} + +// GetDirectionOnEquator returns the direction on the equator. +// The unit is degree. +func (spherical Spherical) GetDirectionOnEquator(direction float64) float64 { + α := direction * math.Pi / 180.0 + φ := *spherical.Latitude() * math.Pi / 180.0 + + sinα := math.Sin(α) + cosα := math.Cos(α) + sinφ := math.Sin(φ) + cosφ := math.Cos(φ) + + tanα0 := sinα*cosφ / math.Sqrt(cosα*cosα+sinα*sinα*sinφ*sinφ) + return math.Atan(tanα0) * 180.0 / math.Pi +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bc7614e --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/trajectoryjp/geodesy_go + +go 1.20 + +require ( + github.com/go-gl/mathgl v1.1.0 + github.com/wroge/wgs84 v1.1.7 +) + +require golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3b629d1 --- /dev/null +++ b/go.sum @@ -0,0 +1,7 @@ +github.com/go-gl/mathgl v1.1.0 h1:0lzZ+rntPX3/oGrDzYGdowSLC2ky8Osirvf5uAwfIEA= +github.com/go-gl/mathgl v1.1.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/wroge/wgs84 v1.1.7 h1:8WVUUrpjysYxrn0ssWX7z90SOUKCuHt9NQ5tg9ovjIY= +github.com/wroge/wgs84 v1.1.7/go.mod h1:mc1F8ubW03DO4zaf/006cmhaiMlfvbKmqVAcPuAtsNA= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f h1:FO4MZ3N56GnxbqxGKqh+YTzUWQ2sDwtFQEZgLOxh9Jc= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/workflows/main.yml b/workflows/main.yml new file mode 100644 index 0000000..f4e24c2 --- /dev/null +++ b/workflows/main.yml @@ -0,0 +1,21 @@ +name: Go package + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.18' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./...