ZingLix Blog

这里是 ZingLix 的个人博客,与你一起在计算机的世界探索~

马上订阅 ZingLix Blog RSS 更新: https://zinglix.xyz/feed.xml

Immich 反向地理编码原理和汉化思路

2025年1月23日 08:00
Immich开源

Immich 默认识别出来的照片位置都奇奇怪怪的,不仅仅是英文,还有一些不常见的名字,在照片分类搜索的时候非常麻烦。周末仔细研究了下 Immich 到底是怎么实现反向地理编码的,并想办法对其进行了汉化。

如果你到这里,是为了实现地名汉化的话,请直接前往 这个项目

Immich 反向地理编码工作原理

为了能够实现汉化的目标,首先我们得先明白 Immich 是怎么在本地实现反向地理编码的。

反向编码

以下以 v1.124.2 为例,Immich 的反向地理编码都实现在 reverseGeocode 这个函数中,传入的是一个 GeoPoint 对象,实际上就是经度和纬度。

之后,根据经纬度,进行了如下的 SQL 查询

1
2
3
4
5
6
7
8
9
10
11
SELECT *
FROM geodata_places
WHERE 
    earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), 25000) 
    @> ll_to_earth_public(latitude, longitude)
ORDER BY 
    earth_distance(
        ll_to_earth_public(${point.latitude}, ${point.longitude}), 
        ll_to_earth_public(latitude, longitude)
    )
LIMIT 1;

这其中

  • earth_box 创建一个以给定点为中心的球体范围
  • ll_to_earth_public 将地理坐标 (纬度和经度) 转换为三维球体上的点

WHERE 子句筛选出 距离输入的目标点 25,000 米(25 公里)范围内 的地理点,ORDER BY 子句根据距离从近到远排序。换句话说,就是找到了 geodata_places 库中,距离输入点最近的地理点。

找到了最近的点之后,取出这个点的 { countryCode, name: city, admin1Name },也就是 国家码名称一级行政区名称。整理一下顺序,将国家码转换成国家名,这就对应了我们在 Immich 中看到的照片位置中的 三级。至于这个表是如何构建的,后面我们再单独分析。

这里名称和一级行政区名称都是直接从数据库表中得到的,而国家名是从国家码转换得到的,这里用到了 node-i18n-iso-countries 这个库的 getName 方法。但在 Immich 中,调用时的代码是 getName(countryCode, 'en'),将语言用 'en' 写死了,所以只能是英文,并没有加上任何 i18n 的机制。

而如果上面没有找到的话,就会再进行一次 SQL 查询

1
2
3
4
SELECT *
FROM naturalearth_countries
WHERE coordinates @> point(:longitude, :latitude)
LIMIT 1;

这段 SQL 就是在 naturalearth_countries 表中找到哪些记录的 coordinates 包含输入的坐标,也就是根据自然地球中国家的划分,确定坐标所在的国家。如果走到这一条,则不会再去确定更细粒度的省市两级划分。

简而言之,Immich 就是在数据库里事先准备好了大量地名,然后用照片的坐标去匹配数据库里最近的地名,之后就以该地名作为照片的地名。找不到的话,就退化到只用国家信息,根据国家的区划划分。

数据构建

接下来的一个大问题就是,数据库里的数据是从哪来的。

Immich 所有的反向地理编码数据都来的