一触即发

探索 Windows Phone 的球面坐标

Charles Petzold

下载代码示例

Charles Petzold自从我结识了在 Windows Phone 7.1 运动传感器,我知道正是我想要用它做:复活一些 C# 位置天文学代码我写了大约五年前和丝它向上运动传感器逻辑到 Windows Phone 的程序。这样一个程序将允许我点在夜晚的天空要显示的行星和星座那里位于就地电话。

该程序 — — 与荒谬名称 AstroPhone — — 在本专栏的下一部分中来。与此同时,一些概念、 数学和方案的基础是必要的。

如我上面讨论之前的专栏中,运动传感器整合来自罗盘、 加速度计、 陀螺仪和 GPS、 输入和计算 (除其他外) 描述了这款手机的定位在空间中的 3D 旋转矩阵。它很方便适用于 3D 矢量相对于手机的坐标系统的这个矩阵。此转换的向量指向概念球上的位置。这一领域上的某个位置是水平的坐标,组成的海拔高度 — — 角的上方或下方观众的视野 — — 的方位,基本上是一个罗盘的方向。

水平坐标是类似于熟悉的地理坐标标识在地球表面上的位置。类似于纬度的上方或下方地球的赤道的海拔高度高于或低于观众的视野。该值指示各地观众的地平线的罗盘方向的方位是类似于绕地球赤道经度。正如您所看到的所有类型的球面坐标都是从根本上是一样的最关键的不同的上部半球分开较低的平面。

拼合球

在上月的列中 (msdn.microsoft.com/magazine/jj553520),我演示了如何使用水平坐标通过改变太空手机的定位扫描大型照片和其它图像。但是,该程序"骗"有点根本无视球面坐标的真正性质。它拒绝承认这一事实的纬度圈变得越来越小的顶部和底部的天体,走向和因此,度的方位成为越来越多地压缩。

只是一个程序,提供了夜晚的天空模拟的查看不能采取这些快捷方式。它必须找到算法的方式将映射到平板手机的屏幕面积的天体。当然,拼合领域一直是几百年来遇到的制图问题。它不能没有失真,但目标是尽量减少这些扭曲。

图 1 显示点缀行的高度和方位每 15 ° 的球体。在内部的球体中心的出发点,红点表示一个点,它你查看或指向您的电话。此特定点的海拔高度为 25 °。方位角取决于你从哪里开始计数和在什么方向。

A Point (in Red) on a Celestial Sphere
图 1 点 (红色) 在天球上

让我们称之为这点"视图中心",因为它指出,将映射到手机的屏幕的中心。球面上的哪些行对应于屏幕的边缘呢?

它很容易承担的这款手机的屏幕边缘将对应行的高度和方位。但这只是看起来合理地点靠近地平线。这一概念开始打破视图中心获取从地平线,以及完全折叠时您正在查看在两极的向上或向下。

相反,让我们来构建视图中心的正交"大圈"中所示图 2

Orthogonal Great Circles Crossing the View Center
图 2 穿越视图中心的正交大圆圈

大圈是一个球形几何学中的重要概念。一个大圆是共享相同的半径和中心作为球体本身的 3D 空间中的一个二维圆。因为他们代表两个点之间最短的距离,类似于飞机上的直线球上的大圆圈。方位 (和经度) 的线是大圆圈。行的高度 (和纬度) 不 ; 相反,它们是"小圈",除了将划分成两球的圆 — — 地平线 (或赤道)。

在红色大圆图 2 通过两极,只是一种方位线。蓝色的大圆越过地平线方位角 90 °,从视图中心的方位。

围绕该视图中心一个矩形将被映射到手机的屏幕。从概念上讲,此矩形可以大圈的网格。让我们来构造两个更大圆蓝色,越过地平线,第一个相同的位置。并让我们构造两个多个红色大圆也与第一个共享的两个点。结果如图 3 所示。共享的红色大圆点作为视图中心 (或方位加 180 °) 的同一方位但高度偏移旋转 90 °。

Additional Great Circles to Create a Rectangular Area
图 3 额外大圆圈的创建一个矩形区域

