# 十一月赛验收清单

2026-11-10 编制，同日第二遍审计后更新，适用于 moongeodesy 0.2.1。

清单的依据有三部分：

- 官方规则：`moonbitlang/Hackathon2026` 仓库 `src/App.tsx` 中的“验收标准”“注意事项”和 FAQ；
- 十月验收没过的三条原因；
- README 和各包说明里的每一条说法。

每条都对应一个测试或检查。

**一键检查：** `bash tools/acceptance.sh` 依次跑完下面所有标“自动”的项，逐项打印 PASS 或 FAIL。

- 不加参数时会在 4 个后端都跑一遍示例，首次编译需要几分钟；
- `--quick` 跳过这一步；
- A2 需要联网。

GitHub CI（`.github/workflows/ci.yml`）在 Linux 和 macOS 上跑同样的检查，见 C1–C9。标“作者”的项需要作者本人操作，例如报名、发布、提交。

## A. 官方验收标准

| ID | 要求（原文要点） | 怎么满足 | 检查 |
| --- | --- | --- | --- |
| A1 | 以 MoonBit 为主要实现语言 | 库、测试、示例全部是 MoonBit；Python 只用于 `tools/` 下生成参考数据 | 自动：`acceptance.sh` A1 统计行数，MoonBit 行数超过 Python 的 9 倍才算通过 |
| A2 | 仓库公开 | https://github.com/ChrisTu117/moongeodesy | 自动：A2 检查页面返回 200 |
| A3 | 公开仓库持续提交，保留 commits、Issues、PR 与更新记录 | 本期的 issue（#33、#35）和 PR（#34、#36、#37 及之后的复核 PR），11 月以来几十个小提交 | 自动：A3 要求 11 月以来至少 20 个提交；人工可看 Issues 和 PR 页面 |
| A4 | 清晰的 README | README 包括包列表、安装、用法、命令行、场景、精度表、移植来源、历史、AI 说明和中文简介；各包另有 `README.mbt.md` | 自动：C3 检查 README 的代码块就是被测试的代码，`readme_claims_test.mbt` 检查代码注释里的数值 |
| A5 | 可运行示例 | demo、命令行，以及 6 个场景（s1–s6） | 自动：C7 在 4 个后端运行全部示例，每个场景结果不对就中止；C8 运行 README 列出的命令行 |
| A6 | 必要测试 | 136 个测试，每个后端都跑 | 自动：C5 运行 `moon test --target all` |
| A7 | 已有项目须包含本期完成的实质新增工作 | 新增 `magnetic` 包（WMM2025）和 `conic` 包（兰勃特、Albers），约 1850 行库代码，配有独立参考数据的测试、命令行和场景；另外重做了 GCJ-02 的国界数据（沿海和小岛）。详见 E 节 | 自动：E 节的测试；人工可看 PR #34、#36 |
| A8 | 使用认可的开源许可证 | MIT（`LICENSE`，`moon.mod` 中 `license = "MIT"`）；第三方许可要求的声明在 `THIRD_PARTY_NOTICES.md`，包括 GeographicLib 的 MIT 声明 | 自动：A8 |
| A9 | 说明移植或参考来源 | README 的“Port source and license”一节；移植文件的文件头都写明来源；`THIRD_PARTY_NOTICES.md` 列出每份数据的来源和条款（WMM 是美国政府作品，Natural Earth 是公有领域，GeoNames 为 CC BY 4.0，GIGS 为 IOGP 条款） | 自动：A9 检查这些说明都在 |
| A10 | AI 可解释：目标、路径与质量由参赛者掌握 | [`十一月新增功能说明.md`](十一月新增功能说明.md) 用中文讲新增代码的思路、关键实现和常见问题；README 有“AI assistance”一节 | 自动：A10 检查文件存在 |
| A11 | 重复提交、拆分项目或简单修改不计 | 新功能是新包、新算法和新数据，不是十月工作的拆分或改名 | 人工：PR #34、#36 的改动内容 |
| A12 | 加入赛事交流群，群昵称改为 GitHub ID | 已完成 | 作者 |
| A13 | 按官方通知提交验收（代码、README、测试、可复现的演示说明） | 复现命令见 README 和本清单 | 作者 |

