Hengefinder:突破曼哈顿局限,探寻全球“悬日”时刻!

Hengefinder:突破曼哈顿局限,探寻全球“悬日”时刻! Hengefinder探寻太阳与街道对齐的时刻2026 年 5 月 22 日下周在曼哈顿日落时太阳将与城市网格中的东西向街道完美对齐。这一景象美不胜收每年有两次人们会聚集在一起见证太阳完美地落在地平线上被两旁的摩天大楼框住的短暂瞬间。这一现象被称为“曼哈顿悬日”Manhattanhenge名字源于巨石阵Stonehenge。不禁让人追问天文学家是如何算出“曼哈顿悬日”出现的时间的呢而且如果能搞清楚这一点为什么要把范围局限在曼哈顿呢这里展示了一张在纽约市第 42 街拍摄的人们拍摄曼哈顿悬日的场景照片。这是在递归中心完成的首批项目之一——Hengefinder这是一个能让你在几乎任何太阳落山的地方找到“悬日”现象的工具。相关信息如下源代码、Hengefinder 网站、Hengefinder 移动应用这是网站的后续产品由递归中心的伙伴 John Pribyl 创建。基本步骤以及很快会学到的一些术语如下1. 确定道路的角度即相对于真北方向的方位角。2. 确定每天日落时太阳的角度即方位角。3. 找出这两个角度相匹配的日期。这个项目吸引之处在于它由许多子问题组成既可以深入思考也可以选择跳过比如借助库、采用暴力解法或进行近似处理等。就像一堆封闭的盒子留了很多盒子没打开比如没有构建自己的天文模型但也打开了一些盒子。后来发现很多原以为很简单的事情在实际操作中却没那么容易因为假设并不成立。例如在现实中不能把道路当作平直线经纬度也不像笛卡尔网格那样规则而且“日落”这个词比最初想的要更模糊。很享受缩小假设与现实之间差距的过程。所以不打算整体介绍这个项目而是要详细讲述在构建过程中遇到的几个挑战以及是如何解决它们的。每个所谓“简单”的步骤都对应一个挑战。挑战 #1确定道路方位角重新认识到地球不是平的第一个挑战是计算街道的方位角即相对于真北方向的角度。如果获取一个地址的经纬度以及街道上另一个地址的经纬度就得到了地球上两个点的坐标。然后可以用一些三角函数来计算角度。最初错误的想法是计算纬度差和经度差然后用 atan2反正切函数来得到角度。问题在于这种方法只适用于地球是平的情况。这里展示了经纬度方向的示意图。地球上的经纬度线排列方式不同。纬线东西向间隔均匀地球上任何地方纬度每一度的距离基本相同。而经线南北向并非如此它们在靠近两极的地方会汇聚。例如在纽约市经度每一度大约跨越 52 英里84 公里但在高纬度地区如安克雷奇这个距离就小得多约 33 英里 / 53 公里。该如何处理这个问题呢长话短说如果你只想知道实用的方法用 cos(纬度) 对经度进行缩放这样经度和纬度就处于相同的“单位”了。只有这样才能使用 atan2 函数。”好吧但为什么要这样做呢“如果你不太关心数学原理可以直接跳到下面的“修正方法”部分。当使用 tan 对边 / 邻边 时假设三角形的各边维度单位是一致的但在这里并非如此。随着靠近两极经度每一度代表的距离会缩小而纬度保持不变。这意味着三角形的水平边和垂直边单位不同。这里展示了相同的东西向距离在不同纬度对应的经度变化不同的示意图。为了解决这个问题需要一点球面几何知识。把地球想象成一个单位球体半径 1并取一个垂直截面。纬度为 φ 的点会形成一个直角三角形斜边是地球半径1垂直边是 sin(φ)水平边是 cos(φ)。这里展示了在纬度 φ 处东西向移动会形成一个半径为 cos(φ) 的较小圆圈的示意图。这个水平边是从地球轴到“观测点”的距离也是在纬度 φ 处东西向移动时所形成圆圈的半径。这意味着在赤道φ 0°处半径是 cos(0) 1在两极φ 90°处半径实际上是 cos(90°) 0。所以随着远离赤道东西向距离会缩小就像在越来越小的圆圈上行走。为了将经度与纬度进行有意义的比较必须用该圆圈的半径即 cos(纬度)对经度进行缩放。一旦经度经过这样的缩放两个坐标轴的单位就具有可比性了这时就可以使用反正切函数。修正方法代码如下# 假设纬度和经度以度为单位delta_y lat_2 - lat_1# 缩放经度使其与纬度单位一致mean_lat math.radians((lat_1 lat_2) / 2) delta_x (lon_2 - lon_1) * math.cos(mean_lat) # 现在可以安全地使用 atan2bearing_rad math.atan2(delta_x, delta_y)bearing_deg math.degrees(bearing_rad)注意这仍然是一个近似值但对于较短的街道段来说已经足够准确了。挑战 #2确定太阳的方位角或者说“日落”到底是什么意思要出现完美的“悬日”时刻太阳需要正好落在地平线上。使用 Python 库 Astral 来计算给定地点太阳事件的时间、高度和方位角角度。但 Astral 对日落的定义与这里的需求略有不同。Astral 使用的是标准的天文日落定义即太阳完全落到地平线以下的时刻。对需求来说这个时间太晚了。想知道太阳正好落在地平线上的时刻。不过Astral 的定义对仍然有用因为从时间上看它能让大致确定某一天的范围。要出现“悬日”时刻希望太阳圆盘完全可见且刚好接触地平线。根据太阳的视大小设定了一个太阳在地平线以上的目标“高度”。这里展示了技术上对日落的定义对于“悬日”来说太晚了的示意图。为什么这是一个边界搜索而不是值搜索所以要找到太阳高度仍高于目标阈值高度的最后时刻。可以在 Astral 定义的日落前的某个时间窗口内逐分钟进行线性搜索。这样做可能也行得通但每次评估都需要调用 Astral API。而且这样做没那么有趣。这是想打开的“盒子”之一。由于太阳高度在日落临近时单调变化这表明可以使用二分搜索。二分搜索的经典方法如下def basic_binary_search(arr, target):left 0 right len(arr) - 1while left right:mid (left right) // 2if arr[mid] target:return midelif arr[mid] target:left mid 1else:right mid - 1# 未找到return -1但这种方法有几个假设在这里并不适用。首先不是在搜索一个精确的目标匹配值。因为以分钟为分辨率可能永远无法得到精确的时刻。相反在寻找“悬日”时实际上是每分钟评估一个布尔值“这一分钟太阳是否仍高于目标高度”这会形成这样的模式True, True, True, True, False, False, False。现在不是在寻找一个特定的值而是在寻找翻转前的最后一个 True。边界最后一个为真二分搜索所以采用了一种不同的二分搜索方法。以下是在 Astral 定义的日落前的时间窗口内进行搜索的核心循环while left right:mid (left right 1) // 2 # 上偏中点用于“最后一个为真”的搜索if altitude target: # 仍然有效保留left mid # 这一分钟有效可能是最后一个else: # 太阳在地平线或以下right mid - 1 # 这一分钟无效往前查找这就变成了一个“最后一个为真”的二分搜索让能够找到太阳仍高于目标高度的最后一分钟。这与一篇很棒的 LeetCode 文章中的通用二分搜索模板类似该文章认为这种方法在整体上更不容易出错。挑战 #3确定方位角相等的时刻两阶段搜索一旦知道了如何获取a道路的方位角和b日落时太阳的方位角只需要找出两者匹配的时间就能得到“悬日”日期。不能在接下来的 365 天里进行二分搜索因为一年中太阳的方位角不是单调变化的。可以采用暴力方法计算一年中每一天的日落方位角然后检查何时如果有的话与道路方位角匹配。这样做也能行得通这又是一个可以不打开的“盒子”。但觉得这种方法不太对而且会进行很多不必要的 API 调用。日落方位角与日期的关系如果绘制特定地点太阳的方位角会得到一条平滑的曲线。这里展示了在给定地点太阳的日落方位角在一年中平滑变化并在至日前后改变方向的示意图。因此根据道路的方位角可能会有两个对齐日期、一个对齐日期或者根本没有对齐日期。例如接近南北走向的街道在大多数纬度上根本不可能出现“悬日”现象。可以通过解析方法对这个函数进行建模然后求解精确的对齐日期。天文学家可能会采用这种方法。但要正确做到这一点意味着要构建一个相当复杂的天文模型这超出了需求范围这条曲线可能看起来像正弦波但实际上并非如此。目标不是完美地解决这个问题而是构建一个易于理解且能可靠找到“悬日”现象的工具。最后采用了一种折中的两阶段搜索方法。第一阶段粗搜索从粗间隔采样开始。比如说每 30 天采样一次。如果采用这种粗采样方式目标是找出可能“错过”“悬日”的时间段以便在该时间段内进行细粒度搜索。不仅要跟踪太阳的方位角还要跟踪日落方位角相对于道路方位角的位置以及其移动方向。原因如下。在粗采样时可能会以两种方式“错过”“悬日”。第一种方式是太阳的方位角可能会稳定地朝着道路方位角移动并在采样间隔之间穿过它继续沿相同方向移动。这实际上就是介值定理如果一个连续量在一个区间内改变符号那么它在该区间内一定穿过了零点。如果发生这种情况那么肯定出现了“悬日”现象。第二种方式是太阳的方位角在采样间隔之间改变方向。在这个反转过程中可能出现了“悬日”现象也有可能根本没有“悬日”只是道路方位角超出了太阳最大方位角的范围例如纽约市一年中没有一天的日落方位角会与方位角为 320˚ 的道路匹配。无论哪种情况都确定了一个值得进一步探索的更精细的窗口。这里展示了粗采样可能错过“悬日”的两种方式每种方式都用一对方位角采样表示的示意图。粉色点位于道路方位角的两侧这个窗口内肯定有“悬日”日期会被标记进行细粒度搜索。蓝色点对完全位于方位角下方但包含方位角方向的反转。这个窗口可能有也可能没有“悬日”同样会被标记进行细粒度搜索。第二阶段细搜索如果确定了一个可能错过“悬日”的窗口就会在这个窗口内进行细粒度的逐日搜索以确定精确的“悬日”日期和时间。整合所有功能此时已经完成了一开始列出的三个步骤1道路方位角2太阳方位角3搜索对齐日期的方法。将这些功能封装在一个小网站 Hengefinder 中。可以输入认为风景优美的地点的地址然后找到下一次“悬日”出现的时间。网站还有一个“悬日是如何形成的”解释页面配有交互式图表展示了为什么会出现“悬日”现象以及为什么它们很罕见。第三页由 John Pribyl 贡献让可以更广泛地探索所在城市的“悬日”现象。这里展示了 2026 年 5 月 28 日 Hengefinder 网站上纽约市的截图交叉街道亮起显示在曼哈顿悬日期间与太阳对齐。John 进一步开发了一个超酷的 Hengefinder 移动应用。它将功能扩展到了月亮并能让找到“索伦悬日”Sauron Henges即太阳或月亮正好落在建筑物顶部的时刻这个名字源于布鲁克林塔的相关事件灵感来自《指环王》中的索伦之眼。”好吧给我讲讲一些有趣的悬日现象。“有了 Hengefinder现在可以在曼哈顿以外的地方探索“悬日”现象了一直在用这个应用寻找它们。到目前为止最喜欢的一个“悬日”现象实际上还没亲眼见过。对哈勒姆运河很感兴趣它沿着阿姆斯特丹韦斯特公园的南边延伸。事实证明欧洲大部分地区由于中世纪的街道设计基本上不太可能出现“悬日”现象。但阿姆斯特丹有很多有着数百年历史的笔直运河这些运河原本是城市之间的交通路线马匹需要没有弯道的道路来拉动大型沉重的驳船。据推测在过去四个世纪里哈勒姆运河每年都会出现两次“悬日”现象太阳倒映在水中与运河完美对齐。据所知并没有关于哈勒姆运河“悬日”的公告也没有人特意去观看。John 比更厉害他真的去实地观看了那些从未被报道过的“悬日”现象。经他允许这里展示一些他探索“悬日”时拍摄的精美照片。这里展示了 John 使用 Hengefinder 见证的一些“悬日”现象的照片。从左上角顺时针依次为1德克萨斯大学奥斯汀分校塔楼的“索伦悬日”台北 101 大楼世界上最高的建筑之一的“索伦悬日”德克萨斯大学奥斯汀分校附近西 24 街的日落“悬日”以及 John 的狗 Luke 在散步时享受的邻里“悬日”。从几何角度来看“悬日”现象很罕见因为太阳很少能与街道完美对齐。觉得它们感觉更罕见是因为很少去发现它们。但这些时刻一直在世界各地发生无论我们是否观看。