这些额外的大圈到电话,约 19 ° 弧和 32 ° 的弧的高度,宽度的纵向模式屏幕定义一个对应的矩形的边缘。如果你假设这款手机的屏幕是大约 2 英尺宽和 3.33 英寸高,这些弧度是恰当的当手机的屏幕举行约 6 英寸从你的脸。这款手机的屏幕是 480 像素宽和 800 像素高,所以这些数字还意味着每圆弧度 25 像素。

推行此投影算法中,当您需要倒推的工作:想想两个点为轴,由三个蓝色的大圆圈共享和共享的三个红色大圆圈作为另一个轴的两个点。与向视图中心向量,这些构成正交坐标系统。

矢量代数允许派生的任何其他坐标从视图中心,角度偏移量的 HorizontalCoordinateProjection 类中所示图 4。类有一个方法来设置视图中心和另一种方法来获取角度偏移量相对于该视图中心的任何其他对象的水平坐标。

图 4 HorizontalCoordinateProjection 类

public class HorizontalCoordinateProjection
{
  Vector3 viewCenterVector, horzAxis, vertAxis;
  public void SetViewCenter(HorizontalCoordinate viewCenterCoord)
  {
    viewCenterVector = viewCenterCoord.ToVector();
    HorizontalCoordinate vertAxisCoord =
      new HorizontalCoordinate(viewCenterCoord.Azimuth + 90, 0);
    vertAxis = vertAxisCoord.ToVector();
    horzAxis = Vector3.Cross(viewCenterVector, vertAxis);
  }
  public void GetAngleOffsets(HorizontalCoordinate objectCoord,
       out double horzAngle, out double vertAngle)
  {
    Vector3 objectVector = objectCoord.ToVector();
    Vector3 horzObjectCross = Vector3.Cross(objectVector, -horzAxis);
    Vector3 vertObjectCross = Vector3.Cross(objectVector, vertAxis);
    horzObjectCross.Normalize();
    vertObjectCross.Normalize();
    double x = Vector3.Dot(horzObjectCross, vertAxis);
    double y = Vector3.Dot(horzObjectCross, viewCenterVector);
    horzAngle = -180 * Math.Atan2(y, x) / Math.PI;
    x = Vector3.Dot(vertObjectCross, horzAxis);
    y = Vector3.Dot(vertObjectCross, viewCenterVector);
    vertAngle = -180 * Math.Atan2(y, x) / Math.PI;
  }
}

若要确定该对象应放置在手机的屏幕上的位置,从 GetAngleOffsets 方法获得的角度需要乘以一个常数,您选择的圆弧度每像素数。 我较早前建议此常数应等于 25,当手机举行 6 英寸从你的脸,但你可能想要去的东西较低,提供更广阔的视野。

查看从里面球

ViewHorizontalCoordinates 程序将其 PIXELS_PER_DEGREE 常数设置为 15 显示水平坐标从里面找出,如中所示图 5。 当电话点有点东、 南部有点地平线之上,该特定视图时发生。

The ViewHorizontalCoordinates Program
图 5 ViewHorizontalCoordinates 程序

为激发运动传感器的每个 CurrentValueChanged 事件,该事件处理程序开始通过获取指示如何,地球是面向与手机的旋转矩阵。 它将,转换为 HorizontalCoordinate 的值,并设置视图中心以前创建的 HorizontalCoordinateProjection 对象中:

Microsoft.Xna.Framework.Matrix matrix =
  args.SensorReading.Attitude.RotationMatrix;
HorizontalCoordinate viewCenter =
  HorizontalCoordinate.FromMotionMatrix(matrix);
coordinateProjection.SetViewCenter(viewCenter);
rotate.Angle = -viewCenter.Tilt;

旋转对象是应用于整个画布 RotateTransform。 该处理程序然后实现几个循环涉及高度和方位在 15 ° 的增量值。 CurrentValueChanged 事件触发时,第一次的事件处理程序创建所有必要的线和 TextBlock 元素,并将它们添加到画布上。 第二次和其后的时间,通过,该处理程序只需访问现有的线与 TextBlock 元素已经在画布上,并设置新的点。