## B. 十月验收没过的三条

| ID | 问题 | 现在的行为 | 测试 |
| --- | --- | --- | --- |
| B1 | 国外坐标被 GCJ-02 偏移（首尔、河内），与 README“国外坐标不做偏移”的说法不符 | 框内其他国家的陆地以及其沿海约 9 公里内不偏移，正算和两种反算都原样返回。Natural Earth 未收录的小岛按海面处理（GeoNames 5000 人以上城镇中有 3 个例外，README 写明） | `src/cn/boundary_test.mbt`：“foreign cities are not offset”（22 个城市）、“foreign coasts and islands are not offset”（27 个沿海和海岛地点）、“GeoNames towns: foreign ones are not offset, Chinese ones are”（12115 个外国城镇、3234 个中国城镇、3 个例外）；`src/cn/README.mbt.md` 中的文档测试 |
| B2 | 矩形边缘反算误差约 0.00545° | 中国境内所有测试点往返误差 < 1e-9° | `boundary_test.mbt` 中的 “round trip next to the box edges”（四条边各 400 点）和 “round trip over the whole box”（1° 网格）；`boundary_wbtest.mbt` 中的 “round trip next to land borders”（隔一个取边界顶点，距离约 200 米和 900 米，8 个方向） |
| B3 | ±90° 处 UPS 计算出 NaN | 两极映射到 (2000000, 2000000)，反算回到 ±90°；MGRS 两极也能编码和解码 | `src/utm/README.mbt.md` 中的文档测试；`src/utm/claims_test.mbt` 中的 “bad zones, latitudes, and grid coordinates raise” |
| B4 | 教训：测试全过，但有一条 README 说法没有测试 | 本清单 C、D、E 节逐条对应；README 和各包说明经过两遍逐条审计 | 本清单 |

## C. 构建与测试（与 CI 一致）

| ID | 检查 | 命令 |
| --- | --- | --- |
| C1 | 警告视为错误 | `moon check --deny-warn` |
| C2 | 格式 | `moon fmt --check` |
| C3 | README 代码块就是测试代码 | `python3 tools/sync_readme_tests.py && moon fmt && git diff --exit-code -- src/examples/readme_check` |
| C4 | 接口文件最新 | `moon info && git diff --exit-code -- '*.mbti'` |
| C5 | 全部测试，4 个后端 | `moon test --target all` |
| C6 | 库代码没有 FFI | 在 `src/geodesic`、`utm`、`cn`、`internal`、`magnetic`、`conic` 中 grep 不到 `extern` |
| C7 | 示例在 4 个后端都能跑，结果正确 | wasm、wasm-gc、js、native 各运行 demo 和 s1–s6 |
| C8 | README 列出的命令行都能运行 | 依次运行 README 中的命令；无参数时的说明里要有 `enu reverse`；未知命令必须失败 |
| C9 | 能打包 | `moon package --list` |

## D. README 说法逐条对应（根目录 `README.md`）

精度表里的每个数都是测试断言的界限，括号里是实测值。

