Sign in

    moonbitgeodb

    geo
    database
    gis
    spatial
    address
    Download zip
    Author
    Version
    0.1.3
    License
    Apache-2.0
    Last updated
    29 days ago
    Downloads
    43

    No dependencies

    #moonbitGEODB

    一个基于 MoonBit 编写的地理信息数据库,专门用于存储、解析、检索中国地址信息。

    #快速开始

    # 一键演示 61 POI(13 城市簇)+ 全部核心功能 moon run cmd demo # 语义地址匹配(20k 真实地址评测语料) moon run cmd addr-match "汉中路180号星汉大厦26楼江苏东方燃油有限公司" "汉中路180号星汉大厦26层A江苏东方燃油有限公司" moon run cmd addr-eval testdata/data.txt 0 # 全量 20k × 5 candidates 评测 moon run cmd corpus-db 500 # 从真实语料构建空间 DB # 性能基准(默认 1000,可指定 10000 等更大规模) moon run cmd benchmark 10000 # 完整测试套件 moon test # 339/339 passed # 高级空间分析示例 moon run cmd kmeans 3 # K-Means 聚类(3 大宏观区域) moon run cmd dbscan 50.0 3 # DBSCAN 密度聚类(自动识别 13 城市簇) moon run cmd tsp-2opt bj-gugong # TSP + 2-opt 路线优化 moon run cmd idw 35.0 115.0 2.0 # IDW 空间插值 moon run cmd kriging 35.0 115.0 12 # Kriging 克里金插值 moon run cmd concave-hull 1.5 # 凹包 (Alpha-Shape) moon run cmd moran-i-knn # Moran's I (KNN 权重) moon run cmd sde # 标准差椭圆

    demo 命令在 61 POI 样本库上依次演示:按 ID/名称检索 → 附近查询 → K-Means 宏观分区 → DBSCAN 城市簇识别(13 簇 + 3 远郊噪声点) → 标准差椭圆 → TSP NN+2-opt(节省约 265 km)→ IDW/Kriging 插值 → 凹包 → 中文地址解析。

    #特性

    • 地址解析:支持多格式地址字符串解析,特别是针对中文地址(如"北京市海淀区颐和园路5号")的智能行政区划识别
    • 语义地址匹配:基于 LightGBM 集成(60 棵决策树,n_estimators=20×3 类,max_depth=6)的三层分类器(完全匹配/部分匹配/不匹配)。25 维中间特征提取 → LightGBM 60 树投票直接决策(旧手工规则链已整体移除)。在 20k 条真实中文地址标注语料上 Agreement 75.90%(96,423 query-candidate 对),吞吐量 ~19k pairs/s,比初始 66.98% 手工规则提升 +8.92%
    • 地理运算:内置 Haversine 距离计算、方位角计算、边界框(BBox)操作
    • 空间索引:基于网格的空间索引(BBox 范围查询)与 R-tree(亚线性的 k 最近邻搜索与范围查询)
    • 名称索引:支持精确匹配、前缀匹配、子串匹配、通配符匹配的名称查找
    • 模糊搜索:基于 Levenshtein 编辑距离的名称模糊匹配
    • 持久化存储:
      • 纯二进制格式(大端字节序 + CRC32 校验)
      • 原子写入(先写 .tmp 再重命名),防止数据损坏
      • 自动备份与恢复机制
      • 数据库完整性检查
    • 随机数据生成:内置中国地址生成器,可快速填充测试数据
    • 原生文件 IO:通过 C FFI 实现高性能的文件读写

    #高级空间分析

    • DBSCAN 密度聚类:R-tree 加速的密度聚类,自动识别空间簇和噪声点
    • K-Means 聚类:Forgy 初始化 + Haversine 距离,确定性结果
    • Elbow 方法:自动跑 k=1..N 计算 WCSS,辅助选最佳 k
    • 标准差椭圆 (SDE):量化方向趋势和离散度(长轴/短轴/旋转角/偏心率)
    • TSP 最近邻路由 + 2-opt 优化:贪心启发式 + 边交换改进,通常再省 3-10% 路程
    • IDW 空间插值:反距离加权从已知点估算任意位置值(支持全量和 KNN 两种模式)
    • Kriging 克里金插值:基于变异函数建模的最优无偏空间插值(球状/指数/高斯模型 + 普通克里金 + KNN 加速)
    • 凹包 (Alpha-Shape):基于 Delaunay 三角化的凹多边形边界,比凸包更贴合点云
    • Moran's I 空间自相关:反距离加权 + 行标准化,正确落在 [-1,1]
    • 多边形操作:面积计算(鞋带公式)和点-in-多边形测试(射线法)
    • Geohash 编码/解码:支持空间索引、前缀查询和邻域查询
    • 坐标转换:WGS84 ↔ GCJ-02(火星坐标)转换
    • 凸包计算:Andrew's monotone chain 算法
    • 质心计算:平面质心和球面地理中心(3D 投影法)
    • 路径距离:多点路径距离计算
    • 径向密度分析:同心圆区域分布统计
    • 平均最近邻距离:空间分布分析
    • 热点分析:Getis-Ord Gi* 热点/冷点检测
    • LISA:Local Moran's I 局部聚类类型(HH/LL/HL/LH)
    • 空间范围查询:便利的 find_bbox(min_lat, max_lat, min_lng, max_lng) 直接调用
    • 罗盘方向:bearing → 16 方位名称

    #数据导入/导出

    • CSV 导入/导出:支持从 CSV 字符串导入数据
    • GeoJSON 导入/导出:支持 GeoJSON 格式的导入导出
    • 带 BBox 的 GeoJSON:导出包含每个点的边界框信息
    • JSON 导出:支持 JSON 格式导出

    #标签与聚合

    • 标签查询:单标签、多标签 AND/OR 逻辑查询
    • 标签管理:添加、删除标签
    • 标签共现分析:标签对分布统计
    • 地理聚合:区县分布、标签计数、省份直方图

    #其他功能

    • 反向地理编码:根据坐标查找最近条目
    • 重复数据检测:基于地理距离+名称相似度识别重复条目
    • 空间网格聚类:按网格单元聚合地理数据
    • 距离矩阵:计算多个地点间的两两距离
    • 地址标准化:自动去除行政区划名称后缀
    • 地理围栏查询:边界框内、距离范围内查询
    • 空间范围查询:半径范围内查询

    #性能优化

    本版本针对查询性能进行了多项优化:

    方法优化前优化后提升
    find_in_ringO(n) 全扫描 + 每个 haversineO(k) 用 bbox 候选集 + 精确过滤10-100x
    nearest_neighbors_rangeO(n) 全计算 + 排序O(log n) R-tree KNN100x+
    tsp_nearestO(n²) 内部 O(n) 扫描O(n·log n) R-tree KNN + 跳过已访问50x
    tsp_2opt内部 O(n²) 嵌套双循环 + 每候选 4 次 HashMap+haversine路线坐标/edge 邻接数组,d_before 零三角函数、零 HashMap10-30x
    cluster_dbscan (正确性)初始邻居未入去重集合 → 同一点重复入队、簇膨胀、额外扩张clustered[] 统一归属/去重,初始邻居先标记再入队正确性 + 1.7x 速度
    spatial_join_nearestO(n·m) 双循环O(n·log m) 临时 R-tree50x
    spatial_join_withinO(n·m) 双循环O(n + k) bbox 候选集 + haversine 精炼50x
    spatial_join_knearestO(n·m·log m) 全距离排序O(n·log m) 临时 R-tree50x
    cluster_dbscanO(n²) 每个点全扫描邻居O(n·k) coarse-grid 范围查询10-100x
    grid 层HashMap[String] 每次插值 + 单网格HashMap[Int] + dual-grid 细(0.1°)/粗(0.5°) 自适应选择5-10x
    bulk_insert (insert 批量)每条目 HashMap 重复查找 + R-tree 逐步插入一次性构建 + R-tree 集中插入5-20x
    morans_i_knnO(n²) 全 n×n 距离矩阵O(n·k·log n) 仅 k 最近邻10x+ (大 n)
    find_neighborsO(n log n) 全扫描O(log n) R-tree 最近邻1000x+
    find_sorted_by_distanceO(n log n) 全扫描O(log n) R-tree 最近邻1000x+
    find_nearest_pairsO(n² log n²) 全对排序O(n log k) R-tree 分批100x+
    find_nearbyO(m log m) 全排序O(m log k) 堆维护大候选集显著
    compute_bboxO(n) 每次O(1) 缓存多次调用显著
    voronoiO(n³) 每次O(n³) 缓存2x+
    voronoi_neighborsO(n³·|a|·|b|)O(n³·(|a|+|b|))邻居查询显著

    Benchmark 实测(随机中国地址,单位:us。使用 bulk_insert 批量构建):

    操作1k10k扩展趋势实现
    构建 DB10,021134,586~O(n) ✓bulk_insert + dual-grid
    构建/条目10.0 μs13.5 μs近常数 ✓
    BBox 查询642O(log n) ✓adaptive fine/coarse/R-tree
    KNN (k=5)4384O(log n) ✓R-tree best-first
    Within 100km948亚线性 ✓unsorted bbox → haversine
    K-Means (k=3)4,380109,800O(n·iters·k)K-Means++ 初始化
    DBSCAN (eps=50km)7,372237,038O(n·k) 扩张clustered[] 去重队列
    TSP 2-opt49,548406,937O(n²·rounds),实测亚线性edge 数组 + 0 HashMap
    Moran's I KNN27,748634,763O(n·k·log n) ✓真 R-tree KNN + 行标准化
    Kriging (k=12)8,18440,882O(n·log n + k²)R-tree KNN + 变异函数

    运行命令:moon run cmd benchmark [N](默认 N=1000)

    缓存策略:
    • Index 层:compute_bbox 和 to_array 结果在 insert/remove 时失效
    • GeoDB 层:Voronoi 图结果在 insert/remove 时失效

    R-tree 索引:
    • 所有空间查询(find_nearest、find_neighbors、find_nearby、find_nearest_pairs)均通过 R-tree 加速
    • 使用 best-first 搜索 + 弦距下界剪枝,结果精确但只访问部分子树

    #模块架构

    moonbitGEODB/ ├── lib/ │ ├── types/ # 核心类型定义 (GeoPoint, Address, GeoEntry, BBox) │ ├── geo/ # 地理数学运算 (Haversine, Geohash, 坐标转换, 凸包, 多边形) │ ├── parser/ # 地址解析器 (多格式解析, 中文行政区划识别) │ ├── index/ # 空间索引 (网格索引, 名称索引) │ ├── persist/ # 持久化 (二进制编解码, 原子保存, 备份, FFI IO) │ ├── gen/ # 随机中国地址生成器 │ └── db/ # 主数据库 API (GeoDB) └── cmd/ # 命令行接口

    #核心模块说明

    模块说明
    types定义 GeoPoint(地理坐标点)、Address(结构化地址)、GeoEntry(数据库条目)、BBox(边界框)等核心数据类型
    geo实现 Haversine 公式、Geohash 编解码、WGS84↔GCJ-02 坐标转换、凸包计算、Voronoi 图(泰森多边形)、Moran's I 空间自相关、Getis-Ord Gi* 热点分析、LISA 局部聚类、多边形面积、点-in-多边形、径向密度等地理运算
    parser支持逗号分隔、制表符分隔、中文行政区划等多种地址格式的解析;核心模块 address_match.mbt 实现语义地址匹配器 — 25 维中间特征提取 + LightGBM 集成(60 棵树投票),在 96,423 对真实中文地址上 Agreement 75.90%,可对数据库内地址做模糊语义检索
    index网格空间索引加速 BBox 查询;R-tree(rtree.mbt)提供亚线性的 k 最近邻与范围查询;基于 HashMap 的名称/标签/地址索引
    persist二进制编解码(codec.mbt)、GeoEntry 序列化(binary.mbt)、C FFI IO(persist_native.mbt)、原子写入与备份
    gen包含 347 个中国城市(含真实坐标)、街道名、门牌号的随机地址生成器
    db提供完整的数据库操作 API:增删改查、空间查询、标签聚合、持久化、备份恢复、高级分析

    #构建与运行

    #环境要求

    • MoonBit 编译器(已在 moon 0.1.20260920 / moonc v0.10.14 上验证,moon build 零警告零错误)
    • GCC 编译器(用于 C FFI 链接)
    • Linux/Unix 环境(用于文件系统操作)

    #构建

    # 设置链接器路径(解决 pthread 和 libc 找不到的问题) export LIBRARY_PATH=/usr/lib64 # 强制使用 GCC 而非 tcc(推荐) export MOON_CC=gcc # 构建项目 moon build # 运行测试 moon test

    #CLI 命令参考

    moon run cmd <address> 解析并存储地址 moon run cmd search <keyword> 按名称关键词搜索 moon run cmd fuzzy <kw> [max_dist] 模糊搜索(Levenshtein 距离) moon run cmd nearby <lat> <lng> [km] 附近查找 moon run cmd within <lat> <lng> <km> 地理围栏查询 moon run cmd tag <tag> 按标签查找 moon run cmd tags 列出所有标签 moon run cmd tag-all <t1> [t2 ...] 多标签 AND 查询 moon run cmd tag-any <t1> [t2 ...] 多标签 OR 查询 moon run cmd province <province> 按省份查找 moon run cmd city <city> 按城市查找 moon run cmd district <district> 按区县查找 moon run cmd stats 地理统计 moon run cmd export [path] 导出 CSV moon run cmd to-json [path] 导出 JSON moon run cmd to-geojson [path] 导出 GeoJSON moon run cmd add-tag <id> <tag> 添加标签 moon run cmd remove-tag <id> <tag> 删除标签 moon run cmd rename <id> <name> 重命名 moon run cmd update-coords <id> <lat> <lng> 更新坐标 moon run cmd seed [n] 生成随机地址(默认 100 条) moon run cmd save <path> 保存数据库 moon run cmd load <path> 加载数据库 moon run cmd backup <path> 创建备份 moon run cmd check <path> 完整性检查 moon run cmd info <path> 查看数据库信息 moon run cmd pattern <pattern> 通配符搜索 moon run cmd distance <id1> <id2> 两点距离 moon run cmd neighbors <id> [k] k 个最近邻 moon run cmd nearest-pairs [k] k 对最近点 moon run cmd districts [province] 区县分布 moon run cmd tag-count <tag> 标签计数 moon run cmd tag-pairs 标签共现对 moon run cmd centroid <tag> 加权质心 moon run cmd import-csv <csv_str> CSV 导入 moon run cmd import-geojson <gj_str> GeoJSON 导入 moon run cmd duplicates [dist] [edit] 重复检测 moon run cmd cluster [cell_size_km] 网格聚类 moon run cmd matrix <id1> [id2 ...] 距离矩阵 moon run cmd reverse-geocode <lat> <lng> 反向地理编码 moon run cmd dbscan [eps_km] [min_points] DBSCAN 聚类(默认 50km / 3 点) moon run cmd polygon <lat1>,<lng1> <lat2>,<lng2> ... 多边形操作 moon run cmd geohash-neighbors <hash> [precision] Geohash 邻域 moon run cmd radial-density <lat> <lng> <km> <rings> 径向密度 moon run cmd to-geojson-bbox [path] 带 BBox 的 GeoJSON moon run cmd distance-histogram [bucket_size_km] 距离直方图 moon run cmd coord-histogram lat|lng [bucket_size] 坐标直方图 moon run cmd distance-summary 距离统计摘要 moon run cmd kmeans <k> [max_iter] K-Means 聚类 moon run cmd elbow [max_k] Elbow 方法选 k moon run cmd centroid-all 全局质心 + 地理中心 moon run cmd geographic-center 球面地理中心 moon run cmd bbox-query <min_lat> <max_lat> <min_lng> <max_lng> 边界框查询 moon run cmd moran-i Moran's I 空间自相关 moon run cmd sde 标准差椭圆 moon run cmd tsp <start_id> TSP 最近邻路由 moon run cmd tsp-2opt <start_id> TSP + 2-opt 优化(再省 3-10%) moon run cmd idw <lat> <lng> [power] IDW 空间插值(反距离加权) moon run cmd concave-hull <alpha> 凹包(Alpha-Shape),alpha=0 → 凸包 moon run cmd benchmark [N] 性能基准测试(默认 1000) moon run cmd demo 一键演示(K-Means + SDE + TSP + IDW + 凹包) moon run cmd moran-i-knn Moran's I(KNN 权重,比 O(n²) 快) moon run cmd voronoi Voronoi 图(泰森多边形) moon run cmd voronoi-cell <id> 单个条目的 Voronoi 多边形 moon run cmd voronoi-neighbors <id> 条目的 Voronoi 邻居 moon run cmd delaunay Delaunay 三角剖分 moon run cmd delaunay-edges Delaunay 边(唯一) moon run cmd morans-i-lat 全局 Moran's I(纬度) moon run cmd morans-i-lng 全局 Moran's I(经度) moon run cmd hotspot-lat Getis-Ord Gi* 热点/冷点(纬度) moon run cmd hotspot-lng Getis-Ord Gi* 热点/冷点(经度) moon run cmd lisa-lat 局部 Moran's I LISA(纬度) moon run cmd lisa-lng 局部 Moran's I LISA(经度) moon run cmd kriging <lat> <lng> [k] Kriging 克里金插值 moon run cmd clear 清空所有条目 moon run cmd help 显示帮助 moon run cmd addr-match <q> <c> 语义地址匹配(查询 vs 候选) moon run cmd addr-eval <path> [N] 评测地址匹配器(JSONL 语料) moon run cmd addr-feat-dump <path> 导出 25 维特征 TSV(用于 sklearn 重训) moon run cmd corpus-db [N] 从真实地址语料构建空间 DB

    #CLI 使用示例

    #基础数据库操作

    # 运行默认演示 moon run cmd # 解析并存储地址 moon run cmd "北京市海淀区颐和园路5号" # 搜索关键字 moon run cmd search "颐和园" # 模糊搜索(最多 2 次编辑距离) moon run cmd fuzzy "Yuan" 2 # 附近查找(5 公里内) moon run cmd nearby 39.9 116.4 5 # 地理围栏查询 moon run cmd within 39.9 116.4 10 # 多标签 AND 查询 moon run cmd tag-all "poi" "attraction" # 按省份查找 moon run cmd province "北京市" # 生成 500 条随机地址 moon run cmd seed 500 # DBSCAN 聚类(1km 半径,最少 2 点成簇) moon run cmd dbscan 1.0 2 # 多边形查询 moon run cmd polygon 0,0 0,1 1,1 1,0 # Geohash 邻域查询 moon run cmd geohash-neighbors wx4g0d 6 # 径向密度分析 moon run cmd radial-density 39.9 116.4 1.0 10 # 保存/加载数据库 moon run cmd save geo.db moon run cmd load geo.db # 完整性检查 moon run cmd check geo.db # 数据导出 moon run cmd export output.csv moon run cmd to-geojson output.json

    #语义地址匹配(真实 CLI 输出示例)

    # Exact — suffix 统一化(幢↔栋、层↔楼 自动折叠) $ moon run cmd addr-match \ "北京市海淀区中关村南大街27号中央民族大学2号楼" \ "北京市海淀区中关村南大街27号中央民族大学2栋" level: 完全匹配 score: 0.84 dice: 0.97 road: 1 num: 0.5 # Exact — 完整 POI + 门牌号 完全一致 $ moon run cmd addr-match \ "陕西省西安市雁塔区小寨路街道长安中路100号赛高国际" \ "陕西省西安市雁塔区小寨路街道长安中路100号赛高国际" level: 完全匹配 score: 0.75 dice: 1.00 road: 1 num: 1 # Partial — 同 POI 但楼层/房间号不同(26楼 vs 26层A) $ moon run cmd addr-match \ "汉中路180号星汉大厦26楼江苏东方燃油有限公司" \ "汉中路180号星汉大厦26层A江苏东方燃油有限公司" level: 部分匹配 score: 0.84 dice: 0.97 road: 1 num: 1 # Partial — 同道路 + 同门牌段号,但建筑不同 $ moon run cmd addr-match \ "上海市浦东新区世纪大道100号环球金融中心" \ "上海市浦东新区世纪大道88号金茂大厦" level: 部分匹配 score: 0.56 dice: 0.67 road: 1 num: 0 # Partial — 同门牌号但跨城市同名 POI(LGBM 判 Partial 而非 None, # 因为 road_b=1 + feature_recall=1.0 + admin_hit=0,属于典型"地名歧义") $ moon run cmd addr-match \ "浙江省杭州市西湖区文三路90号东部软件园" \ "江苏省南京市玄武区文三路90号东部软件园" level: 部分匹配 score: 0.65 dice: 0.43 road: 1 num: 1 # None — 完全不相关 $ moon run cmd addr-match \ "北京市朝阳区建国门外大街1号国贸中心" \ "广州市天河区天河路228号正佳广场" level: 不匹配 score: 0.00 dice: 0.00 road: 0 num: 0

    #评测 + 调试

    # 完整评测:混淆矩阵 + 三分类 F1 $ moon run cmd addr-eval testdata/data.txt 0 === Address matcher evaluation === Records: 20000 pairs: 96423 Throughput: 18284 pairs/s Agreement: 75.90% (73187/96423) Confusion matrix (rows = gold, cols = pred): 完全匹配 部分匹配 不匹配 完全匹配 948 2902 2157 部分匹配 440 21305 10146 不匹配 253 7338 50934 # 导出 25 维特征 TSV + score + gold,供 sklearn 重训 $ moon run cmd addr-feat-dump testdata/data.txt | head -3 dice road_b feat name admin_hit road_hit ... score gold 0.296 0.5 0 0 1 0 ... 0.204 2 0.143 0.5 0 0 0 0 ... 0.125 2

    #编程 API

    // 创建内存数据库
    let db = @db.GeoDB::in_memory()

    // 插入条目(自动解析地址字符串)
    let entry = db.insert_raw(
    "bj-yiheyuan",
    "颐和园",
    39.9999,
    116.2755,
    "北京市海淀区颐和园路5号"
    )

    // 批量插入(比循环 insert 快 5-20 倍,50k 仅 ~1.1s)
    db.bulk_insert([entry1, entry2, entry3])

    // 按 ID 查找
    match db.get("bj-yiheyuan") {
    Some(e) => println(e.name)
    None => println("not found")
    }

    // 按名称查找
    let results = db.find_by_name("颐和园")

    // 按关键字模糊搜索
    let results = db.find_name_contains("海淀")

    // 附近查找(5 公里内)
    let nearby = db.find_nearby(
    @types.GeoPoint::make(39.9999, 116.2755),
    radius_km=5.0,
    limit=10
    )

    // DBSCAN 密度聚类(50km 邻域,至少 3 点成簇)
    let clusters = db.cluster_dbscan(50.0, 3)

    // Geohash 前缀查询
    let results = db.find_by_geohash_prefix("wx4g0d", precision=6)

    // 路径距离计算
    let d = db.path_distance(["id1", "id2", "id3"])

    // 分页查询
    let page = db.page_all(limit=10, offset=0)

    // 地址标准化
    let ok = db.normalize_address("bj-yiheyuan")

    // 保存到文件
    ignore(db.save_to("geo.db"))

    // 从文件加载
    let loaded = @db.GeoDB::open("geo.db")

    #高级 API 使用

    // Geohash 编码/解码
    let gh = @geo.geohash_encode(39.9087, 116.3975, 6)
    let pt = @geo.geohash_decode(gh)

    // Geohash 邻域(3×3 网格)
    let neighbors = @geo.geohash_neighbors(gh)

    // 坐标转换(WGS84 → GCJ-02)
    let (gcj_lng, gcj_lat) = @geo.wgs84_to_gcj02(116.4, 39.9)

    // 凸包计算
    let hull = @geo.convex_hull(points)

    // 多边形面积(km²)
    let area = @geo.polygon_area(polygon_points)

    // 点在多边形内测试
    let inside = @geo.point_in_polygon(point, polygon)

    // 平均最近邻距离
    let avg_nn = @geo.average_nn_distance(points)

    // Voronoi 图(泰森多边形)
    let cells = @geo.voronoi_diagram(points, bbox)

    // 单个条目的 Voronoi 多边形
    let poly = db.voronoi_cell("id1")

    // Voronoi 邻居(共享边的条目)
    let neighbors = db.voronoi_neighbors("id1")

    // Delaunay 三角剖分(Voronoi 对偶)
    let tris = db.delaunay() // 返回三角形索引数组

    // Delaunay 边(唯一)
    let edges = db.delaunay_edges()

    // 空间自相关:Moran's I 全局聚集度检验(纬度)
    let moran = db.morans_i_on_lat()

    // 热点分析:Getis-Ord Gi* 热点/冷点检测(经度)
    let hotspots = db.getis_ord(vals, k = 5, threshold = 1.96)

    // LISA:局部 Moran's I 聚类类型(HH/LL/HL/LH)
    let lisa = db.lisa(vals, k = 5, threshold = 1.96)

    // 重复数据检测
    let groups = db.find_duplicates(max_distance_km=0.1, max_edit_dist=2)

    // 空间网格聚类
    let clusters = db.cluster_by_grid(cell_size_km=1.0)

    // 距离矩阵
    let matrix = db.distance_matrix(["id1", "id2", "id3"])

    // 径向密度分析
    let rings = db.radial_density(center, ring_width_km=1.0, num_rings=10)

    // 反向地理编码
    match db.reverse_geocode(39.9, 116.4) {
    Some((entry, dist)) => println("Nearest: " + entry.name)
    None => println("Not found")
    }

    // 性能优化示例:高效查询(O(log n) 通过 R-tree)
    // 查找最近 10 个邻居(避免 O(n) 全扫描)
    let neighbors = db.find_neighbors(target.location, 10)

    // 查找半径内条目(堆维护,避免 O(m log m) 全排序)
    let nearby = db.find_nearby(center, 5.0, limit=20)

    // 查找最近点对(避免 O(n²) 全对计算)
    let pairs = db.find_nearest_pairs(k=5)

    #语义地址匹配 API

    // 基础用法 — 直接拿 MatchResult
    let q = "汉中路180号星汉大厦26楼江苏东方燃油有限公司"
    let c = "汉中路180号星汉大厦26层A江苏东方燃油有限公司"
    let result = @parser.match_address(q, c)

    match result.level {
    Exact => println("完全匹配,score=" + result.score.to_string())
    Partial => println("部分匹配,dice=" + result.dice.to_string())
    None => println("不匹配")
    }
    // result.dice — 标准化字符串的 Dice 系数
    // result.feature_recall, road_recall, num_recall — 三个手工核心指标

    // 带特征导出 — 拿 MatchResult + FeatureBundle (25 维中间特征)
    let (result2, feats) = @parser.match_address_ex(q, c)

    // feats.dice, feats.road_b, feats.feature_recall, feats.admin_hit,
    // feats.road_hit, feats.foreign_city, feats.feat_overlap,
    // feats.house_missing, feats.veto, feats.main_hit ... 全部可读

    // 地址文本标准化(公开 API)
    let norm = @parser.normalize_address_text("北京市 海淀区 颐和园路5号 ")
    // → "北京市海淀区颐和园路5号"(全角→半角、去标点、trim)

    // MatchLevel 枚举工具
    Exact.rank() // 2
    Partial.rank() // 1
    None.rank() // 0
    MatchLevel::from_label("完全匹配") // → Exact

    #随机数据生成

    // 生成 100 条随机中国地址
    let entries = @gen.gen_batch(100)

    // 预填充数据库
    let db = @gen.gen_db(500)

    // 也可以通过 CLI 生成并持久化
    // moon run cmd seed 500 # 自动保存到 testdata/geo_seeded.db

    #持久化设计

    #二进制文件格式

    数据库采用纯二进制格式存储,由以下部分组成:

    +------------------+ | File Header | 魔数 ("GEO1") + 版本号 + 条目数 + 创建/更新时间戳 +------------------+ | Entry Block 0 | 条目长度 + CRC32 + 序列化数据 | Entry Block 1 | ... +------------------+ | ... | +------------------+

    #测试

    # 运行所有测试 moon test

    测试覆盖(339 个测试):
    • 类型构造与序列化
    • 地理距离与 BBox 运算
    • K-Means / SDE / Moran's I / TSP / 2-opt / IDW / Concave Hull / Kriging 高级算法
    • Geohash 编解码与邻域查询
    • WGS84 ↔ GCJ-02 坐标转换
    • 凸包计算与多边形面积
    • DBSCAN 密度聚类
    • 径向密度分析
    • 地址解析(中文、英文、多格式)
    • 语义地址匹配(中文地址三层分类器)
    • 空间索引(R-tree + grid)与名称索引
    • 数据库 CRUD 操作
    • 标签查询与聚合
    • 地理统计与分布
    • 持久化与恢复
    • 随机地址生成器
    • 变异函数建模与普通克里金插值

    #测试数据

    #1. 内置精选演示数据集(61 个真实 POI)

    moon run cmd demo 使用 build_sample_db() 内置 61 个真实中国地标 POI,按 13 个城市分组,天然具有城市级聚类结构,可直接验证 K-Means / DBSCAN / TSP / Moran's I:

    城市POI 数代表地标
    北京11故宫、天安门、天坛、颐和园、八达岭、鸟巢、水立方、798
    上海8东方明珠、外滩、豫园、南京路、迪士尼、上海中心
    广州5广州塔、陈家祠、越秀公园、白云山、沙面
    深圳5平安金融中心、世界之窗、欢乐谷、莲花山、大梅沙
    成都5宽窄巷子、锦里、武侯祠、熊猫基地、都江堰
    重庆5解放碑、洪崖洞、南山、磁器口、武隆
    杭州4西湖、灵隐寺、西溪湿地、千岛湖
    南京3中山陵、夫子庙、南京博物院
    武汉3黄鹤楼、东湖、武汉大学
    西安3大雁塔、兵马俑、西安城墙
    天津3天津之眼、古文化街、五大道
    青岛3栈桥、崂山、八大关
    昆明3滇池、翠湖、西山

    每条数据都包含 id(城市前缀-地标拼音)、中文名、精确经纬度、完整中文地址(精确到省/市/区/街道/门牌号)。

    无需指定簇数,DBSCAN 可自动还原城市结构(3 个噪声点为距市中心 >50km 的千岛湖、武隆天生三桥、都江堰):

    $ moon run cmd dbscan 50.0 3 === DBSCAN (eps=50km, min_points=3) === Cluster #1 (3 POIs): # 武汉 Cluster #2 (11 POIs): # 北京 ... Cluster #13 (3 POIs): # 昆明 Clusters: 13 Noise: 3 # 58 + 3 = 61,全部点都有归属判定 Noise points: - hz-qiandao (千岛湖) - cq-wulong (武隆天生三桥) - cd-dujiangyan (都江堰景区)

    #2. 随机地址生成器(347 城)

    内置随机地址生成器覆盖 347 个中国主要城市(含直辖市、省会、地级市、台湾地区、港澳特别行政区),每个城市配备真实行政区划(区/县),可生成大规模性能测试数据:

    类别数量示例
    城市347北京、上海、广州、深圳、成都、武汉、西安、杭州、南京、重庆、天津、青岛、郑州、长沙、昆明、厦门、珠海、洛阳、保定、西宁、桂林、三亚、包头、徐州、南通、盐城、扬州、泰州、潍坊、临沂、济宁、德州、淄博、威海、泰安、滨州、菏泽、聊城、东营、襄阳、宜昌、荆州、十堰、黄冈、孝感、咸宁、恩施、株洲、湘潭、衡阳、邵阳、岳阳、常德、张家界、益阳、郴州、永州、怀化、娄底、柳州、梧州、北海、玉林、钦州、鞍山、抚顺、本溪、吉林、四平、齐齐哈尔、大庆、锦州、营口、辽阳、盘锦、铁岭、朝阳、葫芦岛、牡丹江、佳木斯、绥化、大同、运城、临汾、长治、咸阳、宝鸡、渭南、汉中、绵阳、德阳、宜宾、泸州、南充、丽江、大理、玉溪、曲靖、遵义、六盘水、安顺、芜湖、蚌埠、阜阳、马鞍山、安庆、赣州、九江、上饶、宜春、邯郸、廊坊、沧州、开封、新乡、南阳、商丘、长春、通化、延边、鸡西、吴忠、石嘴山、日喀则、林芝、昌都、那曲、海东、眉山、资阳、凉山、达州、广安、巴中、雅安、普洱、临沧、德宏、怒江、迪庆、红河、文山、楚雄、崇左、防城港、贵港、贺州、河池、来宾、朔州、阳泉、忻州、吕梁、晋中、榆林、延安、安康、商洛、毕节、铜仁、昭通、保山、巢湖、池州、宣城、新余、抚州、鹰潭、景德镇、萍乡、鹤壁、济源、三门峡、漯河、信阳、周口、驻马店、随州、仙桃、潜江、天门、神农架、儋州、文昌、万宁、东方、五指山、琼海、乐东、陵水、白沙、昌江、澄迈、临高、定安、屯昌、伊春、黑河、大兴安岭、莱州、滕州、玉树、果洛、海北、黄南、阿里、山南、吐鲁番、哈密、克拉玛依、阿克苏、喀什、和田、伊宁、塔城、阿勒泰、石河子、阿拉尔、图木舒克、北屯、双河、高雄、台北、台中、台南、基隆、新竹、嘉义、彰化、云林、南投、屏东、宜兰、花莲、台东、澎湖、金门、连江、香港、澳门、南沙、天水、庆阳、平凉、酒泉、张掖、武威、定西、陇南、临夏、甘南、嘉兴、湖州、绍兴、金华、衢州、台州、丽水、漳州、三明、南平、龙岩、宁德、惠州、汕头、江门、湛江、茂名、肇庆、揭阳、梅州、清远、潮州、云浮、张家口、承德、衡水、邢台、秦皇岛、辽源、白山、松原、白城、鄂尔多斯、呼伦贝尔、巴彦淖尔、乌兰察布、兴安盟、锡林郭勒、阿拉善盟、固原、中卫、亳州、宿州、六安、镇江、淮安、连云港、宿迁、莆田、黔东南、黔南、黔西南、铜川、渭南、延安、榆林、汉中、安康、商洛、海北、黄南、拉萨、日喀则、林芝、昌都、那曲、阿里、山南、吉林、四平、长春、通化、延边、石家庄、唐山、保定、邯郸、廊坊、沧州、广州、深圳、佛山、东莞、珠海、中山、乌鲁木齐、克拉玛依、吐鲁番、哈密、阿克苏、喀什、和田、伊宁、塔城、阿勒泰、石河子、阿拉尔、图木舒克、五家渠、北屯、双河、昆玉、西宁、海东、西安、铜川、宝鸡、咸阳、渭南、延安、汉中、榆林、安康、商洛、兰州、嘉峪关、金昌、白银、天水、武威、张掖、平凉、酒泉、庆阳、定西、陇南、临夏、甘南等
    行政区划每城市 4-10 个海淀区、朝阳区、天河区、锦江区、武侯区等
    地标/POI 名称100+颐和园、故宫、西湖、兵马俑、鼓浪屿、泰山、张家界等
    街道名称50+中山路、人民路、解放路、幸福路等

    运行 moon run cmd seed 1000 可快速生成 1000 条随机中国地址用于测试。

    #3. 真实地址评测语料库(20k 条)

    testdata/data.txt 是一份包含 20,000 条真实中文地址查询 的标注语料库,每行 1 个 JSONL 记录,每条 query 配有 5 个人工标注的候选地址(完全匹配/部分匹配/不匹配),总共约 96k 对 query-candidate 对。用于:

    • 评测语义地址匹配器:moon run cmd addr-eval testdata/data.txt 0 输出混淆矩阵 + 各分类 precision/recall/F1
    • 构建真实空间 DB:moon run cmd corpus-db 500 取前 500 条记录,自动提取城市坐标,DBSCAN 聚类持久化到 testdata/geo_corpus.db
    • 匹配器调优:通过 addr-feat-dump 导出 FeatureBundle 中间特征后重训 LightGBM(旧手工规则阈值已随规则链一并移除),再观察 addr-eval 效果

    统计量数值
    query 数量20,000
    候选地址对96,423
    整体 Agreement75.90% (73,187/96,423)
    完全匹配 F124.79% (Prec 57.8%, Rec 15.8%)
    部分匹配 F167.17% (Prec 67.5%, Rec 66.8%)
    不匹配 F183.66% (Prec 80.5%, Rec 87.0%)
    吞吐量~19,000 pairs/s
    混淆矩阵3×3 (Exact 948 / Partial 21,305 / None 50,934 正确)

    评测命令末尾自动输出三类典型错误样本 (gold=部分→None、gold=部分→完全、gold=不→完全) 供阈值调优参考。

    #FeatureBundle 25 维中间特征

    手工规则 OR-of-16 无法捕捉非线性特征交互(如 road_b(0.23-0.50) + feat(≥0.30) 应判 Partial 但规则漏判)。FeatureBundle 把所有中间计算结果导出给 sklearn/LightGBM:

    #字段类型含义LGBM 重要性
    0diceDouble标准化字符串 Dice 系数(2A∩B/(|A|+|B|))0.12
    1road_bDouble道路 token B 分数(匹配 + 加权)0.36(最重要)
    2feature_recallDoublePOI/建筑名召回率0.24
    3name_recallDouble公司/机构名召回率0.04
    4admin_hitInt行政区划 hit 数(省/市/区/街道)—
    5road_hitInt道路是否命中(0/1)—
    6town_hitInt乡镇/街道是否命中—
    7context_okInt上下文门控(admin + road 联合 OK)—
    8house_recallDouble门牌号召回率0.04
    9house_conflictInt门牌号冲突(同 road 不同 num → 1)—
    10vetoInt硬 veto(道路冲突 + POI 不重叠 → 必 None)—
    11q_structuredIntquery 是否含完整结构(省市区)—
    12c_structuredIntcandidate 是否含完整结构—
    13feat_overlapIntPOI 子串重叠(3+ chars 跨 token)0.04
    14weak_core_exactInt弱核心词精确匹配—
    15fuzzy_poi_overlapInt模糊 POI 重叠(Dice ≥ 0.6)—
    16reverse_poi_overlapInt反向 POI 子串(candidate→query)—
    17road_conflictInt道路显式冲突(不同道路名同时存在)—
    18main_hitInt主干道 + 门牌号 + POI 三项核心全命中—
    19house_missingInt一方有门牌号另一方没有—
    20foreign_cityInt双方属于不同城市(跨城标志)—
    21q_roadsIntquery 中道路 token 数—
    22c_roadsIntcandidate 中道路 token 数—
    23q_featsIntquery 中 POI token 数—
    24c_featsIntcandidate 中 POI token 数—
    25scoreDouble手工规则输出的加权总分(仅作为调试参考)—

    特征重要性 Top 3:road_b (36%) → feature_recall (24%) → dice (12%)。这三个特征占 LightGBM 决策的 72%,而手工规则几乎不使用 road_b 的中间分数——这是 ML 能提升 7%+ 的核心原因。

    #LightGBM 集成架构

    25 维 FeatureBundle [Double/Int] │ ▼ ┌──────────────────────────────┐ │ 60 棵决策树(纯 MoonBit if-else)│ │ n_estimators=20 × 3 classes │ │ max_depth=6, min_child_samples=50 │ │ tree_idx → class: tree_idx % 3 │ └──────────────────────────────┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ tree0 tree1 tree2 tree3 ... tree59 │ │ │ │ │ s0+L s1+L s2+L s0+L s2+L │ │ │ │ │ └─────┴─────┴─────┴────────────┘ │ ▼ ┌──────────────────────────────┐ │ s0, s1, s2 (累加 raw score) │ │ argmax(s0, s1, s2) │ └──────────────────────────────┘ │ │ │ class0 class1 class2 │ │ │ Exact Partial None

    每棵树遍历 root → leaf,leaf 保存一个 raw score(LightGBM 的 leaf value)。同一类的 20 棵树的 leaf value 累加到 s0/s1/s2,最终 argmax 决定 MatchLevel。

    关键设计:
    • tree_idx % 3 映射:LightGBM 原生用 "交替并行树"(num_parallel_tree=3),第 i 棵树专属于第 i % 3 个类。实测 Python MoonBit 逐行一致(diff=0.0)
    • 纯 MoonBit if-else:每棵树被展开为嵌套 if-else 比较,无运行时依赖。60 棵树 / 1860 个 leaf → 约 7500 行代码
    • 吞吐:~19,000 pairs/s(规则单用 ~24k,LGBM 慢 ~24%,换 +6.79% 准确性)

    #优化历程

    前三轮优化将 Agreement 从 66.98% 提升到 75.90%(+8.92%,多判对 6,553 对):

    第一轮(阈值调优):加强 E1 上下文门控、E2 dice 0.15→0.35、P7 新增 house_conflict 分支、降低 P1b/P6/P7 dice 阈值。消除了 1,018 个 P→Exact 误报。Agreement → 68.37% (+1.39%)。

    第二轮(结构改进 + Tokenizer 修复):
    • normalize_address_text 新增 suffix 统一化:幢→栋、层→楼、弄→号、座→栋
    • floor_ok 严格化:query 有楼层/房间号而 candidate 没有时不再判 Exact
    • POI 子串匹配增强 feat_overlap:跨 token 边界的 3+ chars core 匹配
    • Tokenizer greedy scan via codes_to_string_free 修复:跳过已消费的数字 span,不再把 "25栋2楼" 吞进 POI 长 token
    • Partial TP 从 14,485 升至 17,022(+17%)
    • Grid search 调优:P6→0.41, P8→0.36。Agreement → 69.11% (+2.13%)。

    第三轮(ML 集成,最大跃升 +6.79%):
    • FeatureBundle:25 维中间特征导出给外部分类器(dice, road_b, feature, name, admin_hit, road_hit, town_hit, context_ok, house, house_conflict, veto, q_structured, c_structured, feat_overlap, weak_core_exact, fuzzy_poi_overlap, reverse_poi_overlap, road_conflict, main_hit, house_missing, foreign_city, q/c_roads, q/c_feats 等)
    • sklearn Decision Tree 基线:depth=20, 999 节点,full-data 75.89%。手工规则 OR-of-16 完全无法捕捉 road_b(0.23-0.50) + feat(≥0.30) 这种非线性特征交互(road_b 特征重要性 34%,手工规则几乎不使用)
    • LightGBM 最终方案:n_estimators=20, max_depth=6, lr=0.3, min_child_samples=50, 60 棵树,7527 行 MoonBit if-else 代码。5-split honest test 75.21% ± 0.41%,比单树稳定 +0.79%
    • Full-data 最终 Agreement 75.90%(LGBM 75.90 vs 单树 75.89 微涨,但泛化更好)

    第四轮(规则引擎下线,零行为变化):LightGBM 集成落地后,手工规则的输出(exact/partial)实际已不参与最终决策。本轮删除整条手工规则链(E1–E7、P1–P9)及仅服务于规则的中间变量、辅助函数与阈值常量(standalone_dice_threshold/context_dice_threshold 等),match_address 直接输出 60 树集成结果。Agreement 维持 75.90%(73,187/96,423),339 个测试全部通过;同时适配新版 MoonBit 工具链,构建零警告。

    Honest train/test split 对比:

    模型Holdout Accuracy实现复杂度
    手工 OR-of-16-rules~69%2000 行启发式
    最佳单 Decision Tree (depth 20)74.41% ± 0.29%1996 行 if-else
    LightGBM (20×3, d=6, lr=0.3)75.21% ± 0.41%7527 行 if-else
    Random Forest (500, d=15)76.23%500+ 棵树,不可嵌入
    XGBoost / LightGBM (大集成)76.6%1000+ 棵树,不可嵌入

    #许可证

    Apache-2.0