这其实不是一道天文题
一个窗台、一面墙、一块地,一天里到底能接到几个小时的直射阳光?太阳在天球上的轨迹是教科书内容——给定纬度与日期,可以精确到分钟。让这个问题真正变难的是挡住它的东西:隔壁的楼、挑出的阳台、树冠、地形。这些东西的形状不规则、不整齐,而教科书答案恰好把这一部分抽象掉了。
城市化在加速,高层建筑遮挡引起的采光纠纷在增多,传统日照计算方法很难应对复杂建筑形态的精确分析。这个软件要处理的是三件具体的事:复杂建筑群相互之间的遮挡如何精确建模;阳台这类微观结构的遮光效应如何量化;全年日照数据如何快速统计。
基于天球理论的可视化日照时长预测软件(登记号 2025SR1116569,软著登字第 15772767 号,2025 年 6 月 30 日登记,著作权人中国农业大学)把两侧合到一起:用天文天球理论与儒略历算法算太阳,用 α-shape 边界提取重建遮挡物的轮廓,再给一个能看见答案的桌面界面。手册里给自己的精度目标是分钟级。
把日照问题改写成「天球上的可视区域」
整个模型的思路是一次改写:不再逐时刻判断「这个点在不在阴影里」,而是先建立天球、用建筑遮挡去修改天球模型,得到一块「从这一点往上看,天空是通的」的区域;再把太阳轨迹与这块区域求交,交出来的就是有日照的时段。
这样做把两类计算分开了:几何(向量叉积判断遮挡、坐标旋转处理地理方位、Alpha Shape 提取边界)只做一次,剩下的是轨迹与区域的求交。天文一侧用的是精确太阳赤纬、真太阳时与地方时的转换,以及日照时间与角度的换算。
两条路径:单日与全年
- 单日模块(
singledayfinal.py)。天球建模:setup_ball()建立初始天球,balcony_shadow()算阳台天花板的遮挡,building_shadow()算建筑物阴影,big_left_building()处理特殊体量的建筑。坐标转换:span()把天球旋到赤道面水平,cartesian_to_spherical()做笛卡尔到球面的换算。可视区域:find_boundary()用 alphashape 提取可视边界,count_line()数出日照时间,day2theta()算太阳赤纬。输出:ex_time()格式化日照时段,show()生成 3D / 2D 图。 - 全年模块(
wholeyearfinal.py)。继承单日的全部计算,加上yield_year_days()生成全年日期序列,对每一天批量跑一遍,把「日期 — 日照时长」写进output.txt。
单日模式的 3D 视图可以拖动旋转、360 度观察。全年模式的价值在于那些不直观的答案:邻居那栋楼从哪一天开始挡到这块地、到哪一天为止——这件事靠估是估不准的,看图一眼就看得出来。
精度是一个会吃掉时间的滑块
这一页愿意把时间成本写清楚,因为它是这个软件最容易被低估的地方。
精度越高、计算越细,耗时增长得比直觉快。以全年计算为例:如果 200 的精度需要 0.1 分钟,那么 1000 的精度需要 0.1 × 5 × 5 × 5 = 12.5 分钟——精度提高 5 倍,时间大约涨 125 倍。
我们测过一个比较复杂的真实情况:某个小区,设置了 13 栋楼的遮挡,精度设为 1000,在一台插了 4070 显卡的机器上,30 分钟以内跑出结果。所以这不是一个「点一下就好」的软件;它是那种要先想清楚用多细的精度、再去干别的事的软件。
界面,以及两个不显眼的细节
界面(main_ui.py)把参数一次收齐:精度选择、经纬度、房号(x,y,z)、房间位置(x,y,degree)、阳台角度(自动计算)、多组建筑物参数(x,y,height,degree)、日期(年月日分开输入),以及要跑的脚本路径。
后台用两个线程类跑任务:ScriptRunner 用 subprocess.Popen 起脚本并实时捕获输出,FunctionRunner 动态加载并调用指定函数,两者都通过 output_signal 把输出实时送到日志栏。中间还有一步「脚本动态生成」:读模板 → 把占位符替换成实际参数 → 生成临时执行脚本。好处是改参数不必改代码,代价是多一层间接,所以日志栏里留了实时输出。
手册里有两条容易被跳过的细节,但它们直接决定结果对不对:
- 房号的第一位是楼层号,要按实际楼层填——有些楼没有 4 层、14 层,填错会直接变成计算误差。如果算的是绿地植被或农作物,房号第一位统一填
1。 - 阳台两侧的极限角度用反正切算:
arctan(3600 / 800)。植被或农作物场景则把这一项设为0。
还有一条诚实的说明留在手册最后:输出完成以结果输出完毕为准。如果结果已经写完但窗口还没关,程序仍然会显示「正在运行」。
这一页不写的
对某一栋具体建筑的实测校验。这个软件算的是几何;要对某个场地给出结论,需要那个场地的实测数据,而这样的数据不在这里。