| ID | README 的说法 | 测试或检查 |
| --- | --- | --- |
| D1 | 无 FFI，支持 wasm、wasm-gc、js、native | C6、C5 |
| D2 | 北京→上海 `r.s12 = 1066676.4 m`、`r.azi1 = 152.94°` | `readme_check/readme_claims_test.mbt` 中的 “README usage comments” |
| D3 | 航线插点间距不超过 100 km | 同 D2；另见 `geodesic/claims_test.mbt` 中的 “densify keeps consecutive points at most max_step apart” |
| D4 | 多边形逆时针为正 | 同 D2 |
| D5 | 伦敦→东京恒向线比测地线长 | 同 D2 |
| D6 | `bd09_to_wgs84` 往返 < 1e-9° | 同 D2；`cn_test.mbt` 中的 “exact inverses round-trip below 1e-9 degrees” |
| D7 | UTM 带号 50N、MGRS 为 `"50SMK4850217797"`、解码取 1 米格子中心 | 同 D2；`utm/claims_test.mbt` 中的 “MGRS precisions, centring, tabs, and limits” |
| D8 | Albers 下北京东坐标 956117.8 m；北京磁偏角 −7.56° | 同 D2 |
| D9 | 出错时抛出 `UtmError` | `utmups_test.mbt` 中的 “errors and formatting”；`utm/claims_test.mbt` |
| D10 | 命令行各命令及无参数时的说明 | `cli_wbtest.mbt` 全部 14 个测试（其中 “help lists every command” 覆盖新命令）；C8 |
| D11 | 6 个场景，CI 在每个后端都跑，结果不对就中止 | C7；中止条件见下面的 D11a–D11f |
| D11a | s1：混用原始坐标误差约 764 m | s1 要求在 760–770 m 之间，且往返误差 < 1 µm |
| D11b | s2：Norway 32V、Svalbard 33X、南极 UPS；UTM 往返 < 1 µm，MGRS 格角在 √2 m 内 | s2 检查 5 个地点的带号和格网区，以及这两项误差；任何转换出错都中止 |
| D11c | s3：分段表格，各段 21/55/124 例 | s3 要求个数正确且误差都 < 8 nm |
| D11d | s4：面积和周长与 geographiclib 相对差 < 1e-12 | s4 检查；参考值由 `tools/gen_s4_reference.py` 生成 |
| D11e | s5：2 km 偏 263 m；漠河 −12.8°，喀什 +4.1° | s5 要求偏差在 260–266 m，两地磁偏角各在 ±0.05° 内 |
| D11f | s6：Albers 与椭球面积一致到 1e-8；兰勃特最多差 6%，Web Mercator 最多差 176% | s6 检查三项范围 |
| D12 | 各包说明里的示例都会运行 | `README.mbt.md` 里的 `mbt check` 代码块会被 `moon test` 运行（C5） |
| D13 | 测地线反算（GeodTest 1000 例）< 8 nm | `geodtest_test.mbt` 中的 “GeodTest inverse …” |
| D14 | 测地线正算 < 10 nm | `geodtest_test.mbt` 中的 “GeodTest direct …” |
| D15 | 与 Python geographiclib 2.1 对照 2020 例，相对差 < 1e-11 | `pyref_test.mbt` 的两个测试 |
| D16 | GeodesicExact 111 例：距离 < 8 nm，位置 < 1e-13° | `geodesic_exact_test.mbt` 中的 “GeodesicExact matches GeodSolve -E …” |
| D17 | 多边形 8 例，误差 < 地球面积的 1e-14 | `polygon_test.mbt` 中的 “polygon area and perimeter match geographiclib” |
| D18 | 恒向线：反算 < 4 nm，正算 < 11 nm，面积 < 5e-15 | `rhumb_test.mbt` 中的 “rhumb inverse …” 和 “rhumb direct …” |
| D19 | 交点 139 例：< 0.5 µm，两线交会到 < 10 nm；距离相同的“平局”按距离比较 | `intersect_test.mbt` 中 closest、offset、next、segment 四个测试；closest 和 segment 各最多 3 个平局 |
| D20 | ECEF/ENU 与 50 位精度公式及 PROJ 对照 < 20 nm | `local_cartesian_test.mbt` 中的 “geocentric forward …”、“geocentric reverse …”、“local cartesian forward …”、“local cartesian reverse …” |
| D21 | 横轴墨卡托、UTM、UPS 与 PROJ 对照 600 点 < 10 nm | `tm_test.mbt`；`utmups_test.mbt` 中的 “UTM matches PROJ” 和 “UPS matches PROJ” |
| D22 | MGRS 315/315 与 GeoTrans 一致 | `mgrs_test.mbt` 中的 “MGRS encoding matches GeoTrans” |
| D23 | 圆锥投影：50 位精度 < 25 nm；PROJ < 0.25 µm（标准纬线相差 1e-4° 时 < 1 cm）；GIGS 兰勃特 < 1 mm、Albers < 5 mm | 见 E 节 |
| D24 | WMM2025 精度 | 见 E 节 |
| D25 | GCJ-02 在中国境内 140 点 < 1e-11°；其余 60 点在境外，保持不变；BD-09 200 点 < 1e-11° | `cn_test.mbt` 的前两个测试（断言正好 140 点） |
| D26 | GCJ-02 在框外、框内外国陆地及其沿海约 9 公里内是恒等变换；3 个已知例外 | B1 的测试；`cn_test.mbt` 中的 “identity outside China” |
| D27 | 与官方 GCJ-02 约差 1 m | 无法测试（官方算法不公开）。README 写的是“据报道约 1 m，本库无法核实” |
| D28 | 横轴墨卡托在中央经线 3900 km 内误差为几纳米 | README 引用 Karney (2011)，并注明本库测试到约 930 km |
| D29 | 级数测地线适用于 \|f\| ≤ 1/50，恒向线 ≤ 1/150 | README 注明这是 GeographicLib 的建议，不是本库的测试结论 |
| D30 | 基准测试表 | 与机器有关，README 注明“仅供参考、单次测量”；CI 只做冒烟运行 |
| D31 | 每个参考数据集在 `tools/` 下都有生成脚本 | `gen_*.py` 共 17 个；s3 数据由 `gen_s3_accuracy.py` 逐字节重现，s4 常数由 `gen_s4_reference.py` 打印 |
| D32 | 代码规模表 | 2026-11-10 统计；随代码变化需要更新 |
| D33 | `moon add` 安装后能得到 `conic` 和 `magnetic` | mooncakes 上已有 0.2.0；0.2.1 由作者发布（M1） |
| D34 | 精确测地线的 DST 长度：\|f\| = 0.1 时 12，\|f\| = 0.5 时 24，最大 1536 和 4096 | `geodesic_exact_wbtest.mbt` 中的 “DST length used for the exact area (README claim)” |
| D35 | 极短线需要近似正确舍入的 hypot | `geodesic/claims_test.mbt` 中的 “very short lines (accurate hypot) match geographiclib” |
| D36 | 参考数据不来自本库代码；GeographicLib 移植部分用 GeographicLib 自己的工具对照 | 精度表 “Reference” 一列逐行写明来源 |

