基本坐标转换

Basic coordinates conversions

我正在编写一个可以将任何类型的坐标转换为另一种坐标的库。

我正在用 Go 编写它。我正在通过将坐标转换为一种类型然后转换回初始类型来测试转换。然后我应该获得相同的起始值(包括浮点精度误差)。

我确定 Spherical.ToCartesian 实现,因为当我在 keisan 中测试这些值时,我获得了相同的值。

这是 coordinate.go 文件:

import (
    . "math"
)

/////////////
// Cartesian Coordinates
/////////////
type Cartesian struct {
    X float64 `json:"x"`
    Y float64 `json:"y"`
    Z float64 `json:"z"`
}

// func (c Cartesian) ToCartesian() Cartesian {...}

// Following : https://keisan.casio.com/exec/system/1359533867
func (c Cartesian) ToSpherical() Spherical{
    r := Sqrt(Pow(c.X, 2.) + Pow(c.Y, 2.) + Pow(c.Z, 2.))
    return Spherical{
        Latitude:  RadToDeg(Atan(Sqrt(c.X * c.X + c.Y * c.Y) / c.Z)),
        Longitude: RadToDeg(Atan2(c.Y, c.X)),
        Radius: r,
    }
}

// func (c Cartesian) ToPolar() Polar {...}

/////////////
// Spherical Coordinates
/////////////
type Spherical struct {
    Radius    float64 `json:"radius"`
    // Aka θ
    Longitude float64 `json:"longitude"`
    // Aka ϕ
    Latitude  float64 `json:"latitude"`
}

// Following : https://keisan.casio.com/exec/system/1359534351
func (g Spherical) ToCartesian() Cartesian {
    return Cartesian{
        // x = r * sin ϕ * cos θ
        X: g.Radius * Sin(DegToRad(g.Latitude)) * Cos(DegToRad(g.Longitude)),
        // y = r * sin ϕ * sin θ
        Y: g.Radius * Sin(DegToRad(g.Latitude)) * Sin(DegToRad(g.Longitude)),
        // z = r * cos ϕ
        Z: g.Radius * Cos(DegToRad(g.Latitude)),
    }
}

这是不起作用的测试:

var g = Spherical{
    Longitude: 200,
    Latitude:  100,
    Radius:    10000,
}

func TestSpherical_ToCartesianToSpherical(t *testing.T) {
    v := g.ToCartesian().ToSpherical()

    if !IsFloatEq(g.Radius, v.Radius) {
        t.Error("Bad Radius conversion. Expected", g.Radius, "got", v.Radius)
    }
    if !IsFloatEq(g.Longitude, v.Longitude) {
        t.Error("Bad Longitude conversion. Expected", g.Longitude, "got", v.Longitude)
    }
    if !IsFloatEq(g.Latitude, v.Latitude) {
        t.Error("Bad Latitude conversion. Expected", g.Latitude, "got", v.Latitude)
    }
}

当我go test,我得到这个:

--- FAIL: TestGeographic_ToCartesianToGeographic (0.00s)
    geographic_test.go:34: Bad Longitude conversion. Expected 200 got -160
    geographic_test.go:37: Bad Latitude conversion. Expected 100 got -80
FAIL
FAIL    common/coordinates      0.113s
FAIL

实在是看不出哪里出了问题

希望得到任何帮助:)

球坐标表示为:

  1. r:半径,即距原点的直线距离
  2. φ:倾角或极角,即与垂直轴Z的夹角。
  3. θ:方位角或方位角,即投影点从X轴到XY平面的夹角。

注意 1: 在物理学中,用于表示两个角度的希腊字母被交换了,但我将使用这种表示法,因为它似乎是你使用的基于你的公式。

注意 2: 还有另一种表示倾角的方法,称为仰角,它是从 XY 平面测量的。 elevation = 90° - φ.

NOTE3:在地理上,仰角称为纬度,方位角称为经度。

与笛卡尔坐标的不同之处在于,每个点都具有单一的表示形式,即同一点可以在球面坐标中以不同的方式表示。以下转换都创建了新点 P2 = {r2, φ2, θ2},其值与原始点 P1 = {r1, φ1, θ1} 不同,但它们都是相同的点 (P2 == P1),尽管值不同。

/ r2 = r1              / r2 = - r1
| φ2 = φ1              | φ2 = 180° - φ1
\ θ2 = θ1 + 360°       \ θ2 = θ1 + 180°

解决方法是什么?归一化球坐标。最常见的归一化系统仅使用正半径值并将角度限制为 180° 和 360°:

/ 0  <= r <  inf
| 0° <= φ <= 180°  -> which means -90° <= elevation <= 90°
\ 0° <= θ <  360°

之所以将倾角限定为180°,是因为方位角多旋转180°,倾角小于180°,可以获得更高的倾角。

因此,为了能够比较值以检查它们是否相同,您首先需要进行归一化。按照以下步骤操作:

  1. If r = 0: return {0, 0°, 0°} (如果半径为零,角度不会改变任何东西所以 return 0° 角度)
  2. 如果r < 0r = -rφ = 180° - φθ += 180°
  3. 同时 φ >= 360°φ -= 360°
  4. 同时 φ < 0°φ += 360°
  5. If φ = 0° or φ = 180°: return {r, φ, 0°} (如果倾角为空或180°,则该点在垂直轴Z上,因此方位角不没有任何意义,请使用 0°)
  6. 如果φ > 180°φ = 360° - φθ += 180°
  7. 同时 θ >= 360°θ -= 360°
  8. 同时 θ < 0°θ += 360°
  9. Return{r, φ, θ}

在地理学中,经度有时会标准化为 -180° < θ <= 180°,这会将第 7 步和第 8 步修改为:

  1. 同时 θ > 180°θ -= 360°
  2. 同时 θ <= -180°θ += 360°

这里你有一个 Playground link 和 类 补充了 CartesianSphericalGeograpical 的规范化和转换方法(Spherical 但它是根据纬度和经度而不是倾角和方位角创建并打印的。