本文想要实现的功能很久之前就有想法了,但网上的资料太少,就没花太多时间研究这个,一直搁置到现在。前两天突然发现有个大佬实现了我当初预期的功能,尝试了他的方法后觉得还可以接着改进,于是就有了这篇文章。
我很喜欢旅行,旅行过程中自然会有很多照片,如何管理照片就是很大的问题。之前尝试很多方法后,我认为将照片与地图结合会是一种很直观方法。我用的小米手机有类似的功能,但要想使用此功能就只能使用小米手机,绑定死了硬件平台,不够灵活。Obsidian Leaflet 是 Obsidian 的一个插件,可以将在线地图或图片引入 ob 中,设置标记点,并将标记点与 md 文件或照片绑定。插件默认用的是 OpenStreetMap,国内用的话加载会比较慢,细节信息也会缺失,所以最好还是用国内地图。具体设置请看大佬的文章,我就不再赘述了。
本文改进的地方如下:
- 批量创建每张照片对应的标记点文件
- 自动创建旅行记录文件和文件夹
- 采用取平均的方式自动设置地图中心点的经纬度
使用本文脚本之前需要做的工作
- python 相关
- 安装 Python3.6 及以上版本
- 安装如下 Python 模块,安装命令
pip install 模块名
- exifread
- requests
- obsidian 相关
- 安装 obsidian leaflet 插件
- 按照大佬文章中的方法修改缩放限制
- 脚本相关
- 申请一个高德地图的 KEY,方法见大佬的文章
- 将脚本中的路径和 KEY 替换成你自己的路径和 KEY
- 照片相关
- 拍照时打开 GPS 定位
- 手机向电脑发送照片文件时关闭默认抹除照片位置的设置
- 将照片与脚本放在同一目录下
完成上述设置后,执行本文的脚本就可以生成旅行记录的完整文件夹,将文件夹复制到你的 ob 库的指定路径下,将照片移动到 ob 库中即可。
脚本的完整代码如下:
# coding=utf-8
import exifread
import json
import urllib.request
import requests
import os
gaodeconvert_enable = 1
gaodeapi_key = "换成你的高德KEY"
gaodeapi_sitehead = "https://restapi.amap.com/v3/assistant/coordinate/convert?locations="
global_lat = 0
global_long = 0
global_imgNum = 0
#获取经纬度
def getLatOrLng(refKey, tudeKey, tags):
if refKey not in tags:
return None
ref=tags[refKey].printable
LatOrLng=tags[tudeKey].printable[1:-1].replace(" ","").replace("/",",").split(",")
LatOrLng=float(LatOrLng[0])+float(LatOrLng[1])/60+float(LatOrLng[2])/float(LatOrLng[3])/3600
if refKey == 'GPS GPSLatitudeRef' and tags[refKey].printable != "N":
LatOrLng=LatOrLng*(-1)
if refKey == 'GPS GPSLongitudeRef' and tags[refKey].printable != "E":
LatOrLng=LatOrLng*(-1)
return LatOrLng
#为照片创建md文件
def saveAsMD(imageName,travelFolder_path):
global global_lat
global global_long
f = open(imageName, 'rb')
tags = exifread.process_file(f)
lat = getLatOrLng('GPS GPSLatitudeRef','GPS GPSLatitude',tags)
lng = getLatOrLng('GPS GPSLongitudeRef','GPS GPSLongitude',tags)
if lat==None or lng==None:
print("no gps")
exit()
gaodeapi_site = gaodeapi_sitehead + str(lng)+","+str(lat)+"&coordsys=gps&output=json&key="+gaodeapi_key
response = requests.get(gaodeapi_site)
locations = response.json()['locations']
locations_list = locations.split(',')
gn=locations_list[1]+','+locations_list[0]
global_lat = global_lat + float(locations_list[1])
global_long = global_long + float(locations_list[0])
output_file_name = "{:.10f},{:.10f}.md".format(lat, lng)
output_file_path = os.path.join(travelFolder_path,"markers",output_file_name)
with open(output_file_path, "w", encoding="utf-8") as markdown_file:
markdown_file.write("---\n")
markdown_file.write("mapmarker: default\n")
markdown_file.write("date: {}\n".format(tags['EXIF DateTimeOriginal']))
markdown_file.write("device: {} {}\n".format(str(tags['Image Make']), str(tags['Image Model'])))
markdown_file.write("place: \n")
markdown_file.write('gps: [{},{}]\n'.format(lat, lng))
markdown_file.write("gn: [{}]\n".format(gn))
if gaodeconvert_enable == 1:
markdown_file.write("location: [{}]\n".format(gn))
else:
markdown_file.write("location: [{},{}]\n".format(lat, lng))
markdown_file.write("---\n")
markdown_file.write("![["+imageName+']]\n')
#为每个照片创建标记点文件
def CreateMarkers(travelFolder_path):
global global_imgNum
jpg_files = []
for file in os.listdir('.'):
if file.endswith('.jpg'):
jpg_files.append(file)
global_imgNum = global_imgNum + 1
for file in jpg_files:
saveAsMD(file,travelFolder_path)
print("执行本脚本前务必保证所有照片都在脚本所在目录下,且所有照片都有位置信息\n")
print("执行完成后,将照片和生成的文件夹移入ob库中即可\n")
travel_name = input("输入本次旅行的名称\n")
date_id = input("输入旅行日期(YYYYMMDD)\n")
os.mkdir(travel_name)
travelMD_path = os.path.join(travel_name,travel_name)
markers_path = os.path.join(travel_name,"markers")
os.mkdir(markers_path)
CreateMarkers(travel_name)
global_lat = global_lat / global_imgNum
global_long = global_long / global_imgNum
with open(travelMD_path+".md", "w", encoding="utf-8") as markdown_file:
markdown_file.write("\n")
markdown_file.write("```leaflet\n")
markdown_file.write("id: {}\n".format(date_id))
markdown_file.write("osmLayer: false\n")
markdown_file.write("tileServer: http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}\n")
markdown_file.write("tileSubdomains: [\"1\", \"2\", \"3\", \"4\"]\n")
markdown_file.write(f"lat: {global_lat:.6f}\n")
markdown_file.write(f"long: {global_long:.6f}\n")
markdown_file.write("height: 800px\n")
markdown_file.write("width: 100%\n")
markdown_file.write("defaultZoom: 16\n")
markdown_file.write("maxzoom: 18\n")
markdown_file.write("minzoom: 1\n")
markdown_file.write("unit: meters\n")
markdown_file.write("scale: 1\n")
markdown_file.write("markerFolder: 换成你的路径/{}/markers\n".format(travel_name))
markdown_file.write("```\n")
效果如图所示:
2024-05-11更新:我将写好的脚本分享给了朋友,朋友在此脚本的基础上用pyQt做了一个GUI版的小程序,用起来更加直观方便,需要的可以通过邮件联系我:1165011707@qq.com
2024-12-03更新:我用Fyne框架重构了之前写的那个GUI版的小程序,新增了更多功能,包括编辑YAML属性、压缩图片等,详情见下面这篇 blog。