摘要※
本文提供了一种hack方式,用于自定义Nextcloud Memories app中地图页面的tile server。实现效果如下:

图中的地图图源已经从原本的OpenStreetMap(OSM)被替换成了天地图。
Memories App介绍※
Memories是Nextcloud的一个应用,可以在应用商店直接安装。其主要功能是可以用多种方式(时间线、文件夹、专辑、人物、地图等)展示Nextcloud中存储的照片。因为采取了不同的索引方式,所以,其对大量的图片表现极佳。
Memories Map更换tile server※
Map是Memories的一个功能,可以根据照片EXIF中存储的GPS信息,把照片标记在地图上。这是一个很棒的功能。但是,其使用的地图是OSM。如果特定地区访问OSM有困难,用户可能会希望替换加载地图的tile server。遗憾的是,memories官方并没有提供修改途径,任何现有的资源也没有找到解决办法。本文提供了一种hack方式,用于自定义tile server。
基本的思路是,虽然官方没有提供自定义方法,但这是一个web服务,不涉及编译到二进制文件,所以我们总是可以通过替换源文件url的方式来实现。
在什么地方修改※
可以在Memories源码中搜索“.openstreetmap.”,发现只有4个文件包含了相关链接。涉及到tile server的主要有两个。
lib/Controller/PageController.php:
$policy->addAllowedConnectDomain('data:');
// Allow OSM
$policy->addAllowedFrameDomain('www.openstreetmap.org');
$addImageDomain('https://tile.openstreetmap.org');
$addImageDomain('https://*.a.ssl.fastly.net');
$policy->addAllowedConnectDomain('nominatim.openstreetmap.org');src/components/top-matter/MapSplitMatter.vue:
import 'leaflet/dist/leaflet.css';
import 'leaflet-edgebuffer';
const OSM_TILE_URL = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
const OSM_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
// CSS transition time for zooming in/out cluster animation第二个文件MapSplitMatter.vue中出现的是tile server的地址;第一个文件中出现的是相关的安全策略,允许特定域名的跨域请求。
如何修改※
下方以把openstreetmap图源替换为arcgisonline为例。关于该选择什么地图源,见文章最下方的讨论。
注意:因为要修改源码,所以在修改前,推荐对原文件进行备份,避免操作失误导致插件失效。
更改tile server地址※
Vue项目编译后,原本的MapSplitMatter.vue会产生两个文件,都要进行替换:
把/var/www/html/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js和/var/www/html/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js.map中的'https://tile.openstreetmap.org/{z}/{x}/{y}.png'替换成'https://server.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}.png'。
示例命令如下:
# 替换 .js 文件
sed -i 's|https://tile.openstreetmap.org/{z}/{x}/{y}.png|https://server.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}.png|g' memories-components_top-matter_MapSplitMatter_vue.js
# 替换 .js.map 文件
sed -i 's|https://tile.openstreetmap.org/{z}/{x}/{y}.png|https://server.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}.png|g' memories-components_top-matter_MapSplitMatter_vue.js.map也可以在交互式编辑器中手动进行替换。
更改安全策略※
在/var/www/html/custom_apps/memories/lib/Controller/PageController.php中如下位置(约91行)
// Allow OSM
$policy->addAllowedFrameDomain('www.openstreetmap.org');
$addImageDomain('https://tile.openstreetmap.org');
$addImageDomain('https://*.a.ssl.fastly.net');添加$addImageDomain('https://server.arcgisonline.com');。
更改service worker※
仅进行上述两项修改,在memories map界面,按F12打开开发者工具,右键浏览器左上角刷新图标,选择”清空缓存并硬性重新加载“,可以看到新的地图源被正确的加载了进来。
但是别高兴的太早!当你重新点击刷新后,会发现又回到了旧的地图源。通过一些排查,我发现这是一个缓存问题。浏览器试图在使用旧版本的MapSplitMatter_vue.js。一开始,我以为和我的nginx设置有关,后来发现这个问题来自于service worker。
Service worker是现代web应用起核心作用的浏览器技术,可以实现一些离线优先的体验和高级缓存策略。找到service_worker.js(/var/www/html/custom_apps/memories/js/memories-service-worker.js),发现里面有一个很长的缓存列表(源文件是单行,这里为了阅读体验,进行了格式化):
const ie = [
...
{
'revision': '71e436917cb50dff6a23a7fbff4a2b70',
'url': '/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js.map?v=446bfb0d6731968034b0'
}, {
'revision': null,
'url': '/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js?v=446bfb0d6731968034b0'
},
...
];通过修改revision字段,破坏缓存:
const ie = [
...
{
'revision': 'hacked_by_me_1_' + Date.now(),
'url': '/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js.map?v=446bfb0d6731968034b0'
}, {
'revision': 'hacked_by_me_2_' + Date.now(),
'url': '/custom_apps/memories/js/memories-components_top-matter_MapSplitMatter_vue.js?v=446bfb0d6731968034b0'
},
...
];注意:Date.now()生成的时间戳会随每次修改变化,若多次修改可能导致缓存混乱。更稳妥的方式是修改revision为固定自定义字符串。
问题解决!
我该选取什么地图源※
如果你的图片在原本的OpenStreetMap上显示是正常的,说明照片 EXIF 中的 GPS 信息(WGS84 坐标系,EPSG:4326)可被 OSM 兼容,替换的 tile server 需支持 EPSG:3857 投影坐标系(即OSM 采用的 Web 墨卡托投影)。
上面的教程中,是以arcgisonline为例。这个图源的zoom level只能到13级,继续放大后会显示”Map data not yet available“。
还可以选择天地图,图源免费使用,且国内加载极快。但是需要注册并申请密钥,且矢量图层和注解图层需要分别请求,导致需要修改更多的代码(本文不做介绍,但欢迎讨论)。仅按照本文方法进行修改,最终可以正常加载矢量街道图层,但因为没有注解图层,所以没有街道名显示。
如果大家有更多国内可访问的EPSG:3857地图源推荐,欢迎在评论区讨论。
在测试地图源的过程中,推荐大家使用开源软件QGIS,用于快速验证tile server的坐标系,最大zoom level,加载速度等等。
一些说明※
本文方法的局限※
本文方法的核心内容是对url进行替换,在memories应用升级后,所有更改的文件会被还原。届时需要重新替换。可以考虑写脚本进行替换。
环境※
Nextcloud:Nextcloud Hub 10 (31.0.10),Docker Compose安装。
Memories:7.7.0 (git hash: 5ebd498)