HorizontalCoordinate 的每个值需要转换为屏幕坐标。 这就是工作中的 CalculateScreenPoint 方法的图 6,HorizontalCoordinateProjection 在调用 GetAngleOffsets 方法和相乘的 PIXELS_PER_DEGREE 常数的角度。

图 6 计算水平坐标从屏幕点

Point CalculateScreenPoint(HorizontalCoordinate horizontalCoordinate)
{
  double horzAngle, vertAngle;
  coordinateProjection.GetAngleOffsets(horizontalCoordinate,
                                       out horzAngle, out vertAngle);
  // Use NaN to indicate points clearly out of range of the screen
  float x = float.NaN;
  float y = float.NaN;
  if (horzAngle > -90 && horzAngle < 90 && 
     vertAngle > -90 && vertAngle < 90)
  {
    x = (float)(width / 2 + PIXELS_PER_DEGREE * horzAngle);
    y = (float)(height / 2 + PIXELS_PER_DEGREE * vertAngle);
  }
  return new Point(x, y);
}

GetAngleOffsets 方法总是返回角度范围介于-180 ° 到 180 °。 有时线跨越这些限制,这将创建一个自动换行的问题。 例如,一条线可能 (在理论上) 从扩展-175 ° 到 175 度。 该行应仅 10 ° 的长度,但计算的长度会 380 ° ! CalculateScreenPoint 涉及 NaN ("不是数字") 中的逻辑更正此标记的角度偏移量小于-90 ° 的所有点的问题或大于 90 °,超出屏幕的范围。

我想要显示的海拔高度角可见的方位,无论文本标签和你那些显示指南针点可见无论怎样高或低点的电话。 海拔高度标签的显示屏幕点从视图中心方位,计算和罗盘的标签显示屏幕点从视图的中心高度,所以他们不要所有群集在一起当您指向电话直接向上或向下的小调整计算。 这个小把戏有助于保持在屏幕上,或许有点旋转中心内的标签。

切换到新华社

下个月了工作上配合此列具有 AstroPhone 的程序,我开始注意到某些性能问题。 ViewHorizontalCoordinates 程序只能管理约 10 帧每秒对我发展的电话,这个问题似乎更多在 Silverlight 布局系统中,而不是在我的代码。

有些不情愿,我决定的其它程序将目标 Windows Phone XNA 框架而不是 Silverlight。 这是 ViewThreeCoordinates 项目,它是与 ViewHorizontalCoordinates 类似,但书面新华社,而不是 Silverlight 的理由。

库预览

ViewThreeCoordinates 项目还揭示了一些我会为全面爆发天文学程序使用的策略。 它使用一个名为 Petzold.Astronomy 的大库的一部分。 在构建此库,我主参照已由让 · Meeus (螨铃,1998年)"天文算法"的经典著作的第二版。

位置天文学需要大量的三角。 在 Petzold.Astronomy 库中,我试着通过实施一个名为角的结构避免混乱和度和弧度之间的转换。 您可以创建从学位或使用 FromDegree 或 FromRadian 的静态方法,弧度的角度值,并得到学位或弧度为单位),但它没有又往往是必要的因为角度还实现了所有必要的三角功能作为属性。

例如,如果您有一个名为 myAngle 的角度值,您可以获得该角度的余弦值如下所示:

double x = myAngle.Cosine;

如果您认为正弦和余弦切线作为"属性"的角度,这完全合乎情理。 但通知的余弦值属性如何实施:

public double Cosine
{
  get { return Math.Cos(radians); }
  set { radians = Math.Acos(value); }
}

Set 访问器是反余弦函数,这意味着您可以将现有的角度值设置为使用这样的语句数的反余弦值:

myAngle.Cosine = x;

这一过程并不十分工作的地方唯一的地方是在执行基本的 Atan2 方法。 我开始用一个静态方法,它创建一个角度值:

public static Angle ArcTangent(double y, double x)
{
  return Angle.FromRadians(Math.Atan2(y, x));
}

赤道坐标

