本文基于开源项目 gcoord4cj,总结一套从 0 到 1 的仓颉(Cangjie)三方库开发流程:工程化、API 设计、实现细节、单元测试、文档规范与发布建议。读者可直接参考本文在自己项目中落地实践。

1. 项目目标与定位 🧭

  • 目标:在仓颉语言中实现地理坐标转换库,覆盖 Web/移动地图常用坐标系:

    • WGS84、GCJ02、BD09、BD09MC、EPSG3857

  • 特性:

    • 轻量、无第三方运行时依赖(仅用标准库)

    • 支持坐标别名标准化(如 WGS1984/EPSG4326 → WGS84 等)

    • 支持单点转换、GeoJSON 坐标数组递归转换、Geometry/Feature/FeatureCollection 顶层对象转换

    • 提供 std.unittest 测试样例(HLT/LLT 分层)


2. 初始化与目录规范 📁

推荐目录结构(gcoord4cj 已实践):

 .
 ├── cjpm.toml               # 项目配置
 ├── src                     # 源码
 │   ├── crs_BD09.cj
 │   ├── crs_BD09MC.cj
 │   ├── crs_EPSG3857.cj
 │   ├── crs_GCJ02.cj
 │   ├── helper.cj
 │   ├── index.cj            # 统一对外 API
 │   └── transform.cj        # 编排与别名规范、GeoJSON 支持
 ├── test                    # 测试目录(HLT/LLT)
 │   ├── HLT/transform_test.cj
 │   └── LLT/crs_conversion_test.cj
 └── doc                     # 文档
     ├── design.md
     ├── feature_api.md
     └── cj_third_party_lib_tutorial.md  # 本文

初始化工程:

 cjpm init

构建与测试:

 cjpm update
 cjpm build
 cjpm test

img

注意:默认测试扫描 src 下的 @Test/@TestCase 测试类。若测试文件放在 test 目录,可保留一个位于 src 的“测试入口”或将测试文件迁回 src(gcoord4cj 示例提供了两种使用姿势)。


3. API 设计与演进 ✨

三类核心能力建议:

  1. 单点转换(易用,覆盖 80% 场景)

 public func transformPosition(lng: Float64, lat: Float64, from: String, to: String): (Float64, Float64)
  1. 坐标数组递归转换(面向 GeoJSON 的 coordinates 层)

 public func transformCoordinates(coords: Any, from: String, to: String): Any
  1. 顶层 GeoJSON 对象转换(识别 Geometry/Feature/FeatureCollection)

 public func transformGeoJSON(obj: Any, from: String, to: String): Any

配套:坐标别名标准化,确保外部传参宽松但内部实现统一:

 public func normalizeCrs(name: String): String

建议将 API 汇总到 src/index.cj,便于消费端 import 一个包即可使用全部能力。


4. 实现要点 🔧

4.1 分层思想:数学实现与编排解耦

  • crs_*.cj 只负责数学转换(如 BD09 ↔ BD09MC 的分带多项式、WGS84 ↔ EPSG3857 投影/反投影)。

  •  transform.cj

    负责:

    • normalizeCrs 别名标准化

    • 根据 from/to 分发或链式组合(例如 WGS84→BD09 = WGS84→GCJ02→BD09)

    • GeoJSON 相关:

      • transformCoordinates 对任意维数组递归

      • transformGeoJSON 识别并处理 Geometry/Feature/FeatureCollection

4.2 统一数据模型:Any/HashMap/Array

  • 为减少外部依赖,采用标准库 HashMap<String, Any>Array<Any> 表示 GeoJSON 对象/数组。

  • 这样既能递归处理,又能与多种上层封装兼容(调用方可自行强类型封装)。

4.3 误差策略与边界

  • GCJ02↔WGS84 采用 1e-6 的迭代反解阈值;

  • 测试断言在 1e-3/1e-4/1e-6 级别;

  • 中国大陆范围外坐标不应用 GCJ02 偏移(与行业惯例一致)。


5. 单元测试与分层 🧪

  • 框架:std.unittest + 宏注解 @Test(类) @TestCase(方法)。

  • 组织:

    • HLT(High Level Test):端到端功能验证(例如 transformPositiontransformCoordinates)。

    • LLT(Low Level Test):数学回环与边界精度(例如 EPSG3857/WGS84 回环、BD09MC 回环)。

示例(片段):

 @Test
 public class CRSConversionTest {
     @TestCase
     public func test_epsg3857_roundtrip() {
         let w = (120.0, 30.0)
         let m = transformPosition(w[0], w[1], "WGS84", "EPSG3857")
         let w2 = transformPosition(m[0], m[1], "EPSG3857", "WGS84")
         // 误差校验
         if (!(abs(w2[0] - w[0]) < 1e-6 && abs(w2[1] - w[1]) < 1e-6)) {
             throw Exception("assert failed")
         }
     }
 }

命名约定:使用 *_test.cj,框架能更好地识别并展示。


6. 典型 FAQ ❓

  • Q:为什么 GeoJSON 不使用强类型?

    • A:为减少第三方依赖、兼容多种上层封装,优先使用标准库泛型容器(Any/HashMap/Array),调用方可按需包装。

  • Q:如何让 test 目录被测试框架识别?

    • A:可在 src 新增一个带 @Test 的“测试入口”类,作为聚合/冒烟用例,保持 test 目录结构不变的同时确保有用例被发现与执行。

  • Q:如何扩展更多坐标系?

    • A:新增 crs_*.cj 并在 transform.cj 的分发/链路中接入即可。


7. 小结 ✅

本文以 gcoord4cj 为例讲解了一个仓颉三方库从工程化到实现、测试与文档的完整流程。建议将“分层实现 + 别名标准化 + GeoJSON 递归 + 单元测试分层”作为通用模板,快速复制到你的业务库中,提升质量与效率。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