
authentik 地理数据包 goauthentik/geo 解析hexworld 底图瓦片归档的构建期生成器【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik本文以packages/geo包为主线讲解 authentik 事件地图events map所依赖的 hexworld 六边形底图归档PMTiles是如何在构建期生成的。读完你将掌握该包的定位与「构建期工具、不进入浏览器」的边界、它与运行时地图元素的契约设计、三档 H3 分辨率缩放带的原理、归档的重新生成流程以及品牌级运行时覆盖自定义瓦片服务器的用法。包的定位构建期机器零浏览器代码goauthentik/geo是一个纯构建期工具包其职责是把 Natural Earth 矢量数据和 Protomaps planet 数据切片转换为tiles/hexworld.pmtiles归档再由web构建流程把该归档拷贝进前端产物。它不向浏览器发送任何运行时代码。从 package.json 可以看到包的完整脚本面hexworld:build→node scripts/build-hexworld.ts运行归档生成器tiles:pull-hexworld→node scripts/pull-hexworld.ts拉取/拷贝已发布的归档与字体到tiles/test→vitest runNode 环境单测lint:types→ 对src、scripts、test三处执行tsc --noEmit类型检查。运行环境要求 Node ≥ 24engines与devEngines均做了约束。运行时依赖仅两个h3-jsH3 六边形网格计算与pmtiles读取 PMTiles 归档mapbox/vector-tile、pbf、types/geojson等则用于解码 MVT 瓦片与处理 GeoJSON。实际的地图元素与样式位于web/src/elements/maps/与归档生成器分离——这是「构建期 vs 运行时」职责划分的直接体现。与运行时地图的契约三份共享文件归档与绘制它的地图元素之间必须对齐三件事因此这三件事由运行时元素拥有定义权本包只导入、不重新声明契约项拥有方Zoom → H3 分辨率分段缩放带web/src/elements/maps/bands.ts标签种类与显示缩放级别web/src/elements/maps/labels.ts版权署名attribution字符串web/src/elements/maps/attribution.ts这三份文件刻意保持零依赖。原因正如 README 所解释的如果从样式模块导入会把 MapLibre 等重依赖拖进 Node 构建脚本。依赖方向是单向的——构建工具读取应用的契约反向绝不成立。以缩放带为例bands.ts 定义了全局唯一的HEX_BANDSexport const HEX_BANDS: readonly HexBand[] [ { res: 3, minzoom: 0, maxzoom: 2 }, { res: 4, minzoom: 3, maxzoom: 6 }, { res: 5, minzoom: 7, maxzoom: 7 }, ];而署名串定义在 attribution.tsexport const HEXWORLD_ATTRIBUTION © OpenStreetMap (labels) · Natural Earth;生成器会把同样的字符串盖进 PMTiles 元数据metadata里。值得注意的是该文件注释特别强调「纯文本设计」airgap 测试禁止样式内出现外部 URL地图上仅需 ODbL 文本署名真正的链接由文档页承载。三档缩放带Zoom bands的几何测算构建时归档使用三个 H3 分辨率。README 中给出的测量值通过 h3 的getHexagonEdgeLengthAvg与getHexagonAreaAvg计算得出——这里的「单元格宽度」指对顶点到对顶点的距离约为边长的 2 倍Zoom 范围H3 分辨率边长单元格宽度单元格面积z0–z23~69 km~138 km~12,393 km²z3–z64~26 km~52 km~1,770 km²z75~10 km~20 km~253 km²z7 是归档的maxzoomMapLibre 在其之上进行 overzoom超采样放大。运行时通过bandForZoom将缩放级别钳制到 res-5 分段保证事件在更高缩放级别下仍按 res-5 分箱而不会掉落到最粗的分辨率——见 bands.ts 的实现注释export function bandForZoom(zoom: number): HexBand { // 越过 MAX_BAND_ZOOM 后地图展示的是该分段的 overzoom 瓦片 // 事件必须解析到该分辨率而不是落到最粗的一档。 const z Math.floor(Math.max(0, Math.min(MAX_BAND_ZOOM, zoom))); return HEX_BANDS.find((band) z band.minzoom z band.maxzoom) ?? HEX_BANDS[0]!; }关键约束缩放带被烘焙进每一个已发布的归档。修改HEX_BANDS会使已有瓦片失效因此它被固化在运行时契约中任何变更都需要同步重建并重新发布归档。已发布的归档内容tiles/hexworld.pmtiles被提交进仓库这是当前detail版本约 22 MiB全球完整的 res-3/res-4 网格外加限定在人口密集区 detail 区域内的 res-5 覆盖层。tiles/fonts/随归档一并发布 Latin Noto Sans 字形Regular Medium 两种字重0-255 与 256-511 两个 Unicode 范围采用 SIL Open Font License 1.1许可文本见 tiles/fonts/OFL.txt。归档与字形均提交入库目的是让 web 构建保持密封性hermetic——克隆后无需联网。web构建会把tiles/hexworld.pmtiles和tiles/fonts/拷贝到web/dist/assets/maps/。若构建时缺失任一文件构建会显式失败而不是静默产出坏掉的地图。detail-zone人口密集区的 res-5 覆盖逻辑res-5 覆盖层不是全球铺开的而是由 src/detail-zone.ts 计算出的「人口密集区」每个高于minPop的locality标签以其所在 res-4 单元格为中心用gridDisk(ring)向外扩展一圈所有圆环的并集构成baseCellsres-4 级再对每个 res-4 单元格做七子展开cellToChildren得到 res-5 的detailCells。实现刻意把区域基准放在 res-4 而非 res-5在 z7–z8 时区域外由 res-4 单元格作为底填区域内叠加 res-5 子单元二者面积上是精确的 7 倍父子关系即使顶点互相交错。detailCellsForRes5负责把 res-5 陆地单元与区域求交只输出区域内的 res-5 单元作为覆盖层。生成器的输入与流水线生成器入口为scripts/build-hexworld.ts由 package.json 的hexworld:build指向。前置条件Node ≥ 24且已安装本 workspacepnpm installtippecanoe与go-pmtiles在PATH上一份本地 PMTiles planet 数据解出 z0–8Protomaps planet 构建的约 1–3 GB 切片。构建命令# 仅预览 shell 流水线不实际执行 pnpm run hexworld:build -- --dry-run --out tiles # 完整运行——同时产出两种尺寸的切片 pnpm run hexworld:build -- --dump ./planet-z8.pmtiles --out tiles # tiles/hexworld-detail.pmtiles ← res 3 4 分区 res 5随包发布 # tiles/hexworld-plain.pmtiles ← 仅 res 3 4更小、更粗糙生成器首次运行时会下载的输入被钉死在特定版本保证一年后重跑仍能产出同样的瓦片Natural Earth 矢量数据nvkelso/natural-earth-vectorv5.1.2。其中ne_50m_land.geojson提供陆地多边形ne_50m_admin_0_countries.geojson提供国家归属州/省界来自ne_10m_admin_1_states_provinces.geojson因为 50m 的 admin-1 数据集只覆盖 9 个国家Protomaps planet 构建20260521——标签层的来源。当前随包归档正是从该构建切出若想收录新的地名需对更新的构建重新生成。流水线的源码级拆解README 概括了流水线而 packages/geo/src 下的模块给出了每个环节的具体算法陆地单元化land fillsrc/land.ts 的landCells用 h3 的polygonToCellsExperimental(..., containmentCenter, true)把陆地多边形转成 H3 单元。前置处理normalizePolygon做了两件事把纬度钳制到 Web Mercator 上限 ±85.051129超出即离图h3 在极点附近也不稳定对经度跨度超过 180° 的环如南极洲的 -180..180 多边形在 -60°、60° 两条经线上做 Sutherland–Hodgman 裁剪切成三个子 180° 的条带。单元格多边形输出由 src/cells.ts 的cellPolygon完成它把跨越反经线antimeridian的边界平移进连续的 0..360 范围避免下游出图时产生横跨全球的细长碎片——test/cells.test.ts 里两个用例分别验证了「闭合环」与「不跨越反经线」。国家/区域归属country assignmentsrc/countries.ts 先从 Natural Earth FeatureCollection 构建可检索索引buildCountryIndex/buildRegionIndex每条记录预计算 bbox 用于快速排除再对每个单元格质心做点-多边形包含判断射线投射法支持外环内洞。国家代码依次尝试ISO_A2/iso_a2、ISO_A3/iso_a3、ADM0_A3/adm0_a3区域代码则依次尝试iso_3166_2/ISO_3166_2、adm1_code/ADM1_CODE、code_hasc。质心落在任何多边形之外的单元格海洋或南极冰盖不会出现在结果中。边界提取borderssrc/borders.ts 的borderEdges遍历每个陆地单元及其 6 个邻居只要相邻两单元在最强适用级别上不同就沿共享 H3 边cellsToDirectedEdgedirectedEdgeToBoundary产出边界要素国家不同 →level: 0国家边界优先国家相同但区域不同 →level: 1区域边界仅在两国代码一致时发出邻居非陆地 → 在提供land集合时发出level: 0海岸线边保证每个国家都被水域完整围合。 每个无序单元对只会产生一条要素按两个单元 ID 的字典序规范化去重携带的level属性让运行时样式能从同一 source-layer 用不同线宽渲染国界与区界。aCell/bCell仅存在于内存要素中用于下游区域过滤交给 tippecanoe 前会被剥掉以减小瓦片体积。标签归一化labelssrc/labels.ts 从 dump 的places层读取标签。关键设计是国家/区域的显示缩放由 hexworld 自己决定Protomaps 的min_zoom是为其高密度底图调校的比如中国在 dump 里是min_zoom: 6会埋没大国所以COUNTRY_ZOOM_TIERS按人口分档重写国家标签的起始缩放——人口 ≥1 亿从 z0 显示、≥2000 万 z1、≥500 万 z2其余 z3区域标签则取运行时契约里的LABEL_MIN_ZOOM.region本地地物保留 dump 的min_zoom。此外还有dedupePlaces同一地物在 min_zoom 到 z8 的每张瓦片重复出现按 kindnameres-5 单元聚合成一点、保留最早 minZoom与capLocalities按人口排序截断本地地物数量防止标签爆炸。切片与合并上述要素交给 tippecanoe 和 tile-join 产出 PMTiles。两种尺寸的切片总是成对生成最终选哪种由人工把关——在 pmtiles 查看器里检查两者后再按发布预算决定。重新生成并发布归档AUTHENTIK_HEXWORLD_SOURCE/path/to/hexworld-detail.pmtiles \ pnpm run tiles:pull-hexworld git add tiles/hexworld.pmtiles git committiles:pull-hexworld由 scripts/pull-hexworld.ts 实现若AUTHENTIK_HEXWORLD_SOURCE指向本地路径则直接拷贝文件已就位时跳过否则按默认 URL 拉取随后从 Protomaps 的资源地址抓取Noto Sans Regular/Noto Sans Medium的 0-255、256-511 两个字形段到tiles/fonts/。运行时覆盖品牌级自定义瓦片服务器归档不是事件地图的唯一数据源。一个品牌Brand可以把事件地图指向自己的瓦片服务器System Brands Map tiles设置这会完全绕过本归档——相关逻辑见 web/src/elements/maps/basemap-style.ts。这意味着 hexworld 归档是「开箱即用的默认底图」而多租户/多品牌场景下可以通过品牌配置接入私有地图服务为事件地理可视化提供自定义外观与数据源。测试覆盖本包的测试直接对应流水线的核心算法pnpm run test # Vitest仅 Node 环境 pnpm run lint:types # tsc 检查 src、scripts、test覆盖范围包括陆地填充land、边界提取borders、国家归属countries、detail 区域detail-zone与标签归一化labels的数学逻辑测试文件位于 packages/geo/test。地图元素自身的测试则与元素同住位于web/test/unit/maps/与web/test/component/——这再次印证了「构建期工具测试与运行时组件测试」的分层边界。【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考