各包说明（`src/*/README.mbt.md`）里的接口说法对应以下测试：

- `geodesic/claims_test.mbt`、`utm/claims_test.mbt`、`cn/claims_test.mbt`；
- 各包已有的测试；
- 说明里的 `mbt check` 文档测试。

## E. 本期新增功能

| ID | 说法 | 测试 |
| --- | --- | --- |
| E1 | WMM2025 与 NOAA 100 个官方双精度测试点一致：Y 在文件舍入范围内；Z < 2.5e-6 nT；X、H、F < 1e-3 nT，赤道上 X 在舍入范围内；年变化率 < 2e-6 nT/yr；D、I 在打印的 2 位小数内 | `magnetic_test.mbt` 中的 “WMM2025 matches the 100 official double-precision test values” |
| E2 | 与技术报告表 6 的 12 个标准值一致（含格网磁偏角） | 同文件中的 “… report Table 6 …” |
| E3 | 与技术报告表 3b 的逐步算例一致：最终结果在 10 位小数的半个单位内，中间量在一个单位内 | 同文件中的 “report Table 3b …”；`magnetic_wbtest.mbt` 中的 “report Table 3b intermediate quantities” |
| E4 | 格网磁偏角与表 3d 一致，UTM 收敛角来自本库 `utm` | 同文件中的 “report Table 3d …” |
| E5 | 与独立的 40 位精度计算（119 点）一致：< 1e-10 nT，年变化率 < 1e-13 nT/yr | 同文件中的 “WMM2025 matches an independent 40-digit evaluation” |
| E6 | 极点有限且连续；经度有周期性；年变化率与有限差分一致；派生量自洽 | 同文件中对应的 4 个测试 |
| E7 | 超出纬度、高度（−1 km 到 850 km）、日期（2025.0 到 2030.0）范围时报错 | 同文件中的 “domain errors” |
| E8 | `decimal_year` 与 NOAA 一致，包括闰年和非法日期 | 同文件中的 “decimal_year follows …” |
| E9 | 禁用区（H < 2000 nT）和警告区（H < 6000 nT），在阈值处准确切换 | 同文件中的 “compass zones …” 两个测试 |
| E10 | 罗盘方位与真方位互换，格网磁偏角 | 同文件中的 “bearing conversions”；`magnetic/README.mbt.md` 中的文档测试 |
| E11 | 圆锥投影与 50 位精度 EPSG/Snyder 公式一致（18 套投影、1792 点，含球面）：< 2.5e-8 m，收敛角 < 1e-12°，k < 1e-13，反算 < 2.5e-7 m | `conic_test.mbt` 中的 “matches a 50-digit evaluation …” |
| E12 | 与 PROJ 9.5 对照 < 2.5e-7 m；标准纬线极近的一组 < 1 cm | 同文件中的 “agrees with PROJ” |
| E13 | IOGP GIGS 5102、5103、5109：兰勃特 < 1 mm，Albers < 5 mm（与 PROJ 的差异相同） | 同文件中的 “IOGP GIGS 5102, 5103, 5109” |
| E14 | EPSG 指南 7-2 算例、Snyder 算例 | 同文件中的两个对应测试；`conic/README.mbt.md` 中的牙买加文档测试 |
| E15 | 兰勃特等角、Albers 等积，都用本库测地线量出来验证；负扁率椭球同样验证 | 同文件中的 “Lambert is conformal …”、“Albers is equal-area …”、“prolate ellipsoid …” |
| E16 | 标准纬线上比例为 1；墨卡托和 UPS 极限；标准纬线极近时的情况；两极往返 | 同文件中的 “scale is 1 …”、“limits …”、“Albers: poles round-trip exactly …”、“Lambert: both poles round-trip …” |
| E17 | 参数非法时抛出 `ConicError`；纬度超出 ±90° 得到 NaN | 同文件中的 “invalid parameters raise ConicError” 和 “latitudes beyond 90 degrees give NaN” |
| E18 | 新增命令 `mag`、`lcc`、`albers` | `cli_wbtest.mbt` 中的对应测试；C8 |
| E19 | 新增场景 s5、s6 | C7，结果检查见 D11e、D11f |
| E20 | 参考数据可重现 | `tools/gen_wmm.py`、`gen_wmm_independent.py`、`gen_conic.py`、`gen_china_boundary.py`、`gen_cn_towns.py`。下载的数据文件都校验 SHA-256（GeoNames 每天更新，改为在测试文件里记录哈希） |

## M. 作者手动完成的事项

| ID | 事项 | 状态 |
| --- | --- | --- |
| M1 | 发布到 mooncakes.io | 0.2.0 已于 2026-11-10 发布；0.2.1 待发布 |
| M2 | 合并 PR | #34、#36、#37 已合并；复核 PR 待合并 |
| M3 | 加入交流群，群昵称改为 GitHub ID `ChrisTu117` | 已完成 |
| M4 | 按官方通知提交十一月验收 | 待官方通知 |
