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 所有的反向地理编码数据都来的 剩余内容已隐藏