ViewThreeCoordinates 程序在绿色,再加上两个位置天文学中非常有用其它坐标系中显示水平坐标:在红色的蓝色和黄道坐标中的赤道坐标。 图 7典型显示指向北和海拔为 25 ° 的电话。 (是的我知道屏幕很混乱,但是您可以点击屏幕显示只有一个或两个集的坐标。

The ViewThreeCoordinates Display
图 7 ViewThreeCoordinates 显示

随着地球绕轴自转和绕着太阳,地球的赤道呆差不多在同一平面。 地球平面是赤道的赤道坐标与相关联的基本平面。 赤道坐标组成的衰落 — — 赤道上空正值和负值下面 — — 与在赤道周围会权提升。 右阿森松通常被指定的时间,而不是度,每小时等于 15 °。

当然,地理坐标 (经度和纬度) 也基于地球的赤道,但地理坐标旋转与地球赤道坐标是相对于宇宙,其余固定和因此似乎把地球在旋转的轴上。 恒星的位置被指定在赤道坐标中,他们不能改变很多随着时间的推移。

ViewThreeCoordinates 通过将它们转换为水平坐标显示赤道坐标的网格。 这种转换取决于观察者的地理位置 (经度和纬度) 和当前时间,并在 HorizontalCoordinate 结构中实施。

然而,权利提升到方位非常粗糙转换是可行的:春分日午夜当地时间 (3 月 20 日左右)、 0 小时右提升是北 (方位的 0 °)。 明亮的星昴赤经约 14 个小时,等该日期,那时候,你需要摆动西进 210 ° (或由 150 ° 东) 以查看它。 或者,您可以只是等待直到 2 上午两个小时 和昴南将到期。

您可以通过减去其权提升从当地时间的午夜以来的小时数来计算的一颗星的地方时角。 转换度乘以 15,并添加的天数,自 3 月 21,和该测量从北东恒星的方位。

如果您运行 ViewThreeCoordinates 和赤道北极的夜晚天空握手机 (可见非常顶部的图 7) 将对应的北极星,北明星,已经非常接近 90 ° 的偏差,因此相符的地球中轴矢量的位置。 请注意如何北的方位线相交的极点。

黄道坐标

所有的太阳系的行星的轨道大致在同一平面。 这架飞机被称为黄道,,它是黄道坐标 (也称为天体坐标),以红色显示的 ViewThreeCoordinates 的基础。 黄道坐标由黄道经度和纬度黄道组成。

黄道坐标大多用于计算太阳、 行星和月亮的位置时。 因为太阳和行星躺在大约同一平面,这些对象的黄道纬度通常是接近于零。 黄道本身将显示一条厚厚的在 ViewThreeCoordinates 中的红线。 如果你拿到白天或夜晚的天空、 太阳和行星电话应配合那条线。

赤道坐标和黄道坐标纯粹由于地球倾斜的约 23 ° 与黄道面而有所不同。 0 小时右阿森松和 0 ° 赤纬赤道坐标 0 ° 经度和纬度 0 °,赤道坐标相同,这两个系统也正好在 180 °。

在春分点,太阳已 0 ° 黄道经度。 那经度增加大约 1 ° 每日过去的一年。 因此,以度为单位,太阳的黄道经度是大约等于自 3 月 21 日的天数。

黄道经度通常标有星座,开始为 0 °-30 ° 的白羊座、 金牛座为 30 ° 60 °、 60 °-90 °,等等通过为 330 °-360 ° 的双鱼座的双子星座。 我也做过这在 ViewThreeCoordinates 中。 这些星座是零的大约位于黄道附近纬度与这些黄道经度的星座。 因此,太阳就是"在白羊座 」 (白羊座是超越太阳的意思) 在 3 月 21 日开始的一个月。

理论已经足够。 在此列中的下一个外观中的网格线的球面坐标系统将替换为太阳、 月亮、 行星、 恒星和星座。

查尔斯 · Petzold 是 MSDN 杂志,长期贡献和 Windows 8 的当前正在更新他的经典著作"编程 Windows"(微软出版社,1998年)。 他的网站是 charlespetzold.com

衷心感谢以下技术专家对本文的审阅:音莫尔斯